(61d00a474) v0.9.7.1
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
interface ISerializableEntity
|
||||
{
|
||||
string Name
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
Dictionary<string, SerializableProperty> SerializableProperties
|
||||
{
|
||||
get;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,729 @@
|
||||
using Barotrauma.Items.Components;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
class Editable : Attribute
|
||||
{
|
||||
public int MaxLength;
|
||||
public int DecimalCount = 1;
|
||||
|
||||
public int MinValueInt = int.MinValue, MaxValueInt = int.MaxValue;
|
||||
public float MinValueFloat = float.MinValue, MaxValueFloat = float.MaxValue;
|
||||
public float ValueStep;
|
||||
|
||||
/// <summary>
|
||||
/// Currently implemented only for int fields. TODO: implement the remaining types (SerializableEntityEditor)
|
||||
/// </summary>
|
||||
public bool ReadOnly;
|
||||
|
||||
public Editable(int maxLength = 20)
|
||||
{
|
||||
MaxLength = maxLength;
|
||||
}
|
||||
|
||||
public Editable(int minValue, int maxValue)
|
||||
{
|
||||
MinValueInt = minValue;
|
||||
MaxValueInt = maxValue;
|
||||
}
|
||||
|
||||
public Editable(float minValue, float maxValue, int decimals = 1)
|
||||
{
|
||||
MinValueFloat = minValue;
|
||||
MaxValueFloat = maxValue;
|
||||
DecimalCount = decimals;
|
||||
}
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
class InGameEditable : Editable
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
public class Serialize : Attribute
|
||||
{
|
||||
public object defaultValue;
|
||||
public bool isSaveable;
|
||||
public string translationTextTag;
|
||||
|
||||
public string Description;
|
||||
|
||||
/// <summary>
|
||||
/// Makes the property serializable to/from XML
|
||||
/// </summary>
|
||||
/// <param name="defaultValue">The property is set to this value during deserialization if the value is not defined in XML.</param>
|
||||
/// <param name="isSaveable">Is the value saved to XML when serializing.</param>
|
||||
/// <param name="translationTextTag">If set to anything else than null, SerializableEntityEditors will show what the text gets translated to or warn if the text is not found in the language files.
|
||||
/// Setting the value to a non-empty string will let the user select the text from one whose tag starts with the given string (e.g. RoomName. would show all texts with a RoomName.* tag)</param>
|
||||
public Serialize(object defaultValue, bool isSaveable, string description = "", string translationTextTag = null)
|
||||
{
|
||||
this.defaultValue = defaultValue;
|
||||
this.isSaveable = isSaveable;
|
||||
this.translationTextTag = translationTextTag;
|
||||
this.Description = description;
|
||||
}
|
||||
}
|
||||
|
||||
class SerializableProperty
|
||||
{
|
||||
private static Dictionary<Type, string> supportedTypes = new Dictionary<Type, string>
|
||||
{
|
||||
{ typeof(bool), "bool" },
|
||||
{ typeof(int), "int" },
|
||||
{ typeof(float), "float" },
|
||||
{ typeof(string), "string" },
|
||||
{ typeof(Point), "point" },
|
||||
{ typeof(Vector2), "vector2" },
|
||||
{ typeof(Vector3), "vector3" },
|
||||
{ typeof(Vector4), "vector4" },
|
||||
{ typeof(Rectangle), "rectangle" },
|
||||
{ typeof(Color), "color" },
|
||||
};
|
||||
|
||||
private static readonly Dictionary<Type, Dictionary<string, SerializableProperty>> cachedProperties =
|
||||
new Dictionary<Type, Dictionary<string, SerializableProperty>>();
|
||||
public readonly string Name;
|
||||
public readonly string NameToLowerInvariant;
|
||||
public readonly AttributeCollection Attributes;
|
||||
public readonly Type PropertyType;
|
||||
|
||||
public PropertyInfo PropertyInfo { get; private set; }
|
||||
|
||||
public SerializableProperty(PropertyDescriptor property)
|
||||
{
|
||||
Name = property.Name;
|
||||
NameToLowerInvariant = Name.ToLowerInvariant();
|
||||
PropertyInfo = property.ComponentType.GetProperty(property.Name);
|
||||
PropertyType = property.PropertyType;
|
||||
Attributes = property.Attributes;
|
||||
}
|
||||
|
||||
public T GetAttribute<T>() where T : Attribute
|
||||
{
|
||||
foreach (Attribute a in Attributes)
|
||||
{
|
||||
if (a is T) return (T)a;
|
||||
}
|
||||
|
||||
return default(T);
|
||||
}
|
||||
|
||||
public void SetValue(object parentObject, object val)
|
||||
{
|
||||
PropertyInfo.SetValue(parentObject, val);
|
||||
}
|
||||
|
||||
public bool TrySetValue(object parentObject, string value)
|
||||
{
|
||||
if (value == null) { return false; }
|
||||
|
||||
if (!supportedTypes.TryGetValue(PropertyType, out string typeName))
|
||||
{
|
||||
if (PropertyType.IsEnum)
|
||||
{
|
||||
object enumVal;
|
||||
try
|
||||
{
|
||||
enumVal = Enum.Parse(PropertyInfo.PropertyType, value, true);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("Failed to set the value of the property \"" + Name + "\" of \"" + parentObject + "\" to " + value + " (not a valid " + PropertyInfo.PropertyType + ")", e);
|
||||
return false;
|
||||
}
|
||||
try
|
||||
{
|
||||
PropertyInfo.SetValue(parentObject, enumVal);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("Failed to set the value of the property \"" + Name + "\" of \"" + parentObject.ToString() + "\" to " + value.ToString(), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError("Failed to set the value of the property \"" + Name + "\" of \"" + parentObject + "\" to " + value);
|
||||
DebugConsole.ThrowError("(Type not supported)");
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
switch (typeName)
|
||||
{
|
||||
case "bool":
|
||||
bool boolValue = value == "true" || value == "True";
|
||||
if (TrySetValueWithoutReflection(parentObject, boolValue)) { return true; }
|
||||
PropertyInfo.SetValue(parentObject, boolValue, null);
|
||||
break;
|
||||
case "int":
|
||||
if (int.TryParse(value, out int intVal))
|
||||
{
|
||||
if (TrySetValueWithoutReflection(parentObject, intVal)) { return true; }
|
||||
PropertyInfo.SetValue(parentObject, intVal, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case "float":
|
||||
if (float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out float floatVal))
|
||||
{
|
||||
if (TrySetValueWithoutReflection(parentObject, floatVal)) { return true; }
|
||||
PropertyInfo.SetValue(parentObject, floatVal, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case "string":
|
||||
PropertyInfo.SetValue(parentObject, value, null);
|
||||
break;
|
||||
case "point":
|
||||
PropertyInfo.SetValue(parentObject, XMLExtensions.ParsePoint(value));
|
||||
break;
|
||||
case "vector2":
|
||||
PropertyInfo.SetValue(parentObject, XMLExtensions.ParseVector2(value));
|
||||
break;
|
||||
case "vector3":
|
||||
PropertyInfo.SetValue(parentObject, XMLExtensions.ParseVector3(value));
|
||||
break;
|
||||
case "vector4":
|
||||
PropertyInfo.SetValue(parentObject, XMLExtensions.ParseVector4(value));
|
||||
break;
|
||||
case "color":
|
||||
PropertyInfo.SetValue(parentObject, XMLExtensions.ParseColor(value));
|
||||
break;
|
||||
case "rectangle":
|
||||
PropertyInfo.SetValue(parentObject, XMLExtensions.ParseRect(value, true));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("Failed to set the value of the property \"" + Name + "\" of \"" + parentObject.ToString() + "\" to " + value.ToString(), e);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TrySetValue(object parentObject, object value)
|
||||
{
|
||||
if (value == null || parentObject == null || PropertyInfo == null) return false;
|
||||
|
||||
try
|
||||
{
|
||||
if (!supportedTypes.TryGetValue(PropertyType, out string typeName))
|
||||
{
|
||||
if (PropertyType.IsEnum)
|
||||
{
|
||||
object enumVal;
|
||||
try
|
||||
{
|
||||
enumVal = Enum.Parse(PropertyInfo.PropertyType, value.ToString(), true);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("Failed to set the value of the property \"" + Name + "\" of \"" + parentObject + "\" to " + value + " (not a valid " + PropertyInfo.PropertyType + ")", e);
|
||||
return false;
|
||||
}
|
||||
PropertyInfo.SetValue(parentObject, enumVal);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError("Failed to set the value of the property \"" + Name + "\" of \"" + parentObject + "\" to " + value);
|
||||
DebugConsole.ThrowError("(Type not supported)");
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (value.GetType() == typeof(string))
|
||||
{
|
||||
switch (typeName)
|
||||
{
|
||||
case "string":
|
||||
PropertyInfo.SetValue(parentObject, value, null);
|
||||
return true;
|
||||
case "point":
|
||||
PropertyInfo.SetValue(parentObject, XMLExtensions.ParsePoint((string)value));
|
||||
return true;
|
||||
case "vector2":
|
||||
PropertyInfo.SetValue(parentObject, XMLExtensions.ParseVector2((string)value));
|
||||
return true;
|
||||
case "vector3":
|
||||
PropertyInfo.SetValue(parentObject, XMLExtensions.ParseVector3((string)value));
|
||||
return true;
|
||||
case "vector4":
|
||||
PropertyInfo.SetValue(parentObject, XMLExtensions.ParseVector4((string)value));
|
||||
return true;
|
||||
case "color":
|
||||
PropertyInfo.SetValue(parentObject, XMLExtensions.ParseColor((string)value));
|
||||
return true;
|
||||
case "rectangle":
|
||||
PropertyInfo.SetValue(parentObject, XMLExtensions.ParseRect((string)value, false));
|
||||
return true;
|
||||
default:
|
||||
DebugConsole.ThrowError("Failed to set the value of the property \"" + Name + "\" of \"" + parentObject.ToString() + "\" to " + value.ToString());
|
||||
DebugConsole.ThrowError("(Cannot convert a string to a " + PropertyType.ToString() + ")");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (PropertyType != value.GetType())
|
||||
{
|
||||
DebugConsole.ThrowError("Failed to set the value of the property \"" + Name + "\" of \"" + parentObject.ToString() + "\" to " + value.ToString());
|
||||
DebugConsole.ThrowError("(Non-matching type, should be " + PropertyType + " instead of " + value.GetType() + ")");
|
||||
return false;
|
||||
}
|
||||
|
||||
PropertyInfo.SetValue(parentObject, value, null);
|
||||
}
|
||||
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("Failed to set the value of the property \"" + Name + "\" of \"" + parentObject.ToString() + "\" to " + value.ToString(), e);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in SerializableProperty.TrySetValue", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool TrySetValue(object parentObject, float value)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (TrySetValueWithoutReflection(parentObject, value)) { return true; }
|
||||
PropertyInfo.SetValue(parentObject, value, null);
|
||||
}
|
||||
catch (TargetInvocationException e)
|
||||
{
|
||||
DebugConsole.ThrowError("Exception thrown by the target of SerializableProperty.TrySetValue", e.InnerException);
|
||||
return false;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in SerializableProperty.TrySetValue", e);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TrySetValue(object parentObject, bool value)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (TrySetValueWithoutReflection(parentObject, value)) { return true; }
|
||||
PropertyInfo.SetValue(parentObject, value, null);
|
||||
}
|
||||
catch (TargetInvocationException e)
|
||||
{
|
||||
DebugConsole.ThrowError("Exception thrown by the target of SerializableProperty.TrySetValue", e.InnerException);
|
||||
return false;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in SerializableProperty.TrySetValue", e);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TrySetValue(object parentObject, int value)
|
||||
{
|
||||
try
|
||||
{
|
||||
PropertyInfo.SetValue(parentObject, value, null);
|
||||
}
|
||||
catch (TargetInvocationException e)
|
||||
{
|
||||
DebugConsole.ThrowError("Exception thrown by the target of SerializableProperty.TrySetValue", e.InnerException);
|
||||
return false;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in SerializableProperty.TrySetValue", e);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public object GetValue(object parentObject)
|
||||
{
|
||||
if (parentObject == null || PropertyInfo == null) { return false; }
|
||||
|
||||
var value = TryGetValueWithoutReflection(parentObject);
|
||||
if (value != null) { return value; }
|
||||
|
||||
try
|
||||
{
|
||||
return PropertyInfo.GetValue(parentObject, null);
|
||||
}
|
||||
catch (TargetInvocationException e)
|
||||
{
|
||||
DebugConsole.ThrowError("Exception thrown by the target of SerializableProperty.GetValue", e.InnerException);
|
||||
return false;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in SerializableProperty.TrySetValue", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetSupportedTypeName(Type type)
|
||||
{
|
||||
if (type.IsEnum) return "Enum";
|
||||
if (!supportedTypes.TryGetValue(type, out string typeName))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return typeName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try getting the values of some commonly used properties directly without reflection
|
||||
/// </summary>
|
||||
private object TryGetValueWithoutReflection(object parentObject)
|
||||
{
|
||||
switch (Name)
|
||||
{
|
||||
case "Voltage":
|
||||
if (parentObject is Powered powered) { return powered.Voltage; }
|
||||
break;
|
||||
case "Charge":
|
||||
if (parentObject is PowerContainer powerContainer) { return powerContainer.Charge; }
|
||||
break;
|
||||
case "Overload":
|
||||
if (parentObject is PowerTransfer powerTransfer) { return powerTransfer.Overload; }
|
||||
break;
|
||||
case "AvailableFuel":
|
||||
{ if (parentObject is Reactor reactor) { return reactor.AvailableFuel; } }
|
||||
break;
|
||||
case "FissionRate":
|
||||
{ if (parentObject is Reactor reactor) { return reactor.FissionRate; } }
|
||||
break;
|
||||
case "OxygenFlow":
|
||||
if (parentObject is Vent vent) { return vent.OxygenFlow; }
|
||||
break;
|
||||
case "CurrFlow":
|
||||
if (parentObject is Pump pump) { return pump.CurrFlow; }
|
||||
if (parentObject is OxygenGenerator oxygenGenerator) { return oxygenGenerator.CurrFlow; }
|
||||
break;
|
||||
case "CurrentVolume":
|
||||
if (parentObject is Engine engine) { return engine.CurrentVolume; }
|
||||
break;
|
||||
case "MotionDetected":
|
||||
if (parentObject is MotionSensor motionSensor) { return motionSensor.MotionDetected; }
|
||||
break;
|
||||
case "Oxygen":
|
||||
{ if (parentObject is Character character) { return character.Oxygen; } }
|
||||
break;
|
||||
case "Health":
|
||||
{ if (parentObject is Character character) { return character.Health; } }
|
||||
break;
|
||||
case "OxygenAvailable":
|
||||
{ if (parentObject is Character character) { return character.OxygenAvailable; } }
|
||||
break;
|
||||
case "PressureProtection":
|
||||
{ if (parentObject is Character character) { return character.PressureProtection; } }
|
||||
break;
|
||||
case "IsDead":
|
||||
{ if (parentObject is Character character) { return character.IsDead; } }
|
||||
break;
|
||||
case "IsOn":
|
||||
{ if (parentObject is LightComponent lightComponent) { return lightComponent.IsOn; } }
|
||||
break;
|
||||
case "Condition":
|
||||
{
|
||||
if (parentObject is Item item) { return item.Condition; }
|
||||
}
|
||||
break;
|
||||
case "ContainerIdentifier":
|
||||
{
|
||||
if (parentObject is Item item) { return item.ContainerIdentifier; }
|
||||
}
|
||||
break;
|
||||
case "PhysicsBodyActive":
|
||||
{
|
||||
if (parentObject is Item item) { return item.PhysicsBodyActive; }
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try setting the values of some commonly used properties directly without reflection
|
||||
/// </summary>
|
||||
private bool TrySetValueWithoutReflection(object parentObject, object value)
|
||||
{
|
||||
switch (Name)
|
||||
{
|
||||
case "Condition":
|
||||
if (parentObject is Item item && value is float) { item.Condition = (float)value; return true; }
|
||||
break;
|
||||
case "Voltage":
|
||||
if (parentObject is Powered powered && value is float) { powered.Voltage = (float)value; return true; }
|
||||
break;
|
||||
case "Charge":
|
||||
if (parentObject is PowerContainer powerContainer && value is float) { powerContainer.Charge = (float)value; return true; }
|
||||
break;
|
||||
case "AvailableFuel":
|
||||
if (parentObject is Reactor reactor && value is float) { reactor.AvailableFuel = (float)value; return true; }
|
||||
break;
|
||||
case "Oxygen":
|
||||
{ if (parentObject is Character character && value is float) { character.Oxygen = (float)value; return true; } }
|
||||
break;
|
||||
case "HideFace":
|
||||
{ if (parentObject is Character character && value is bool) { character.HideFace = (bool)value; return true; } }
|
||||
break;
|
||||
case "OxygenAvailable":
|
||||
{ if (parentObject is Character character && value is float) { character.OxygenAvailable = (float)value; return true; } }
|
||||
break;
|
||||
case "ObstructVision":
|
||||
{ if (parentObject is Character character && value is bool) { character.ObstructVision = (bool)value; return true; } }
|
||||
break;
|
||||
case "PressureProtection":
|
||||
{ if (parentObject is Character character && value is float) { character.PressureProtection = (float)value; return true; } }
|
||||
break;
|
||||
case "LowPassMultiplier":
|
||||
{ if (parentObject is Character character && value is float) { character.LowPassMultiplier = (float)value; return true; } }
|
||||
break;
|
||||
case "SpeedMultiplier":
|
||||
{ if (parentObject is Character character && value is float) { character.StackSpeedMultiplier((float)value); return true; } }
|
||||
break;
|
||||
case "IsOn":
|
||||
{ if (parentObject is LightComponent lightComponent && value is bool) { lightComponent.IsOn = (bool)value; return true; } }
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static List<SerializableProperty> GetProperties<T>(ISerializableEntity obj)
|
||||
{
|
||||
List<SerializableProperty> editableProperties = new List<SerializableProperty>();
|
||||
foreach (var property in obj.SerializableProperties.Values)
|
||||
{
|
||||
if (property.Attributes.OfType<T>().Any()) editableProperties.Add(property);
|
||||
}
|
||||
|
||||
return editableProperties;
|
||||
}
|
||||
|
||||
public static Dictionary<string, SerializableProperty> GetProperties(object obj)
|
||||
{
|
||||
Type objType = obj.GetType();
|
||||
if (cachedProperties.ContainsKey(objType))
|
||||
{
|
||||
return cachedProperties[objType];
|
||||
}
|
||||
|
||||
var properties = TypeDescriptor.GetProperties(obj.GetType()).Cast<PropertyDescriptor>();
|
||||
Dictionary<string, SerializableProperty> dictionary = new Dictionary<string, SerializableProperty>();
|
||||
foreach (var property in properties)
|
||||
{
|
||||
var serializableProperty = new SerializableProperty(property);
|
||||
dictionary.Add(serializableProperty.NameToLowerInvariant, serializableProperty);
|
||||
}
|
||||
|
||||
cachedProperties[objType] = dictionary;
|
||||
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
public static Dictionary<string, SerializableProperty> DeserializeProperties(object obj, XElement element = null)
|
||||
{
|
||||
Dictionary<string, SerializableProperty> dictionary = GetProperties(obj);
|
||||
|
||||
foreach (var property in dictionary.Values)
|
||||
{
|
||||
//set the value of the property to the default value if there is one
|
||||
foreach (var ini in property.Attributes.OfType<Serialize>())
|
||||
{
|
||||
property.TrySetValue(obj, ini.defaultValue);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (element != null)
|
||||
{
|
||||
//go through all the attributes in the xml element
|
||||
//and set the value of the matching property if it is initializable
|
||||
foreach (XAttribute attribute in element.Attributes())
|
||||
{
|
||||
if (!dictionary.TryGetValue(attribute.Name.ToString().ToLowerInvariant(), out SerializableProperty property)) { continue; }
|
||||
if (!property.Attributes.OfType<Serialize>().Any()) { continue; }
|
||||
property.TrySetValue(obj, attribute.Value);
|
||||
}
|
||||
}
|
||||
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
public static void SerializeProperties(ISerializableEntity obj, XElement element, bool saveIfDefault = false)
|
||||
{
|
||||
var saveProperties = GetProperties<Serialize>(obj);
|
||||
foreach (var property in saveProperties)
|
||||
{
|
||||
object value = property.GetValue(obj);
|
||||
if (value == null) continue;
|
||||
|
||||
if (!saveIfDefault)
|
||||
{
|
||||
//only save
|
||||
// - if the attribute is saveable and it's different from the default value
|
||||
// - or can be changed in-game or in the editor
|
||||
bool save = false;
|
||||
foreach (var attribute in property.Attributes.OfType<Serialize>())
|
||||
{
|
||||
if ((attribute.isSaveable && !attribute.defaultValue.Equals(value)) ||
|
||||
property.Attributes.OfType<Editable>().Any())
|
||||
{
|
||||
save = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!save) continue;
|
||||
}
|
||||
|
||||
string stringValue;
|
||||
if (!supportedTypes.TryGetValue(value.GetType(), out string typeName))
|
||||
{
|
||||
if (property.PropertyType.IsEnum)
|
||||
{
|
||||
stringValue = value.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError("Failed to serialize the property \"" + property.Name + "\" of \"" + obj + "\" (type " + property.PropertyType + " not supported)");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (typeName)
|
||||
{
|
||||
case "float":
|
||||
//make sure the decimal point isn't converted to a comma or anything else
|
||||
stringValue = ((float)value).ToString("G", CultureInfo.InvariantCulture);
|
||||
break;
|
||||
case "point":
|
||||
stringValue = XMLExtensions.PointToString((Point)value);
|
||||
break;
|
||||
case "vector2":
|
||||
stringValue = XMLExtensions.Vector2ToString((Vector2)value);
|
||||
break;
|
||||
case "vector3":
|
||||
stringValue = XMLExtensions.Vector3ToString((Vector3)value);
|
||||
break;
|
||||
case "vector4":
|
||||
stringValue = XMLExtensions.Vector4ToString((Vector4)value);
|
||||
break;
|
||||
case "color":
|
||||
stringValue = XMLExtensions.ColorToString((Color)value);
|
||||
break;
|
||||
case "rectangle":
|
||||
stringValue = XMLExtensions.RectToString((Rectangle)value);
|
||||
break;
|
||||
default:
|
||||
stringValue = value.ToString();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
element.Attribute(property.Name)?.Remove();
|
||||
element.SetAttributeValue(property.NameToLowerInvariant, stringValue);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Upgrade the properties of an entity saved with an older version of the game. Properties that should be upgraded are defined using "Upgrade" elements in the config file.
|
||||
/// for example, <Upgrade gameversion="0.9.2.0" scale="0.5"/> would force the scale of the entity to 0.5 if it was saved with a version prior to 0.9.2.0.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity to upgrade</param>
|
||||
/// <param name="configElement">The XML element to get the upgrade instructions from (e.g. the config of an item prefab)</param>
|
||||
/// <param name="savedVersion">The game version the entity was saved with</param>
|
||||
public static void UpgradeGameVersion(ISerializableEntity entity, XElement configElement, Version savedVersion)
|
||||
{
|
||||
foreach (XElement subElement in configElement.Elements())
|
||||
{
|
||||
if (!subElement.Name.ToString().Equals("upgrade", StringComparison.OrdinalIgnoreCase)) { continue; }
|
||||
var upgradeVersion = new Version(subElement.GetAttributeString("gameversion", "0.0.0.0"));
|
||||
if (savedVersion >= upgradeVersion) { continue; }
|
||||
|
||||
foreach (XAttribute attribute in subElement.Attributes())
|
||||
{
|
||||
string attributeName = attribute.Name.ToString().ToLowerInvariant();
|
||||
if (attributeName == "gameversion") { continue; }
|
||||
if (entity.SerializableProperties.TryGetValue(attributeName, out SerializableProperty property))
|
||||
{
|
||||
property.TrySetValue(entity, attribute.Value);
|
||||
}
|
||||
else if (entity is Item item1)
|
||||
{
|
||||
foreach (ISerializableEntity component in item1.AllPropertyObjects)
|
||||
{
|
||||
if (component.SerializableProperties.TryGetValue(attributeName, out SerializableProperty componentProperty))
|
||||
{
|
||||
componentProperty.TrySetValue(component, attribute.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (entity is Item item2)
|
||||
{
|
||||
XElement componentElement = subElement.FirstElement();
|
||||
if (componentElement == null) continue;
|
||||
ItemComponent itemComponent = item2.Components.First(c => c.Name == componentElement.Name.ToString());
|
||||
if (itemComponent == null) continue;
|
||||
foreach (XElement element in componentElement.Elements())
|
||||
{
|
||||
switch (element.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "requireditem":
|
||||
case "requireditems":
|
||||
itemComponent.requiredItems.Clear();
|
||||
itemComponent.DisabledRequiredItems.Clear();
|
||||
|
||||
itemComponent.SetRequiredItems(element);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,666 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
public static class XMLExtensions
|
||||
{
|
||||
public static string ParseContentPathFromUri(this XObject element) => ToolBox.ConvertAbsoluteToRelativePath(element.BaseUri);
|
||||
|
||||
public static XDocument TryLoadXml(string filePath)
|
||||
{
|
||||
XDocument doc;
|
||||
try
|
||||
{
|
||||
ToolBox.IsProperFilenameCase(filePath);
|
||||
doc = XDocument.Load(filePath, LoadOptions.SetBaseUri);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("Couldn't load xml document \"" + filePath + "\"!", e);
|
||||
return null;
|
||||
}
|
||||
if (doc?.Root == null)
|
||||
{
|
||||
DebugConsole.ThrowError("File \"" + filePath + "\" could not be loaded: Document or the root element is invalid!");
|
||||
return null;
|
||||
}
|
||||
return doc;
|
||||
}
|
||||
|
||||
public static XDocument LoadXml(string filePath)
|
||||
{
|
||||
XDocument doc = null;
|
||||
|
||||
ToolBox.IsProperFilenameCase(filePath);
|
||||
|
||||
if (File.Exists(filePath))
|
||||
{
|
||||
try
|
||||
{
|
||||
doc = XDocument.Load(filePath, LoadOptions.SetBaseUri);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (doc.Root == null) return null;
|
||||
}
|
||||
|
||||
return doc;
|
||||
}
|
||||
|
||||
public static object GetAttributeObject(XAttribute attribute)
|
||||
{
|
||||
if (attribute == null) return null;
|
||||
|
||||
return ParseToObject(attribute.Value.ToString());
|
||||
}
|
||||
|
||||
public static object ParseToObject(string value)
|
||||
{
|
||||
float floatVal;
|
||||
int intVal;
|
||||
if (value.Contains(".") && Single.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out floatVal))
|
||||
{
|
||||
return floatVal;
|
||||
}
|
||||
if (Int32.TryParse(value, out intVal))
|
||||
{
|
||||
return intVal;
|
||||
}
|
||||
|
||||
string lowerTrimmedVal = value.ToLowerInvariant().Trim();
|
||||
if (lowerTrimmedVal == "true")
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (lowerTrimmedVal == "false")
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
public static string GetAttributeString(this XElement element, string name, string defaultValue)
|
||||
{
|
||||
if (element?.Attribute(name) == null) return defaultValue;
|
||||
return GetAttributeString(element.Attribute(name), defaultValue);
|
||||
}
|
||||
|
||||
private static string GetAttributeString(XAttribute attribute, string defaultValue)
|
||||
{
|
||||
string value = attribute.Value;
|
||||
return String.IsNullOrEmpty(value) ? defaultValue : value;
|
||||
}
|
||||
|
||||
public static string[] GetAttributeStringArray(this XElement element, string name, string[] defaultValue, bool trim = true, bool convertToLowerInvariant = false)
|
||||
{
|
||||
if (element?.Attribute(name) == null) return defaultValue;
|
||||
|
||||
string stringValue = element.Attribute(name).Value;
|
||||
if (string.IsNullOrEmpty(stringValue)) return defaultValue;
|
||||
|
||||
string[] splitValue = stringValue.Split(',', ',');
|
||||
|
||||
if (convertToLowerInvariant)
|
||||
{
|
||||
for (int i = 0; i < splitValue.Length; i++)
|
||||
{
|
||||
splitValue[i] = splitValue[i].ToLowerInvariant();
|
||||
}
|
||||
}
|
||||
if (trim)
|
||||
{
|
||||
for (int i = 0; i < splitValue.Length; i++)
|
||||
{
|
||||
splitValue[i] = splitValue[i].Trim();
|
||||
}
|
||||
}
|
||||
|
||||
return splitValue;
|
||||
}
|
||||
|
||||
public static float GetAttributeFloat(this XElement element, float defaultValue, params string[] matchingAttributeName)
|
||||
{
|
||||
if (element == null) return defaultValue;
|
||||
|
||||
foreach (string name in matchingAttributeName)
|
||||
{
|
||||
if (element.Attribute(name) == null) continue;
|
||||
|
||||
float val;
|
||||
|
||||
try
|
||||
{
|
||||
if (!Single.TryParse(element.Attribute(name).Value, NumberStyles.Float, CultureInfo.InvariantCulture, out val))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in " + element + "!", e);
|
||||
continue;
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public static float GetAttributeFloat(this XElement element, string name, float defaultValue)
|
||||
{
|
||||
if (element?.Attribute(name) == null) return defaultValue;
|
||||
|
||||
float val = defaultValue;
|
||||
|
||||
try
|
||||
{
|
||||
if (!Single.TryParse(element.Attribute(name).Value, NumberStyles.Float, CultureInfo.InvariantCulture, out val))
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in " + element + "!", e);
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
public static float GetAttributeFloat(this XAttribute attribute, float defaultValue)
|
||||
{
|
||||
if (attribute == null) return defaultValue;
|
||||
|
||||
float val = defaultValue;
|
||||
|
||||
try
|
||||
{
|
||||
val = Single.Parse(attribute.Value, CultureInfo.InvariantCulture);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in " + attribute + "! ", e);
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
public static float[] GetAttributeFloatArray(this XElement element, string name, float[] defaultValue)
|
||||
{
|
||||
if (element?.Attribute(name) == null) return defaultValue;
|
||||
|
||||
string stringValue = element.Attribute(name).Value;
|
||||
if (string.IsNullOrEmpty(stringValue)) return defaultValue;
|
||||
|
||||
string[] splitValue = stringValue.Split(',');
|
||||
float[] floatValue = new float[splitValue.Length];
|
||||
for (int i = 0; i < splitValue.Length; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
float val = Single.Parse(splitValue[i], CultureInfo.InvariantCulture);
|
||||
floatValue[i] = val;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in " + element + "! ", e);
|
||||
}
|
||||
}
|
||||
|
||||
return floatValue;
|
||||
}
|
||||
|
||||
public static int GetAttributeInt(this XElement element, string name, int defaultValue)
|
||||
{
|
||||
if (element?.Attribute(name) == null) return defaultValue;
|
||||
|
||||
int val = defaultValue;
|
||||
|
||||
try
|
||||
{
|
||||
val = Int32.Parse(element.Attribute(name).Value);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in " + element + "! ", e);
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
public static uint GetAttributeUInt(this XElement element, string name, uint defaultValue)
|
||||
{
|
||||
if (element?.Attribute(name) == null) return defaultValue;
|
||||
|
||||
uint val = defaultValue;
|
||||
|
||||
try
|
||||
{
|
||||
val = UInt32.Parse(element.Attribute(name).Value);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in " + element + "! ", e);
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
public static UInt64 GetAttributeUInt64(this XElement element, string name, UInt64 defaultValue)
|
||||
{
|
||||
if (element?.Attribute(name) == null) return defaultValue;
|
||||
|
||||
UInt64 val = defaultValue;
|
||||
|
||||
try
|
||||
{
|
||||
val = UInt64.Parse(element.Attribute(name).Value);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in " + element + "! ", e);
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
public static UInt64 GetAttributeSteamID(this XElement element, string name, UInt64 defaultValue)
|
||||
{
|
||||
if (element?.Attribute(name) == null) return defaultValue;
|
||||
|
||||
UInt64 val = defaultValue;
|
||||
|
||||
try
|
||||
{
|
||||
val = Steam.SteamManager.SteamIDStringToUInt64(element.Attribute(name).Value);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in " + element + "! ", e);
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
public static int[] GetAttributeIntArray(this XElement element, string name, int[] defaultValue)
|
||||
{
|
||||
if (element?.Attribute(name) == null) return defaultValue;
|
||||
|
||||
string stringValue = element.Attribute(name).Value;
|
||||
if (string.IsNullOrEmpty(stringValue)) return defaultValue;
|
||||
|
||||
string[] splitValue = stringValue.Split(',');
|
||||
int[] intValue = new int[splitValue.Length];
|
||||
for (int i = 0; i < splitValue.Length; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
int val = Int32.Parse(splitValue[i]);
|
||||
intValue[i] = val;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in " + element + "! ", e);
|
||||
}
|
||||
}
|
||||
|
||||
return intValue;
|
||||
}
|
||||
public static ushort[] GetAttributeUshortArray(this XElement element, string name, ushort[] defaultValue)
|
||||
{
|
||||
if (element?.Attribute(name) == null) return defaultValue;
|
||||
|
||||
string stringValue = element.Attribute(name).Value;
|
||||
if (string.IsNullOrEmpty(stringValue)) return defaultValue;
|
||||
|
||||
string[] splitValue = stringValue.Split(',');
|
||||
ushort[] ushortValue = new ushort[splitValue.Length];
|
||||
for (int i = 0; i < splitValue.Length; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
ushort val = ushort.Parse(splitValue[i]);
|
||||
ushortValue[i] = val;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in " + element + "! ", e);
|
||||
}
|
||||
}
|
||||
|
||||
return ushortValue;
|
||||
}
|
||||
|
||||
public static bool GetAttributeBool(this XElement element, string name, bool defaultValue)
|
||||
{
|
||||
if (element?.Attribute(name) == null) return defaultValue;
|
||||
return element.Attribute(name).GetAttributeBool(defaultValue);
|
||||
}
|
||||
|
||||
public static bool GetAttributeBool(this XAttribute attribute, bool defaultValue)
|
||||
{
|
||||
if (attribute == null) return defaultValue;
|
||||
|
||||
string val = attribute.Value.ToLowerInvariant().Trim();
|
||||
if (val == "true")
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (val == "false")
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
DebugConsole.ThrowError("Error in " + attribute.Value.ToString() + "! \"" + val + "\" is not a valid boolean value");
|
||||
return false;
|
||||
}
|
||||
|
||||
public static Point GetAttributePoint(this XElement element, string name, Point defaultValue)
|
||||
{
|
||||
if (element?.Attribute(name) == null) return defaultValue;
|
||||
return ParsePoint(element.Attribute(name).Value);
|
||||
}
|
||||
|
||||
public static Vector2 GetAttributeVector2(this XElement element, string name, Vector2 defaultValue)
|
||||
{
|
||||
if (element?.Attribute(name) == null) return defaultValue;
|
||||
return ParseVector2(element.Attribute(name).Value);
|
||||
}
|
||||
|
||||
public static Vector3 GetAttributeVector3(this XElement element, string name, Vector3 defaultValue)
|
||||
{
|
||||
if (element == null || element.Attribute(name) == null) return defaultValue;
|
||||
return ParseVector3(element.Attribute(name).Value);
|
||||
}
|
||||
|
||||
public static Vector4 GetAttributeVector4(this XElement element, string name, Vector4 defaultValue)
|
||||
{
|
||||
if (element == null || element.Attribute(name) == null) return defaultValue;
|
||||
return ParseVector4(element.Attribute(name).Value);
|
||||
}
|
||||
|
||||
public static Color GetAttributeColor(this XElement element, string name, Color defaultValue)
|
||||
{
|
||||
if (element == null || element.Attribute(name) == null) return defaultValue;
|
||||
return ParseColor(element.Attribute(name).Value);
|
||||
}
|
||||
|
||||
public static Color? GetAttributeColor(this XElement element, string name)
|
||||
{
|
||||
if (element == null || element.Attribute(name) == null) { return null; }
|
||||
return ParseColor(element.Attribute(name).Value);
|
||||
}
|
||||
|
||||
public static Color[] GetAttributeColorArray(this XElement element, string name, Color[] defaultValue)
|
||||
{
|
||||
if (element?.Attribute(name) == null) return defaultValue;
|
||||
|
||||
string stringValue = element.Attribute(name).Value;
|
||||
if (string.IsNullOrEmpty(stringValue)) return defaultValue;
|
||||
|
||||
string[] splitValue = stringValue.Split(';');
|
||||
Color[] colorValue = new Color[splitValue.Length];
|
||||
for (int i = 0; i < splitValue.Length; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
Color val = ParseColor(splitValue[i], true);
|
||||
colorValue[i] = val;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in " + element + "! ", e);
|
||||
}
|
||||
}
|
||||
|
||||
return colorValue;
|
||||
}
|
||||
|
||||
public static Rectangle GetAttributeRect(this XElement element, string name, Rectangle defaultValue)
|
||||
{
|
||||
if (element == null || element.Attribute(name) == null) return defaultValue;
|
||||
return ParseRect(element.Attribute(name).Value, false);
|
||||
}
|
||||
|
||||
public static string ElementInnerText(this XElement el)
|
||||
{
|
||||
StringBuilder str = new StringBuilder();
|
||||
foreach (XNode element in el.DescendantNodes().Where(x => x.NodeType == XmlNodeType.Text))
|
||||
{
|
||||
str.Append(element.ToString());
|
||||
}
|
||||
return str.ToString();
|
||||
}
|
||||
|
||||
public static string PointToString(Point point)
|
||||
{
|
||||
return point.X.ToString() + "," + point.Y.ToString();
|
||||
}
|
||||
|
||||
public static string Vector2ToString(Vector2 vector)
|
||||
{
|
||||
return vector.X.ToString("G", CultureInfo.InvariantCulture) + "," + vector.Y.ToString("G", CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
public static string Vector3ToString(Vector3 vector, string format = "G")
|
||||
{
|
||||
return vector.X.ToString(format, CultureInfo.InvariantCulture) + "," +
|
||||
vector.Y.ToString(format, CultureInfo.InvariantCulture) + "," +
|
||||
vector.Z.ToString(format, CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
public static string Vector4ToString(Vector4 vector, string format = "G")
|
||||
{
|
||||
return vector.X.ToString(format, CultureInfo.InvariantCulture) + "," +
|
||||
vector.Y.ToString(format, CultureInfo.InvariantCulture) + "," +
|
||||
vector.Z.ToString(format, CultureInfo.InvariantCulture) + "," +
|
||||
vector.W.ToString(format, CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
public static string ColorToString(Color color)
|
||||
{
|
||||
return color.R + "," + color.G + "," + color.B + "," + color.A;
|
||||
}
|
||||
|
||||
public static string RectToString(Rectangle rect)
|
||||
{
|
||||
return rect.X + "," + rect.Y + "," + rect.Width + "," + rect.Height;
|
||||
}
|
||||
|
||||
public static Point ParsePoint(string stringPoint, bool errorMessages = true)
|
||||
{
|
||||
string[] components = stringPoint.Split(',');
|
||||
Point point = Point.Zero;
|
||||
|
||||
if (components.Length != 2)
|
||||
{
|
||||
if (!errorMessages) return point;
|
||||
DebugConsole.ThrowError("Failed to parse the string \"" + stringPoint + "\" to Vector2");
|
||||
return point;
|
||||
}
|
||||
|
||||
int.TryParse(components[0], NumberStyles.Any, CultureInfo.InvariantCulture, out point.X);
|
||||
int.TryParse(components[1], NumberStyles.Any, CultureInfo.InvariantCulture, out point.Y);
|
||||
return point;
|
||||
}
|
||||
|
||||
public static Vector2 ParseVector2(string stringVector2, bool errorMessages = true)
|
||||
{
|
||||
string[] components = stringVector2.Split(',');
|
||||
|
||||
Vector2 vector = Vector2.Zero;
|
||||
|
||||
if (components.Length != 2)
|
||||
{
|
||||
if (!errorMessages) return vector;
|
||||
DebugConsole.ThrowError("Failed to parse the string \"" + stringVector2 + "\" to Vector2");
|
||||
return vector;
|
||||
}
|
||||
|
||||
float.TryParse(components[0], NumberStyles.Any, CultureInfo.InvariantCulture, out vector.X);
|
||||
float.TryParse(components[1], NumberStyles.Any, CultureInfo.InvariantCulture, out vector.Y);
|
||||
|
||||
return vector;
|
||||
}
|
||||
|
||||
public static Vector3 ParseVector3(string stringVector3, bool errorMessages = true)
|
||||
{
|
||||
string[] components = stringVector3.Split(',');
|
||||
|
||||
Vector3 vector = Vector3.Zero;
|
||||
|
||||
if (components.Length != 3)
|
||||
{
|
||||
if (!errorMessages) return vector;
|
||||
DebugConsole.ThrowError("Failed to parse the string \"" + stringVector3 + "\" to Vector3");
|
||||
return vector;
|
||||
}
|
||||
|
||||
Single.TryParse(components[0], NumberStyles.Any, CultureInfo.InvariantCulture, out vector.X);
|
||||
Single.TryParse(components[1], NumberStyles.Any, CultureInfo.InvariantCulture, out vector.Y);
|
||||
Single.TryParse(components[2], NumberStyles.Any, CultureInfo.InvariantCulture, out vector.Z);
|
||||
|
||||
return vector;
|
||||
}
|
||||
|
||||
public static Vector4 ParseVector4(string stringVector4, bool errorMessages = true)
|
||||
{
|
||||
string[] components = stringVector4.Split(',');
|
||||
|
||||
Vector4 vector = Vector4.Zero;
|
||||
|
||||
if (components.Length < 3)
|
||||
{
|
||||
if (errorMessages) DebugConsole.ThrowError("Failed to parse the string \"" + stringVector4 + "\" to Vector4");
|
||||
return vector;
|
||||
}
|
||||
|
||||
Single.TryParse(components[0], NumberStyles.Float, CultureInfo.InvariantCulture, out vector.X);
|
||||
Single.TryParse(components[1], NumberStyles.Float, CultureInfo.InvariantCulture, out vector.Y);
|
||||
Single.TryParse(components[2], NumberStyles.Float, CultureInfo.InvariantCulture, out vector.Z);
|
||||
if (components.Length > 3)
|
||||
Single.TryParse(components[3], NumberStyles.Float, CultureInfo.InvariantCulture, out vector.W);
|
||||
|
||||
return vector;
|
||||
}
|
||||
|
||||
public static Color ParseColor(string stringColor, bool errorMessages = true)
|
||||
{
|
||||
string[] strComponents = stringColor.Split(',');
|
||||
|
||||
Color color = Color.White;
|
||||
|
||||
float[] components = new float[4] { 1.0f, 1.0f, 1.0f, 1.0f };
|
||||
|
||||
if (strComponents.Length == 1)
|
||||
{
|
||||
bool hexFailed = true;
|
||||
stringColor = stringColor.Trim();
|
||||
if (stringColor.Length > 0 && stringColor[0] == '#')
|
||||
{
|
||||
stringColor = stringColor.Substring(1);
|
||||
|
||||
int colorInt = 0;
|
||||
if (int.TryParse(stringColor, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out colorInt))
|
||||
{
|
||||
if (stringColor.Length == 6)
|
||||
{
|
||||
colorInt = (colorInt << 8) | 0xff;
|
||||
}
|
||||
components[0] = ((float)((colorInt & 0xff000000) >> 24)) / 255.0f;
|
||||
components[1] = ((float)((colorInt & 0x00ff0000) >> 16)) / 255.0f;
|
||||
components[2] = ((float)((colorInt & 0x0000ff00) >> 8)) / 255.0f;
|
||||
components[3] = ((float)(colorInt & 0x000000ff)) / 255.0f;
|
||||
|
||||
hexFailed = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (hexFailed)
|
||||
{
|
||||
if (errorMessages) DebugConsole.ThrowError("Failed to parse the string \"" + stringColor + "\" to Color");
|
||||
return Color.White;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < 4 && i < strComponents.Length; i++)
|
||||
{
|
||||
float.TryParse(strComponents[i], NumberStyles.Float, CultureInfo.InvariantCulture, out components[i]);
|
||||
}
|
||||
|
||||
if (components.Any(c => c > 1.0f))
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
components[i] = components[i] / 255.0f;
|
||||
}
|
||||
//alpha defaults to 1.0 if not given
|
||||
if (strComponents.Length < 4) components[3] = 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
return new Color(components[0], components[1], components[2], components[3]);
|
||||
}
|
||||
|
||||
public static Rectangle ParseRect(string stringRect, bool requireSize, bool errorMessages = true)
|
||||
{
|
||||
string[] strComponents = stringRect.Split(',');
|
||||
if ((strComponents.Length < 3 && requireSize) || strComponents.Length < 2)
|
||||
{
|
||||
if (errorMessages) DebugConsole.ThrowError("Failed to parse the string \"" + stringRect + "\" to Rectangle");
|
||||
return new Rectangle(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
int[] components = new int[4] { 0, 0, 0, 0 };
|
||||
for (int i = 0; i < 4 && i < strComponents.Length; i++)
|
||||
{
|
||||
int.TryParse(strComponents[i], out components[i]);
|
||||
}
|
||||
|
||||
return new Rectangle(components[0], components[1], components[2], components[3]);
|
||||
}
|
||||
|
||||
public static float[] ParseFloatArray(string[] stringArray)
|
||||
{
|
||||
if (stringArray == null || stringArray.Length == 0) return null;
|
||||
|
||||
float[] floatArray = new float[stringArray.Length];
|
||||
for (int i = 0; i < floatArray.Length; i++)
|
||||
{
|
||||
floatArray[i] = 0.0f;
|
||||
Single.TryParse(stringArray[i], NumberStyles.Float, CultureInfo.InvariantCulture, out floatArray[i]);
|
||||
}
|
||||
|
||||
return floatArray;
|
||||
}
|
||||
|
||||
public static bool IsOverride(this XElement element) => element.Name.ToString().Equals("override", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
public static XElement FirstElement(this XElement element) => element.Elements().FirstOrDefault();
|
||||
|
||||
/// <summary>
|
||||
/// Returns the first child element that matches the name using the provided comparison method.
|
||||
/// </summary>
|
||||
public static XElement GetChildElement(this XContainer container, string name, StringComparison comparisonMethod = StringComparison.OrdinalIgnoreCase) => container.Elements().FirstOrDefault(e => e.Name.ToString().Equals(name, comparisonMethod));
|
||||
|
||||
/// <summary>
|
||||
/// Returns all child elements that match the name using the provided comparison method.
|
||||
/// </summary>
|
||||
public static IEnumerable<XElement> GetChildElements(this XContainer container, string name, StringComparison comparisonMethod = StringComparison.OrdinalIgnoreCase) => container.Elements().Where(e => e.Name.ToString().Equals(name, comparisonMethod));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user