public static SDEFVariant ReadData(BinaryStream bs, SDEFMetaDataEntry entry, ValueType valType)
        {
            SDEFVariant variant;

            if (valType == ValueType.Float)
            {
                variant = new SDEFVariant(bs.ReadSingle());
            }
            else if (valType == ValueType.Int)
            {
                variant = new SDEFVariant(bs.ReadInt32());
            }
            else if (valType == ValueType.Byte)
            {
                variant = new SDEFVariant((byte)bs.ReadByte());
            }
            else if (valType == ValueType.Bool)
            {
                variant = new SDEFVariant(bs.ReadBoolean(BooleanCoding.Byte));
            }
            else if (valType == ValueType.UInt)
            {
                variant = new SDEFVariant(bs.ReadUInt32());
            }
            else if (valType == ValueType.Double)
            {
                variant = new SDEFVariant(bs.ReadDouble());
            }
            else if (valType == ValueType.SByte)
            {
                variant = new SDEFVariant((sbyte)bs.ReadByte());
            }
            else if (valType == ValueType.ULong)
            {
                variant = new SDEFVariant(bs.ReadUInt64());
            }
            else if (valType == ValueType.String)
            {
                variant = new SDEFVariant(bs.ReadString(StringCoding.Int32CharCount));
            }
            else
            {
                throw new InvalidDataException($"Encountered unsupported type {valType} at 0x{bs.Position:X2}");
            }

            return(variant);
        }
        public static PseudoReflectionObject FromFile(string path)
        {
            var bytes = File.ReadAllBytes(path);

            // Read the sdef type metadata
            using (var bs = new BinaryStream(new MemoryStream(bytes)))
            {
                SDEFMetaData sdef = new SDEFMetaData();
                if (bs.ReadString(4) != MAGIC)
                {
                    throw new InvalidDataException("Not a SDEF file.");
                }

                uint unk = bs.ReadUInt32();
                int  fixedArrLengthVersion;
                if (unk >= 1)                  // GT7SP Uses that
                {
                    fixedArrLengthVersion = 1; // By default seems like its fixed arrays
                    uint unkSize = bs.ReadUInt32();
                    bs.Position += unkSize;    // Not figured
                }
                else
                {
                    // When 1, all arrays are fixed length and provided in the type metadata.
                    fixedArrLengthVersion = bs.ReadInt32();
                    if (fixedArrLengthVersion >= 1)
                    {
                        bs.ReadByte(); // Empty
                    }
                }

                int catCount = bs.ReadInt32();

                for (int i = 0; i < catCount; i++)
                {
                    int strLength    = bs.ReadInt32();
                    var categoryName = bs.ReadString(strLength - 1); bs.Position += 1; // Null

                    var category = new SDEFMetaDataCategory();
                    category.Name = categoryName;
                    sdef.Categories.Add(category);
                    int entryCount = bs.ReadInt32();
                    for (int j = 0; j < entryCount; j++)
                    {
                        int entryNameLength = bs.ReadInt32();
                        var entryName       = bs.ReadString(entryNameLength - 1); bs.Position += 1; // Null

                        var entry = new SDEFMetaDataEntry();
                        entry.Name = entryName;
                        category.Entries.Add(entry);

                        entry.TypeOrIndex   = bs.ReadUInt16();
                        entry.HasCustomType = bs.ReadBoolean(BooleanCoding.Word);

                        // If its an array, additional data is stored to know if its an array of types rather than raw values
                        if (entry.TypeOrIndex == (int)(ValueType.Array))
                        {
                            if (!entry.HasCustomType)
                            {
                                entry.ArrayCategoryIndex = bs.ReadUInt16();
                                entry.ArrayHasCustomType = bs.ReadBoolean(BooleanCoding.Word);

                                entry.ArrayLength = bs.ReadUInt32(); // 0 when version is 0 - its variable
                            }
                        }
                    }
                }

                // Final data is pretty much which type is first
                sdef.MasterTypeIndexOrID = bs.ReadUInt16();
                sdef.MasterHasCustomType = bs.ReadBoolean(BooleanCoding.Word);

                // The data part is the tree structure reassembling & data
                var def = new PseudoReflectionObject();
                def.Version = fixedArrLengthVersion;
                var mainCategory = sdef.Categories[sdef.MasterTypeIndexOrID];

                def.ParameterRoot = new SDEFParam();
                def.ParameterRoot.CustomTypeName = mainCategory.Name;
                def.ParameterRoot.NodeType       = NodeType.CustomType;

                int depth = 0;
                Traverse(bs, fixedArrLengthVersion, def, def.ParameterRoot, sdef, mainCategory, ref depth);
                return(def);
            }
        }