Skip to content
This repository was archived by the owner on May 9, 2023. It is now read-only.

Commit d868819

Browse files
committed
1.4.7
* Added support for Il2Cpp IList objects * Improved support for Lists in general, they should now work better.
1 parent 30b48b1 commit d868819

File tree

4 files changed

+197
-69
lines changed

4 files changed

+197
-69
lines changed

src/CachedObjects/CacheList.cs

Lines changed: 185 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Collections.Generic;
44
using System.Linq;
55
using System.Reflection;
6+
using MelonLoader;
67
using UnityEngine;
78

89
namespace Explorer
@@ -16,74 +17,216 @@ public partial class CacheList : CacheObjectBase
1617
public float WhiteSpace = 215f;
1718
public float ButtonWidthOffset = 290f;
1819

20+
private CacheObjectBase[] m_cachedEntries;
21+
22+
// Type of Entries in the Array
1923
public Type EntryType
20-
{
21-
get
24+
{
25+
get => GetEntryType();
26+
set => m_entryType = value;
27+
}
28+
private Type m_entryType;
29+
30+
// Cached IEnumerable object
31+
public IEnumerable Enumerable
32+
{
33+
get => GetEnumerable();
34+
}
35+
private IEnumerable m_enumerable;
36+
37+
// Generic Type Definition for Lists
38+
public Type GenericTypeDef
39+
{
40+
get => GetGenericTypeDef();
41+
}
42+
private Type m_genericTypeDef;
43+
44+
// Cached ToArray method for Lists
45+
public MethodInfo GenericToArrayMethod
46+
{
47+
get => GetGenericToArrayMethod();
48+
}
49+
private MethodInfo m_genericToArray;
50+
51+
// Cached Item Property for ILists
52+
public PropertyInfo ItemProperty
53+
{
54+
get => GetItemProperty();
55+
}
56+
private PropertyInfo m_itemProperty;
57+
58+
// ========== Methods ==========
59+
60+
private IEnumerable GetEnumerable()
61+
{
62+
if (m_enumerable == null && Value != null)
2263
{
23-
if (m_entryType == null)
64+
m_enumerable = Value as IEnumerable ?? CastValueFromList();
65+
}
66+
return m_enumerable;
67+
}
68+
69+
private Type GetGenericTypeDef()
70+
{
71+
if (m_genericTypeDef == null && Value != null)
72+
{
73+
var type = Value.GetType();
74+
if (type.IsGenericType)
2475
{
25-
if (this.MemberInfo != null)
26-
{
27-
switch (this.MemberInfo.MemberType)
28-
{
29-
case MemberTypes.Field:
30-
m_entryType = (MemberInfo as FieldInfo).FieldType.GetGenericArguments()[0];
31-
break;
32-
case MemberTypes.Property:
33-
m_entryType = (MemberInfo as PropertyInfo).PropertyType.GetGenericArguments()[0];
34-
break;
35-
}
36-
}
37-
else if (Value != null)
38-
{
39-
m_entryType = Value.GetType().GetGenericArguments()[0];
40-
}
76+
m_genericTypeDef = type.GetGenericTypeDefinition();
4177
}
42-
return m_entryType;
4378
}
44-
set
79+
return m_genericTypeDef;
80+
}
81+
82+
private MethodInfo GetGenericToArrayMethod()
83+
{
84+
if (GenericTypeDef == null) return null;
85+
86+
if (m_genericToArray == null)
4587
{
46-
m_entryType = value;
47-
}
88+
m_genericToArray = GenericTypeDef
89+
.MakeGenericType(new Type[] { this.EntryType })
90+
.GetMethod("ToArray");
91+
}
92+
return m_genericToArray;
4893
}
49-
private Type m_entryType;
5094

51-
public IEnumerable Enumerable
95+
private PropertyInfo GetItemProperty()
96+
{
97+
if (m_itemProperty == null)
98+
{
99+
m_itemProperty = Value?.GetType().GetProperty("Item");
100+
}
101+
return m_itemProperty;
102+
}
103+
104+
private IEnumerable CastValueFromList()
52105
{
53-
get
106+
if (Value == null) return null;
107+
108+
if (GenericTypeDef == typeof(Il2CppSystem.Collections.Generic.List<>))
109+
{
110+
return (IEnumerable)GenericToArrayMethod?.Invoke(Value, new object[0]);
111+
}
112+
else
54113
{
55-
if (m_enumerable == null && Value != null)
114+
return CastFromIList();
115+
}
116+
}
117+
118+
private IList CastFromIList()
119+
{
120+
try
121+
{
122+
var genericType = typeof(List<>).MakeGenericType(new Type[] { this.EntryType });
123+
var list = (IList)Activator.CreateInstance(genericType);
124+
125+
for (int i = 0; ; i++)
56126
{
57-
m_enumerable = Value as IEnumerable ?? CastValueFromList();
127+
try
128+
{
129+
var itm = ItemProperty.GetValue(Value, new object[] { i });
130+
list.Add(itm);
131+
}
132+
catch { break; }
58133
}
59-
return m_enumerable;
134+
135+
return list;
136+
}
137+
catch (Exception e)
138+
{
139+
MelonLogger.Log("Exception casting IList to Array: " + e.GetType() + ", " + e.Message);
140+
return null;
60141
}
61142
}
62143

63-
private IEnumerable m_enumerable;
64-
private CacheObjectBase[] m_cachedEntries;
65-
66-
public MethodInfo GenericToArrayMethod
144+
private Type GetEntryType()
67145
{
68-
get
146+
if (m_entryType == null)
69147
{
70-
if (EntryType == null) return null;
148+
if (this.MemberInfo != null)
149+
{
150+
Type memberType = null;
151+
switch (this.MemberInfo.MemberType)
152+
{
153+
case MemberTypes.Field:
154+
memberType = (MemberInfo as FieldInfo).FieldType;
155+
break;
156+
case MemberTypes.Property:
157+
memberType = (MemberInfo as PropertyInfo).PropertyType;
158+
break;
159+
}
71160

72-
return m_genericToArray ??
73-
(m_genericToArray = typeof(Il2CppSystem.Collections.Generic.List<>)
74-
.MakeGenericType(new Type[] { this.EntryType })
75-
.GetMethod("ToArray"));
161+
if (memberType != null && memberType.IsGenericType)
162+
{
163+
m_entryType = memberType.GetGenericArguments()[0];
164+
}
165+
}
166+
else if (Value != null)
167+
{
168+
var type = Value.GetType();
169+
if (type.IsGenericType)
170+
{
171+
m_entryType = type.GetGenericArguments()[0];
172+
}
173+
}
76174
}
175+
176+
// IList probably won't be able to get any EntryType.
177+
if (m_entryType == null)
178+
{
179+
m_entryType = typeof(object);
180+
}
181+
182+
return m_entryType;
77183
}
78-
private MethodInfo m_genericToArray;
79184

80-
private IEnumerable CastValueFromList()
185+
public override void UpdateValue()
81186
{
82-
return (Value == null) ? null : (IEnumerable)GenericToArrayMethod?.Invoke(Value, new object[0]);
187+
base.UpdateValue();
188+
189+
if (Value == null)
190+
{
191+
return;
192+
}
193+
194+
var enumerator = Enumerable?.GetEnumerator();
195+
196+
if (enumerator == null)
197+
{
198+
return;
199+
}
200+
201+
var list = new List<CacheObjectBase>();
202+
while (enumerator.MoveNext())
203+
{
204+
var obj = enumerator.Current;
205+
var type = ReflectionHelpers.GetActualType(obj);
206+
207+
if (obj is Il2CppSystem.Object iObj)
208+
{
209+
obj = iObj.Il2CppCast(type);
210+
}
211+
212+
var cached = GetCacheObject(obj, null, null, type);
213+
cached.UpdateValue();
214+
215+
list.Add(cached);
216+
}
217+
m_cachedEntries = list.ToArray();
83218
}
84219

220+
// ============= GUI Draw =============
221+
85222
public override void DrawValue(Rect window, float width)
86223
{
224+
if (m_cachedEntries == null)
225+
{
226+
GUILayout.Label("m_cachedEntries is null!", null);
227+
return;
228+
}
229+
87230
int count = m_cachedEntries.Length;
88231

89232
if (!IsExpanded)
@@ -182,27 +325,5 @@ public override void DrawValue(Rect window, float width)
182325
GUI.skin.label.alignment = TextAnchor.UpperLeft;
183326
}
184327
}
185-
186-
/// <summary>
187-
/// Called only when the user presses the "Update" button, or if AutoUpdate is on.
188-
/// </summary>
189-
public override void UpdateValue()
190-
{
191-
base.UpdateValue();
192-
193-
if (Value == null) return;
194-
195-
var enumerator = Enumerable?.GetEnumerator();
196-
197-
if (enumerator == null) return;
198-
199-
var list = new List<CacheObjectBase>();
200-
while (enumerator.MoveNext())
201-
{
202-
list.Add(GetCacheObject(enumerator.Current, null, null, this.EntryType));
203-
}
204-
205-
m_cachedEntries = list.ToArray();
206-
}
207328
}
208329
}

src/CachedObjects/CacheMethod.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ public override void Init()
6363

6464
public override void UpdateValue()
6565
{
66-
base.UpdateValue();
66+
//base.UpdateValue();
6767
}
6868

6969
public override void DrawValue(Rect window, float width)

src/CppExplorer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ namespace Explorer
1212
public class CppExplorer : MelonMod
1313
{
1414
public const string GUID = "com.sinai.cppexplorer";
15-
public const string VERSION = "1.4.6";
15+
public const string VERSION = "1.4.7";
1616
public const string AUTHOR = "Sinai";
1717

1818
public const string NAME = "CppExplorer"

src/Helpers/ReflectionHelpers.cs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,16 @@ public static bool IsArray(Type t)
7979

8080
public static bool IsList(Type t)
8181
{
82-
return t.IsGenericType
83-
&& t.GetGenericTypeDefinition() is Type typeDef
84-
&& (typeDef.IsAssignableFrom(typeof(List<>)) || typeDef.IsAssignableFrom(typeof(Il2CppSystem.Collections.Generic.List<>)));
82+
if (t.IsGenericType)
83+
{
84+
return t.GetGenericTypeDefinition() is Type typeDef
85+
&& (typeDef.IsAssignableFrom(typeof(Il2CppSystem.Collections.Generic.List<>))
86+
|| typeDef.IsAssignableFrom(typeof(Il2CppSystem.Collections.Generic.IList<>)));
87+
}
88+
else
89+
{
90+
return t.IsAssignableFrom(typeof(Il2CppSystem.Collections.IList));
91+
}
8592
}
8693

8794
public static bool IsDictionary(Type t)

0 commit comments

Comments
 (0)