diff --git a/src/Core/Runtime/Il2Cpp/Il2CppReflection.cs b/src/Core/Runtime/Il2Cpp/Il2CppReflection.cs index 4af0405..caf7342 100644 --- a/src/Core/Runtime/Il2Cpp/Il2CppReflection.cs +++ b/src/Core/Runtime/Il2Cpp/Il2CppReflection.cs @@ -311,6 +311,8 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp return il2cppPtr != IntPtr.Zero; } + #region EXTERN CAST METHODS + // Extern C++ methods [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] public static extern bool il2cpp_class_is_assignable_from(IntPtr klass, IntPtr oklass); @@ -318,10 +320,13 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] public static extern IntPtr il2cpp_object_get_class(IntPtr obj); - public override bool IsString(object obj) - { - return obj is string || obj is Il2CppSystem.String; - } + #endregion + + // Strings + + private const string IL2CPP_STRING_FULLNAME = "Il2CppSystem.String"; + + public override bool IsString(object obj) => obj is string || obj.GetActualType().FullName == IL2CPP_STRING_FULLNAME; public override void BoxStringToType(ref object value, Type castTo) { diff --git a/src/Core/Tests/TestClass.cs b/src/Core/Tests/TestClass.cs index 3227552..c27c206 100644 --- a/src/Core/Tests/TestClass.cs +++ b/src/Core/Tests/TestClass.cs @@ -9,6 +9,9 @@ namespace UnityExplorer.Tests { public static class TestClass { + public static List AWritableList = new List { 1, 2, 3, 4, 5 }; + public static Dictionary AWritableDict = new Dictionary { { "one", 1 }, { "two", 2 } }; + public static IEnumerable ANestedList = new List>> { new List> @@ -86,11 +89,6 @@ namespace UnityExplorer.Tests ExplorerCore.Log("Test2 " + typeof(T).FullName); } - //private static void TestGenericMultiInterface() where T : IEnumerable, IList, ICollection - //{ - // ExplorerCore.Log("Test3 " + typeof(T).FullName); - //} - private static void TestComponent() where T : Component { ExplorerCore.Log("Test3 " + typeof(T).FullName); @@ -121,22 +119,23 @@ namespace UnityExplorer.Tests } #if CPP + public static List TestWritableBoxedList; + public static string testStringOne = "Test"; public static Il2CppSystem.Object testStringTwo = "string boxed as cpp object"; public static Il2CppSystem.String testStringThree = "string boxed as cpp string"; public static string nullString = null; - public static List boxedList; + public static List cppBoxedList; + public static UnhollowerBaseLib.Il2CppStructArray CppIntStructArray; + public static UnhollowerBaseLib.Il2CppStringArray CppStringArray; + public static UnhollowerBaseLib.Il2CppReferenceArray CppReferenceArray; - public static Il2CppSystem.Object boxedInt; - public static Il2CppSystem.Int32 cppint; + public static Il2CppSystem.Object cppBoxedInt; + public static Il2CppSystem.Int32 cppInt; - public static Il2CppSystem.Collections.Hashtable testHashset; - public static Il2CppSystem.Collections.Generic.List testList; + public static Il2CppSystem.Collections.Hashtable cppHashset; - //public static Il2CppSystem.Nullable NullableQuaternion; - //public static Il2CppSystem.Nullable NullableInt = new Il2CppSystem.Nullable(5); - //public static Il2CppSystem.Nullable NullableBool = new Il2CppSystem.Nullable(false); #endif static TestClass() @@ -145,24 +144,39 @@ namespace UnityExplorer.Tests BigList.Add(i.ToString()); #if CPP - //NullableQuaternion = new Il2CppSystem.Nullable(); - //NullableQuaternion.value = Quaternion.identity; + CppIntStructArray = new UnhollowerBaseLib.Il2CppStructArray(5); + CppIntStructArray[0] = 0; + CppIntStructArray[1] = 1; + CppIntStructArray[2] = 2; + CppIntStructArray[3] = 3; + CppIntStructArray[4] = 4; - boxedInt = new Il2CppSystem.Int32() { m_value = 5 }.BoxIl2CppObject(); - boxedList = new List(); - boxedList.Add((Il2CppSystem.String)"boxedString"); - boxedList.Add(new Il2CppSystem.Int32 { m_value = 5 }.BoxIl2CppObject()); - cppint = new Il2CppSystem.Int32 { m_value = 420 }; + CppStringArray = new UnhollowerBaseLib.Il2CppStringArray(2); + CppStringArray[0] = "hello, "; + CppStringArray[1] = "world!"; - testHashset = new Il2CppSystem.Collections.Hashtable(); - testHashset.Add("key1", "itemOne"); - testHashset.Add("key2", "itemTwo"); - testHashset.Add("key3", "itemThree"); + CppReferenceArray = new UnhollowerBaseLib.Il2CppReferenceArray(3); + CppReferenceArray[0] = new Il2CppSystem.Int32 { m_value = 5 }.BoxIl2CppObject(); + CppReferenceArray[1] = null; + CppReferenceArray[2] = (Il2CppSystem.String)"whats up"; + + cppBoxedInt = new Il2CppSystem.Int32() { m_value = 5 }.BoxIl2CppObject(); + cppBoxedList = new List(); + cppBoxedList.Add((Il2CppSystem.String)"boxedString"); + cppBoxedList.Add(new Il2CppSystem.Int32 { m_value = 5 }.BoxIl2CppObject()); + cppInt = new Il2CppSystem.Int32 { m_value = 420 }; + + TestWritableBoxedList = new List(); + TestWritableBoxedList.Add(new Il2CppSystem.Int32 { m_value = 1 }.BoxIl2CppObject()); + TestWritableBoxedList.Add(new Il2CppSystem.Int32 { m_value = 2 }.BoxIl2CppObject()); + TestWritableBoxedList.Add(new Il2CppSystem.Int32 { m_value = 3 }.BoxIl2CppObject()); + TestWritableBoxedList.Add(new Il2CppSystem.Int32 { m_value = 4 }.BoxIl2CppObject()); + + cppHashset = new Il2CppSystem.Collections.Hashtable(); + cppHashset.Add("key1", "itemOne"); + cppHashset.Add("key2", "itemTwo"); + cppHashset.Add("key3", "itemThree"); - testList = new Il2CppSystem.Collections.Generic.List(3); - testList.Add("One"); - testList.Add("Two"); - testList.Add("Three"); #endif } } diff --git a/src/UI/CacheObject/CacheKeyValuePair.cs b/src/UI/CacheObject/CacheKeyValuePair.cs index 38e4eb4..0012964 100644 --- a/src/UI/CacheObject/CacheKeyValuePair.cs +++ b/src/UI/CacheObject/CacheKeyValuePair.cs @@ -26,7 +26,7 @@ namespace UnityExplorer.UI.CacheObject public override bool ShouldAutoEvaluate => true; public override bool HasArguments => false; - public override bool CanWrite => false; // TODO Parent.CanWrite; + public override bool CanWrite => Owner.CanWrite; public void SetDictOwner(InteractiveDictionary dict, int index) { @@ -84,7 +84,7 @@ namespace UnityExplorer.UI.CacheObject public override void TrySetUserValue(object value) { - throw new NotImplementedException("TODO"); + (Owner as InteractiveDictionary).TrySetValueToKey(DictKey, value, DictIndex); } diff --git a/src/UI/CacheObject/CacheListEntry.cs b/src/UI/CacheObject/CacheListEntry.cs index c24d414..be9c736 100644 --- a/src/UI/CacheObject/CacheListEntry.cs +++ b/src/UI/CacheObject/CacheListEntry.cs @@ -33,7 +33,7 @@ namespace UnityExplorer.UI.CacheObject public override void TrySetUserValue(object value) { - throw new NotImplementedException("TODO"); + (Owner as InteractiveList).TrySetValueToIndex(value, this.ListIndex); } diff --git a/src/UI/CacheObject/CacheObjectBase.cs b/src/UI/CacheObject/CacheObjectBase.cs index 3fb9c1f..2383ad8 100644 --- a/src/UI/CacheObject/CacheObjectBase.cs +++ b/src/UI/CacheObject/CacheObjectBase.cs @@ -100,6 +100,7 @@ namespace UnityExplorer.UI.CacheObject ReflectionProvider.Instance.BoxStringToType(ref value, FallbackType); else value = value.TryCast(FallbackType); + TrySetUserValue(value); } @@ -283,62 +284,6 @@ namespace UnityExplorer.UI.CacheObject cell.SubContentButton.Button.gameObject.SetActive(args.subContentButtonActive); } - // IValues - - internal static GameObject InactiveIValueHolder - { - get - { - if (!inactiveIValueHolder) - { - inactiveIValueHolder = new GameObject("Temp_IValue_Holder"); - GameObject.DontDestroyOnLoad(inactiveIValueHolder); - inactiveIValueHolder.transform.parent = UIManager.PoolHolder.transform; - inactiveIValueHolder.SetActive(false); - } - return inactiveIValueHolder; - } - } - private static GameObject inactiveIValueHolder; - - public virtual void OnCellSubContentToggle() - { - if (this.IValue == null) - { - var ivalueType = InteractiveValue.GetIValueTypeForState(State); - IValue = (InteractiveValue)Pool.Borrow(ivalueType); - CurrentIValueType = ivalueType; - - IValue.OnBorrowed(this); - IValue.SetValue(this.Value); - IValue.UIRoot.transform.SetParent(CellView.SubContentHolder.transform, false); - CellView.SubContentHolder.SetActive(true); - SubContentShowWanted = true; - - // update our cell after creating the ivalue (the value may have updated, make sure its consistent) - this.ProcessOnEvaluate(); - this.SetDataToCell(this.CellView); - } - else - { - SubContentShowWanted = !SubContentShowWanted; - CellView.SubContentHolder.SetActive(SubContentShowWanted); - } - - CellView.RefreshSubcontentButton(); - } - - public virtual void ReleaseIValue() - { - if (IValue == null) - return; - - IValue.ReleaseFromOwner(); - Pool.Return(CurrentIValueType, IValue); - - IValue = null; - } - // CacheObjectCell Apply public virtual void OnCellApplyClicked() @@ -368,6 +313,76 @@ namespace UnityExplorer.UI.CacheObject SetDataToCell(this.CellView); } + // IValues + + public virtual void OnCellSubContentToggle() + { + if (this.IValue == null) + { + var ivalueType = InteractiveValue.GetIValueTypeForState(State); + IValue = (InteractiveValue)Pool.Borrow(ivalueType); + CurrentIValueType = ivalueType; + + IValue.OnBorrowed(this); + IValue.SetValue(this.Value); + IValue.UIRoot.transform.SetParent(CellView.SubContentHolder.transform, false); + CellView.SubContentHolder.SetActive(true); + SubContentShowWanted = true; + + // update our cell after creating the ivalue (the value may have updated, make sure its consistent) + this.ProcessOnEvaluate(); + this.SetDataToCell(this.CellView); + } + else + { + SubContentShowWanted = !SubContentShowWanted; + CellView.SubContentHolder.SetActive(SubContentShowWanted); + } + + CellView.RefreshSubcontentButton(); + } + + public virtual void SetValueFromIValue(object value) + { + if (CellView == null) + { + ExplorerCore.LogWarning("Trying to set value from IValue but CellView is null!?"); + return; + } + + SetUserValue(value); + SetDataToCell(CellView); + } + + public virtual void ReleaseIValue() + { + if (IValue == null) + return; + + IValue.ReleaseFromOwner(); + Pool.Return(CurrentIValueType, IValue); + + IValue = null; + } + + internal static GameObject InactiveIValueHolder + { + get + { + if (!inactiveIValueHolder) + { + inactiveIValueHolder = new GameObject("Temp_IValue_Holder"); + GameObject.DontDestroyOnLoad(inactiveIValueHolder); + inactiveIValueHolder.transform.parent = UIManager.PoolHolder.transform; + inactiveIValueHolder.SetActive(false); + } + return inactiveIValueHolder; + } + } + private static GameObject inactiveIValueHolder; + + // Value state args helper + public struct ValueStateArgs { public ValueStateArgs(bool valueActive = true, bool valueRichText = true, Color? valueColor = null, diff --git a/src/UI/CacheObject/Views/CacheKeyValuePairCell.cs b/src/UI/CacheObject/Views/CacheKeyValuePairCell.cs index 1d2884c..456fdb3 100644 --- a/src/UI/CacheObject/Views/CacheKeyValuePairCell.cs +++ b/src/UI/CacheObject/Views/CacheKeyValuePairCell.cs @@ -84,10 +84,5 @@ namespace UnityExplorer.UI.CacheObject.Views { // not used } - - //protected override void ConstructUpdateToggle(GameObject parent) - //{ - // // not used - //} } } diff --git a/src/UI/IValues/InteractiveDictionary.cs b/src/UI/IValues/InteractiveDictionary.cs index db85733..7cce385 100644 --- a/src/UI/IValues/InteractiveDictionary.cs +++ b/src/UI/IValues/InteractiveDictionary.cs @@ -19,7 +19,7 @@ namespace UnityExplorer.UI.IValues object ICacheObjectController.Target => this.CurrentOwner.Value; public Type TargetType { get; private set; } - public override bool CanWrite => false;// TODO RefIDictionary != null && !RefIDictionary.IsReadOnly; + public override bool CanWrite => base.CanWrite && RefIDictionary != null && !RefIDictionary.IsReadOnly; public Type KeyType; public Type ValueType; @@ -149,6 +149,24 @@ namespace UnityExplorer.UI.IValues } } + // Setting value to dictionary + + public void TrySetValueToKey(object key, object value, int keyIndex) + { + try + { + RefIDictionary[key] = value; + + var entry = cachedEntries[keyIndex]; + entry.SetValueFromSource(value); + entry.SetDataToCell(entry.CellView); + } + catch (Exception ex) + { + ExplorerCore.LogWarning($"Exception setting IDictionary key! {ex}"); + } + } + // KVP entry scroll pool public void OnCellBorrowed(CacheKeyValuePairCell cell) diff --git a/src/UI/IValues/InteractiveList.cs b/src/UI/IValues/InteractiveList.cs index 6e34a93..ec43a66 100644 --- a/src/UI/IValues/InteractiveList.cs +++ b/src/UI/IValues/InteractiveList.cs @@ -19,7 +19,7 @@ namespace UnityExplorer.UI.IValues object ICacheObjectController.Target => this.CurrentOwner.Value; public Type TargetType { get; private set; } - public override bool CanWrite => RefIList != null && !RefIList.IsReadOnly; + public override bool CanWrite => base.CanWrite && RefIList != null && !RefIList.IsReadOnly; public Type EntryType; public IEnumerable RefIEnumerable; @@ -60,6 +60,7 @@ namespace UnityExplorer.UI.IValues cachedEntries.Clear(); } + // Setting the List value itself to this model public override void SetValue(object value) { if (value == null) @@ -100,6 +101,8 @@ namespace UnityExplorer.UI.IValues int idx = 0; foreach (var entry in RefIEnumerable) { + // var entry = item.TryCast(); + values.Add(entry); // If list count increased, create new cache entries @@ -133,6 +136,24 @@ namespace UnityExplorer.UI.IValues } } + // Setting the value of an index to the list + + public void TrySetValueToIndex(object value, int index) + { + try + { + RefIList[index] = value; + + var entry = cachedEntries[index]; + entry.SetValueFromSource(value); + entry.SetDataToCell(entry.CellView); + } + catch (Exception ex) + { + ExplorerCore.LogWarning($"Exception setting IList value: {ex}"); + } + } + // List entry scroll pool public override void SetLayout() diff --git a/src/UI/IValues/InteractiveValue.cs b/src/UI/IValues/InteractiveValue.cs index 84e61a5..361dcdd 100644 --- a/src/UI/IValues/InteractiveValue.cs +++ b/src/UI/IValues/InteractiveValue.cs @@ -17,10 +17,10 @@ namespace UnityExplorer.UI.IValues public virtual bool CanWrite => this.CurrentOwner.CanWrite; - public CacheObjectBase CurrentOwner { get; } + public CacheObjectBase CurrentOwner => m_owner; private CacheObjectBase m_owner; - public object EditedValue { get; private set; } + //public object EditedValue { get; private set; } public virtual void SetLayout() { } @@ -53,12 +53,6 @@ namespace UnityExplorer.UI.IValues } this.m_owner = owner; - // ... - } - - public virtual void SetValue(object value) - { - this.EditedValue = value; } public virtual void ReleaseFromOwner() @@ -69,6 +63,13 @@ namespace UnityExplorer.UI.IValues this.m_owner = null; } + public virtual void SetValue(object value) { } + + //public virtual void SetValue(object value) + //{ + // this.EditedValue = value; + //} + public virtual GameObject CreateContent(GameObject parent) { UIRoot = UIFactory.CreateUIObject(this.GetType().Name, parent);