Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.Collections.Specialized;
- using System.ComponentModel;
- using System.Linq;
- using System.Reflection;
- using System.Xml;
- using System.Xml.Linq;
- using UnityEngine;
- namespace EpsilonFramework.Utilities
- {
- //
- //If you're here to edit/bugfix this, sorry man. Reflection tends to turn into a mess
- //~Deukhoofd
- //
- public class DontSaveAttribute : Attribute
- {
- }
- [Serializable]
- public abstract class Saveable
- {
- public virtual XmlElement GetSaveData(XmlDocument doc)
- {
- //Get all fields
- var bindingFlags = BindingFlags.Instance |
- BindingFlags.NonPublic |
- BindingFlags.Public;
- var fields = this.GetType().GetFields(bindingFlags);
- XmlElement root = doc.CreateElement(this.GetType().Name);
- foreach (var field in fields)
- {
- if (field.GetCustomAttributes(true).Any(x => x is DontSaveAttribute))
- {
- continue;
- }
- var fieldName = field.Name;
- if (fieldName.Contains("_BackingField"))
- {
- var temp = fieldName.Split('>')[0];
- fieldName = temp.Substring(1, temp.Length - 1);
- var prop = this.GetType().GetProperty(fieldName, bindingFlags);
- if (prop.GetCustomAttributes(true).Any(x => x is DontSaveAttribute))
- {
- continue;
- }
- if (!prop.CanWrite)
- continue;
- }
- if (field.FieldType == typeof(string))
- {
- var data = field.GetValue(this);
- var s = "null";
- if (data != null)
- s = data.ToString();
- root.SetAttribute(fieldName, s);
- }
- //If this is an array
- else if (typeof(IEnumerable).IsAssignableFrom(field.FieldType))
- {
- var el = ConvertArray(doc, fieldName, field.GetValue(this));
- root.AppendChild(el);
- }
- //If the field is a class (so not a primal value like int, string)
- else if (field.FieldType.IsClass)
- {
- var ele = ConvertClassType(doc, fieldName, field.GetValue(this));
- root.AppendChild(ele);
- }
- else
- {
- root.SetAttribute(fieldName, field.GetValue(this).ToString());
- }
- }
- return root;
- }
- private XmlElement ConvertClassType(XmlDocument doc, string name, object o)
- {
- var ele = doc.CreateElement(name);
- if (o == null)
- {
- ele.SetAttribute("value", "null");
- return ele;
- }
- var saveable = o as Saveable;
- if (saveable != null)
- {
- var data = saveable.GetSaveData(doc);
- ele.AppendChild(data);
- }
- else if (o is string)
- {
- ele.SetAttribute("value", o.ToString());
- }
- else if (o is int)
- {
- ele.SetAttribute("value", o.ToString());
- }
- else if (o is bool)
- {
- ele.SetAttribute("value", o.ToString());
- }
- else if (o.GetType().IsEnum)
- {
- // if this is an enum, get the type it inherits
- var underlying = o.GetType().GetEnumUnderlyingType();
- // convert the value to that type
- var val = Convert.ChangeType(o, underlying);
- // and save it. If the cast is somehow invalid, set it to 0
- ele.SetAttribute("value", val?.ToString() ?? "0");
- }
- else if (o is Vector3)
- {
- var vec = (Vector3)o;
- ele.SetAttribute("value", $"{vec.x},{vec.y},{vec.z}");
- }
- else
- {
- throw new Exception($"An object could not be saved in class {this.GetType()}, Name: {name}, Type: {o.GetType()}");
- }
- return ele;
- }
- private XmlElement ConvertArray(XmlDocument doc,string name, object o)
- {
- var el = doc.CreateElement(name);
- var temp = o as IEnumerable;
- if (temp == null)
- return el;
- var arr = temp.Cast<object>();
- el.SetAttribute("Length", arr.Count().ToString());
- var enumerable = arr as object[] ?? arr.ToArray();
- if (enumerable.Length == 0)
- {
- return el;
- }
- if (enumerable.First() is byte)
- {
- el.SetAttribute("value", Convert.ToBase64String(enumerable.Cast<byte>().ToArray()));
- }
- else if (enumerable.First() is string)
- {
- var strarr = enumerable.Cast<string>();
- for (var index = 0; index < arr.Count(); index++)
- {
- string t = strarr.ElementAt(index);
- var child = doc.CreateElement("data");
- child.SetAttribute("value", t);
- child.SetAttribute("ArrayIndex", index.ToString());
- el.AppendChild(child);
- }
- }
- else if (enumerable.First() is bool)
- {
- var barr = enumerable.Cast<bool>();
- var bools = barr as bool[] ?? barr.ToArray();
- for (var index = 0; index < bools.Count(); index++)
- {
- var b = bools.ElementAt(index);
- var child = doc.CreateElement("data");
- child.SetAttribute("value", b.ToString());
- child.SetAttribute("ArrayIndex", index.ToString());
- el.AppendChild(child);
- }
- }
- else if (enumerable.First() is DictionaryEntry)
- {
- var dic = enumerable.Cast<DictionaryEntry>();
- foreach (var kp in dic)
- {
- var child = doc.CreateElement("data");
- child.SetAttribute("key", kp.Key.ToString());
- child.SetAttribute("value", kp.Value.ToString());
- el.AppendChild(child);
- }
- }
- // ReSharper disable once OperatorIsCanBeUsed
- else if (temp is IDictionary)
- {
- var first = enumerable.First();
- var t = first.GetType();
- var keyType = t.GenericTypeArguments[0];
- var valueType = t.GenericTypeArguments[1];
- foreach (var o1 in enumerable)
- {
- var keyProp = o1.GetType().GetProperty("Key");
- var valueProp = o1.GetType().GetProperty("Value");
- var key = keyProp.GetValue(o1);
- var value = valueProp.GetValue(o1);
- var child = doc.CreateElement("data");
- var keyEle = ConvertClassType(doc, "key", key);
- var valueEle = ConvertClassType(doc, "value", value);
- child.AppendChild(keyEle);
- child.AppendChild(valueEle);
- el.AppendChild(child);
- }
- }
- else
- {
- return ConvertClassArray(doc, el, enumerable);
- }
- return el;
- }
- private XmlElement ConvertClassArray(XmlDocument doc, XmlElement el, IEnumerable basearr)
- {
- var arr = basearr.Cast<object>();
- for (var i = 0; i < arr.Count(); i++)
- {
- var ele = ConvertClassType(doc, "data", arr.ElementAt(i));
- ele.SetAttribute("ArrayIndex", i.ToString());
- el.AppendChild(ele);
- }
- return el;
- }
- internal virtual void LoadData(XElement doc, bool topMost = false)
- {
- //Get all fields
- var bindingFlags = BindingFlags.Instance |
- BindingFlags.NonPublic |
- BindingFlags.Public;
- var fields = this.GetType().GetFields(bindingFlags);
- XElement root = null;
- if (topMost)
- {
- root = doc;
- }
- else
- {
- root = doc.Element(this.GetType().Name);
- }
- foreach (var field in fields)
- {
- MemberInfo info = field;
- if (field.GetCustomAttributes(true).Any(x => x is DontSaveAttribute))
- {
- continue;
- }
- var fieldName = field.Name;
- if (fieldName.Contains("_BackingField"))
- {
- var temp = fieldName.Split('>')[0];
- fieldName = temp.Substring(1, temp.Length - 1);
- var prop = this.GetType().GetProperty(fieldName, bindingFlags);
- if (prop.GetCustomAttributes(true).Any(x => x is DontSaveAttribute))
- {
- continue;
- }
- if (!prop.CanWrite)
- continue;
- }
- if (fieldName.Contains("_BackingField"))
- {
- var temp = fieldName.Split('>')[0];
- fieldName = temp.Substring(1, temp.Length - 1);
- info = this.GetType().GetProperty(fieldName);
- }
- if (root == null)
- continue;
- var checkElement = root.Element(fieldName);
- if (checkElement != null)
- {
- if (checkElement.Attribute("value") != null)
- {
- if (checkElement.Attribute("value").Value == "null")
- {
- continue;
- }
- }
- }
- if (field.FieldType == typeof(string))
- {
- var name = root.Attribute(fieldName)?.Value;
- if (name != null && name != "null")
- {
- SetValue(info, root.Attribute(fieldName).Value);
- // field.SetValue(this, root.Attribute(fieldName).Value);
- }
- }
- else if (typeof(IEnumerable).IsAssignableFrom(field.FieldType))
- {
- LoadArray(field, root.Element(fieldName));
- }
- //If the field is a class (so not a primal value like int, string)
- else if (field.FieldType.IsClass)
- {
- SetValue(info, LoadObject(field.FieldType, root.Element(fieldName)));
- //field.SetValue(this, LoadObject(field.FieldType, root.Element(fieldName)));
- }
- else
- {
- XAttribute xAttribute = null;
- try
- {
- xAttribute = root.Attribute(fieldName);
- }
- catch
- {
- Debug.Log(fieldName);
- }
- if (xAttribute != null)
- {
- var stored = xAttribute.Value;
- if (stored != "null")
- {
- var converter = TypeDescriptor.GetConverter(field.FieldType);
- try
- {
- var converted = converter.ConvertFrom(stored);
- SetValue(info, converted);
- //field.SetValue(this, converted);
- }
- catch
- {
- Debug.Log("Cant convert " + field.FieldType + " from " + stored);
- }
- }
- }
- else
- {
- //Debug.Log(fieldName);
- }
- }
- }
- }
- private void SetValue(MemberInfo info, object value)
- {
- var propertyInfo = info as PropertyInfo;
- if (propertyInfo != null)
- {
- propertyInfo.SetValue(this, value);
- }
- var fieldInfo = info as FieldInfo;
- if (fieldInfo != null)
- {
- fieldInfo.SetValue(this, value);
- }
- }
- private object LoadObject(Type type, XElement element)
- {
- if (typeof(Saveable).IsAssignableFrom(type))
- {
- ConstructorInfo ci = type.GetConstructor(
- BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
- null, new Type[]{}, null);
- if (ci == null)
- {
- throw new NullReferenceException($"Class {type} does not have a parameterless constructor. " +
- "Please add a constructor to load it.");
- }
- var o = (Saveable) ci.Invoke(new object[] { });
- var savedata = (Saveable) o;
- savedata.LoadData(element);
- return savedata;
- }
- else if (type == typeof(string))
- {
- return element.Attribute("value").Value;
- }
- else if (type == typeof(int))
- {
- return int.Parse(element.Attribute("value").Value);
- }
- else if (type == typeof(bool))
- {
- return bool.Parse(element.Attribute("value").Value);
- }
- else if (type.IsEnum)
- {
- var underlying = type.GetEnumUnderlyingType();
- return Convert.ChangeType(element.Attribute("value").Value, underlying);
- }
- else if (type == typeof(Vector3))
- {
- var val = element.Attribute("value").Value;
- var a = val.Split(',');
- var vec = new Vector3(float.Parse(a[0]),float.Parse(a[1]),float.Parse(a[2]));
- return vec;
- }
- Debug.Log($"{type} does not implement saveable, and doesn't appear to be a based on a base type that's saved");
- return null;
- }
- private IEnumerable LoadArray(FieldInfo field, XElement element)
- {
- if (element == null)
- {
- throw new NullReferenceException(field + " is empty. " + this.GetType().Name);
- }
- if (!element.HasElements)
- {
- if (element.Attribute("value") != null && field.FieldType == typeof(byte[]))
- {
- var bar = Convert.FromBase64String(element.Attribute("value").Value);
- field.SetValue(this, bar);
- return bar;
- }
- }
- else
- {
- if (field.FieldType.IsArray)
- {
- var t = field.FieldType.GetElementType();
- var count = int.Parse(element.Attribute("Length").Value);
- var arr = Array.CreateInstance(t, count);
- foreach (var xElement in element.Elements())
- {
- if (xElement.Attribute("value") != null && xElement.Attribute("value").Value == "null")
- {
- continue;
- }
- var index = int.Parse(xElement.Attribute("ArrayIndex").Value);
- if (typeof(Saveable).IsAssignableFrom(t))
- {
- ConstructorInfo ci = t.GetConstructor(
- BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
- null, new Type[]{}, null);
- if (ci == null)
- {
- throw new Exception($"Can't find a parameteless constructor for type {t}");
- }
- var o = (Saveable) ci.Invoke(new object[] { });
- var save = (Saveable) o;
- save.LoadData(xElement);
- arr.SetValue(save, index);
- }
- else
- {
- var converter = TypeDescriptor.GetConverter(t);
- arr.SetValue(converter.ConvertFrom(xElement.Attribute("value").Value), index);
- }
- }
- field.SetValue(this, arr);
- return arr;
- }
- else if (field.FieldType == typeof(OrderedDictionary))
- {
- var od = new OrderedDictionary();
- field.SetValue(this, od);
- return od;
- }
- else if (field.FieldType.IsGenericType &&
- field.FieldType.GetGenericTypeDefinition() == typeof(Dictionary<,>))
- {
- var args = field.FieldType.GetGenericArguments();
- var keyType = args.First();
- var valueType = args[1];
- var dicType = typeof(Dictionary<,>).MakeGenericType(keyType, valueType);
- var dic = (IDictionary)Activator.CreateInstance(dicType);
- foreach (var xElement in element.Elements())
- {
- var keyElement = xElement.Element("key");
- var keyObject = LoadObject(keyType, keyElement);
- var valueElement = xElement.Element("value");
- var valueObject = LoadObject(valueType, valueElement);
- dic.Add(keyObject, valueObject);
- }
- field.SetValue(this, dic);
- }
- else
- {
- var t = field.FieldType.GetGenericArguments().First();
- var l = typeof(List<>).MakeGenericType(t);
- var ls = (IList) Activator.CreateInstance(l);
- foreach (var xElement in element.Elements())
- {
- if (typeof(Saveable).IsAssignableFrom(t))
- {
- var xAttribute = xElement.Attribute("value");
- if (xAttribute != null && xAttribute.Value == "null")
- continue;
- var save = (Saveable) Activator.CreateInstance(t);
- save.LoadData(xElement);
- ls.Add(save);
- }
- else if (typeof(IEnumerable).IsAssignableFrom(t))
- {
- var subArr = LoadArray(field, xElement);
- ls.Add(subArr);
- }
- else
- {
- throw new Exception(t.ToString());
- }
- //ls.Add(xElement);
- }
- field.SetValue(this, ls);
- return ls;
- }
- //field.SetValue(this, l);
- }
- return null;
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement