private static Struct ParseStruct(TypeMappings context, FArchive Ar, IReadOnlyList <string> nameLut)
        {
            var name      = Ar.ReadName(nameLut) !;
            var superType = Ar.ReadName(nameLut);

            var propertyCount             = Ar.Read <ushort>();
            var serializablePropertyCount = Ar.Read <ushort>();
            var properties = new Dictionary <int, PropertyInfo>();

            for (int i = 0; i < serializablePropertyCount; i++)
            {
                var propInfo = ParsePropertyInfo(Ar, nameLut);
                for (int j = 0; j < propInfo.ArraySize; j++)
                {
                    properties[propInfo.Index + j] = propInfo;
                }
            }
            return(new Struct(context, name, superType, properties, propertyCount));
        }
        protected void AddEnums(string enumsJson, string game)
        {
            if (!MappingsByGame.TryGetValue(game, out TypeMappings mappingsForGame))
            {
                mappingsForGame      = new TypeMappings();
                MappingsByGame[game] = mappingsForGame;
            }

            var token = JArray.Parse(enumsJson);

            foreach (var entry in token)
            {
                if (entry == null)
                {
                    continue;
                }
                var values = entry["values"] !.ToObject <string[]>() !;
                var i      = 0;
                mappingsForGame.Enums[entry["name"] !.ToObject <string>() !] = values.ToDictionary(it => i++);
        protected bool AddStructs(string structsJson, string game)
        {
            if (!MappingsByGame.TryGetValue(game, out TypeMappings mappingsForGame))
            {
                mappingsForGame      = new TypeMappings();
                MappingsByGame[game] = mappingsForGame;
            }

            var token = JArray.Parse(structsJson);

            foreach (var structToken in token)
            {
                if (structToken == null)
                {
                    continue;
                }
                var structEntry = ParseStruct(mappingsForGame, structToken);
                mappingsForGame.Types[structEntry.Name] = structEntry;
            }
            return(true);
        }
        private Struct ParseStruct(TypeMappings context, JToken structToken)
        {
            var name      = structToken["name"] !.ToObject <string>() !;
            var superType = structToken["superType"]?.ToObject <string>();

            var propertiesToken = (JArray)structToken["properties"] !;
            var properties      = new Dictionary <int, PropertyInfo>();

            foreach (var propToken in propertiesToken)
            {
                if (propToken == null)
                {
                    continue;
                }
                var prop = ParsePropertyInfo(propToken);
                for (int i = 0; i < prop.ArraySize; i++)
                {
                    properties[prop.Index + i] = prop;
                }
            }
            var propertyCount = structToken["propertyCount"] !.ToObject <int>() !;

            return(new Struct(context, name, superType, properties, propertyCount));
        }
        public static TypeMappings Parse(FArchive Ar)
        {
            var magic = Ar.Read <ushort>();

            if (magic != FileMagic)
            {
                throw new ParserException(".usmap file has an invalid magic constant");
            }

            var version = Ar.Read <Version>();

            if (version < 0 || version > Version.LATEST)
            {
                throw new ParserException($".usmap has an invalid version {(byte) version}");
            }

            var compression = Ar.Read <ECompressionMethod>();

            var compSize   = Ar.Read <uint>();
            var decompSize = Ar.Read <uint>();

            var data = new byte[decompSize];

            switch (compression)
            {
            case ECompressionMethod.None:
                if (compSize != decompSize)
                {
                    throw new ParserException("No compression: Compression size must be equal to decompression size");
                }
                Ar.Read(data, 0, (int)compSize);
                break;

            case ECompressionMethod.Oodle:
                Oodle.Decompress(Ar.ReadBytes((int)compSize), 0, (int)compSize, data, 0, (int)decompSize);
                break;

            case ECompressionMethod.Brotli:
                throw new NotImplementedException();

            default:
                throw new ParserException($"Invalid compression method {compression}");
            }

            Ar = new FByteArchive(Ar.Name, data);
            var nameSize = Ar.Read <uint>();
            var nameLut  = new List <String>((int)nameSize);

            for (int i = 0; i < nameSize; i++)
            {
                var nameLength = Ar.Read <byte>();
                nameLut.Add(ReadStringUnsafe(Ar, nameLength));
            }

            var enumCount = Ar.Read <uint>();
            var enums     = new Dictionary <string, Dictionary <int, string> >((int)enumCount);

            for (int i = 0; i < enumCount; i++)
            {
                var enumName = Ar.ReadName(nameLut) !;

                var enumNamesSize = Ar.Read <byte>();
                var enumNames     = new Dictionary <int, string>(enumNamesSize);
                for (int j = 0; j < enumNamesSize; j++)
                {
                    var value = Ar.ReadName(nameLut) !;
                    enumNames[j] = value;
                }

                enums.Add(enumName, enumNames);
            }

            var structCount = Ar.Read <uint>();
            var structs     = new Dictionary <string, Struct>();

            var mappings = new TypeMappings(structs, enums);

            for (int i = 0; i < structCount; i++)
            {
                var s = ParseStruct(mappings, Ar, nameLut);
                structs[s.Name] = s;
            }

            return(mappings);
        }