public static NMSTemplate DeserializeEXml(EXmlData xmlData) { NMSTemplate template = TemplateFromName(xmlData.Template); Type templateType = template.GetType(); var templateFields = templateType.GetFields().OrderBy(field => field.MetadataToken); // hack to get fields in order of declaration (todo: use something less hacky, this might break mono?) foreach (var templateField in templateFields) { NMSAttribute settings = templateField.GetCustomAttribute <NMSAttribute>(); if (settings?.DefaultValue != null) { templateField.SetValue(template, settings.DefaultValue); } } foreach (var xmlProperty in xmlData.Elements.OfType <EXmlProperty>()) { FieldInfo field = templateType.GetField(xmlProperty.Name); Type fieldType = field.FieldType; NMSAttribute settings = field.GetCustomAttribute <NMSAttribute>(); object fieldValue = DeserializeEXmlValue(template, fieldType, field, xmlProperty, templateType, settings); field.SetValue(template, fieldValue); } foreach (EXmlData innerXmlData in xmlData.Elements.OfType <EXmlData>()) { FieldInfo field = templateType.GetField(innerXmlData.Name); NMSTemplate innerTemplate = DeserializeEXml(innerXmlData); field.SetValue(template, innerTemplate); } return(template); }
public static NMSTemplate DeserializeBinaryTemplate(BinaryReader reader, string templateName) { if (templateName.StartsWith("c") && templateName.Length > 1) { templateName = templateName.Substring(1); } NMSTemplate obj = TemplateFromName(templateName); if (obj == null) { return(null); } long templatePosition = reader.BaseStream.Position; if (PrintToDebug) { Debug.WriteLine($"{templateName} position: 0x{templatePosition:X}"); } if (templateName == "VariableSizeString") { long stringPos = reader.ReadInt64(); int stringLength = reader.ReadInt32(); int unkC = reader.ReadInt32(); reader.BaseStream.Position = templatePosition + stringPos; ((VariableSizeString)obj).Value = reader.ReadString(Encoding.UTF8, stringLength); reader.BaseStream.Position = templatePosition + 0x10; return(obj); } var type = obj.GetType(); var fields = type.GetFields().OrderBy(field => field.MetadataToken); // hack to get fields in order of declaration (todo: use something less hacky, this might break mono?) foreach (var field in fields) { NMSAttribute settings = field.GetCustomAttribute <NMSAttribute>(); field.SetValue(obj, DeserializeValue(reader, field.FieldType, settings, templatePosition, field, obj)); } obj.FinishDeserialize(); if (PrintToDebug) { Debug.WriteLine($"{templateName} end position: 0x{reader.BaseStream.Position:X}"); } return(obj); }
public static NMSTemplate DeserializeEXml(EXmlData xmlData) { NMSTemplate template = TemplateFromName(xmlData.Template); Type templateType = template.GetType(); foreach (var xmlProperty in xmlData.Elements.OfType <EXmlProperty>()) { FieldInfo field = templateType.GetField(xmlProperty.Name); Type fieldType = field.FieldType; object fieldValue; switch (fieldType.Name) { case "String": fieldValue = xmlProperty.Value; break; case "Single": fieldValue = float.Parse(xmlProperty.Value); break; case "Boolean": fieldValue = bool.Parse(xmlProperty.Value); break; case "Int16": fieldValue = short.Parse(xmlProperty.Value); break; case "Int32": var valuesMethod = templateType.GetMethod(field.Name + "Values"); if (valuesMethod != null) { if (String.IsNullOrEmpty(xmlProperty.Value)) { fieldValue = (int)-1; } else { string[] values = (string[])valuesMethod.Invoke(template, null); fieldValue = Array.FindIndex(values, v => v == xmlProperty.Value); } } else { fieldValue = int.Parse(xmlProperty.Value); } break; case "Int64": fieldValue = long.Parse(xmlProperty.Value); break; case "Byte[]": fieldValue = xmlProperty.Value == null ? null : Convert.FromBase64String(xmlProperty.Value); break; case "List`1": Type elementType = fieldType.GetGenericArguments()[0]; Type listType = typeof(List <>).MakeGenericType(elementType); IList list = (IList)Activator.CreateInstance(listType); foreach (EXmlData innerXmlData in xmlProperty.Elements.OfType <EXmlData>()) { NMSTemplate element = DeserializeEXml(innerXmlData); list.Add(element); } fieldValue = list; break; default: fieldValue = fieldType.IsValueType ? Activator.CreateInstance(fieldType) : null; break; } field.SetValue(template, fieldValue); } foreach (EXmlData innerXmlData in xmlData.Elements.OfType <EXmlData>()) { FieldInfo field = templateType.GetField(innerXmlData.Name); NMSTemplate innerTemplate = DeserializeEXml(innerXmlData); field.SetValue(template, innerTemplate); } return(template); }
public EXmlData SerializeEXml() { Type type = GetType(); EXmlData xmlData = new EXmlData { Template = type.Name }; var fields = type.GetFields().OrderBy(field => field.MetadataToken); // hack to get fields in order of declaration (todo: use something less hacky, this might break mono?) foreach (var field in fields) { if (field.Name.StartsWith("Padding")) { continue; } var fieldName = field.Name; var fieldType = field.FieldType.Name; switch (fieldType) { case "String": case "Single": case "Boolean": case "Int16": case "UInt16": case "Int32": case "UInt32": case "Int64": case "UInt64": var value = field.GetValue(this); var valueStr = value.ToString(); var valuesMethod = type.GetMethod(field.Name + "Values"); // if we have an "xxxValues()" method in the struct, use that to get the value name if (valuesMethod != null) { if (((int)value) == -1) { valueStr = ""; } else { string[] values = (string[])valuesMethod.Invoke(this, null); valueStr = values[(int)value]; } } xmlData.Elements.Add(new EXmlProperty { Name = fieldName, Value = valueStr }); break; case "Byte[]": byte[] bytes = (byte[])field.GetValue(this); string base64Value = bytes == null ? null : Convert.ToBase64String(bytes); xmlData.Elements.Add(new EXmlProperty { Name = fieldName, Value = base64Value }); break; case "List`1": EXmlProperty listProperty = new EXmlProperty { Name = fieldName }; IList templates = (IList)field.GetValue(this); foreach (var template in templates.Cast <NMSTemplate>()) { listProperty.Elements.Add(template.SerializeEXml()); } xmlData.Elements.Add(listProperty); break; case "NMSTemplate": if (field.GetValue(this) != null) { NMSTemplate template = (NMSTemplate)field.GetValue(this); EXmlData templateXmlData = template.SerializeEXml(); templateXmlData.Name = fieldName; xmlData.Elements.Add(templateXmlData); } break; default: if (field.FieldType.BaseType.Name == "NMSTemplate") { NMSTemplate template = (NMSTemplate)field.GetValue(this); EXmlData templateXmlData = template.SerializeEXml(); templateXmlData.Name = fieldName; xmlData.Elements.Add(templateXmlData); } break; } } return(xmlData); }
public static NMSTemplate DeserializeBinaryTemplate(BinaryReader reader, string templateName) { if (templateName.StartsWith("c") && templateName.Length > 1) { templateName = templateName.Substring(1); } NMSTemplate obj = TemplateFromName(templateName); if (obj == null) { return(null); } long templatePosition = reader.BaseStream.Position; System.Diagnostics.Debug.Print(templateName + " position: " + templatePosition.ToString("X")); if (templateName == "VariableSizeString") { long stringPos = reader.ReadInt64(); int stringLength = reader.ReadInt32(); int unkC = reader.ReadInt32(); reader.BaseStream.Position = templatePosition + stringPos; ((VariableSizeString)obj).Value = reader.ReadString(Encoding.UTF8, stringLength); reader.BaseStream.Position = templatePosition + 0x10; return(obj); } var type = obj.GetType(); var fields = type.GetFields().OrderBy(field => field.MetadataToken); // hack to get fields in order of declaration (todo: use something less hacky, this might break mono?) foreach (var field in fields) { var fieldAddr = reader.BaseStream.Position - templatePosition; var fieldName = field.Name; var fieldType = field.FieldType.Name; switch (fieldType) { case "String": case "Byte[]": int size = 0; foreach (var attr in field.CustomAttributes) { if (attr.AttributeType.Name != "MarshalAsAttribute") { continue; } foreach (var named in attr.NamedArguments) { if (named.MemberName != "SizeConst") { continue; } size = (int)named.TypedValue.Value; } } if (fieldType == "String") { // reader.Align(0x4, templatePosition); var str = reader.ReadString(Encoding.UTF8, size, true); field.SetValue(obj, str); } else { var str = reader.ReadBytes(size); field.SetValue(obj, str); } break; case "Single": reader.Align(4, 0); field.SetValue(obj, reader.ReadSingle()); break; case "Boolean": field.SetValue(obj, reader.ReadByte() != 0); break; case "Int16": case "UInt16": reader.Align(2, 0); field.SetValue(obj, fieldType == "Int16" ? reader.ReadInt16() : (object)reader.ReadUInt16()); break; case "Int32": case "UInt32": reader.Align(4, 0); field.SetValue(obj, fieldType == "Int32" ? reader.ReadInt32() : (object)reader.ReadUInt32()); break; case "Int64": case "UInt64": reader.Align(8, 0); field.SetValue(obj, fieldType == "Int64" ? reader.ReadInt64() : (object)reader.ReadUInt64()); break; case "List`1": reader.Align(8, 0); if (field.FieldType.IsGenericType && field.FieldType.GetGenericTypeDefinition() == typeof(List <>)) { Type itemType = field.FieldType.GetGenericArguments()[0]; // use this... if (itemType == typeof(NMSTemplate)) { field.SetValue(obj, DeserializeGenericList(reader, templatePosition)); } else { // todo: get rid of this nastiness MethodInfo method = typeof(NMSTemplate).GetMethod("DeserializeList", BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) .MakeGenericMethod(new Type[] { itemType }); var list = method.Invoke(null, new object[] { reader, templatePosition }); field.SetValue(obj, list); } } break; case "NMSTemplate": reader.Align(8, 0); long startPos = reader.BaseStream.Position; long offset = reader.ReadInt64(); string name = reader.ReadString(Encoding.ASCII, 0x40, true); long endPos = reader.BaseStream.Position; if (offset != 0 && !String.IsNullOrEmpty(name)) { reader.BaseStream.Position = startPos + offset; NMSTemplate val = DeserializeBinaryTemplate(reader, name); if (val == null) { throw new Exception("Failed to deserialize template " + name + "!"); } field.SetValue(obj, val); } reader.BaseStream.Position = endPos; break; default: if (fieldType == "Colour") // unsure if this is needed? { reader.Align(0x10, 0); } // todo: align for VariableSizeString? var data = DeserializeBinaryTemplate(reader, fieldType); if (data != null) { field.SetValue(obj, data); } break; } } return(obj); }
public static object DeserializeEXmlValue(NMSTemplate template, Type fieldType, FieldInfo field, EXmlProperty xmlProperty, Type templateType, NMSAttribute settings) { switch (fieldType.Name) { case "String": return(xmlProperty.Value); case "Single": return(float.Parse(xmlProperty.Value)); case "Boolean": return(bool.Parse(xmlProperty.Value)); case "Int16": return(short.Parse(xmlProperty.Value)); case "UInt16": return(ushort.Parse(xmlProperty.Value)); case "Int32": var valuesMethod = templateType.GetMethod(field.Name + "Values"); if (valuesMethod != null) { if (String.IsNullOrEmpty(xmlProperty.Value)) { return(-1); } else { string[] values = (string[])valuesMethod.Invoke(template, null); return(Array.FindIndex(values, v => v == xmlProperty.Value)); } } else if (settings?.EnumValue != null) { if (String.IsNullOrEmpty(xmlProperty.Value)) { return(-1); } else { return(Array.FindIndex(settings.EnumValue, v => v == xmlProperty.Value)); } } else { return(int.Parse(xmlProperty.Value)); } case "UInt32": return(uint.Parse(xmlProperty.Value)); case "Int64": return(long.Parse(xmlProperty.Value)); case "UInt64": return(ulong.Parse(xmlProperty.Value)); case "Byte[]": return(xmlProperty.Value == null ? null : Convert.FromBase64String(xmlProperty.Value)); case "List`1": Type elementType = fieldType.GetGenericArguments()[0]; Type listType = typeof(List <>).MakeGenericType(elementType); IList list = (IList)Activator.CreateInstance(listType); foreach (EXmlData innerXmlData in xmlProperty.Elements.OfType <EXmlData>()) // child templates { NMSTemplate element = DeserializeEXml(innerXmlData); list.Add(element); } foreach (EXmlProperty innerXmlData in xmlProperty.Elements.OfType <EXmlProperty>()) // primitive types { object element = DeserializeEXmlValue(template, elementType, field, innerXmlData, templateType, settings); list.Add(element); } return(list); default: if (field.FieldType.IsArray && field.FieldType.GetElementType().BaseType.Name == "NMSTemplate") { Array array = Array.CreateInstance(field.FieldType.GetElementType(), settings.Size); List <EXmlData> data = xmlProperty.Elements.OfType <EXmlData>().ToList(); for (int i = 0; i < data.Count; ++i) { NMSTemplate element = DeserializeEXml(data[i]); array.SetValue(element, i); } return(array); } else if (field.FieldType.IsArray) { Array array = Array.CreateInstance(field.FieldType.GetElementType(), settings.Size); List <EXmlProperty> data = xmlProperty.Elements.OfType <EXmlProperty>().ToList(); for (int i = 0; i < data.Count; ++i) { object element = DeserializeEXmlValue(template, field.FieldType.GetElementType(), field, data[i], templateType, settings); array.SetValue(element, i); } return(array); } else { return(fieldType.IsValueType ? Activator.CreateInstance(fieldType) : null); } } }
public EXmlBase SerializeEXmlValue(Type fieldType, FieldInfo field, NMSAttribute settings, object value) { string t = fieldType.Name; int i = 0; switch (fieldType.Name) { case "String": case "Single": case "Boolean": case "Int16": case "UInt16": case "Int32": case "UInt32": case "Int64": case "UInt64": var valueStr = value.ToString(); if (fieldType.Name == "Int32") { var valuesMethod = GetType().GetMethod(field.Name + "Values"); // if we have an "xxxValues()" method in the struct, use that to get the value name if (valuesMethod != null) { if (((int)value) == -1) { valueStr = ""; } else { string[] values = (string[])valuesMethod.Invoke(this, null); valueStr = values[(int)value]; } } else if (settings?.EnumValue != null) { if (((int)value) == -1) { valueStr = ""; } else { valueStr = settings.EnumValue[(int)value]; } } } return(new EXmlProperty { Name = field.Name, Value = valueStr }); case "Byte[]": byte[] bytes = (byte[])value; string base64Value = bytes == null ? null : Convert.ToBase64String(bytes); return(new EXmlProperty { Name = field.Name, Value = base64Value }); case "List`1": var listType = field.FieldType.GetGenericArguments()[0]; EXmlProperty listProperty = new EXmlProperty { Name = field.Name }; IList templates = (IList)value; i = 0; foreach (var template in templates) { EXmlBase data = SerializeEXmlValue(listType, field, settings, template); if (settings?.EnumValue != null) { data.Name = settings.EnumValue[i]; i++; } else { data.Name = null; } listProperty.Elements.Add(data); } return(listProperty); case "NMSTemplate": if (value != null) { NMSTemplate template = (NMSTemplate)value; EXmlData templateXmlData = template.SerializeEXml(); templateXmlData.Name = field.Name; return(templateXmlData); } return(null); default: if (fieldType.BaseType.Name == "NMSTemplate") { NMSTemplate template = (NMSTemplate)value; EXmlData templateXmlData = template.SerializeEXml(); templateXmlData.Name = field.Name; return(templateXmlData); } else if (fieldType.IsArray) { var arrayType = field.FieldType.GetElementType(); EXmlProperty arrayProperty = new EXmlProperty { Name = field.Name }; Array array = (Array)value; i = 0; foreach (var template in array) { EXmlBase data = SerializeEXmlValue(arrayType, field, settings, template); if (settings?.EnumValue != null) { data.Name = settings.EnumValue[i]; i++; } else { data.Name = null; } arrayProperty.Elements.Add(data); } return(arrayProperty); } else { throw new Exception(string.Format("Unable to encode {0} to EXml!", field)); } } }
public static object DeserializeValue(BinaryReader reader, Type field, NMSAttribute settings, long templatePosition, FieldInfo fieldInfo, NMSTemplate parent) { var template = parent.CustomDeserialize(reader, field, settings, templatePosition, fieldInfo); if (template != null) { return(template); } var fieldType = field.Name; switch (fieldType) { case "String": case "Byte[]": int size = settings?.Size ?? 0; MarshalAsAttribute legacySettings = fieldInfo.GetCustomAttribute <MarshalAsAttribute>(); if (legacySettings != null) { size = legacySettings.SizeConst; } if (fieldType == "String") { // reader.Align(0x4, templatePosition); var str = reader.ReadString(Encoding.UTF8, size, true); return(str); } else { var str = reader.ReadBytes(size); return(str); } case "Single": reader.Align(4, 0); return(reader.ReadSingle()); case "Boolean": return(reader.ReadByte() != 0); case "Int16": case "UInt16": reader.Align(2, 0); return(fieldType == "Int16" ? (object)reader.ReadInt16() : (object)reader.ReadUInt16()); case "Int32": case "UInt32": reader.Align(4, 0); return(fieldType == "Int32" ? (object)reader.ReadInt32() : (object)reader.ReadUInt32()); case "Int64": case "UInt64": reader.Align(8, 0); return(fieldType == "Int64" ? (object)reader.ReadInt64() : (object)reader.ReadUInt64()); case "List`1": reader.Align(8, 0); if (field.IsGenericType && field.GetGenericTypeDefinition() == typeof(List <>)) { Type itemType = field.GetGenericArguments()[0]; if (itemType == typeof(NMSTemplate)) { return(DeserializeGenericList(reader, templatePosition, parent)); } else { // todo: get rid of this nastiness MethodInfo method = typeof(NMSTemplate).GetMethod("DeserializeList", BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) .MakeGenericMethod(new Type[] { itemType }); var list = method.Invoke(null, new object[] { reader, fieldInfo, settings, templatePosition, parent }); return(list); } } return(null); case "NMSTemplate": reader.Align(8, 0); long startPos = reader.BaseStream.Position; long offset = reader.ReadInt64(); string name = reader.ReadString(Encoding.ASCII, 0x40, true); long endPos = reader.BaseStream.Position; NMSTemplate val = null; if (offset != 0 && !String.IsNullOrEmpty(name)) { reader.BaseStream.Position = startPos + offset; val = DeserializeBinaryTemplate(reader, name); if (val == null) { throw new Exception("Failed to deserialize template " + name + "!"); } } reader.BaseStream.Position = endPos; return(val); default: if (fieldType == "Colour") // unsure if this is needed? { reader.Align(0x10, 0); } // todo: align for VariableSizeString? if (field.IsArray) { var arrayType = field.GetElementType(); Array array = Array.CreateInstance(arrayType, settings.Size); for (int i = 0; i < settings.Size; ++i) { array.SetValue(DeserializeValue(reader, field.GetElementType(), settings, templatePosition, fieldInfo, parent), i); } return(array); } else { var data = DeserializeBinaryTemplate(reader, fieldType); return(data); } } }
public static List <T> DeserializeList <T>(BinaryReader reader, FieldInfo field, NMSAttribute settings, long templateStartOffset, NMSTemplate parent) { long listPosition = reader.BaseStream.Position; Debug.WriteLine($"DeserializeList start 0x{listPosition:X}"); long listStartOffset = reader.ReadInt64(); int numEntries = reader.ReadInt32(); uint listMagic = reader.ReadUInt32(); if ((listMagic & 0xFF) != 1) { throw new Exception($"Invalid list read, magic {listMagic:X8} expected xxxxxx01"); } long listEndPosition = reader.BaseStream.Position; reader.BaseStream.Position = listPosition + listStartOffset; var list = new List <T>(); for (int i = 0; i < numEntries; i++) { // todo: get rid of DeserializeGenericList? this seems like it would work fine with List<NMSTemplate> var template = DeserializeValue(reader, field.FieldType.GetGenericArguments()[0], settings, templateStartOffset, field, parent); if (template == null) { throw new Exception($"Failed to deserialize type {typeof(T).Name}!"); } list.Add((T)template); } reader.BaseStream.Position = listEndPosition; reader.Align(0x8, 0); return(list); }
public static List <NMSTemplate> DeserializeGenericList(BinaryReader reader, long templateStartOffset, NMSTemplate parent) { long listPosition = reader.BaseStream.Position; Debug.WriteLine($"DeserializeGenericList start 0x{listPosition:X}"); long templateNamesOffset = reader.ReadInt64(); int numTemplates = reader.ReadInt32(); uint listMagic = reader.ReadUInt32(); if ((listMagic & 0xFF) != 1) { throw new Exception($"Invalid generic list read, magic {listMagic:X8} expected xxxxxx01"); } long listEndPosition = reader.BaseStream.Position; reader.BaseStream.Position = listPosition + templateNamesOffset; var list = new List <NMSTemplate>(); if (numTemplates > 0) { Dictionary <long, string> templates = new Dictionary <long, string>(); for (int i = 0; i < numTemplates; i++) { long nameOffset = reader.BaseStream.Position; long templateOffset = reader.ReadInt64(); var name = reader.ReadString(Encoding.UTF8, 0x40, true); if (templateOffset == 0) { continue; // wtf? seen in METADATA\UI\HUD\WEAPONS\LASER.MBIN @ 0x2A8, numTemplates = 4 but only 1 is set? } templates.Add(nameOffset + templateOffset, name); } long pos = reader.BaseStream.Position; foreach (var templateInfo in templates) { reader.BaseStream.Position = templateInfo.Key; var template = DeserializeBinaryTemplate(reader, templateInfo.Value); if (template == null) { throw new Exception($"Failed to deserialize template {templateInfo.Value}!"); } list.Add(template); } } reader.BaseStream.Position = listEndPosition; reader.Align(0x8, 0); return(list); }
public static NMSTemplate DeserializeEXml(EXmlData xmlData) { NMSTemplate template = TemplateFromName(xmlData.Template); Type templateType = template.GetType(); var templateFields = templateType.GetFields().OrderBy(field => field.MetadataToken); // hack to get fields in order of declaration (todo: use something less hacky, this might break mono?) foreach (var templateField in templateFields) { NMSAttribute settings = templateField.GetCustomAttribute <NMSAttribute>(); if (settings == null) { break; } if (settings.DefaultValue != null) { templateField.SetValue(template, settings.DefaultValue); } } foreach (var xmlProperty in xmlData.Elements.OfType <EXmlProperty>()) { FieldInfo field = templateType.GetField(xmlProperty.Name); Type fieldType = field.FieldType; NMSAttribute settings = field.GetCustomAttribute <NMSAttribute>(); if (settings == null) { settings = new NMSAttribute(); } object fieldValue; switch (fieldType.Name) { case "String": fieldValue = xmlProperty.Value; break; case "Single": fieldValue = float.Parse(xmlProperty.Value); break; case "Boolean": fieldValue = bool.Parse(xmlProperty.Value); break; case "Int16": fieldValue = short.Parse(xmlProperty.Value); break; case "Int32": var valuesMethod = templateType.GetMethod(field.Name + "Values"); if (valuesMethod != null) { if (String.IsNullOrEmpty(xmlProperty.Value)) { fieldValue = (int)-1; } else { string[] values = (string[])valuesMethod.Invoke(template, null); fieldValue = Array.FindIndex(values, v => v == xmlProperty.Value); } } else if (settings.EnumValue != null) { if (String.IsNullOrEmpty(xmlProperty.Value)) { fieldValue = (int)-1; } else { fieldValue = Array.FindIndex(settings.EnumValue, v => v == xmlProperty.Value); } } else { fieldValue = int.Parse(xmlProperty.Value); } break; case "Int64": fieldValue = long.Parse(xmlProperty.Value); break; case "Byte[]": fieldValue = xmlProperty.Value == null ? null : Convert.FromBase64String(xmlProperty.Value); break; case "List`1": Type elementType = fieldType.GetGenericArguments()[0]; Type listType = typeof(List <>).MakeGenericType(elementType); IList list = (IList)Activator.CreateInstance(listType); foreach (EXmlData innerXmlData in xmlProperty.Elements.OfType <EXmlData>()) { NMSTemplate element = DeserializeEXml(innerXmlData); list.Add(element); } fieldValue = list; break; default: if (field.FieldType.IsArray && field.FieldType.GetElementType().BaseType.Name == "NMSTemplate") { Array array = Array.CreateInstance(field.FieldType.GetElementType(), settings.Size); List <EXmlData> data = xmlProperty.Elements.OfType <EXmlData>().ToList(); for (int i = 0; i < data.Count; ++i) { NMSTemplate element = DeserializeEXml(data[i]); array.SetValue(element, i); } fieldValue = array; } else { fieldValue = fieldType.IsValueType ? Activator.CreateInstance(fieldType) : null; } break; } field.SetValue(template, fieldValue); } foreach (EXmlData innerXmlData in xmlData.Elements.OfType <EXmlData>()) { FieldInfo field = templateType.GetField(innerXmlData.Name); NMSTemplate innerTemplate = DeserializeEXml(innerXmlData); field.SetValue(template, innerTemplate); } return(template); }
public EXmlData SerializeEXml() { Type type = GetType(); EXmlData xmlData = new EXmlData { Template = type.Name }; var fields = type.GetFields().OrderBy(field => field.MetadataToken); // hack to get fields in order of declaration (todo: use something less hacky, this might break mono?) foreach (var field in fields) { NMSAttribute settings = field.GetCustomAttribute <NMSAttribute>(); if (settings == null) { settings = new NMSAttribute(); } if (settings.Ignore) { continue; } var fieldName = field.Name; var fieldType = field.FieldType.Name; switch (fieldType) { case "String": case "Single": case "Boolean": case "Int16": case "UInt16": case "Int32": case "UInt32": case "Int64": case "UInt64": var value = field.GetValue(this); var valueStr = value.ToString(); var valuesMethod = type.GetMethod(field.Name + "Values"); // if we have an "xxxValues()" method in the struct, use that to get the value name if (valuesMethod != null) { if (((int)value) == -1) { valueStr = ""; } else { string[] values = (string[])valuesMethod.Invoke(this, null); valueStr = values[(int)value]; } } else if (settings.EnumValue != null) { if (((int)value) == -1) { valueStr = ""; } else { valueStr = settings.EnumValue[(int)value]; } } xmlData.Elements.Add(new EXmlProperty { Name = fieldName, Value = valueStr }); break; case "Byte[]": byte[] bytes = (byte[])field.GetValue(this); string base64Value = bytes == null ? null : Convert.ToBase64String(bytes); xmlData.Elements.Add(new EXmlProperty { Name = fieldName, Value = base64Value }); break; case "List`1": EXmlProperty listProperty = new EXmlProperty { Name = fieldName }; IList templates = (IList)field.GetValue(this); foreach (var template in templates.Cast <NMSTemplate>()) { listProperty.Elements.Add(template.SerializeEXml()); } xmlData.Elements.Add(listProperty); break; case "NMSTemplate": if (field.GetValue(this) != null) { NMSTemplate template = (NMSTemplate)field.GetValue(this); EXmlData templateXmlData = template.SerializeEXml(); templateXmlData.Name = fieldName; xmlData.Elements.Add(templateXmlData); } break; default: if (field.FieldType.BaseType.Name == "NMSTemplate") { NMSTemplate template = (NMSTemplate)field.GetValue(this); EXmlData templateXmlData = template.SerializeEXml(); templateXmlData.Name = fieldName; xmlData.Elements.Add(templateXmlData); } else if (field.FieldType.IsArray && field.FieldType.GetElementType().BaseType.Name == "NMSTemplate") { var arrayType = field.FieldType.GetElementType(); EXmlProperty arrayProperty = new EXmlProperty { Name = fieldName }; Array array = (Array)field.GetValue(this); int i = 0; foreach (var template in array) { EXmlData data = ((NMSTemplate)template).SerializeEXml(); if (settings.EnumValue != null) { data.Name = settings.EnumValue[i]; i++; } arrayProperty.Elements.Add(data); } xmlData.Elements.Add(arrayProperty); } else { throw new Exception(string.Format("Unable to encode {0} to EXml!", field)); } break; } } return(xmlData); }
public static object DeserializeEXmlValue(NMSTemplate template, Type fieldType, FieldInfo field, EXmlProperty xmlProperty, Type templateType, NMSAttribute settings) { switch (fieldType.Name) { case "String": return(xmlProperty.Value); case "Single": return(float.Parse(xmlProperty.Value)); case "Boolean": return(bool.Parse(xmlProperty.Value)); case "Int16": return(short.Parse(xmlProperty.Value)); case "UInt16": return(ushort.Parse(xmlProperty.Value)); case "Int32": var valuesMethod = templateType.GetMethod(field.Name + "Values"); if (valuesMethod != null) { if (String.IsNullOrEmpty(xmlProperty.Value)) { return(-1); } else { string[] values = (string[])valuesMethod.Invoke(template, null); return(Array.FindIndex(values, v => v == xmlProperty.Value)); } } else if (settings?.EnumValue != null) { if (String.IsNullOrEmpty(xmlProperty.Value)) { return(-1); } else { return(Array.FindIndex(settings.EnumValue, v => v == xmlProperty.Value)); } } else { return(int.Parse(xmlProperty.Value)); } case "UInt32": return(uint.Parse(xmlProperty.Value)); case "Int64": return(long.Parse(xmlProperty.Value)); case "UInt64": return(ulong.Parse(xmlProperty.Value)); case "Byte[]": return(xmlProperty.Value == null ? null : Convert.FromBase64String(xmlProperty.Value)); case "List`1": Type elementType = fieldType.GetGenericArguments()[0]; Type listType = typeof(List <>).MakeGenericType(elementType); IList list = (IList)Activator.CreateInstance(listType); foreach (var innerXmlData in xmlProperty.Elements) // child templates { object element = null; if (innerXmlData.GetType() == typeof(EXmlData) || (innerXmlData.GetType() == typeof(EXmlProperty) && ((EXmlProperty)innerXmlData).Value.EndsWith(".xml"))) { element = DeserializeEXml(innerXmlData); // child template if <Data> tag or <Property> tag with value ending in .xml (todo: better way of finding <Property> child templates) } else if (innerXmlData.GetType() == typeof(EXmlProperty)) { element = DeserializeEXmlValue(template, elementType, field, (EXmlProperty)innerXmlData, templateType, settings); } if (element == null) { throw new Exception("element == null ??!"); } list.Add(element); } return(list); default: if (field.FieldType.IsArray && field.FieldType.GetElementType().BaseType.Name == "NMSTemplate") { Array array = Array.CreateInstance(field.FieldType.GetElementType(), settings.Size); var data = xmlProperty.Elements.OfType <EXmlProperty>().ToList(); if (data.Count != settings.Size) { // todo: add a comment in the XML to indicate arrays (+ their size), also need to do the same for showing valid enum values var error = $"{field.Name}: XML array size {data.Count} doesn't match expected array size {settings.Size}"; Console.WriteLine($"Error: {error}!"); Console.WriteLine("You might have added/removed an item from an array field"); Console.WriteLine("(arrays can't be shortened or extended as they're a fixed size set by the game)"); throw new Exception(error); } for (int i = 0; i < data.Count; ++i) { NMSTemplate element = DeserializeEXml(data[i]); array.SetValue(element, i); } return(array); } else if (field.FieldType.IsArray) { Array array = Array.CreateInstance(field.FieldType.GetElementType(), settings.Size); List <EXmlProperty> data = xmlProperty.Elements.OfType <EXmlProperty>().ToList(); for (int i = 0; i < data.Count; ++i) { object element = DeserializeEXmlValue(template, field.FieldType.GetElementType(), field, data[i], templateType, settings); array.SetValue(element, i); } return(array); } else { return(fieldType.IsValueType ? Activator.CreateInstance(fieldType) : null); } } }