Example #1
0
            public TagStructureAttribute GetTagStructureAttribute(Type type, CacheVersion version = CacheVersion.Unknown)
            {
                if (!TagStructureAttributes.TryGetValue(type, out TagStructureAttribute attribute))
                {
                    lock (TagStructureAttributes)
                    {
                        if (!TagStructureAttributes.TryGetValue(type, out attribute))
                        {
                            TagStructureAttributes[type] = attribute = GetStructureAttribute();
                        }
                    }
                }
                return(attribute);

                TagStructureAttribute GetStructureAttribute()
                {
                    // First match against any TagStructureAttributes that have version restrictions
                    var attrib = type.GetCustomAttributes(typeof(TagStructureAttribute), false)
                                 .Cast <TagStructureAttribute>()
                                 .Where(a => a.MinVersion != CacheVersion.Unknown || a.MaxVersion != CacheVersion.Unknown)
                                 .FirstOrDefault(a => CacheVersionDetection.IsBetween(version, a.MinVersion, a.MaxVersion));

                    // If nothing was found, find the first attribute without any version restrictions
                    return(attrib ?? type.GetCustomAttributes(typeof(TagStructureAttribute), false)
                           .Cast <TagStructureAttribute>()
                           .FirstOrDefault(a => a.MinVersion == CacheVersion.Unknown && a.MaxVersion == CacheVersion.Unknown));
                }
            }
Example #2
0
        public static uint GetSize(this Type type, CacheVersion version = CacheVersion.Unknown)
        {
            if (TypeSizes.ContainsKey(type.FullName))
            {
                return(TypeSizes[type.FullName]);
            }

            var attr = type.GetCustomAttributes(typeof(TagStructureAttribute), false)
                       .Cast <TagStructureAttribute>()
                       .FirstOrDefault(a => version == CacheVersion.Unknown || CacheVersionDetection.IsBetween(version, a.MinVersion, a.MaxVersion));

            if (attr == null)
            {
                throw new NotSupportedException(type.FullName);
            }

            return(TypeSizes[type.FullName] = attr.Size);
        }
        /// <summary>
        /// Builds the <see cref="Tags.TagFieldInfo"/> <see cref="List{T}"/> to be enumerated.
        /// </summary>
        private void Build()
        {
            uint offset = 0;

            // Build the field list. Scan through the type's inheritance
            // hierarchy and add any fields belonging to tag structures.
            foreach (var type in Info.Types.Reverse <Type>())
            {
                // Ensure that fields are in declaration order - GetFields does NOT guarantee
                foreach (var field in type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly).OrderBy(i => i.MetadataToken))
                {
                    var attr = TagStructure.GetTagFieldAttribute(type, field);

                    if ((attr.Version != CacheVersion.Unknown && attr.Version == Info.Version) ||
                        (attr.Version == CacheVersion.Unknown && CacheVersionDetection.IsBetween(Info.Version, attr.MinVersion, attr.MaxVersion)))
                    {
                        CreateTagFieldInfo(field, attr, Info.Version, ref offset);
                    }
                }
            }
        }
Example #4
0
        /// <summary>
        /// Deserializes a property of a structure.
        /// </summary>
        /// <param name="reader">The reader.</param>
        /// <param name="context">The serialization context to use.</param>
        /// <param name="instance">The instance to store the property to.</param>
        /// <param name="tagFieldInfo">The active element enumerator.</param>
        /// <param name="baseOffset">The offset of the start of the structure.</param>
        /// <exception cref="System.InvalidOperationException">Offset for property is outside of its structure</exception>
        public void DeserializeProperty(EndianReader reader, ISerializationContext context, object instance, TagFieldInfo tagFieldInfo, long baseOffset)
        {
            var attr = tagFieldInfo.Attribute;

            if (attr.Flags.HasFlag(TagFieldFlags.Runtime))
            {
                return;
            }

            if (tagFieldInfo.Attribute.Flags.HasFlag(TagFieldFlags.Padding))
            {
                reader.BaseStream.Position += tagFieldInfo.Attribute.Length;
            }
            else
            {
                if ((attr.Version != CacheVersion.Unknown && attr.Version == Version) ||
                    (attr.Version == CacheVersion.Unknown && CacheVersionDetection.IsBetween(Version, attr.MinVersion, attr.MaxVersion)))
                {
                    var value = DeserializeValue(reader, context, attr, tagFieldInfo.FieldType);
                    tagFieldInfo.SetValue(instance, value);
                }
            }
        }
Example #5
0
        /// <summary>
        /// Gets the size of a tag-field.
        /// </summary>
        /// <param name="type">The <see cref="Type"/> of the field.</param>
        /// <param name="attr">The <see cref="TagFieldAttribute"/> of the field.</param>
        /// <param name="targetVersion">The <see cref="CacheVersion"/> to target.</param>
        /// <returns></returns>
        public static uint GetFieldSize(Type type, TagFieldAttribute attr, CacheVersion targetVersion)
        {
            switch (Type.GetTypeCode(type))
            {
            case TypeCode.Boolean:
            case TypeCode.SByte:
            case TypeCode.Byte:
                return(0x01);

            case TypeCode.Char:
            case TypeCode.Int16:
            case TypeCode.UInt16:
                return(0x02);

            case TypeCode.Single:
            case TypeCode.Int32:
            case TypeCode.UInt32:
            case TypeCode.Object when attr != null && attr.Flags.HasFlag(TagFieldFlags.Pointer):
            case TypeCode.Object when type == typeof(Tag):
            case TypeCode.Object when type == typeof(CacheAddress):
            case TypeCode.Object when type == typeof(CachedTagInstance) && attr.Flags.HasFlag(TagFieldFlags.Short):
            //case TypeCode.Object when type == typeof(RgbColor):
            case TypeCode.Object when type == typeof(ArgbColor):
            case TypeCode.Object when type == typeof(Point2d):
            case TypeCode.Object when type == typeof(StringId):
            case TypeCode.Object when type == typeof(Angle):
            case TypeCode.Object when type == typeof(VertexShaderReference):
            case TypeCode.Object when type == typeof(PixelShaderReference):
                return(0x04);

            case TypeCode.Double:
            case TypeCode.Int64:
            case TypeCode.UInt64:
            case TypeCode.Object when type == typeof(CachedTagInstance) && targetVersion != CacheVersion.Unknown && CacheVersionDetection.IsBetween(targetVersion, CacheVersion.Halo2Xbox, CacheVersion.Halo2Vista):
            case TypeCode.Object when type == typeof(byte[]) && targetVersion != CacheVersion.Unknown && CacheVersionDetection.IsBetween(targetVersion, CacheVersion.Halo2Xbox, CacheVersion.Halo2Vista):
            case TypeCode.Object when type == typeof(Rectangle2d):
            case TypeCode.Object when type == typeof(RealEulerAngles2d):
            case TypeCode.Object when type == typeof(RealPoint2d):
            case TypeCode.Object when type == typeof(RealVector2d):
            case TypeCode.Object when type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List <>) && targetVersion != CacheVersion.Unknown && CacheVersionDetection.IsBetween(targetVersion, CacheVersion.Halo2Xbox, CacheVersion.Halo2Vista):
            case TypeCode.Object when type.IsGenericType && type.GetGenericTypeDefinition() == typeof(TagBlock <>) && targetVersion != CacheVersion.Unknown && CacheVersionDetection.IsBetween(targetVersion, CacheVersion.Halo2Xbox, CacheVersion.Halo2Vista):
                return(0x08);

            case TypeCode.Object when type == typeof(RealRgbColor):
            case TypeCode.Object when type == typeof(RealEulerAngles3d):
            case TypeCode.Object when type == typeof(RealPoint3d):
            case TypeCode.Object when type == typeof(RealVector3d):
            case TypeCode.Object when type == typeof(RealPlane2d):
            case TypeCode.Object when type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List <>) && CacheVersionDetection.IsBetween(targetVersion, CacheVersion.Halo3Retail, CacheVersion.Unknown):
            case TypeCode.Object when type.IsGenericType && type.GetGenericTypeDefinition() == typeof(TagBlock <>) && CacheVersionDetection.IsBetween(targetVersion, CacheVersion.Halo3Retail, CacheVersion.Unknown):
                return(0x0C);

            case TypeCode.Decimal:
            case TypeCode.Object when type == typeof(CachedTagInstance) && CacheVersionDetection.IsBetween(targetVersion, CacheVersion.Halo3Retail, CacheVersion.Unknown):
            case TypeCode.Object when type == typeof(RealArgbColor):
            case TypeCode.Object when type == typeof(RealQuaternion):
            case TypeCode.Object when type == typeof(RealPlane3d):
                return(0x10);

            case TypeCode.Object when type == typeof(byte[]) && CacheVersionDetection.IsBetween(targetVersion, CacheVersion.Halo3Retail, CacheVersion.Unknown):
                return(0x14);

            case TypeCode.Object when type == typeof(RealMatrix4x3):
                return(0x30);

            case TypeCode.Object when type == typeof(DatumIndex):
                return(sizeof(uint));

            case TypeCode.String:
            case TypeCode.Object when type.IsArray:
                return((uint)attr.Length);

            case TypeCode.Object when type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Bounds <>):
                return(TagFieldInfo.GetFieldSize(type.GenericTypeArguments[0], attr, targetVersion) * 2);

            case TypeCode.Object when type.IsEnum:
                return(TagFieldInfo.GetFieldSize(type.GetEnumUnderlyingType(), attr, targetVersion));

            // Assume the field is a structure
            default:
                return(TagStructure.GetTagStructureInfo(type, targetVersion).TotalSize);
            }
        }
Example #6
0
        private static void CompareBlocks(object dataA, CacheVersion versionA, object dataB, CacheVersion versionB, TagVersionMap result, Queue <QueuedTag> tagQueue)
        {
            if (dataA == null || dataB == null)
            {
                return;
            }

            if (dataA is CachedTagInstance)
            {
                // If the objects are tags, then we've found a match
                var tagA = dataA as CachedTagInstance;
                var tagB = dataB as CachedTagInstance;

                if (tagA.Group.Tag != tagB.Group.Tag)
                {
                    return;
                }

                if (tagA.IsInGroup("rmt2") || tagA.IsInGroup("rmdf") || tagA.IsInGroup("vtsh") || tagA.IsInGroup("pixl") || tagA.IsInGroup("rm  ") || tagA.IsInGroup("bitm"))
                {
                    return;
                }

                var translated = result.Translate(versionA, tagA.Index, versionB);
                if (translated >= 0)
                {
                    return;
                }

                result.Add(versionA, tagA.Index, versionB, tagB.Index);
                tagQueue.Enqueue(new QueuedTag {
                    Tag = tagB
                });
            }

            else if (dataA is IList) // Compare lists and arrays
            {
                var collectionA = dataA as IList;
                var collectionB = dataB as IList;

                if (collectionA.Count == 0 || collectionA[0].GetType().IsPrimitive)
                {
                    return;
                }

                // If the sizes are different, we probably can't compare them
                if (collectionA.Count != collectionB.Count)
                {
                    return;
                }

                // Compare each element
                for (var i = 0; i < collectionA.Count; i++)
                {
                    var itemA = collectionA[i];
                    var itemB = collectionB[i];
                    CompareBlocks(itemA, versionA, itemB, versionB, result, tagQueue);
                }
            }

            else if (dataA is TagStructure)
            {
                var tagFieldInfosA = TagStructure.GetTagFieldEnumerable(dataA.GetType(), versionA);
                var tagFieldInfosB = TagStructure.GetTagFieldEnumerable(dataB.GetType(), versionB);

                // The objects are structures
                for (int a = 0, b = 0; a < tagFieldInfosA.Count && b < tagFieldInfosB.Count; a++, b++)
                {
                    var tagFieldInfoA = tagFieldInfosA[a];
                    var tagFieldInfoB = tagFieldInfosB[b];

                    // Keep going on the left until the field is on the right
                    while (!CacheVersionDetection.IsBetween(versionB, tagFieldInfoA.Attribute.MinVersion, tagFieldInfoA.Attribute.MaxVersion))
                    {
                        if (++a < tagFieldInfosA.Count)
                        {
                            tagFieldInfoA = tagFieldInfosA[a];
                        }
                        else
                        {
                            return;
                        }
                    }

                    // Keep going on the right until the field is on the left
                    while (!CacheVersionDetection.IsBetween(versionA, tagFieldInfoB.Attribute.MinVersion, tagFieldInfoB.Attribute.MaxVersion))
                    {
                        if (++b < tagFieldInfosB.Count)
                        {
                            tagFieldInfoB = tagFieldInfosB[b];
                        }
                        else
                        {
                            return;
                        }
                    }

                    if (tagFieldInfoA.MetadataToken != tagFieldInfoB.MetadataToken)
                    {
                        throw new InvalidOperationException("WTF, left and right fields don't match!");
                    }

                    // Process the fields
                    var fieldDataA = tagFieldInfoA.GetValue(dataA);
                    var fieldDataB = tagFieldInfoB.GetValue(dataB);
                    CompareBlocks(fieldDataA, versionA, fieldDataB, versionB, result, tagQueue);
                }
            }
        }
Example #7
0
        private ExportedType ExportType(Type type, FileInfo file)
        {
            if (file.Name == ".hpp")
            {
                return(null);
            }

            if (ExportedTypes.ContainsKey(type.FullName))
            {
                return(ExportedTypes[type.FullName]);
            }

            using (var writer = file.CreateText())
            {
                writer.WriteLine("#pragma once");
                writer.WriteLine("#include \"../cseries/cseries.hpp\"");
                writer.WriteLine("#include \"../math/real_math.hpp\"");
                writer.WriteLine("#include \"../tag_files/tag_groups.hpp\"");

                foreach (var field in type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly).OrderBy(i => i.MetadataToken))
                {
                    if (field.FieldType.IsPrimitive)
                    {
                        continue;
                    }

                    var info = type.GetCustomAttributes <TagStructureAttribute>(false).Where(attr =>
                                                                                             CacheVersionDetection.IsBetween(CacheContext.Version, attr.MinVersion, attr.MaxVersion)).First();

                    var typeBaseName = field.FieldType.FullName.ToSnakeCase().Replace("._", ".");
                    typeBaseName = typeBaseName.Substring(typeBaseName.LastIndexOf('.') + 1).Replace("+", "");

                    while (typeBaseName.StartsWith("_"))
                    {
                        typeBaseName = typeBaseName.Substring(1);
                    }

                    ExportedType typeInfo = null;

                    if (ExportedTypes.ContainsKey(field.FieldType.FullName))
                    {
                        typeInfo = ExportedTypes[field.FieldType.FullName];

                        continue;
                    }

                    if (field.FieldType.IsArray)
                    {
                    }
                    else if (field.FieldType.IsGenericType && field.FieldType.GetGenericTypeDefinition() == typeof(List <>))
                    {
                        typeBaseName = field.FieldType.GetGenericArguments()[0].FullName
                                       .ToSnakeCase().Replace("._", ".").Replace("+", "");

                        typeBaseName = typeBaseName.Substring(typeBaseName.LastIndexOf('.') + 1);

                        while (typeBaseName.StartsWith("_"))
                        {
                            typeBaseName = typeBaseName.Substring(1);
                        }

                        ExportedTypes[field.FieldType.FullName] = new ExportedType
                        {
                            Name = $"c_tag_block<s_{typeBaseName}>",
                            File = file.FullName
                        };

                        typeInfo = ExportedTypes[field.FieldType.GetGenericArguments()[0].FullName] = new ExportedType
                        {
                            Name = $"s_{typeBaseName}",
                            File = file.FullName
                        };
                    }
                    else if (field.FieldType.IsEnum)
                    {
                        if (typeBaseName.EndsWith("_value"))
                        {
                            typeBaseName = typeBaseName.Substring(0, typeBaseName.Length - 6);
                        }

                        typeInfo = ExportedTypes[field.FieldType.FullName] = new ExportedType
                        {
                            Name = $"e_{typeBaseName}",
                            File = file.FullName
                        };
                    }
                    else if (field.FieldType.IsSubclassOf(typeof(TagStructure)))
                    {
                        typeInfo = ExportedTypes[field.FieldType.FullName] = new ExportedType
                        {
                            Name = $"s_{typeBaseName}",
                            File = file.FullName
                        };
                    }

                    if (field.FieldType.IsArray)
                    {
                    }
                    else if (field.FieldType.IsGenericType && field.FieldType.GetGenericTypeDefinition() == typeof(List <>))
                    {
                        writer.WriteLine();

                        if (typeInfo.Name.Contains('.'))
                        {
                            break;
                        }

                        writer.WriteLine($"struct {typeInfo.Name}");
                        writer.WriteLine("{");

                        writer.WriteLine("};");
                    }
                    else if (field.FieldType.IsEnum)
                    {
                        //
                        // Export the enum
                        //

                        writer.WriteLine();

                        writer.WriteLine($"enum {typeInfo.Name}");
                        writer.WriteLine("{");

                        var isFlags = field.FieldType.GetCustomAttribute <FlagsAttribute>(false) != null;

                        foreach (var option in Enum.GetValues(field.FieldType))
                        {
                            var optionValue = (int)Convert.ChangeType(option, typeof(int));

                            if (isFlags)
                            {
                                if (optionValue == 0)
                                {
                                    continue;
                                }

                                var bitSet = false;
                                for (var i = 0; i < 32; i++)
                                {
                                    if ((optionValue & (1 << i)) != 0)
                                    {
                                        if (bitSet)
                                        {
                                            throw new NotSupportedException("multiple bits set in flags enum");
                                        }

                                        optionValue = i;
                                        bitSet      = true;
                                    }
                                }
                            }

                            writer.WriteLine($"\t_{typeBaseName}_{option.ToString().ToSnakeCase()} = {optionValue},");
                        }

                        writer.WriteLine("};");
                    }
                    else if (field.FieldType.IsSubclassOf(typeof(TagStructure)))
                    {
                        writer.WriteLine();

                        writer.WriteLine($"struct {typeInfo.Name}");
                        writer.WriteLine("{");
                        writer.WriteLine("};");
                    }
                }
            }

            return(ExportedTypes.ContainsKey(type.FullName) ? ExportedTypes[type.FullName] : null);
        }
Example #8
0
        public override object Execute(List <string> args)
        {
            if (args.Count != 1)
            {
                return(false);
            }

            var destDir = new DirectoryInfo(args[0]);

            if (!destDir.Exists)
            {
                destDir.Create();
            }

            foreach (var entry in TagGroup.Instances)
            {
                if (entry.Key.Value == -1)
                {
                    continue;
                }

                var tagGroupName     = CacheContext.GetString(entry.Value.Name).ToSnakeCase();
                var tagStructureInfo = TagStructure.GetTagStructureInfo(TagDefinition.Find(entry.Key), CacheContext.Version);

                foreach (var type in tagStructureInfo.Types.Reverse <Type>())
                {
                    var info = type.GetCustomAttributes <TagStructureAttribute>(false).Where(attr =>
                                                                                             CacheVersionDetection.IsBetween(CacheContext.Version, attr.MinVersion, attr.MaxVersion)).First();

                    if (info.Name == null)
                    {
                        Console.WriteLine($"WARNING: {type.FullName} has no tag structure name defined!");
                        continue;
                    }

                    ExportType(type, new FileInfo(Path.Combine(destDir.FullName, $"{info.Name}.hpp")));
                }
            }

            return(true);
        }