diff --git a/src/Core/ReflectionUtility.cs b/src/Core/ReflectionUtility.cs
index c56d589..03af37e 100644
--- a/src/Core/ReflectionUtility.cs
+++ b/src/Core/ReflectionUtility.cs
@@ -105,14 +105,7 @@ namespace UnityExplorer
///
/// The object to cast
/// The object, cast to the underlying Type if possible, otherwise the original object.
- public static object TryCast(this object obj)
- {
- var type = GetActualType(obj);
-
- if (type.IsValueType)
- return obj;
- return ReflectionProvider.Instance.Cast(obj, type);
- }
+ public static object TryCast(this object obj) => ReflectionProvider.Instance.Cast(obj, GetActualType(obj));
///
/// Cast an object to a Type, if possible.
@@ -120,20 +113,10 @@ namespace UnityExplorer
/// The object to cast
/// The Type to cast to
/// The object, cast to the Type provided if possible, otherwise the original object.
- public static object TryCast(this object obj, Type castTo)
- {
- if (castTo.IsValueType)
- return obj;
- return ReflectionProvider.Instance.Cast(obj, castTo);
- }
+ public static object TryCast(this object obj, Type castTo) => ReflectionProvider.Instance.Cast(obj, castTo);
- public static T TryCast(this object obj)
- {
- var type = typeof(T);
- if (type.IsValueType)
- return (T)obj;
- return ReflectionProvider.Instance.TryCast(obj);
- }
+ /// Try to cast the object to the type.
+ public static T TryCast(this object obj) => ReflectionProvider.Instance.TryCast(obj);
///
/// Check if the provided Type is assignable to IEnumerable.
diff --git a/src/Core/Runtime/Il2Cpp/Il2CppReflection.cs b/src/Core/Runtime/Il2Cpp/Il2CppReflection.cs
index 614b857..4af0405 100644
--- a/src/Core/Runtime/Il2Cpp/Il2CppReflection.cs
+++ b/src/Core/Runtime/Il2Cpp/Il2CppReflection.cs
@@ -53,67 +53,6 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
}
}
- public override bool IsString(object obj)
- {
- return obj is string || obj is Il2CppSystem.String;
- }
-
- public override void BoxStringToType(ref object value, Type castTo)
- {
- if (castTo == typeof(Il2CppSystem.String))
- value = (Il2CppSystem.String)(value as string);
- else
- value = (Il2CppSystem.Object)(value as string);
- }
-
- public override string UnboxString(object value)
- {
- if (value is string s)
- return s;
-
- s = null;
- // strings boxed as Il2CppSystem.Objects can behave weirdly.
- // GetActualType will find they are a string, but if its boxed
- // then we need to unbox it like this...
- if (value is Il2CppSystem.Object cppObject)
- s = cppObject.ToString();
- else if (value is Il2CppSystem.String cppString)
- s = cppString;
-
- return s;
- }
-
- public override Type GetDeobfuscatedType(Type type)
- {
- try
- {
- if (Il2CppToMonoType.ContainsKey(type.AssemblyQualifiedName))
- return Il2CppToMonoType[type.AssemblyQualifiedName];
- //var cppType = Il2CppType.From(type);
- //var monoType = GetMonoType(cppType);
- //if (monoType != null)
- // return monoType;
- }
- catch { }
-
- return type;
- }
-
- public override string ProcessTypeFullNameInString(Type type, string theString, ref string typeName)
- {
- if (!Il2CppTypeNotNull(type))
- return theString;
-
- var cppType = Il2CppType.From(type);
- if (cppType != null && s_deobfuscatedTypeNames.ContainsKey(cppType.FullName))
- {
- typeName = s_deobfuscatedTypeNames[cppType.FullName];
- theString = theString.Replace(cppType.FullName, typeName);
- }
-
- return theString;
- }
-
public override Type GetActualType(object obj)
{
if (obj == null)
@@ -125,13 +64,6 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
{
if (obj is Il2CppSystem.Object cppObject)
{
- // weird specific case - if the object is an Il2CppSystem.Type, then return so manually.
- if (cppObject is CppType)
- return typeof(CppType);
-
- if (type.FullName.StartsWith("System.") || type.FullName.StartsWith("Il2CppSystem."))
- return type;
-
var cppType = cppObject.GetIl2CppType();
// check if type is injected
@@ -141,15 +73,10 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
// Note: This will fail on injected subclasses.
// - {Namespace}.{Class}.{Subclass} would be {Namespace}.{Subclass} when injected.
// Not sure on solution yet.
- var typeByName = ReflectionUtility.GetTypeByName(cppType.FullName);
- if (typeByName != null)
- return typeByName;
+ return ReflectionUtility.GetTypeByName(cppType.FullName) ?? type;
}
- // this should be fine for all other il2cpp objects
- var getType = GetMonoType(cppType);
- if (getType != null)
- return getType;
+ return GetMonoType(cppType) ?? type;
}
}
catch //(Exception ex)
@@ -160,58 +87,17 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
return type;
}
- // caching for GetMonoType
- private static readonly Dictionary Il2CppToMonoType = new Dictionary();
-
- // keep deobfuscated type name cache, used to display proper name.
- internal static Dictionary s_deobfuscatedTypeNames = new Dictionary();
-
- private static void BuildDeobfuscationCache()
- {
- foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
- {
- foreach (var type in asm.TryGetTypes())
- TryCacheDeobfuscatedType(type);
- }
-
- if (s_deobfuscatedTypeNames.Count > 0)
- ExplorerCore.Log($"Built deobfuscation cache, count: {s_deobfuscatedTypeNames.Count}");
- }
-
- private static void TryCacheDeobfuscatedType(Type type)
- {
- try
- {
- if (type.CustomAttributes.Any(it => it.AttributeType.Name == "ObfuscatedNameAttribute"))
- {
- var cppType = Il2CppType.From(type);
-
- if (!Il2CppToMonoType.ContainsKey(cppType.AssemblyQualifiedName))
- {
- Il2CppToMonoType.Add(cppType.AssemblyQualifiedName, type);
- s_deobfuscatedTypeNames.Add(cppType.FullName, type.FullName);
- }
- }
- }
- catch { }
- }
-
- ///
- /// Try to get the Mono (Unhollowed) Type representation of the provided .
- ///
- /// The Cpp Type you want to convert to Mono.
- /// The Mono Type if found, otherwise null.
public static Type GetMonoType(CppType cppType)
{
- string name = cppType.AssemblyQualifiedName;
+ if (DeobfuscatedTypes.TryGetValue(cppType.AssemblyQualifiedName, out Type deob))
+ return deob;
- if (Il2CppToMonoType.ContainsKey(name))
- return Il2CppToMonoType[name];
+ var fullname = cppType.FullName;
+ if (fullname.StartsWith("System."))
+ fullname = $"Il2Cpp{fullname}";
- Type ret = Type.GetType(name);
- Il2CppToMonoType.Add(name, ret);
-
- return ret;
+ ReflectionUtility.AllTypes.TryGetValue(fullname, out Type monoType);
+ return monoType;
}
// cached class pointers for Il2CppCast
@@ -232,9 +118,24 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
/// The object, as the type (or a normal C# object) if successful or the input value if not.
public static object Il2CppCast(object obj, Type castTo)
{
+ if (obj == null)
+ return null;
+
+ var type = obj.GetType();
+
+ if (type.IsValueType && typeof(Il2CppSystem.Object).IsAssignableFrom(castTo))
+ return BoxIl2CppObject(obj);
+
if (!(obj is Il2CppSystem.Object cppObj))
return obj;
+ if (castTo.IsValueType)
+ {
+ if (castTo.FullName.StartsWith("Il2CppSystem."))
+ ReflectionUtility.AllTypes.TryGetValue(cppObj.GetIl2CppType().FullName, out castTo);
+ return Unbox(cppObj, castTo);
+ }
+
if (!Il2CppTypeNotNull(castTo, out IntPtr castToPtr))
return obj;
@@ -249,9 +150,6 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
return injectedObj ?? obj;
}
- if (castTo == typeof(string))
- return cppObj.ToString();
-
try
{
return Activator.CreateInstance(castTo, cppObj.Pointer);
@@ -262,6 +160,129 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
}
}
+ // struct boxing
+
+ // cached il2cpp unbox methods
+ internal static readonly Dictionary s_unboxMethods = new Dictionary();
+
+ ///
+ /// Attempt to unbox the object to the underlying struct type.
+ ///
+ /// The object which is a struct underneath.
+ /// The struct if successful, otherwise null.
+ public static object Unbox(object obj) => Unbox(obj, Instance.GetActualType(obj));
+
+ ///
+ /// Attempt to unbox the object to the struct type.
+ ///
+ /// The object which is a struct underneath.
+ /// The type of the struct you want to unbox to.
+ /// The struct if successful, otherwise null.
+ public static object Unbox(object obj, Type type)
+ {
+ if (!type.IsValueType)
+ return null;
+
+ if (!(obj is Il2CppSystem.Object))
+ return obj;
+
+ var name = type.AssemblyQualifiedName;
+
+ if (!s_unboxMethods.ContainsKey(name))
+ {
+ s_unboxMethods.Add(name, typeof(Il2CppObjectBase)
+ .GetMethod("Unbox")
+ .MakeGenericMethod(type));
+ }
+
+ return s_unboxMethods[name].Invoke(obj, new object[0]);
+ }
+
+ //internal static Dictionary monoToIl2CppType = new Dictionary();
+ internal static Dictionary structBoxMethods = new Dictionary();
+ internal static Dictionary structValueFields = new Dictionary();
+
+ ///
+ /// Try to box a value to Il2CppSystem.Object using the Il2CppSystem representation of the value type.
+ ///
+ /// The value, eg 5 (System.Int32)
+ /// The boxed Il2CppSystem.Object for the value, eg for '5' it would be a boxed Il2CppSytem.Int32.
+ public static Il2CppSystem.Object BoxIl2CppObject(object value)
+ {
+ string key = $"Il2Cpp{value.GetType().FullName}";
+
+ if (ReflectionUtility.AllTypes.TryGetValue(key, out Type type) && type.IsValueType)
+ {
+ var cppStruct = Activator.CreateInstance(type);
+
+ if (!structValueFields.ContainsKey(type))
+ structValueFields.Add(type, type.GetField("m_value"));
+
+ structValueFields[type].SetValue(cppStruct, value);
+
+ if (!structBoxMethods.ContainsKey(type))
+ structBoxMethods.Add(type, type.GetMethod("BoxIl2CppObject"));
+
+ return structBoxMethods[type].Invoke(cppStruct, null) as Il2CppSystem.Object;
+ }
+ else
+ {
+ ExplorerCore.LogWarning("Couldn't get type to box to");
+ return null;
+ }
+ }
+
+ // deobfuscation
+
+ private static readonly Dictionary DeobfuscatedTypes = new Dictionary();
+ internal static Dictionary s_deobfuscatedTypeNames = new Dictionary();
+
+ private static void BuildDeobfuscationCache()
+ {
+ foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
+ {
+ foreach (var type in asm.TryGetTypes())
+ TryCacheDeobfuscatedType(type);
+ }
+
+ if (s_deobfuscatedTypeNames.Count > 0)
+ ExplorerCore.Log($"Built deobfuscation cache, count: {s_deobfuscatedTypeNames.Count}");
+ }
+
+ private static void TryCacheDeobfuscatedType(Type type)
+ {
+ try
+ {
+ // Thanks to Slaynash for this
+
+ if (type.CustomAttributes.Any(it => it.AttributeType.Name == "ObfuscatedNameAttribute"))
+ {
+ var cppType = Il2CppType.From(type);
+
+ if (!DeobfuscatedTypes.ContainsKey(cppType.AssemblyQualifiedName))
+ {
+ DeobfuscatedTypes.Add(cppType.AssemblyQualifiedName, type);
+ s_deobfuscatedTypeNames.Add(cppType.FullName, type.FullName);
+ }
+ }
+ }
+ catch { }
+ }
+
+ public override Type GetDeobfuscatedType(Type type)
+ {
+ try
+ {
+ if (DeobfuscatedTypes.ContainsKey(type.AssemblyQualifiedName))
+ return DeobfuscatedTypes[type.AssemblyQualifiedName];
+ }
+ catch { }
+
+ return type;
+ }
+
+ // misc helpers
+
///
/// Get the Il2Cpp Class Pointer for the provided Mono (Unhollowed) Type.
///
@@ -297,6 +318,132 @@ 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;
+ }
+
+ public override void BoxStringToType(ref object value, Type castTo)
+ {
+ if (castTo == typeof(Il2CppSystem.String))
+ value = (Il2CppSystem.String)(value as string);
+ else
+ value = (Il2CppSystem.Object)(value as string);
+ }
+
+ public override string UnboxString(object value)
+ {
+ if (value is string s)
+ return s;
+
+ s = null;
+ // strings boxed as Il2CppSystem.Objects can behave weirdly.
+ // GetActualType will find they are a string, but if its boxed
+ // then we need to unbox it like this...
+ if (value is Il2CppSystem.Object cppObject)
+ s = cppObject.ToString();
+ else if (value is Il2CppSystem.String cppString)
+ s = cppString;
+
+ return s;
+ }
+
+ public override string ProcessTypeFullNameInString(Type type, string theString, ref string typeName)
+ {
+ if (!Il2CppTypeNotNull(type))
+ return theString;
+
+ var cppType = Il2CppType.From(type);
+ if (cppType != null && s_deobfuscatedTypeNames.ContainsKey(cppType.FullName))
+ {
+ typeName = s_deobfuscatedTypeNames[cppType.FullName];
+ theString = theString.Replace(cppType.FullName, typeName);
+ }
+
+ return theString;
+ }
+
+
+ //// Not currently using, not sure if its necessary anymore, was necessary to prevent crashes at one point.
+ //public override bool IsReflectionSupported(Type type)
+ //{
+ // try
+ // {
+ // var gArgs = type.GetGenericArguments();
+ // if (!gArgs.Any())
+ // return true;
+ //
+ // foreach (var gType in gArgs)
+ // {
+ // if (!Supported(gType))
+ // return false;
+ // }
+ //
+ // return true;
+ //
+ // bool Supported(Type t)
+ // {
+ // if (!typeof(Il2CppSystem.Object).IsAssignableFrom(t))
+ // return true;
+ //
+ // if (!Il2CppTypeNotNull(t, out IntPtr ptr))
+ // return false;
+ //
+ // return CppType.internal_from_handle(IL2CPP.il2cpp_class_get_type(ptr)) is CppType;
+ // }
+ // }
+ // catch
+ // {
+ // return false;
+ // }
+ //}
+
+
+
+ #region FORCE LOADING GAME MODULES
+
+ // Helper for IL2CPP to try to make sure the Unhollowed game assemblies are actually loaded.
+
+ internal static void TryLoadGameModules()
+ {
+ Instance.LoadModule("Assembly-CSharp");
+ Instance.LoadModule("Assembly-CSharp-firstpass");
+ }
+
+ public override bool LoadModule(string module)
+ {
+#if ML
+ var path = Path.Combine("MelonLoader", "Managed", $"{module}.dll");
+#else
+ var path = Path.Combine("BepInEx", "unhollowed", $"{module}.dll");
+#endif
+ return LoadModuleInternal(path);
+ }
+
+ internal static bool LoadModuleInternal(string fullPath)
+ {
+ if (!File.Exists(fullPath))
+ return false;
+
+ try
+ {
+ Assembly.Load(File.ReadAllBytes(fullPath));
+ return true;
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine(e.GetType() + ", " + e.Message);
+ }
+
+ return false;
+ }
+
+ #endregion
+
+ #region IL2CPP IENUMERABLE / IDICTIONARY
+
+ // dictionary and enumerable cast helpers (until unhollower rewrite)
+
internal static IntPtr s_cppEnumerableClassPtr;
internal static IntPtr s_cppDictionaryClassPtr;
@@ -341,76 +488,6 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
return false;
}
- // Not currently using, not sure if its necessary anymore, was necessary to prevent crashes at one point.
- public override bool IsReflectionSupported(Type type)
- {
- try
- {
- var gArgs = type.GetGenericArguments();
- if (!gArgs.Any())
- return true;
-
- foreach (var gType in gArgs)
- {
- if (!Supported(gType))
- return false;
- }
-
- return true;
-
- bool Supported(Type t)
- {
- if (!typeof(Il2CppSystem.Object).IsAssignableFrom(t))
- return true;
-
- if (!Il2CppTypeNotNull(t, out IntPtr ptr))
- return false;
-
- return CppType.internal_from_handle(IL2CPP.il2cpp_class_get_type(ptr)) is CppType;
- }
- }
- catch
- {
- return false;
- }
- }
-
- // Helper for IL2CPP to try to make sure the Unhollowed game assemblies are actually loaded.
-
- internal static void TryLoadGameModules()
- {
- Instance.LoadModule("Assembly-CSharp");
- Instance.LoadModule("Assembly-CSharp-firstpass");
- }
-
- public override bool LoadModule(string module)
- {
-#if ML
- var path = Path.Combine("MelonLoader", "Managed", $"{module}.dll");
-#else
- var path = Path.Combine("BepInEx", "unhollowed", $"{module}.dll");
-#endif
- return LoadModuleInternal(path);
- }
-
- internal static bool LoadModuleInternal(string fullPath)
- {
- if (!File.Exists(fullPath))
- return false;
-
- try
- {
- Assembly.Load(File.ReadAllBytes(fullPath));
- return true;
- }
- catch (Exception e)
- {
- Console.WriteLine(e.GetType() + ", " + e.Message);
- }
-
- return false;
- }
-
internal static readonly Dictionary s_getEnumeratorMethods = new Dictionary();
internal static readonly Dictionary s_enumeratorInfos = new Dictionary();
@@ -536,43 +613,7 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
base.FindSingleton(possibleNames, type, flags, instances);
}
- // ~~~~~~~~~~ not used ~~~~~~~~~~~~
-
- // cached il2cpp unbox methods
- internal static readonly Dictionary s_unboxMethods = new Dictionary();
-
- ///
- /// Attempt to unbox the object to the underlying struct type.
- ///
- /// The object which is a struct underneath.
- /// The struct if successful, otherwise null.
- public static object Unbox(object obj) => Unbox(obj, Instance.GetActualType(obj));
-
- ///
- /// Attempt to unbox the object to the struct type.
- ///
- /// The object which is a struct underneath.
- /// The type of the struct you want to unbox to.
- /// The struct if successful, otherwise null.
- public static object Unbox(object obj, Type type)
- {
- if (!type.IsValueType)
- return null;
-
- if (!(obj is Il2CppSystem.Object))
- return obj;
-
- var name = type.AssemblyQualifiedName;
-
- if (!s_unboxMethods.ContainsKey(name))
- {
- s_unboxMethods.Add(name, typeof(Il2CppObjectBase)
- .GetMethod("Unbox")
- .MakeGenericMethod(type));
- }
-
- return s_unboxMethods[name].Invoke(obj, new object[0]);
- }
+ #endregion
}
}
diff --git a/src/Core/Runtime/Mono/MonoReflection.cs b/src/Core/Runtime/Mono/MonoReflection.cs
index 5620e1b..5ac3db3 100644
--- a/src/Core/Runtime/Mono/MonoReflection.cs
+++ b/src/Core/Runtime/Mono/MonoReflection.cs
@@ -31,9 +31,6 @@ namespace UnityExplorer.Core.Runtime.Mono
public override bool IsAssignableFrom(Type toAssignTo, Type toAssignFrom)
=> toAssignTo.IsAssignableFrom(toAssignFrom);
- public override bool IsReflectionSupported(Type type)
- => true;
-
public override bool LoadModule(string module)
=> true;
diff --git a/src/Core/Runtime/ReflectionProvider.cs b/src/Core/Runtime/ReflectionProvider.cs
index 80fca0f..2d63b5c 100644
--- a/src/Core/Runtime/ReflectionProvider.cs
+++ b/src/Core/Runtime/ReflectionProvider.cs
@@ -26,7 +26,7 @@ namespace UnityExplorer.Core.Runtime
public abstract bool IsAssignableFrom(Type toAssignTo, Type toAssignFrom);
- public abstract bool IsReflectionSupported(Type type);
+ //public abstract bool IsReflectionSupported(Type type);
public abstract string ProcessTypeFullNameInString(Type type, string theString, ref string typeName);
diff --git a/src/Core/Tests/TestClass.cs b/src/Core/Tests/TestClass.cs
index 10964cd..3227552 100644
--- a/src/Core/Tests/TestClass.cs
+++ b/src/Core/Tests/TestClass.cs
@@ -126,10 +126,14 @@ namespace UnityExplorer.Tests
public static Il2CppSystem.String testStringThree = "string boxed as cpp string";
public static string nullString = null;
+ public static List boxedList;
+
+ public static Il2CppSystem.Object boxedInt;
+ public static Il2CppSystem.Int32 cppint;
+
public static Il2CppSystem.Collections.Hashtable testHashset;
public static Il2CppSystem.Collections.Generic.List testList;
-
//public static Il2CppSystem.Nullable NullableQuaternion;
//public static Il2CppSystem.Nullable NullableInt = new Il2CppSystem.Nullable(5);
//public static Il2CppSystem.Nullable NullableBool = new Il2CppSystem.Nullable(false);
@@ -144,6 +148,12 @@ namespace UnityExplorer.Tests
//NullableQuaternion = new Il2CppSystem.Nullable();
//NullableQuaternion.value = Quaternion.identity;
+ 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 };
+
testHashset = new Il2CppSystem.Collections.Hashtable();
testHashset.Add("key1", "itemOne");
testHashset.Add("key2", "itemTwo");
diff --git a/src/UI/CacheObject/CacheField.cs b/src/UI/CacheObject/CacheField.cs
index c3ad47f..6a7375e 100644
--- a/src/UI/CacheObject/CacheField.cs
+++ b/src/UI/CacheObject/CacheField.cs
@@ -43,7 +43,7 @@ namespace UnityExplorer.UI.CacheObject
{
try
{
- FieldInfo.SetValue(FieldInfo.IsStatic ? null : Owner.Target.TryCast(this.DeclaringType), value);
+ FieldInfo.SetValue(DeclaringInstance, value);
}
catch (Exception ex)
{
diff --git a/src/UI/CacheObject/CacheKeyValuePair.cs b/src/UI/CacheObject/CacheKeyValuePair.cs
index 880ef22..38e4eb4 100644
--- a/src/UI/CacheObject/CacheKeyValuePair.cs
+++ b/src/UI/CacheObject/CacheKeyValuePair.cs
@@ -82,7 +82,7 @@ namespace UnityExplorer.UI.CacheObject
}
}
- public override void SetUserValue(object value)
+ public override void TrySetUserValue(object value)
{
throw new NotImplementedException("TODO");
}
diff --git a/src/UI/CacheObject/CacheListEntry.cs b/src/UI/CacheObject/CacheListEntry.cs
index d1b9b97..c24d414 100644
--- a/src/UI/CacheObject/CacheListEntry.cs
+++ b/src/UI/CacheObject/CacheListEntry.cs
@@ -31,7 +31,7 @@ namespace UnityExplorer.UI.CacheObject
listCell.Image.color = ListIndex % 2 == 0 ? CacheListEntryCell.EvenColor : CacheListEntryCell.OddColor;
}
- public override void SetUserValue(object value)
+ public override void TrySetUserValue(object value)
{
throw new NotImplementedException("TODO");
}
diff --git a/src/UI/CacheObject/CacheMember.cs b/src/UI/CacheObject/CacheMember.cs
index e9ae5dc..5fb203d 100644
--- a/src/UI/CacheObject/CacheMember.cs
+++ b/src/UI/CacheObject/CacheMember.cs
@@ -4,6 +4,7 @@ using System.Linq;
using System.Reflection;
using System.Text;
using UnityEngine;
+using UnityExplorer.Core.Runtime;
using UnityExplorer.UI.CacheObject.Views;
using UnityExplorer.UI.Inspectors;
using UnityExplorer.UI.ObjectPool;
@@ -74,10 +75,11 @@ namespace UnityExplorer.UI.CacheObject
SetValueFromSource(TryEvaluate());
}
- public override void SetUserValue(object value)
+ public override void TrySetUserValue(object value)
{
- // TODO unbox string, cast, etc
-
+ if (State == ValueState.String)
+ ReflectionProvider.Instance.BoxStringToType(ref value, FallbackType);
+
TrySetValue(value);
Evaluate();
diff --git a/src/UI/CacheObject/CacheObjectBase.cs b/src/UI/CacheObject/CacheObjectBase.cs
index 67ec399..23aa9c1 100644
--- a/src/UI/CacheObject/CacheObjectBase.cs
+++ b/src/UI/CacheObject/CacheObjectBase.cs
@@ -95,7 +95,13 @@ namespace UnityExplorer.UI.CacheObject
// Updating and applying values
- public abstract void SetUserValue(object value);
+ public void SetUserValue(object value)
+ {
+ value = value.TryCast(FallbackType);
+ TrySetUserValue(value);
+ }
+
+ public abstract void TrySetUserValue(object value);
public virtual void SetValueFromSource(object value)
{
diff --git a/src/UI/CacheObject/CacheProperty.cs b/src/UI/CacheObject/CacheProperty.cs
index e932927..6dfdeee 100644
--- a/src/UI/CacheObject/CacheProperty.cs
+++ b/src/UI/CacheObject/CacheProperty.cs
@@ -52,12 +52,11 @@ namespace UnityExplorer.UI.CacheObject
try
{
bool _static = PropertyInfo.GetAccessors(true)[0].IsStatic;
- var target = _static ? null : Owner.Target.TryCast(DeclaringType);
if (HasArguments)
- PropertyInfo.SetValue(target, value, Evaluator.TryParseArguments());
+ PropertyInfo.SetValue(DeclaringInstance, value, Evaluator.TryParseArguments());
else
- PropertyInfo.SetValue(target, value, null);
+ PropertyInfo.SetValue(DeclaringInstance, value, null);
}
catch (Exception ex)
{