using System; using System.Collections.Generic; using System.Text; namespace UnityFileApi { // A TypeTreeNode represents how a property of a serialized object was written to disk. // See the TextDumper library for an example. public class TypeTreeNode { int m_FirstChildNodeIndex; int m_NextNodeIndex; TypeTreeHandle m_Handle; Lazy> m_Children; Lazy m_CSharpType; Lazy m_hasConstantSize; // The type of the property (basic type like int or float or class names for objects) public readonly string Type; // The name of the property (e.g. m_IndexBuffer for a Mesh or m_Width for a Texture) public readonly string Name; // The size of the property (for basic types only, otherwise -1) public readonly int Size; // The offset of the property (mostly useless). public readonly int Offset; // Flags used for different things (e.g. field is an array, data alignment, etc.) public readonly TypeTreeFlags Flags; public readonly TypeTreeMetaFlags MetaFlags; // Child nodes container. public List Children => m_Children.Value; // True if the field has no child. public bool IsLeaf => m_FirstChildNodeIndex == 0; // True if the field is a basic type. (int, float, char, etc.) public bool IsBasicType => IsLeaf && Size > 0; // True if the field is an array. public bool IsArray => ((int)Flags & (int)TypeTreeFlags.IsArray) != 0; // True if the field is a ManagedReferenceRegistry public bool IsManagedReferenceRegistry => ((int)Flags & (int)TypeTreeFlags.IsManagedReferenceRegistry) != 0; // C# type corresponding to the node type public Type CSharpType => m_CSharpType.Value; // True if the node has a constant size (it contains no array or other containers with variable size). public bool HasConstantSize => m_hasConstantSize.Value; [ThreadStatic] static StringBuilder s_NodeType; [ThreadStatic] static StringBuilder s_NodeName; // Properties are required to initialize the ThreadStatic members. static StringBuilder NodeTypeBuilder { get { if (s_NodeType == null) { s_NodeType = new StringBuilder(512); } return s_NodeType; } } static StringBuilder NodeNameBuilder { get { if (s_NodeName == null) { s_NodeName = new StringBuilder(512); } return s_NodeName; } } internal TypeTreeNode(TypeTreeHandle typeTreeHandle, int nodeIndex) { m_Handle = typeTreeHandle; var r = DllWrapper.GetTypeTreeNodeInfo(m_Handle, nodeIndex, NodeTypeBuilder, NodeTypeBuilder.Capacity, NodeNameBuilder, NodeNameBuilder.Capacity, out Offset, out Size, out Flags, out MetaFlags, out m_FirstChildNodeIndex, out m_NextNodeIndex); UnityFileSystem.HandleErrors(r); Type = NodeTypeBuilder.ToString(); Name = NodeNameBuilder.ToString(); m_Children = new Lazy>(GetChildren); m_CSharpType = new Lazy(GetCSharpType); m_hasConstantSize = new Lazy(GetHasConstantSize); } internal List GetChildren() { var children = new List(); var current = m_FirstChildNodeIndex; while (current != 0) { var child = new TypeTreeNode(m_Handle, current); children.Add(child); current = child.m_NextNodeIndex; } return children; } bool GetHasConstantSize() { if (IsArray || CSharpType == typeof(string)) return false; foreach (var child in Children) { if (!child.HasConstantSize) return false; } return true; } Type GetCSharpType() { switch (Type) { case "int": case "SInt32": case "TypePtr": return typeof(int); case "unsigned int": case "UInt32": return typeof(uint); case "float": return typeof(float); case "double": return typeof(double); case "SInt16": return typeof(short); case "UInt16": return typeof(ushort); case "SInt64": return typeof(long); case "FileSize": case "UInt64": return typeof(ulong); case "SInt8": return typeof(sbyte); case "UInt8": case "char": return typeof(byte); case "bool": return typeof(bool); case "string": return typeof(string); default: { if (Size == 8) { return typeof(long); } else if (Size == 4) { return typeof(int); } else if (Size == 2) { return typeof(short); } else if (Size == 1) { return typeof(sbyte); } return typeof(object); } } throw new Exception($"Unknown type {Type}"); } } }