Example #1
0
        public object ParseArgs(Type type, TagFieldInfo info, List <string> args)
        {
            var    input  = args[0];
            object output = null;

            if (type == typeof(byte))
            {
                if (args.Count != 1)
                {
                    return(false);
                }
                if (!byte.TryParse(input, out byte value))
                {
                    return(false);
                }
                output = value;
            }
            else if (type == typeof(sbyte))
            {
                if (args.Count != 1)
                {
                    return(false);
                }
                if (!sbyte.TryParse(input, out sbyte value))
                {
                    return(false);
                }
                output = value;
            }
            else if (type == typeof(short))
            {
                if (args.Count != 1)
                {
                    return(false);
                }
                if (!short.TryParse(input, out short value))
                {
                    return(false);
                }
                output = value;
            }
            else if (type == typeof(ushort))
            {
                if (args.Count != 1)
                {
                    return(false);
                }
                if (!ushort.TryParse(input, out ushort value))
                {
                    return(false);
                }
                output = value;
            }
            else if (type == typeof(int))
            {
                if (args.Count != 1)
                {
                    return(false);
                }
                if (!int.TryParse(input, out int value))
                {
                    return(false);
                }
                output = value;
            }
            else if (type == typeof(uint))
            {
                if (args.Count != 1)
                {
                    return(false);
                }
                if (!uint.TryParse(input, out uint value))
                {
                    return(false);
                }
                output = value;
            }
            else if (type == typeof(long))
            {
                if (args.Count != 1)
                {
                    return(false);
                }
                if (!long.TryParse(input, out long value))
                {
                    return(false);
                }
                output = value;
            }
            else if (type == typeof(ulong))
            {
                if (args.Count != 1)
                {
                    return(false);
                }
                if (!ulong.TryParse(input, out ulong value))
                {
                    return(false);
                }
                output = value;
            }
            else if (type == typeof(float))
            {
                if (args.Count != 1)
                {
                    return(false);
                }
                if (!float.TryParse(input, out float value))
                {
                    return(false);
                }
                output = value;
            }
            else if (type == typeof(string))
            {
                if (args.Count != 1)
                {
                    return(false);
                }
                output = input;
            }
            else if (type == typeof(CachedTagInstance))
            {
                if (args.Count != 1 || !CacheContext.TryGetTag(input, out var tag))
                {
                    return(false);
                }
                output = tag;
            }
            else if (type == typeof(Tag))
            {
                if (args.Count != 1)
                {
                    return(false);
                }
                if (!CacheContext.TryParseGroupTag(args[0], out var result))
                {
                    Console.WriteLine($"Invalid tag group specifier: {args[0]}");
                    return(false);
                }
                output = result;
            }
            else if (type == typeof(StringId))
            {
                if (args.Count != 1)
                {
                    return(false);
                }
                output = CacheContext.GetStringId(input);
            }
            else if (type == typeof(Angle))
            {
                if (args.Count != 1)
                {
                    return(false);
                }
                if (!float.TryParse(input, out float value))
                {
                    return(false);
                }
                output = Angle.FromDegrees(value);
            }
            else if (type == typeof(RealEulerAngles2d))
            {
                if (args.Count != 2)
                {
                    return(false);
                }
                if (!float.TryParse(args[0], out float yaw) ||
                    !float.TryParse(args[1], out float pitch))
                {
                    return(false);
                }
                output = new RealEulerAngles2d(
                    Angle.FromDegrees(yaw),
                    Angle.FromDegrees(pitch));
            }
            else if (type == typeof(RealEulerAngles3d))
            {
                if (args.Count != 3)
                {
                    return(false);
                }
                if (!float.TryParse(args[0], out float yaw) ||
                    !float.TryParse(args[1], out float pitch) ||
                    !float.TryParse(args[2], out float roll))
                {
                    return(false);
                }
                output = new RealEulerAngles3d(
                    Angle.FromDegrees(yaw),
                    Angle.FromDegrees(pitch),
                    Angle.FromDegrees(roll));
            }
            else if (type == typeof(RealPoint2d))
            {
                if (args.Count != 2)
                {
                    return(false);
                }
                if (!float.TryParse(args[0], out float x) ||
                    !float.TryParse(args[1], out float y))
                {
                    return(false);
                }
                output = new RealPoint2d(x, y);
            }
            else if (type == typeof(RealPoint3d))
            {
                if (args.Count != 3)
                {
                    return(false);
                }
                if (!float.TryParse(args[0], out float x) ||
                    !float.TryParse(args[1], out float y) ||
                    !float.TryParse(args[2], out float z))
                {
                    return(false);
                }
                output = new RealPoint3d(x, y, z);
            }
            else if (type == typeof(RealVector2d))
            {
                if (args.Count != 2)
                {
                    return(false);
                }
                if (!float.TryParse(args[0], out float i) ||
                    !float.TryParse(args[1], out float j))
                {
                    return(false);
                }
                output = new RealVector2d(i, j);
            }
            else if (type == typeof(RealVector3d))
            {
                if (args.Count != 3)
                {
                    return(false);
                }
                if (!float.TryParse(args[0], out float i) ||
                    !float.TryParse(args[1], out float j) ||
                    !float.TryParse(args[2], out float k))
                {
                    return(false);
                }
                output = new RealVector3d(i, j, k);
            }
            else if (type == typeof(RealQuaternion))
            {
                if (args.Count != 4)
                {
                    return(false);
                }
                if (!float.TryParse(args[0], out float i) ||
                    !float.TryParse(args[1], out float j) ||
                    !float.TryParse(args[2], out float k) ||
                    !float.TryParse(args[3], out float w))
                {
                    return(false);
                }
                output = new RealQuaternion(i, j, k, w);
            }
            else if (type == typeof(RealPlane2d))
            {
                if (args.Count != 3)
                {
                    return(false);
                }
                if (!float.TryParse(args[0], out float i) ||
                    !float.TryParse(args[1], out float j) ||
                    !float.TryParse(args[2], out float d))
                {
                    return(false);
                }
                output = new RealPlane2d(i, j, d);
            }
            else if (type == typeof(RealPlane3d))
            {
                if (args.Count != 4)
                {
                    return(false);
                }
                if (!float.TryParse(args[0], out float i) ||
                    !float.TryParse(args[1], out float j) ||
                    !float.TryParse(args[2], out float k) ||
                    !float.TryParse(args[3], out float d))
                {
                    return(false);
                }
                output = new RealPlane3d(i, j, k, d);
            }
            else if (type.IsEnum)
            {
                if (args.Count != 1)
                {
                    return(false);
                }

                var query = args[0];

                object found;

                try
                {
                    found = Enum.Parse(type, query);
                }
                catch
                {
                    found = null;
                }

                var names = Enum.GetNames(type).ToList();

                if (found == null)
                {
                    var nameLow  = query.ToLower();
                    var namesLow = names.Select(i => i.ToLower()).ToList();

                    found = namesLow.Find(n => n == nameLow);

                    if (found == null)
                    {
                        var nameSnake  = query.ToSnakeCase();
                        var namesSnake = names.Select(i => i.ToSnakeCase()).ToList();
                        found = namesSnake.Find(n => n == nameSnake);

                        if (found == null)
                        {
                            Console.WriteLine("Invalid {0} enum option: {1}", type.Name, args[0]);
                            Console.WriteLine("");

                            Console.WriteLine("Valid options:");
                            foreach (var name in Enum.GetNames(type))
                            {
                                var fieldName         = $"{type.FullName}.{name}".Replace("+", ".");
                                var documentationNode = EditTagContextFactory.Documentation.SelectSingleNode($"//member[starts-with(@name, 'F:{fieldName}')]");

                                Console.WriteLine("\t{0} {1}", name,
                                                  documentationNode != null ?
                                                  $":: {documentationNode.FirstChild.InnerText.Replace("\r\n", "").TrimStart().TrimEnd()}" :
                                                  "");
                            }
                            Console.WriteLine();

                            return(false);
                        }
                        else
                        {
                            found = Enum.Parse(type, names[namesSnake.IndexOf((string)found)]);
                        }
                    }
                    else
                    {
                        found = Enum.Parse(type, names[namesLow.IndexOf((string)found)]);
                    }
                }

                output = found;
            }
            else if (type == typeof(Bounds <>))
            {
                var rangeType = type.GenericTypeArguments[0];
                var argCount  = RangeArgCount(rangeType);

                var min = ParseArgs(rangeType, null, args.Take(argCount).ToList());

                if (min.Equals(false))
                {
                    return(false);
                }

                var max = ParseArgs(rangeType, null, args.Skip(argCount).Take(argCount).ToList());

                if (max.Equals(false))
                {
                    return(false);
                }

                output = Activator.CreateInstance(type, new object[] { min, max });
            }
            else if (type.IsArray)
            {
                if (info?.FieldType == typeof(byte[]) && info?.Attribute.Length == 0)
                {   // tag_data field
                    if (args.Count != 1)
                    {
                        return(false);
                    }
                    if (input.Length % 2 != 0)
                    {
                        return(false);
                    }

                    List <byte> bytes = new List <byte>();

                    for (int i = 0; i < input.Length; i = i + 2)
                    {
                        bytes.Add(Convert.ToByte(input.Substring(i, 2), 16));
                    }

                    output = bytes.ToArray();
                }
                else
                {
                    if (info == null || args.Count != info.Attribute.Length)
                    {
                        return(false);
                    }

                    var elementType = info.FieldType.GetElementType();
                    var values      = Array.CreateInstance(elementType, info.Attribute.Length);

                    for (var i = 0; i < info.Attribute.Length; i++)
                    {
                        values.SetValue(Convert.ChangeType(ParseArgs(elementType, null, new List <string> {
                            args[i]
                        }), elementType), i);
                    }

                    return(values);
                }
            }
            else if (type == typeof(RealRgbColor))
            {
                if (args.Count != 3)
                {
                    return(false);
                }
                if (!float.TryParse(args[0], out float i) ||
                    !float.TryParse(args[1], out float j) ||
                    !float.TryParse(args[2], out float k))
                {
                    return(false);
                }
                output = new RealRgbColor(i, j, k);
            }
            else if (type == typeof(ArgbColor))
            {
                if (args.Count != 4)
                {
                    return(false);
                }
                if (!byte.TryParse(args[0], out byte i) ||
                    !byte.TryParse(args[1], out byte j) ||
                    !byte.TryParse(args[2], out byte k) ||
                    !byte.TryParse(args[3], out byte w))
                {
                    return(false);
                }
                output = new ArgbColor(i, j, k, w);
            }
            else if (type == typeof(Bounds <Angle>))
            {
                if (args.Count != 2)
                {
                    return(false);
                }

                if (!float.TryParse(args[0], out float i) ||
                    !float.TryParse(args[1], out float j))
                {
                    return(false);
                }

                output = new Bounds <Angle> {
                    Lower = Angle.FromDegrees(i), Upper = Angle.FromDegrees(j)
                };
            }
            else if (type == typeof(PageableResource))
            {
                if (args.Count < 1 || args.Count > 2)
                {
                    return(false);
                }

                if (args.Count == 1)
                {
                    switch (args[0].ToLower())
                    {
                    case "null":
                        output = null;
                        break;

                    default:
                        output = new FileInfo(args[0]);
                        if (!((FileInfo)output).Exists)
                        {
                            throw new FileNotFoundException(args[0]);
                        }
                        break;
                    }
                }
                else if (args.Count == 2)
                {
                    var resourceLocation = ResourceLocation.None;

                    switch (args[0].ToSnakeCase())
                    {
                    case "resources":
                        resourceLocation = ResourceLocation.Resources;
                        break;

                    case "textures":
                        resourceLocation = ResourceLocation.Textures;
                        break;

                    case "textures_b":
                        resourceLocation = ResourceLocation.TexturesB;
                        break;

                    case "audio":
                        resourceLocation = ResourceLocation.Audio;
                        break;

                    case "resources_b":
                        resourceLocation = ResourceLocation.ResourcesB;
                        break;

                    case "render_models" when CacheContext.Version >= CacheVersion.HaloOnline235640:
                        resourceLocation = ResourceLocation.RenderModels;
                        break;

                    case "lightmaps" when CacheContext.Version >= CacheVersion.HaloOnline235640:
                        resourceLocation = ResourceLocation.Lightmaps;
                        break;

                    default:
                        throw new FormatException($"Invalid resource location: {args[0]}");
                    }

                    var resourceFile = new FileInfo(args[1]);

                    if (!resourceFile.Exists)
                    {
                        throw new FileNotFoundException(args[1]);
                    }

                    output = (resourceLocation, resourceFile);
                }
                else
                {
                    throw new NotImplementedException();
                }
            }
            else
            {
                Console.WriteLine($"ERROR: Not Implemented.");
                return(false);
                // throw new NotImplementedException();
            }

            return(output);
        }
Example #2
0
        public override object Execute(List <string> args)
        {
            if (args.Count < 2)
            {
                return(false);
            }

            var fieldName      = args[0];
            var fieldNameLow   = fieldName.ToLower();
            var fieldNameSnake = fieldName.ToSnakeCase();

            var previousContext   = ContextStack.Context;
            var previousOwner     = Owner;
            var previousStructure = Structure;

            if (fieldName.Contains("."))
            {
                var lastIndex = fieldName.LastIndexOf('.');
                var blockName = fieldName.Substring(0, lastIndex);
                fieldName      = fieldName.Substring(lastIndex + 1, (fieldName.Length - lastIndex) - 1);
                fieldNameLow   = fieldName.ToLower();
                fieldNameSnake = fieldName.ToSnakeCase();

                var command = new EditBlockCommand(ContextStack, CacheContext, Tag, Owner);

                if (command.Execute(new List <string> {
                    blockName
                }).Equals(false))
                {
                    while (ContextStack.Context != previousContext)
                    {
                        ContextStack.Pop();
                    }
                    Owner     = previousOwner;
                    Structure = previousStructure;
                    return(false);
                }

                command = (ContextStack.Context.GetCommand("EditBlock") as EditBlockCommand);

                Owner     = command.Owner;
                Structure = command.Structure;

                if (Owner == null)
                {
                    while (ContextStack.Context != previousContext)
                    {
                        ContextStack.Pop();
                    }
                    Owner     = previousOwner;
                    Structure = previousStructure;
                    return(false);
                }
            }

            var field = TagStructure.GetTagFieldEnumerable(Structure)
                        .Find(f =>
                              f.Name == fieldName ||
                              f.Name.ToLower() == fieldNameLow ||
                              f.Name.ToSnakeCase() == fieldNameSnake);

            if (field == null)
            {
                Console.WriteLine("ERROR: {0} does not contain a field named \"{1}\".", Structure.Types[0].Name, fieldName);
                while (ContextStack.Context != previousContext)
                {
                    ContextStack.Pop();
                }
                Owner     = previousOwner;
                Structure = previousStructure;
                return(false);
            }

            var fieldType  = field.FieldType;
            var fieldAttrs = field.GetCustomAttributes(typeof(TagFieldAttribute), false);
            var fieldAttr  = fieldAttrs?.Length < 1 ? new TagFieldAttribute() : (TagFieldAttribute)fieldAttrs[0];
            var fieldInfo  = new TagFieldInfo(field, fieldAttr, uint.MaxValue, uint.MaxValue);
            var fieldValue = ParseArgs(field.FieldType, fieldInfo, args.Skip(1).ToList());

            if (fieldValue != null && fieldValue.Equals(false))
            {
                while (ContextStack.Context != previousContext)
                {
                    ContextStack.Pop();
                }
                Owner     = previousOwner;
                Structure = previousStructure;
                return(false);
            }

            if (field.FieldType == typeof(PageableResource))
            {
                var ownerValue = field.GetValue(Owner);

                if (fieldValue == null)
                {
                    field.SetValue(Owner, null);
                }
                else if (ownerValue is PageableResource pageable)
                {
                    var newLocation = ResourceLocation.None;

                    FileInfo resourceFile = null;

                    switch (fieldValue)
                    {
                    case FileInfo file:
                        if (!pageable.GetLocation(out newLocation))
                        {
                            newLocation = ResourceLocation.ResourcesB;
                        }
                        resourceFile = file;
                        break;

                    case ValueTuple <ResourceLocation, FileInfo> tuple:
                        newLocation  = tuple.Item1;
                        resourceFile = tuple.Item2;
                        break;

                    default:
                        throw new FormatException(fieldValue.ToString());
                    }

                    ResourceCache oldCache = null;

                    if (pageable.GetLocation(out var oldLocation))
                    {
                        oldCache = CacheContext.GetResourceCache(oldLocation);
                    }

                    var newCache = CacheContext.GetResourceCache(newLocation);

                    var data = File.ReadAllBytes(resourceFile.FullName);

                    pageable.Page.UncompressedBlockSize = (uint)data.Length;

                    if (oldLocation == newLocation && pageable.Page.Index != -1)
                    {
                        using (var stream = CacheContext.OpenResourceCacheReadWrite(oldLocation))
                        {
                            pageable.Page.CompressedBlockSize = oldCache.Compress(stream, pageable.Page.Index, data);
                        }
                    }
                    else
                    {
                        using (var destStream = CacheContext.OpenResourceCacheReadWrite(newLocation))
                        {
                            pageable.Page.Index = newCache.Add(destStream, data, out pageable.Page.CompressedBlockSize);
                        }

                        pageable.ChangeLocation(newLocation);
                    }

                    pageable.DisableChecksum();

                    field.SetValue(Owner, fieldValue = pageable);
                }
            }
            else
            {
                field.SetValue(Owner, fieldValue);
            }

            var typeString =
                fieldType.IsGenericType ?
                $"{fieldType.Name}<{fieldType.GenericTypeArguments[0].Name}>" :
                fieldType.Name;

            string valueString;

#if !DEBUG
            try
            {
#endif
            if (fieldValue == null)
            {
                valueString = "null";
            }
            else if (fieldType == typeof(StringId))
            {
                valueString = CacheContext.GetString((StringId)fieldValue);
            }
            else if (fieldType == typeof(CachedTagInstance))
            {
                var instance = (CachedTagInstance)fieldValue;

                var tagName = instance?.Name ?? $"0x{instance.Index:X4}";

                valueString = $"[0x{instance.Index:X4}] {tagName}.{CacheContext.GetString(instance.Group.Name)}";
            }
            else if (fieldType == typeof(TagFunction))
            {
                var function = (TagFunction)fieldValue;
                valueString = "";
                foreach (var datum in function.Data)
                {
                    valueString += datum.ToString("X2");
                }
            }
            else if (fieldType == typeof(PageableResource))
            {
                var pageable = (PageableResource)fieldValue;
                pageable.GetLocation(out var location);
                valueString = pageable == null ? "null" : $"{{ Location: {location}, Index: 0x{pageable.Page.Index:X4}, CompressedSize: 0x{pageable.Page.CompressedBlockSize:X8} }}";
            }
            else if (fieldInfo.FieldType.IsArray && fieldInfo.Attribute.Length != 0)
            {
                valueString = fieldValue == null ? "null" : $"[{fieldInfo.Attribute.Length}] {{ ";
                var valueArray = (Array)fieldValue;

                if (fieldValue != null)
                {
                    for (var i = 0; i < fieldInfo.Attribute.Length; i++)
                    {
                        valueString += $"{valueArray.GetValue(i)}{((i + 1) < fieldInfo.Attribute.Length ? "," : "")} ";
                    }

                    valueString += "}";
                }
            }
            else if (fieldType.GetInterface(typeof(IList).Name) != null)
            {
                valueString =
                    ((IList)fieldValue).Count != 0 ?
                    $"{{...}}[{((IList)fieldValue).Count}]" :
                    "null";
            }
            else
            {
                valueString = fieldValue.ToString();
            }
#if !DEBUG
        }

        catch (Exception e)
        {
            valueString = $"<ERROR MESSAGE=\"{e.Message}\" />";
        }
#endif

            var fieldFullName     = $"{field.DeclaringType.FullName}.{field.Name}".Replace("+", ".");
            var documentationNode = EditTagContextFactory.Documentation.SelectSingleNode($"//member[starts-with(@name, 'F:{fieldFullName}')]");

            Console.WriteLine("{0}: {1} = {2} {3}", field.Name, typeString, valueString,
                              documentationNode != null ?
                              $":: {documentationNode.FirstChild.InnerText.Replace("\r\n", "").TrimStart().TrimEnd()}" :
                              "");

            while (ContextStack.Context != previousContext)
            {
                ContextStack.Pop();
            }
            Owner     = previousOwner;
            Structure = previousStructure;

            return(true);
        }
Example #3
0
        /// <summary>
        /// Serializes a property.
        /// </summary>
        /// <param name="version"></param>
        /// <param name="context">The serialization context to use.</param>
        /// <param name="tagStream">The stream to write completed blocks of tag data to.</param>
        /// <param name="block">The temporary block to write incomplete tag data to.</param>
        /// <param name="instance">The object that the property belongs to.</param>
        /// <param name="tagFieldInfo">The field enumerator.</param>
        /// <param name="baseOffset">The base offset of the structure from the start of its block.</param>
        /// <exception cref="System.InvalidOperationException">Offset for property \ + property.Name + \ is outside of its structure</exception>
        private void SerializeProperty(CacheVersion version, ISerializationContext context, MemoryStream tagStream, IDataBlock block, object instance, TagFieldInfo tagFieldInfo, long baseOffset)
        {
            if (tagFieldInfo.Attribute.Flags.HasFlag(TagFieldFlags.Runtime))
            {
                return;
            }

            SerializeValue(version, context, tagStream, block,
                           tagFieldInfo.GetValue(instance), tagFieldInfo.Attribute, tagFieldInfo.FieldType);
        }
Example #4
0
 private void PrintField(StreamWriter writer, TagFieldInfo tagFieldInfo)
 {
     var type = tagFieldInfo.FieldType;
     var name = SanitizeName(tagFieldInfo.Name);
 }
Example #5
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 #6
0
        public static object ParseArgs(HaloOnlineCacheContext cacheContext, Type type, TagFieldInfo info, List <string> args)
        {
            var    input  = args[0];
            object output = null;

            if (type == typeof(byte))
            {
                if (args.Count != 1)
                {
                    return(false);
                }
                if (!byte.TryParse(input, out byte value))
                {
                    return(false);
                }
                output = value;
            }
            else if (type == typeof(sbyte))
            {
                if (args.Count != 1)
                {
                    return(false);
                }
                if (!sbyte.TryParse(input, out sbyte value))
                {
                    return(false);
                }
                output = value;
            }
            else if (type == typeof(short))
            {
                if (args.Count != 1)
                {
                    return(false);
                }
                if (!short.TryParse(input, out short value))
                {
                    return(false);
                }
                output = value;
            }
            else if (type == typeof(ushort))
            {
                if (args.Count != 1)
                {
                    return(false);
                }
                if (!ushort.TryParse(input, out ushort value))
                {
                    return(false);
                }
                output = value;
            }
            else if (type == typeof(int))
            {
                if (args.Count != 1)
                {
                    return(false);
                }
                if (!int.TryParse(input, out int value))
                {
                    return(false);
                }
                output = value;
            }
            else if (type == typeof(uint))
            {
                if (args.Count != 1)
                {
                    return(false);
                }
                if (!uint.TryParse(input, out uint value))
                {
                    return(false);
                }
                output = value;
            }
            else if (type == typeof(long))
            {
                if (args.Count != 1)
                {
                    return(false);
                }
                if (!long.TryParse(input, out long value))
                {
                    return(false);
                }
                output = value;
            }
            else if (type == typeof(ulong))
            {
                if (args.Count != 1)
                {
                    return(false);
                }
                if (!ulong.TryParse(input, out ulong value))
                {
                    return(false);
                }
                output = value;
            }
            else if (type == typeof(float))
            {
                if (args.Count != 1)
                {
                    return(false);
                }
                if (!float.TryParse(input, out float value))
                {
                    return(false);
                }
                output = value;
            }
            else if (type == typeof(string))
            {
                if (args.Count != 1)
                {
                    return(false);
                }
                output = input;
            }
            else if (type.IsEnum)
            {
                if (args.Count != 1)
                {
                    return(false);
                }

                var query = args[0];

                object found;

                try
                {
                    found = Enum.Parse(type, query);
                }
                catch
                {
                    found = null;
                }

                var names = Enum.GetNames(type).ToList();

                if (found == null)
                {
                    var nameLow  = query.ToLower();
                    var namesLow = names.Select(i => i.ToLower()).ToList();

                    found = namesLow.Find(n => n == nameLow);

                    if (found == null)
                    {
                        var nameSnake  = query.ToSnakeCase();
                        var namesSnake = names.Select(i => i.ToSnakeCase()).ToList();
                        found = namesSnake.Find(n => n == nameSnake);

                        if (found == null)
                        {
                            Console.WriteLine("Invalid {0} enum option: {1}", type.Name, args[0]);
                            Console.WriteLine("");

                            Console.WriteLine("Valid options:");
                            foreach (var name in Enum.GetNames(type))
                            {
                                var fieldName         = $"{type.FullName}.{name}".Replace("+", ".");
                                var documentationNode = EditTagContextFactory.Documentation.SelectSingleNode($"//member[starts-with(@name, 'F:{fieldName}')]");

                                Console.WriteLine("\t{0} {1}", name,
                                                  documentationNode != null ?
                                                  $":: {documentationNode.FirstChild.InnerText.Replace("\r\n", "").TrimStart().TrimEnd()}" :
                                                  "");
                            }
                            Console.WriteLine();

                            return(false);
                        }
                        else
                        {
                            found = Enum.Parse(type, names[namesSnake.IndexOf((string)found)]);
                        }
                    }
                    else
                    {
                        found = Enum.Parse(type, names[namesLow.IndexOf((string)found)]);
                    }
                }

                output = found;
            }
            else if (type.IsArray)
            {
                if (info?.FieldType == typeof(byte[]) && info?.Attribute.Length == 0)
                {   // tag_data field
                    if (args.Count != 1)
                    {
                        return(false);
                    }
                    if (input.Length % 2 != 0)
                    {
                        return(false);
                    }

                    List <byte> bytes = new List <byte>();

                    for (int i = 0; i < input.Length; i += 2)
                    {
                        bytes.Add(Convert.ToByte(input.Substring(i, 2), 16));
                    }

                    output = bytes.ToArray();
                }
                else
                {
                    if (info == null || args.Count != info.Attribute.Length)
                    {
                        return(false);
                    }

                    var elementType = info.FieldType.GetElementType();
                    var values      = Array.CreateInstance(elementType, info.Attribute.Length);

                    for (var i = 0; i < info.Attribute.Length; i++)
                    {
                        values.SetValue(Convert.ChangeType(ParseArgs(cacheContext, elementType, null, new List <string> {
                            args[i]
                        }), elementType), i);
                    }

                    return(values);
                }
            }
            else if (type.IsBlamType())
            {
                if (type.IsGenericType)
                {
                    var tDefinition = type.GetGenericTypeDefinition();
                    var tArguments  = type.GetGenericArguments();
                    type = tDefinition.MakeGenericType(tArguments);
                }

                var blamType = Activator.CreateInstance(type) as IBlamType;
                if (!blamType.TryParse(cacheContext, args, out blamType, out string error))
                {
                    Console.WriteLine(error);
                }
                return(blamType);
            }
            else if (type == typeof(PageableResource))
            {
                if (args.Count < 1 || args.Count > 2)
                {
                    return(false);
                }

                if (args.Count == 1)
                {
                    switch (args[0].ToLower())
                    {
                    case "null":
                        output = null;
                        break;

                    default:
                        output = new FileInfo(args[0]);
                        if (!((FileInfo)output).Exists)
                        {
                            throw new FileNotFoundException(args[0]);
                        }
                        break;
                    }
                }
                else if (args.Count == 2)
                {
                    var resourceLocation = ResourceLocation.None;

                    switch (args[0].ToSnakeCase())
                    {
                    case "resources":
                        resourceLocation = ResourceLocation.Resources;
                        break;

                    case "textures":
                        resourceLocation = ResourceLocation.Textures;
                        break;

                    case "textures_b":
                        resourceLocation = ResourceLocation.TexturesB;
                        break;

                    case "audio":
                        resourceLocation = ResourceLocation.Audio;
                        break;

                    case "resources_b":
                        resourceLocation = ResourceLocation.ResourcesB;
                        break;

                    case "render_models" when cacheContext.Version >= CacheVersion.HaloOnline235640:
                        resourceLocation = ResourceLocation.RenderModels;
                        break;

                    case "lightmaps" when cacheContext.Version >= CacheVersion.HaloOnline235640:
                        resourceLocation = ResourceLocation.Lightmaps;
                        break;

                    default:
                        throw new FormatException($"Invalid resource location: {args[0]}");
                    }

                    var resourceFile = new FileInfo(args[1]);

                    if (!resourceFile.Exists)
                    {
                        throw new FileNotFoundException(args[1]);
                    }

                    output = (resourceLocation, resourceFile);
                }
                else
                {
                    throw new NotImplementedException();
                }
            }
            else
            {
                Console.WriteLine($"ERROR: Not Implemented.");
                return(false);
                // throw new NotImplementedException();
            }

            return(output);
        }
Example #7
0
        /// <summary>
        /// Serializes a property.
        /// </summary>
        /// <param name="version"></param>
        /// <param name="context">The serialization context to use.</param>
        /// <param name="tagStream">The stream to write completed blocks of tag data to.</param>
        /// <param name="block">The temporary block to write incomplete tag data to.</param>
        /// <param name="instance">The object that the property belongs to.</param>
        /// <param name="tagFieldInfo">The field enumerator.</param>
        /// <param name="baseOffset">The base offset of the structure from the start of its block.</param>
        /// <exception cref="System.InvalidOperationException">Offset for property \ + property.Name + \ is outside of its structure</exception>
        private void SerializeProperty(CacheVersion version, ISerializationContext context, MemoryStream tagStream, IDataBlock block, object instance, TagFieldInfo tagFieldInfo, long baseOffset)
        {
            if (tagFieldInfo.Attribute.Flags.HasFlag(Runtime))
            {
                return;
            }

            object objectValue = tagFieldInfo.GetValue(instance);

            // second condition is a hack to prevent exceptions when encountering cached tags
            if (objectValue != null)
            {
                if (objectValue.GetType() == tagFieldInfo.FieldType || tagFieldInfo.FieldType == typeof(CachedTag))
                {
                    SerializeValue(version, context, tagStream, block,
                                   objectValue, tagFieldInfo.Attribute, tagFieldInfo.FieldType);
                }
                else
                {
                    throw new Exception($"TagFieldInfo.GetValue return type {objectValue.GetType().ToString()} is not the same as the FieldInfo Type {tagFieldInfo.FieldType.ToString()}!");
                }
            }
            else
            {
                SerializeValue(version, context, tagStream, block,
                               objectValue, tagFieldInfo.Attribute, tagFieldInfo.FieldType);
            }
        }
Example #8
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(Runtime) || !CacheVersionDetection.AttributeInCacheVersion(attr, Version))
            {
                return;
            }

            if (tagFieldInfo.FieldType.IsArray && attr.Flags.HasFlag(Relative))
            {
                var type  = instance.GetType();
                var field = type.GetField(
                    attr.Field,
                    BindingFlags.Instance | BindingFlags.Public);

                var attr2 = TagStructure.GetTagFieldAttribute(type, field);
                if (CacheVersionDetection.AttributeInCacheVersion(attr2, Version))
                {
                    attr.Length = (int)Convert.ChangeType(field.GetValue(instance), typeof(int));
                }
                else
                {
                    throw new InvalidOperationException(attr2.Field);
                }
            }

            if (attr.Flags.HasFlag(Padding))
            {
#if DEBUG
                var unused = reader.ReadBytes(attr.Length);

                foreach (var b in unused)
                {
                    if (b != 0)
                    {
                        Console.WriteLine($"WARNING: non-zero padding found in {tagFieldInfo.FieldInfo.DeclaringType.FullName}.{tagFieldInfo.FieldInfo.Name} = {b}");
                        break;
                    }
                }
#else
                reader.BaseStream.Position += attr.Length;
#endif
            }
            else
            {
                var value = DeserializeValue(reader, context, attr, tagFieldInfo.FieldType);
                tagFieldInfo.SetValue(instance, value);
            }
        }
        public override object Execute(List <string> args)
        {
            if (args.Count < 2)
            {
                return(false);
            }

            var fieldName = args[0];
            var file      = new FileInfo(args[1]);

            var fieldNameLow   = fieldName.ToLower();
            var fieldNameSnake = fieldName.ToSnakeCase();

            var previousContext   = ContextStack.Context;
            var previousOwner     = Owner;
            var previousStructure = Structure;

            if (fieldName.Contains("."))
            {
                var lastIndex = fieldName.LastIndexOf('.');
                var blockName = fieldName.Substring(0, lastIndex);
                fieldName      = fieldName.Substring(lastIndex + 1, (fieldName.Length - lastIndex) - 1);
                fieldNameLow   = fieldName.ToLower();
                fieldNameSnake = fieldName.ToSnakeCase();

                var command = new EditBlockCommand(ContextStack, CacheContext, Tag, Owner);

                if (command.Execute(new List <string> {
                    blockName
                }).Equals(false))
                {
                    while (ContextStack.Context != previousContext)
                    {
                        ContextStack.Pop();
                    }
                    Owner     = previousOwner;
                    Structure = previousStructure;
                    return(false);
                }

                command = (ContextStack.Context.GetCommand("EditBlock") as EditBlockCommand);

                Owner     = command.Owner;
                Structure = command.Structure;

                if (Owner == null)
                {
                    while (ContextStack.Context != previousContext)
                    {
                        ContextStack.Pop();
                    }
                    Owner     = previousOwner;
                    Structure = previousStructure;
                    return(false);
                }
            }

            var field = TagStructure.GetTagFieldEnumerable(Structure)
                        .Find(f =>
                              f.Name == fieldName ||
                              f.Name.ToLower() == fieldNameLow ||
                              f.Name.ToSnakeCase() == fieldNameSnake);

            if (field == null)
            {
                Console.WriteLine($"ERROR: {Structure.Types[0].Name} does not contain a field named \"{fieldName}\".");
                while (ContextStack.Context != previousContext)
                {
                    ContextStack.Pop();
                }
                Owner     = previousOwner;
                Structure = previousStructure;
                return(false);
            }
            else if (field.FieldType != typeof(PageableResource))
            {
                Console.WriteLine($"ERROR: {Structure.Types[0].Name}.{field.Name} is not of type {nameof(PageableResource)}.");
                while (ContextStack.Context != previousContext)
                {
                    ContextStack.Pop();
                }
                Owner     = previousOwner;
                Structure = previousStructure;
                return(false);
            }

            var fieldType  = field.FieldType;
            var fieldAttrs = field.GetCustomAttributes(typeof(TagFieldAttribute), false);
            var fieldAttr  = fieldAttrs?.Length < 1 ? new TagFieldAttribute() : (TagFieldAttribute)fieldAttrs[0];
            var fieldInfo  = new TagFieldInfo(field, fieldAttr, uint.MaxValue, uint.MaxValue);
            var fieldValue = field.GetValue(Owner) as PageableResource;

            if (!file.Directory.Exists)
            {
                file.Directory.Create();
            }

            File.WriteAllBytes(file.FullName, CacheContext.ExtractRawResource(fieldValue));
            Console.WriteLine($"Wrote 0x{fieldValue.Page.CompressedBlockSize:X} bytes to \"{file.FullName}\".");

            while (ContextStack.Context != previousContext)
            {
                ContextStack.Pop();
            }
            Owner     = previousOwner;
            Structure = previousStructure;

            return(true);
        }