Esempio n. 1
0
        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;
                }
                xmlData.Elements.Add(SerializeEXmlValue(field.FieldType, field, settings, field.GetValue(this)));
            }

            return(xmlData);
        }
Esempio n. 2
0
        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);
        }
Esempio n. 3
0
        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);
        }
Esempio n. 4
0
        public void AppendToWriter(BinaryWriter writer, ref List <Tuple <long, object> > additionalData)
        {
            long templatePosition = writer.BaseStream.Position;

            Debug.WriteLine($"[C] writing {GetType().Name} to offset 0x{templatePosition:X}");

            var type   = 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 = writer.BaseStream.Position - templatePosition;
                var          fieldData = field.GetValue(this);
                NMSAttribute settings  = field.GetCustomAttribute <NMSAttribute>();
                SerializeValue(writer, field.FieldType, fieldData, settings, field, ref additionalData);
            }
        }
Esempio n. 5
0
 public virtual bool CustomSerialize(BinaryWriter writer, Type field, object fieldData, NMSAttribute settings, FieldInfo fieldInfo, ref List <Tuple <long, object> > additionalData)
 {
     return(false);
 }
Esempio n. 6
0
 public virtual object CustomDeserialize(BinaryReader reader, Type field, NMSAttribute settings, long templatePosition, FieldInfo fieldInfo)
 {
     return(null);
 }
Esempio n. 7
0
        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);
                }
            }
        }
Esempio n. 8
0
        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));
                }
            }
        }
Esempio n. 9
0
        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);
                }
            }
        }
Esempio n. 10
0
        public void SerializeValue(BinaryWriter writer, Type fieldType, object fieldData, NMSAttribute settings, FieldInfo field, ref List <Tuple <long, object> > additionalData)
        {
            if (CustomSerialize(writer, fieldType, fieldData, settings, field, ref additionalData))
            {
                return;
            }

            switch (fieldType.Name)
            {
            case "String":
            case "Byte[]":
                int size = settings?.Size ?? 0;
                MarshalAsAttribute legacySettings = field?.GetCustomAttribute <MarshalAsAttribute>();
                if (legacySettings != null)
                {
                    size = legacySettings.SizeConst;
                }

                if (fieldType.Name == "String")
                {
                    writer.WriteString((string)fieldData, Encoding.UTF8, size);
                }
                else
                {
                    byte[] bytes = (byte[])fieldData;
                    Array.Resize(ref bytes, size);
                    writer.Write(bytes);
                }
                break;

            case "Byte":
                writer.Write((Byte)fieldData);
                break;

            case "Single":
                writer.Align(4, 0);
                writer.Write((Single)fieldData);
                break;

            case "Boolean":
                var value = (bool)fieldData;
                writer.Write(value ? (byte)1 : (byte)0);
                break;

            case "Int16":
            case "UInt16":
                writer.Align(2, 0);
                if (fieldType.Name == "Int16")
                {
                    writer.Write((Int16)fieldData);
                }
                else
                {
                    writer.Write((UInt16)fieldData);
                }
                break;

            case "Int32":
            case "UInt32":
                writer.Align(4, 0);
                if (fieldType.Name == "Int32")
                {
                    writer.Write((Int32)fieldData);
                }
                else
                {
                    writer.Write((UInt32)fieldData);
                }
                break;

            case "Int64":
            case "UInt64":
                writer.Align(8, 0);
                if (fieldType.Name == "Int64")
                {
                    writer.Write((Int64)fieldData);
                }
                else
                {
                    writer.Write((UInt64)fieldData);
                }
                break;

            case "List`1":
                writer.Align(8, 0);
                if (field != null && field.FieldType.IsGenericType && field.FieldType.GetGenericTypeDefinition() == typeof(List <>))
                {
                    // write empty list header
                    long listPos = writer.BaseStream.Position;
                    writer.Write((Int64)0);     // listPosition
                    writer.Write((Int32)0);     // listCount
                    writer.Write((UInt32)0xAAAAAA01);

                    var list = (IList)fieldData;
                    additionalData.Add(new Tuple <long, object>(listPos, list));
                }

                break;

            case "NMSTemplate":
                writer.Align(8, 0);
                long refPos = writer.BaseStream.Position;
                writer.Write((Int64)0);     // listPosition
                var template = (NMSTemplate)fieldData;
                if (template == null)
                {
                    writer.WriteString("", Encoding.UTF8, 0x40);
                }
                else
                {
                    writer.WriteString("c" + template.GetType().Name, Encoding.UTF8, 0x40);
                    additionalData.Add(new Tuple <long, object>(refPos, template));
                }
                break;

            default:

                if (fieldType.Name == "Colour")     // unsure if this is needed?
                {
                    writer.Align(0x10, 0);
                }

                // todo: align for VariableSizeString?
                if (fieldType.Name == "VariableSizeString")
                {
                    // write empty DynamicString header
                    long fieldPos = writer.BaseStream.Position;
                    writer.Write((Int64)0);     // listPosition
                    writer.Write((Int32)0);     // listCount
                    writer.Write((UInt32)0xAAAAAA01);

                    var fieldValue = (VariableSizeString)fieldData;
                    additionalData.Add(new Tuple <long, object>(fieldPos, fieldValue));
                }
                else if (fieldType.IsArray)
                {
                    var   arrayType = fieldType.GetElementType();
                    Array array     = (Array)fieldData;
                    foreach (var obj in array)
                    {
                        SerializeValue(writer, obj.GetType(), obj, settings, field, ref additionalData);
                    }
                }
                else
                {
                    if (fieldType.BaseType == typeof(NMSTemplate))
                    {
                        ((NMSTemplate)fieldData).AppendToWriter(writer, ref additionalData);
                    }
                    else
                    {
                        throw new Exception($"[C] Unknown type {fieldType} not NMSTemplate" + (field != null ? $" for {field.Name}" : ""));
                    }
                }

                break;
            }
        }
Esempio n. 11
0
        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);
        }
Esempio n. 12
0
        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);
        }
Esempio n. 13
0
        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);
        }
Esempio n. 14
0
        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;

            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>();
                if (settings == null)
                {
                    settings = new NMSAttribute();
                }
                var fieldAddr = reader.BaseStream.Position - templatePosition;
                var fieldName = field.Name;
                var fieldType = field.FieldType.Name;
                switch (fieldType)
                {
                case "String":
                case "Byte[]":
                    int size = settings.Size;

                    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?
                    if (field.FieldType.IsArray && field.FieldType.GetElementType().BaseType.Name == "NMSTemplate")
                    {
                        var   arrayType = field.FieldType.GetElementType();
                        Array array     = Array.CreateInstance(arrayType, settings.Size);
                        for (int i = 0; i < settings.Size; ++i)
                        {
                            array.SetValue(DeserializeBinaryTemplate(reader, field.FieldType.GetElementType().Name), i);
                        }
                        field.SetValue(obj, array);
                    }
                    else
                    {
                        var data = DeserializeBinaryTemplate(reader, fieldType);
                        if (data != null)
                        {
                            field.SetValue(obj, data);
                        }
                    }
                    break;
                }
            }

            return(obj);
        }
Esempio n. 15
0
        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);
                }
            }
        }