示例#1
0
 public void AddPtrRef(ISmartPtr <AssetsObject> ptr)
 {
     if (!_knownPointers.Contains(ptr))
     {
         _knownPointers.Add(ptr);
     }
 }
示例#2
0
 private void Parse(AssetsFile assetsFile, AssetsObject owner, AssetsReader reader)
 {
     Difficulty              = (Difficulty)reader.ReadInt32();
     DifficultyRank          = reader.ReadInt32();
     NoteJumpMovementSpeed   = reader.ReadSingle();
     NoteJumpStartBeatOffset = reader.ReadInt32();
     BeatmapDataPtr          = SmartPtr <BeatmapDataObject> .Read(assetsFile, owner, reader);
 }
示例#3
0
        protected override void Parse(AssetsReader reader)
        {
            base.Parse(reader);
            PlayerModel = SmartPtr <MonoBehaviourObject> .Read(ObjectInfo.ParentFile, this, reader);

            ColorA = SmartPtr <MonoBehaviourObject> .Read(ObjectInfo.ParentFile, this, reader);

            ColorB = SmartPtr <MonoBehaviourObject> .Read(ObjectInfo.ParentFile, this, reader);
        }
示例#4
0
        public override void Parse(AssetsReader reader)
        {
            base.ParseBase(reader);
            PackID     = reader.ReadString();
            PackName   = reader.ReadString();
            CoverImage = SmartPtr <SpriteObject> .Read(ObjectInfo.ParentFile, this, reader);

            BeatmapLevelCollection = SmartPtr <BeatmapLevelCollectionObject> .Read(ObjectInfo.ParentFile, this, reader);
        }
示例#5
0
        public override void Parse(AssetsReader reader)
        {
            base.ParseBase(reader);
            PlayerModel = SmartPtr <AssetsObject> .Read(ObjectInfo.ParentFile, this, reader);

            ColorA = SmartPtr <SimpleColorSO> .Read(ObjectInfo.ParentFile, this, reader);

            ColorB = SmartPtr <SimpleColorSO> .Read(ObjectInfo.ParentFile, this, reader);
        }
示例#6
0
        protected override void Parse(AssetsReader reader)
        {
            base.Parse(reader);
            PackID     = reader.ReadString();
            PackName   = reader.ReadString();
            CoverImage = SmartPtr <SpriteObject> .Read(ObjectInfo.ParentFile, this, reader);

            IsPackAlwaysOwned      = reader.ReadBoolean();
            BeatmapLevelCollection = SmartPtr <BeatmapLevelCollectionObject> .Read(ObjectInfo.ParentFile, this, reader);
        }
        public static void AddObjectToEnum(ISmartPtr <AssetsObject> toAdd, IEnumerable <ISmartPtr <AssetsObject> > target)
        {
            var addMethod = target.GetType().GetMethod("Add");

            if (addMethod == null)
            {
                throw new Exception($"Add method could not be found on type {target.GetType().Name}!  The passed object should be a List<>!");
            }

            addMethod.Invoke(target, new object[] { toAdd });
        }
示例#8
0
 public static void Write <T>(this ISmartPtr <T> ptr, AssetsWriter writer) where T : AssetsObject
 {
     if (ptr == null)
     {
         writer.Write((Int32)0);
         writer.Write((Int64)0);
     }
     else
     {
         ptr.WritePtr(writer);
     }
 }
        protected override void Parse(AssetsReader reader)
        {
            base.Parse(reader);
            LevelID         = reader.ReadString();
            SongName        = reader.ReadString();
            SongSubName     = reader.ReadString();
            SongAuthorName  = reader.ReadString();
            LevelAuthorName = reader.ReadString();
            AudioClip       = SmartPtr <AudioClipObject> .Read(ObjectInfo.ParentFile, this, reader);

            BeatsPerMinute      = reader.ReadSingle();
            SongTimeOffset      = reader.ReadSingle();
            Shuffle             = reader.ReadSingle();
            ShufflePeriod       = reader.ReadSingle();
            PreviewStartTime    = reader.ReadSingle();
            PreviewDuration     = reader.ReadSingle();
            CoverImageTexture2D = SmartPtr <Texture2DObject> .Read(ObjectInfo.ParentFile, this, reader);

            EnvironmentSceneInfo = SmartPtr <AssetsObject> .Read(ObjectInfo.ParentFile, this, reader);

            DifficultyBeatmapSets = reader.ReadArrayOf(x => new DifficultyBeatmapSet(ObjectInfo.ParentFile, this, x));
        }
示例#10
0
        private static void ClonePropsInObj(object curObj, AssetsObject parentObj, Dictionary <AssetsObject, AssetsObject> clonedObjects, AssetsFile toFile, List <AssetsObject> addedObjects, List <CloneExclusion> exclusions)
        {
            var file        = parentObj.ObjectInfo.ParentFile.AssetsFilename;
            var updateProps = curObj.GetType().GetProperties().ToList();

            //remove any array properties that are a string or a value type
            updateProps.Where(x => x.PropertyType.IsArray && (x.PropertyType.GetElementType().IsValueType || x.PropertyType.GetElementType() == typeof(string)))
            .ToList().ForEach(x => updateProps.Remove(x));

            //look through any properties that are smart pointers, clone their targets and make a new pointer, then remove them from the list of props to update
            var propsToClone = updateProps.Where(x => typeof(ISmartPtr <AssetsObject>).IsAssignableFrom(x.PropertyType)).ToList();

            foreach (var prop in propsToClone)
            {
                var baseVal = prop.GetValue(curObj, null);
                if (baseVal == null)
                {
                    continue;
                }
                var          propPtr      = (prop.GetValue(curObj, null) as ISmartPtr <AssetsObject>);
                var          propObj      = propPtr.Target.Object;
                AssetsObject assignObject = null;

                switch (exclusions.GetExclusionMode(propPtr, prop))
                {
                case ExclusionMode.LeaveRef:
                    assignObject = propObj;
                    break;

                case ExclusionMode.Remove:
                    assignObject = null;
                    Log.LogErr($"WARNING: Cloner is leaving the pointer NULL on property '{curObj.GetType().Name}.{prop.Name}'");
                    break;

                default:
                    assignObject = DeepClone(propObj, toFile, addedObjects, clonedObjects, exclusions);
                    break;
                }

                prop.SetValue(curObj, ReflectionHelper.MakeTypedPointer(parentObj, assignObject), null);
            }
            propsToClone.ForEach(x => updateProps.Remove(x));

            //look through any properties that lists of smart pointers, this code isn't ideal because it only actually supports things that have a default indexer
            //  I should clean this up to work better
            var listsToClone = updateProps.Where(x => typeof(IEnumerable <ISmartPtr <AssetsObject> >).IsAssignableFrom(x.PropertyType)).ToList();

            foreach (var listProp in listsToClone)
            {
                var listVal = listProp.GetValue(curObj, null) as IEnumerable <ISmartPtr <AssetsObject> >;

                if (listVal == null)
                {
                    continue;
                }
                if (listProp.PropertyType.IsArray)
                {
                    Array listArr = (Array)listVal;
                    for (int i = 0; i < listArr.Length; i++)
                    {
                        var ptrVal = listArr.GetValue(i) as ISmartPtr <AssetsObject>;
                        ISmartPtr <AssetsObject> clonedObj = null;
                        switch (exclusions.GetExclusionMode(ptrVal, listProp))
                        {
                        case ExclusionMode.LeaveRef:
                            clonedObj = listArr.GetValue(i) as ISmartPtr <AssetsObject>;
                            break;

                        case ExclusionMode.Remove:
                            clonedObj = null;
                            break;

                        default:
                            clonedObj = ReflectionHelper.MakeTypedPointer(DeepClone(ptrVal.Target.Object, toFile, addedObjects, clonedObjects, exclusions), parentObj);
                            break;
                        }
                        if (clonedObj == null)
                        {
                            listArr.RemoveAt(i);
                            i--;
                        }
                        else
                        {
                            listArr.SetValue(clonedObj, i);
                        }
                    }
                }
                else
                {
                    var indexerProp = listVal.GetType().GetProperties().Where(x => x.Name == "Item").FirstOrDefault();

                    if (indexerProp == null)
                    {
                        throw new NotSupportedException($"Couldn't find the default indexer property on {curObj.GetType().Name}.{listProp.Name}!");
                    }
                    for (int i = 0; i < listVal.Count(); i++)
                    {
                        var          ptrVal    = indexerProp.GetValue(listVal, new object[] { i }) as ISmartPtr <AssetsObject>;
                        AssetsObject clonedObj = null;
                        switch (exclusions.GetExclusionMode(ptrVal, listProp))
                        {
                        case ExclusionMode.LeaveRef:
                            clonedObj = ptrVal.Target.Object;
                            break;

                        case ExclusionMode.Remove:
                            clonedObj = null;
                            break;

                        default:
                            clonedObj = DeepClone(ptrVal.Target.Object, toFile, addedObjects, clonedObjects, exclusions);
                            break;
                        }

                        //if the cloned object comes back null, remove it from the list instead of setting it null
                        if (clonedObj == null)
                        {
                            ReflectionHelper.InvokeRemoveAt(listVal, i);
                        }
                        else
                        {
                            indexerProp.SetValue(listVal, ReflectionHelper.MakeTypedPointer(parentObj as AssetsObject, clonedObj), new object[] { i });
                        }
                    }
                }
            }
            listsToClone.ForEach(x => updateProps.Remove(x));

            //look through any objects that are plain old lists of whatever.  this is to catch lists of "structs" that may have pointers in them
            var plainEnumerableToClone = updateProps.Where(x => !x.PropertyType.IsValueType && !(x.PropertyType == typeof(string)) && typeof(IEnumerable).IsAssignableFrom(x.PropertyType)).ToList();

            foreach (var enumProp in plainEnumerableToClone)
            {
                var listVal = enumProp.GetValue(curObj, null) as IEnumerable;

                if (listVal == null)
                {
                    continue;
                }

                foreach (var plainObj in listVal)
                {
                    //pass in the parent AssetsObject that was passed to us since that object will be the "owner", not the struct object
                    ClonePropsInObj(plainObj, parentObj, clonedObjects, toFile, addedObjects, exclusions);
                }
            }
            plainEnumerableToClone.ForEach(x => updateProps.Remove(x));
            //look through any "struct" type properties that may have pointers in them
            var plainObjToClone = updateProps.Where(x => !x.PropertyType.IsValueType && !(x.PropertyType == typeof(string)));

            foreach (var plainProp in plainObjToClone)
            {
                var objVal = plainProp.GetValue(curObj, null) as IEnumerable;
                if (objVal == null)
                {
                    continue;
                }

                foreach (var plainObj in objVal)
                {
                    //pass in the parent AssetsObject that was passed to us since that object will be the "owner", not the struct object
                    ClonePropsInObj(plainObj, parentObj, clonedObjects, toFile, addedObjects, exclusions);
                }
            }
        }
示例#11
0
 public void RemovePtrRef(ISmartPtr <AssetsObject> ptr)
 {
     _knownPointers.Remove(ptr);
 }
示例#12
0
 public static RawPtr ToRawPtr <T>(this ISmartPtr <T> ptr) where T : AssetsObject
 {
     return(new RawPtr(ptr.FileID, ptr.PathID));
 }
示例#13
0
        public static void AssignPtrToPropName(object targetObject, string targetPropName, ISmartPtr <AssetsObject> ptr)
        {
            var prop = targetObject.GetType().GetProperty(targetPropName);

            if (prop == null)
            {
                throw new Exception($"Couldn't find property {targetPropName} on type {targetObject.GetType().Name}.");
            }
            prop.SetValue(targetObject, ptr, null);
        }
示例#14
0
        public static Node MakeNode(object o, int depth = 0, Dictionary <object, Node> trackedObjects = null)
        {
            if (trackedObjects == null)
            {
                trackedObjects = new Dictionary <object, Node>();
            }
            else
            {
                depth += 1;
            }

            Node node = null;

            if (o == null)
            {
                node = new Node()
                {
                    Text = "(null)", TypeName = null, Obj = null, Depth = depth
                };
                return(node);
            }
            Type t = o.GetType();

            if (typeof(IObjectInfo <AssetsObject>).IsAssignableFrom(t))
            {
                o = (o as IObjectInfo <AssetsObject>).Object;
                t = o.GetType();
            }

            node = new Node(o, depth);
            if (!t.IsValueType && t != typeof(string) && !(t.IsArray && (t.GetElementType().IsValueType || t.GetElementType() == typeof(string))) &&
                trackedObjects.ContainsKey(o))
            {
                var otherNode = trackedObjects[o];
                node.Text       = "";
                node.StubToNode = otherNode;


                if (otherNode.Depth > depth)
                {
                    node.Depth              = otherNode.Depth;
                    node.Parent             = otherNode.Parent;
                    node.ParentPropertyName = otherNode.ParentPropertyName;
                    int otherIndex = otherNode.Parent.Nodes.IndexOf(otherNode);
                    otherNode.Parent.Nodes.RemoveAt(otherIndex);
                    otherNode.Parent.Nodes.Insert(otherIndex, node);
                    otherNode.Depth  = depth;
                    otherNode.Parent = null;

                    node = otherNode;
                }

                return(node);
            }
            else
            {
                if (!t.IsValueType && t != typeof(string) && !(t.IsArray && (t.GetElementType().IsValueType || t.GetElementType() == typeof(string))))
                {
                    trackedObjects.Add(o, node);
                }
            }
            string nodeName1;
            string typeName1;

            MakeNodeAndTypeName(o, out nodeName1, out typeName1);
            node.Set(nodeName1, typeName1);
            if (typeof(ISmartPtr <AssetsObject>).IsAssignableFrom(t))
            {
                ISmartPtr <AssetsObject> ptr = o as ISmartPtr <AssetsObject>;

                var targetNode = MakeNode(ptr.Target.Object, depth, trackedObjects);
                //targetNode.Text =  targetNode.Text;
                //targetNode.TypeName = o.GetType().GetGenericArguments()[0].Name;
                node.AddNode(targetNode);
            }
            else if (typeof(IEnumerable).IsAssignableFrom(t))
            {
                int i = 0;

                foreach (object obj in o as IEnumerable)
                {
                    Node childNode = MakeNode(obj, depth, trackedObjects);
                    childNode.Text = $"{("[" + i.ToString() + "]").PadRight(3)} {childNode?.Text}";
                    node.AddNode(childNode);
                    i++;
                }
            }
            else if (t.GetProperties().Count() > 0)
            {
                List <PropertyInfo> props = new List <PropertyInfo>(t.GetProperties());

                foreach (PropertyInfo prop in props)
                {
                    object propValue = null;
                    try
                    {
                        //filters just to stop the exceptions, even though they're handled
                        if (!(prop.Name == "Data" && o.GetType() != typeof(AssetsObject)) &&
                            !(prop.Name == "ScriptParametersData" && o.GetType() != typeof(MonoBehaviourObject)))

                        {
                            propValue = prop.GetValue(o, null);
                        }
                    }
                    catch (Exception ex)
                    {
                        QuestomAssets.Log.LogErr($"Failed loading property {prop.Name} on object type {o.GetType().Name}", ex);
                        node.AddNode(new Node()
                        {
                            Text = $"{prop.Name}: (Inaccessible)", TypeName = "", Depth = depth, ParentPropertyName = prop.Name
                        });
                        continue;
                    }

                    if (propValue != null && typeof(IObjectInfo <AssetsObject>).IsAssignableFrom(propValue.GetType()) && prop.Name == nameof(AssetsObject.ObjectInfo))
                    {
                        continue;
                    }

                    if (prop.PropertyType.IsValueType || prop.PropertyType == typeof(string) || (prop.PropertyType.IsArray && (prop.PropertyType.GetElementType().IsValueType || prop.PropertyType.GetElementType() == typeof(string))))
                    {
                        continue;
                    }
                    var childNode = MakeNode(propValue, depth, trackedObjects);
                    childNode.ParentPropertyName = prop.Name;
                    //stupid place for this
                    if (typeof(IEnumerable).IsAssignableFrom(propValue?.GetType()))
                    {
                        foreach (Node n in childNode.Nodes)
                        {
                            if (string.IsNullOrWhiteSpace(n.ParentPropertyName))
                            {
                                n.ParentPropertyName = prop.Name;
                            }
                        }
                    }
                    if (string.IsNullOrEmpty(childNode.Text))
                    {
                        childNode.Text = $"{prop.Name}: {propValue?.GetType()?.Name ?? "((null)"}";
                    }
                    else
                    {
                        childNode.Text = $"{prop.Name}: {childNode.Text}";
                    }
                    node.AddNode(childNode);
                }
            }
            else
            {
                node.Set($"{o}", t.Name);
            }

            return(node);
        }
示例#15
0
        public static void MakeNodeAndTypeName(object o, out string nodeName, out string typeName)
        {
            Type t = o.GetType();

            if (typeof(IObjectInfo <AssetsObject>).IsAssignableFrom(t))
            {
                o = (o as IObjectInfo <AssetsObject>).Object;
                t = o.GetType();
            }
            if (t.IsValueType)
            {
                nodeName = $"{o}";
                typeName = t.Name;
            }
            else if (t == typeof(string))
            {
                nodeName = $"\"{o}\"";
                typeName = t.Name;
            }
            else if (t.IsArray && (t.GetElementType().IsValueType || t.GetElementType() == typeof(string)))
            {
                nodeName = $"{o}";
                typeName = t.Name;
            }
            else if (typeof(ISmartPtr <AssetsObject>).IsAssignableFrom(t))
            {
                ISmartPtr <AssetsObject> ptr = o as ISmartPtr <AssetsObject>;

                nodeName = $"PtrTo F:{ptr.FileID} P:{ptr.PathID} ({o.GetType().GetGenericArguments()[0].Name})";
                typeName = o.GetType().GetGenericArguments()[0].Name;
            }
            else if (typeof(IEnumerable).IsAssignableFrom(t))
            {
                int i            = 0;
                var thisNodeName = "";
                if (t.IsGenericType)
                {
                    thisNodeName = "[List<";
                    //half assed, only 1 generic arg to keep it simple
                    var genArg = t.GetGenericArguments()[0];

                    if (genArg.IsGenericType)
                    {
                        thisNodeName += genArg.GetGenericTypeDefinition().Name;

                        thisNodeName  = thisNodeName.Substring(0, thisNodeName.LastIndexOf('`'));
                        thisNodeName += "<";
                        thisNodeName += genArg.GetGenericArguments()[0].Name;
                        thisNodeName += ">";
                        //nodeName = nodeName.Substring(0, nodeName.Length - 1);
                        //nodeName += string.Join(", ", t.GetGenericArguments().Select(x => x.Name));
                        //nodeName += ">";
                    }
                    else
                    {
                        thisNodeName += genArg.Name;
                    }
                    thisNodeName += ">]";
                }
                else
                {
                    thisNodeName = "[List]";
                }
                nodeName = thisNodeName;
                typeName = t.Name;
            }
            else if (t.GetProperties().Count() > 0)
            {
                if (typeof(AssetsObject).IsAssignableFrom(t))
                {
                    var ao = o as AssetsObject;
                    nodeName = $"{ao.ObjectInfo.ObjectID,5} {t.Name} {((o is IHaveName) ? (": " + (o as IHaveName)?.Name) : "")}";
                    typeName = ao.GetType().Name;
                }
                else
                {
                    nodeName = o.GetType().Name;
                    typeName = o.GetType().Name;
                }
            }
            else
            {
                nodeName = $"{o}";
                typeName = t.Name;
            }
        }
示例#16
0
        public static Node MakeNode(object o, int depth = 0, Dictionary <object, Node> trackedObjects = null)
        {
            if (trackedObjects == null)
            {
                trackedObjects = new Dictionary <object, Node>();
            }
            else
            {
                depth += 1;
            }

            Node node = null;

            if (o == null)
            {
                node = new Node()
                {
                    Text = "(null)", TypeName = null, Obj = null, Depth = depth
                };
                return(node);
            }
            Type t = o.GetType();

            if (typeof(IObjectInfo <AssetsObject>).IsAssignableFrom(t))
            {
                o = (o as IObjectInfo <AssetsObject>).Object;
                t = o.GetType();
            }
            node = new Node(o, depth);
            if (!t.IsValueType && t != typeof(string) && !(t.IsArray && (t.GetElementType().IsValueType || t.GetElementType() == typeof(string))) &&
                trackedObjects.ContainsKey(o))
            {
                var otherNode = trackedObjects[o];
                node.Text       = "";
                node.StubToNode = otherNode;


                if (otherNode.Depth > depth)
                {
                    node.Depth              = otherNode.Depth;
                    node.Parent             = otherNode.Parent;
                    node.ParentPropertyName = otherNode.ParentPropertyName;
                    int otherIndex = otherNode.Parent.Nodes.IndexOf(otherNode);
                    otherNode.Parent.Nodes.RemoveAt(otherIndex);
                    otherNode.Parent.Nodes.Insert(otherIndex, node);
                    otherNode.Depth  = depth;
                    otherNode.Parent = null;

                    node = otherNode;
                }

                return(node);
            }
            else
            {
                if (!t.IsValueType && t != typeof(string) && !(t.IsArray && (t.GetElementType().IsValueType || t.GetElementType() == typeof(string))))
                {
                    trackedObjects.Add(o, node);
                }
            }

            if (t.IsValueType)
            {
                node.Set($"{o}", t.Name);
            }
            else if (t == typeof(string))
            {
                node.Set($"\"{o}\"", t.Name);
            }
            else if (t.IsArray && (t.GetElementType().IsValueType || t.GetElementType() == typeof(string)))
            {
                node.Set($"{o}", t.Name);
            }
            else if (typeof(ISmartPtr <AssetsObject>).IsAssignableFrom(t))
            {
                ISmartPtr <AssetsObject> ptr = o as ISmartPtr <AssetsObject>;

                node.Set($"PtrTo F:{ptr.FileID} P:{ptr.PathID} ({o.GetType().GetGenericArguments()[0].Name})", o.GetType().GetGenericArguments()[0].Name);
                var targetNode = MakeNode(ptr.Target.Object, depth, trackedObjects);
                //targetNode.Text =  targetNode.Text;
                //targetNode.TypeName = o.GetType().GetGenericArguments()[0].Name;
                node.AddNode(targetNode);
            }
            else if (typeof(IEnumerable).IsAssignableFrom(t))
            {
                int i        = 0;
                var nodeName = "";
                if (t.IsGenericType)
                {
                    nodeName = "[List<";
                    //half assed, only 1 generic arg to keep it simple
                    var genArg = t.GetGenericArguments()[0];

                    if (genArg.IsGenericType)
                    {
                        nodeName += genArg.GetGenericTypeDefinition().Name;

                        nodeName  = nodeName.Substring(0, nodeName.LastIndexOf('`'));
                        nodeName += "<";
                        nodeName += genArg.GetGenericArguments()[0].Name;
                        nodeName += ">";
                        //nodeName = nodeName.Substring(0, nodeName.Length - 1);
                        //nodeName += string.Join(", ", t.GetGenericArguments().Select(x => x.Name));
                        //nodeName += ">";
                    }
                    else
                    {
                        nodeName += genArg.Name;
                    }
                    nodeName += ">]";
                }
                else
                {
                    nodeName = "[List]";
                }
                node.Set(nodeName, t.Name);
                foreach (object obj in o as IEnumerable)
                {
                    Node childNode = MakeNode(obj, depth, trackedObjects);
                    childNode.Text = $"{("[" + i.ToString() + "]").PadRight(3)} {childNode?.Text}";
                    node.AddNode(childNode);
                    i++;
                }
            }
            else if (t.GetProperties().Count() > 0)
            {
                if (typeof(AssetsObject).IsAssignableFrom(t))
                {
                    var ao = o as AssetsObject;
                    node.Set($"{ao.ObjectInfo.ObjectID,5} {t.Name} {((o is IHaveName) ? (": " + (o as IHaveName)?.Name) : "")}", ao.GetType().Name);
                }
                else
                {
                    node.Set(o.GetType().Name, o.GetType().Name);
                }

                List <PropertyInfo> props = new List <PropertyInfo>(t.GetProperties());

                foreach (PropertyInfo prop in props)
                {
                    object propValue = null;
                    try
                    {
                        //filters just to stop the exceptions, even though they're handled
                        if (!(prop.Name == "Data" && o.GetType() != typeof(AssetsObject)) &&
                            !(prop.Name == "ScriptParametersData" && o.GetType() != typeof(MonoBehaviourObject)))

                        {
                            propValue = prop.GetValue(o, null);
                        }
                    }
                    catch (Exception ex)
                    {
                        QuestomAssets.Log.LogErr($"Failed loading property {prop.Name} on object type {o.GetType().Name}", ex);
                        node.AddNode(new Node()
                        {
                            Text = $"{prop.Name}: (Inaccessible)", TypeName = "", Depth = depth, ParentPropertyName = prop.Name
                        });
                        continue;
                    }

                    if (propValue != null && typeof(IObjectInfo <AssetsObject>).IsAssignableFrom(propValue.GetType()) && prop.Name == nameof(AssetsObject.ObjectInfo))
                    {
                        continue;
                    }

                    var childNode = MakeNode(propValue, depth, trackedObjects);
                    childNode.ParentPropertyName = prop.Name;
                    //stupid place for this
                    if (typeof(IEnumerable).IsAssignableFrom(propValue?.GetType()))
                    {
                        foreach (Node n in childNode.Nodes)
                        {
                            if (string.IsNullOrWhiteSpace(n.ParentPropertyName))
                            {
                                n.ParentPropertyName = prop.Name;
                            }
                        }
                    }
                    if (string.IsNullOrEmpty(childNode.Text))
                    {
                        childNode.Text = $"{prop.Name}: {propValue?.GetType()?.Name ?? "((null)"}";
                    }
                    else
                    {
                        childNode.Text = $"{prop.Name}: {childNode.Text}";
                    }
                    node.AddNode(childNode);
                }
            }
            else
            {
                node.Set($"{o}", t.Name);
            }

            return(node);
        }