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); }
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); }
/// <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); }
private void PrintField(StreamWriter writer, TagFieldInfo tagFieldInfo) { var type = tagFieldInfo.FieldType; var name = SanitizeName(tagFieldInfo.Name); }
/// <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); } } }
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); }
/// <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); } }
/// <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); }