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

Commit 692a376

Browse files
committed
1.5.9
* Added beta support for Dictionaries. Should work fine for simple dictionaries, may be janky or broken for more complex ones (eg. Dicts nested inside a Dict). * Fixed a bug with Lists of primitive values.
1 parent 9cb1cea commit 692a376

File tree

6 files changed

+294
-49
lines changed

6 files changed

+294
-49
lines changed

src/CachedObjects/CacheObjectBase.cs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,11 @@ private static CacheObjectBase GetCacheObjectImpl(object obj, MemberInfo memberI
115115
{
116116
CacheObjectBase holder;
117117

118+
// This is pretty ugly, could probably make a cleaner implementation.
119+
// However, the only cleaner ways I can think of are slower and probably not worth it.
120+
121+
// Note: the order is somewhat important.
122+
118123
if (memberInfo is MethodInfo mi)
119124
{
120125
if (CacheMethod.CanEvaluate(mi))
@@ -154,14 +159,15 @@ private static CacheObjectBase GetCacheObjectImpl(object obj, MemberInfo memberI
154159
{
155160
holder = new CacheRect();
156161
}
157-
else if (ReflectionHelpers.IsArray(valueType) || ReflectionHelpers.IsList(valueType))
158-
{
159-
holder = new CacheList();
160-
}
162+
// must check this before IsEnumerable
161163
else if (ReflectionHelpers.IsDictionary(valueType))
162164
{
163165
holder = new CacheDictionary();
164166
}
167+
else if (ReflectionHelpers.IsEnumerable(valueType) || ReflectionHelpers.IsCppList(valueType))
168+
{
169+
holder = new CacheList();
170+
}
165171
else
166172
{
167173
holder = new CacheOther();
Lines changed: 249 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,278 @@
11
using System;
2+
using System.Collections;
23
using System.Collections.Generic;
34
using System.Linq;
45
using System.Text;
56
using System.Threading.Tasks;
67
using MelonLoader;
78
using UnityEngine;
9+
using System.Reflection;
810

911
namespace Explorer
1012
{
1113
public class CacheDictionary : CacheObjectBase
1214
{
15+
public bool IsExpanded { get; set; }
16+
public PageHelper Pages = new PageHelper();
1317

18+
public float WhiteSpace = 215f;
19+
public float ButtonWidthOffset = 290f;
1420

15-
public override void Init()
21+
private CacheObjectBase[] m_cachedKeys;
22+
private CacheObjectBase[] m_cachedValues;
23+
24+
public Type TypeOfKeys
25+
{
26+
get
27+
{
28+
if (m_keysType == null) GetGenericArguments();
29+
return m_keysType;
30+
}
31+
}
32+
private Type m_keysType;
33+
34+
public Type TypeOfValues
35+
{
36+
get
37+
{
38+
if (m_valuesType == null) GetGenericArguments();
39+
return m_valuesType;
40+
}
41+
}
42+
private Type m_valuesType;
43+
44+
public IDictionary IDict
45+
{
46+
get => m_iDictionary ?? (m_iDictionary = Value as IDictionary) ?? Il2CppDictionaryToMono();
47+
set => m_iDictionary = value;
48+
}
49+
private IDictionary m_iDictionary;
50+
51+
// This is a bit janky due to Il2Cpp Dictionary not implementing IDictionary.
52+
private IDictionary Il2CppDictionaryToMono()
53+
{
54+
// note: "ValueType" is the Dictionary itself, TypeOfValues is the 'Dictionary.Values' type.
55+
56+
// make generic dictionary from key and value type
57+
var dict = (IDictionary)Activator.CreateInstance(typeof(Dictionary<,>)
58+
.MakeGenericType(TypeOfKeys, TypeOfValues));
59+
60+
// get keys and values
61+
var keys = ValueType.GetProperty("Keys") .GetValue(Value);
62+
var values = ValueType.GetProperty("Values").GetValue(Value);
63+
64+
// create a list to hold them
65+
var keyList = new List<object>();
66+
var valueList = new List<object>();
67+
68+
// get keys enumerator and store keys
69+
var keyEnumerator = keys.GetType().GetMethod("GetEnumerator").Invoke(keys, null);
70+
var keyCollectionType = keyEnumerator.GetType();
71+
var keyMoveNext = keyCollectionType.GetMethod("MoveNext");
72+
var keyCurrent = keyCollectionType.GetProperty("Current");
73+
while ((bool)keyMoveNext.Invoke(keyEnumerator, null))
74+
{
75+
keyList.Add(keyCurrent.GetValue(keyEnumerator));
76+
}
77+
78+
// get values enumerator and store values
79+
var valueEnumerator = values.GetType().GetMethod("GetEnumerator").Invoke(values, null);
80+
var valueCollectionType = valueEnumerator.GetType();
81+
var valueMoveNext = valueCollectionType.GetMethod("MoveNext");
82+
var valueCurrent = valueCollectionType.GetProperty("Current");
83+
while ((bool)valueMoveNext.Invoke(valueEnumerator, null))
84+
{
85+
valueList.Add(valueCurrent.GetValue(valueEnumerator));
86+
}
87+
88+
// finally iterate into actual dictionary
89+
for (int i = 0; i < keyList.Count; i++)
90+
{
91+
dict.Add(keyList[i], valueList[i]);
92+
}
93+
94+
return dict;
95+
}
96+
97+
// ========== Methods ==========
98+
99+
private void GetGenericArguments()
16100
{
17-
//base.Init();
101+
if (m_keysType == null || m_valuesType == null)
102+
{
103+
if (this.MemInfo != null)
104+
{
105+
Type memberType = null;
106+
switch (this.MemInfo.MemberType)
107+
{
108+
case MemberTypes.Field:
109+
memberType = (MemInfo as FieldInfo).FieldType;
110+
break;
111+
case MemberTypes.Property:
112+
memberType = (MemInfo as PropertyInfo).PropertyType;
113+
break;
114+
}
18115

19-
Value = "Unsupported";
116+
if (memberType != null && memberType.IsGenericType)
117+
{
118+
m_keysType = memberType.GetGenericArguments()[0];
119+
m_valuesType = memberType.GetGenericArguments()[1];
120+
}
121+
}
122+
else if (Value != null)
123+
{
124+
var type = Value.GetType();
125+
if (type.IsGenericType)
126+
{
127+
m_keysType = type.GetGenericArguments()[0];
128+
m_valuesType = type.GetGenericArguments()[1];
129+
}
130+
}
131+
}
132+
133+
return;
20134
}
21135

22136
public override void UpdateValue()
23137
{
24-
//base.UpdateValue();
138+
base.UpdateValue();
139+
140+
// reset
141+
IDict = null;
142+
143+
if (Value == null || IDict == null)
144+
{
145+
return;
146+
}
25147

148+
var keys = new List<CacheObjectBase>();
149+
foreach (var key in IDict.Keys)
150+
{
151+
var cache = GetCacheObject(key, TypeOfKeys);
152+
cache.UpdateValue();
153+
keys.Add(cache);
154+
}
26155

156+
var values = new List<CacheObjectBase>();
157+
foreach (var val in IDict.Values)
158+
{
159+
var cache = GetCacheObject(val, TypeOfValues);
160+
cache.UpdateValue();
161+
values.Add(cache);
162+
}
163+
164+
m_cachedKeys = keys.ToArray();
165+
m_cachedValues = values.ToArray();
27166
}
28167

168+
// ============= GUI Draw =============
169+
29170
public override void DrawValue(Rect window, float width)
30171
{
31-
GUILayout.Label("<color=red>Dictionary (unsupported)</color>", null);
172+
if (m_cachedKeys == null || m_cachedValues == null)
173+
{
174+
GUILayout.Label("Cached keys or values is null!", null);
175+
return;
176+
}
177+
178+
int count = m_cachedKeys.Length;
179+
180+
if (!IsExpanded)
181+
{
182+
if (GUILayout.Button("v", new GUILayoutOption[] { GUILayout.Width(25) }))
183+
{
184+
IsExpanded = true;
185+
}
186+
}
187+
else
188+
{
189+
if (GUILayout.Button("^", new GUILayoutOption[] { GUILayout.Width(25) }))
190+
{
191+
IsExpanded = false;
192+
}
193+
}
194+
195+
GUI.skin.button.alignment = TextAnchor.MiddleLeft;
196+
string btnLabel = $"<color=yellow>[{count}] Dictionary<{TypeOfKeys.FullName}, {TypeOfValues.FullName}></color>";
197+
if (GUILayout.Button(btnLabel, new GUILayoutOption[] { GUILayout.MaxWidth(window.width - ButtonWidthOffset) }))
198+
{
199+
WindowManager.InspectObject(Value, out bool _);
200+
}
201+
GUI.skin.button.alignment = TextAnchor.MiddleCenter;
202+
203+
GUILayout.Space(5);
204+
205+
if (IsExpanded)
206+
{
207+
float whitespace = WhiteSpace;
208+
if (whitespace > 0)
209+
{
210+
ClampLabelWidth(window, ref whitespace);
211+
}
212+
213+
Pages.ItemCount = count;
214+
215+
if (count > Pages.ItemsPerPage)
216+
{
217+
GUILayout.EndHorizontal();
218+
GUILayout.BeginHorizontal(null);
219+
220+
GUILayout.Space(whitespace);
221+
222+
Pages.CurrentPageLabel();
223+
224+
// prev/next page buttons
225+
if (GUILayout.Button("< Prev", new GUILayoutOption[] { GUILayout.Width(60) }))
226+
{
227+
Pages.TurnPage(Turn.Left);
228+
}
229+
if (GUILayout.Button("Next >", new GUILayoutOption[] { GUILayout.Width(60) }))
230+
{
231+
Pages.TurnPage(Turn.Right);
232+
}
233+
234+
Pages.DrawLimitInputArea();
235+
236+
GUILayout.Space(5);
237+
}
238+
239+
int offset = Pages.CalculateOffsetIndex();
240+
241+
for (int i = offset; i < offset + Pages.ItemsPerPage && i < count; i++)
242+
{
243+
var key = m_cachedKeys[i];
244+
var val = m_cachedValues[i];
245+
246+
//collapsing the BeginHorizontal called from ReflectionWindow.WindowFunction or previous array entry
247+
GUILayout.EndHorizontal();
248+
GUILayout.BeginHorizontal(null);
249+
250+
//GUILayout.Space(whitespace);
251+
252+
if (key == null || val == null)
253+
{
254+
GUILayout.Label($"[{i}] <i><color=grey>(null)</color></i>", null);
255+
}
256+
else
257+
{
258+
GUI.skin.label.alignment = TextAnchor.MiddleCenter;
259+
GUILayout.Label($"[{i}]", new GUILayoutOption[] { GUILayout.Width(30) });
260+
261+
GUILayout.BeginHorizontal(new GUILayoutOption[] { GUILayout.MinWidth((window.width / 3) - 60f) });
262+
GUILayout.Label("Key:", new GUILayoutOption[] { GUILayout.Width(40) });
263+
key.DrawValue(window, (window.width / 2) - 30f);
264+
GUILayout.EndHorizontal();
265+
266+
GUILayout.BeginHorizontal(null);
267+
GUILayout.Label("Value:", new GUILayoutOption[] { GUILayout.Width(40) });
268+
val.DrawValue(window, (window.width / 2) - 30f);
269+
GUILayout.EndHorizontal();
270+
}
271+
272+
}
273+
274+
GUI.skin.label.alignment = TextAnchor.UpperLeft;
275+
}
32276
}
33277
}
34278
}

src/CachedObjects/Object/CacheList.cs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@ public override void UpdateValue()
218218

219219
if (GetCacheObject(obj, t) is CacheObjectBase cached)
220220
{
221+
cached.UpdateValue();
221222
list.Add(cached);
222223
}
223224
else
@@ -262,7 +263,7 @@ public override void DrawValue(Rect window, float width)
262263
}
263264

264265
GUI.skin.button.alignment = TextAnchor.MiddleLeft;
265-
string btnLabel = "<color=yellow>[" + count + "] " + EntryType + "</color>";
266+
string btnLabel = "<color=yellow>[" + count + "] " + EntryType.FullName + "</color>";
266267
if (GUILayout.Button(btnLabel, new GUILayoutOption[] { GUILayout.MaxWidth(window.width - ButtonWidthOffset) }))
267268
{
268269
WindowManager.InspectObject(Value, out bool _);
@@ -305,12 +306,6 @@ public override void DrawValue(Rect window, float width)
305306
GUILayout.Space(5);
306307
}
307308

308-
//int offset = ArrayOffset * ArrayLimit;
309-
//if (offset >= count)
310-
//{
311-
// offset = 0;
312-
// ArrayOffset = 0;
313-
//}
314309
int offset = Pages.CalculateOffsetIndex();
315310

316311
for (int i = offset; i < offset + Pages.ItemsPerPage && i < count; i++)

src/CppExplorer.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections;
23
using System.Collections.Generic;
34
using System.Linq;
45
using System.Reflection;
@@ -12,7 +13,7 @@ namespace Explorer
1213
public class CppExplorer : MelonMod
1314
{
1415
public const string GUID = "com.sinai.cppexplorer";
15-
public const string VERSION = "1.5.8";
16+
public const string VERSION = "1.5.9";
1617
public const string AUTHOR = "Sinai";
1718

1819
public const string NAME = "CppExplorer"

0 commit comments

Comments
 (0)