Add support for Attributes!
This commit is contained in:
34
Tokens/Axes.cs
Normal file
34
Tokens/Axes.cs
Normal file
@ -0,0 +1,34 @@
|
||||
using System.Xml;
|
||||
|
||||
using RobloxFiles.DataTypes;
|
||||
using RobloxFiles.XmlFormat;
|
||||
|
||||
namespace RobloxFiles.Tokens
|
||||
{
|
||||
public class AxesToken : IXmlPropertyToken
|
||||
{
|
||||
public string XmlPropertyToken => "Axes";
|
||||
|
||||
public bool ReadProperty(Property prop, XmlNode token)
|
||||
{
|
||||
if (XmlPropertyTokens.ReadPropertyGeneric(token, out uint value))
|
||||
{
|
||||
Axes axes = (Axes)value;
|
||||
prop.Value = axes;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
|
||||
{
|
||||
XmlElement axes = doc.CreateElement("axes");
|
||||
node.AppendChild(axes);
|
||||
|
||||
int value = prop.CastValue<int>();
|
||||
axes.InnerText = value.ToInvariantString();
|
||||
}
|
||||
}
|
||||
}
|
46
Tokens/BinaryString.cs
Normal file
46
Tokens/BinaryString.cs
Normal file
@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using System.Xml;
|
||||
|
||||
namespace RobloxFiles.Tokens
|
||||
{
|
||||
public class BinaryStringToken : IXmlPropertyToken
|
||||
{
|
||||
public string XmlPropertyToken => "BinaryString";
|
||||
|
||||
public bool ReadProperty(Property prop, XmlNode token)
|
||||
{
|
||||
// BinaryStrings are encoded in base64
|
||||
string base64 = token.InnerText.Replace("\n", "");
|
||||
byte[] buffer = Convert.FromBase64String(base64);
|
||||
|
||||
prop.Value = buffer;
|
||||
prop.RawBuffer = buffer;
|
||||
prop.Type = PropertyType.String;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
|
||||
{
|
||||
byte[] data = prop.RawBuffer;
|
||||
string value = Convert.ToBase64String(data);
|
||||
|
||||
if (value.Length > 72)
|
||||
{
|
||||
string buffer = "";
|
||||
|
||||
while (value.Length > 72)
|
||||
{
|
||||
string chunk = value.Substring(0, 72);
|
||||
value = value.Substring(72);
|
||||
buffer += chunk + '\n';
|
||||
}
|
||||
|
||||
value = buffer + value;
|
||||
}
|
||||
|
||||
XmlCDataSection cdata = doc.CreateCDataSection(value);
|
||||
node.AppendChild(cdata);
|
||||
}
|
||||
}
|
||||
}
|
28
Tokens/Boolean.cs
Normal file
28
Tokens/Boolean.cs
Normal file
@ -0,0 +1,28 @@
|
||||
using System.Xml;
|
||||
using RobloxFiles.XmlFormat;
|
||||
|
||||
namespace RobloxFiles.Tokens
|
||||
{
|
||||
public class BoolToken : IXmlPropertyToken, IAttributeToken<bool>
|
||||
{
|
||||
public string XmlPropertyToken => "bool";
|
||||
public AttributeType AttributeType => AttributeType.Bool;
|
||||
|
||||
public bool ReadAttribute(Attribute attr) => attr.ReadBool();
|
||||
public void WriteAttribute(Attribute attr, bool value) => attr.WriteBool(value);
|
||||
|
||||
public bool ReadProperty(Property prop, XmlNode token)
|
||||
{
|
||||
return XmlPropertyTokens.ReadPropertyGeneric<bool>(prop, PropertyType.Bool, token);
|
||||
}
|
||||
|
||||
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
|
||||
{
|
||||
string boolString = prop.Value
|
||||
.ToString()
|
||||
.ToLower();
|
||||
|
||||
node.InnerText = boolString;
|
||||
}
|
||||
}
|
||||
}
|
43
Tokens/BrickColor.cs
Normal file
43
Tokens/BrickColor.cs
Normal file
@ -0,0 +1,43 @@
|
||||
using System.Xml;
|
||||
using RobloxFiles.DataTypes;
|
||||
using RobloxFiles.XmlFormat;
|
||||
|
||||
namespace RobloxFiles.Tokens
|
||||
{
|
||||
public class BrickColorToken : IXmlPropertyToken, IAttributeToken<BrickColor>
|
||||
{
|
||||
// This is a lie: The token is actually int, but that would cause a name collision.
|
||||
// Since BrickColors are written as ints, the IntToken class will try to redirect
|
||||
// to this handler if it believes that its representing a BrickColor.
|
||||
public string XmlPropertyToken => "BrickColor";
|
||||
public AttributeType AttributeType => AttributeType.BrickColor;
|
||||
|
||||
public BrickColor ReadAttribute(Attribute attr) => attr.ReadInt();
|
||||
public void WriteAttribute(Attribute attr, BrickColor value) => attr.WriteInt(value.Number);
|
||||
|
||||
public bool ReadProperty(Property prop, XmlNode token)
|
||||
{
|
||||
if (XmlPropertyTokens.ReadPropertyGeneric(token, out int value))
|
||||
{
|
||||
BrickColor brickColor = BrickColor.FromNumber(value);
|
||||
prop.XmlToken = "BrickColor";
|
||||
prop.Value = brickColor;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
|
||||
{
|
||||
BrickColor value = prop.CastValue<BrickColor>();
|
||||
|
||||
XmlElement brickColor = doc.CreateElement("int");
|
||||
brickColor.InnerText = value.Number.ToInvariantString();
|
||||
|
||||
brickColor.SetAttribute("name", prop.Name);
|
||||
brickColor.AppendChild(node);
|
||||
}
|
||||
}
|
||||
}
|
64
Tokens/CFrame.cs
Normal file
64
Tokens/CFrame.cs
Normal file
@ -0,0 +1,64 @@
|
||||
using System.Xml;
|
||||
using RobloxFiles.DataTypes;
|
||||
|
||||
namespace RobloxFiles.Tokens
|
||||
{
|
||||
public class CFrameToken : IXmlPropertyToken
|
||||
{
|
||||
public string XmlPropertyToken => "CoordinateFrame; CFrame";
|
||||
private static readonly string[] Coords = new string[12] { "X", "Y", "Z", "R00", "R01", "R02", "R10", "R11", "R12", "R20", "R21", "R22"};
|
||||
|
||||
public static CFrame ReadCFrame(XmlNode token)
|
||||
{
|
||||
float[] components = new float[12];
|
||||
|
||||
for (int i = 0; i < 12; i++)
|
||||
{
|
||||
string key = Coords[i];
|
||||
|
||||
try
|
||||
{
|
||||
var coord = token[key];
|
||||
components[i] = Formatting.ParseFloat(coord.InnerText);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return new CFrame(components);
|
||||
}
|
||||
|
||||
public bool ReadProperty(Property prop, XmlNode token)
|
||||
{
|
||||
CFrame result = ReadCFrame(token);
|
||||
bool success = (result != null);
|
||||
|
||||
if (success)
|
||||
{
|
||||
prop.Type = PropertyType.CFrame;
|
||||
prop.Value = result;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
|
||||
{
|
||||
CFrame cf = prop.CastValue<CFrame>();
|
||||
float[] components = cf.GetComponents();
|
||||
|
||||
for (int i = 0; i < 12; i++)
|
||||
{
|
||||
string coordName = Coords[i];
|
||||
float coordValue = components[i];
|
||||
|
||||
XmlElement coord = doc.CreateElement(coordName);
|
||||
coord.InnerText = coordValue.ToInvariantString();
|
||||
|
||||
node.AppendChild(coord);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
97
Tokens/Color3.cs
Normal file
97
Tokens/Color3.cs
Normal file
@ -0,0 +1,97 @@
|
||||
using System.Xml;
|
||||
using RobloxFiles.DataTypes;
|
||||
using RobloxFiles.XmlFormat;
|
||||
|
||||
namespace RobloxFiles.Tokens
|
||||
{
|
||||
public class Color3Token : IXmlPropertyToken, IAttributeToken<Color3>
|
||||
{
|
||||
public string XmlPropertyToken => "Color3";
|
||||
private readonly string[] XmlFields = new string[3] { "R", "G", "B" };
|
||||
|
||||
public AttributeType AttributeType => AttributeType.Color3;
|
||||
public Color3 ReadAttribute(Attribute attr) => ReadColor3(attr);
|
||||
public void WriteAttribute(Attribute attr, Color3 value) => WriteColor3(attr, value);
|
||||
|
||||
public static Color3 ReadColor3(Attribute attr)
|
||||
{
|
||||
float r = attr.ReadFloat(),
|
||||
g = attr.ReadFloat(),
|
||||
b = attr.ReadFloat();
|
||||
|
||||
return new Color3(r, g, b);
|
||||
}
|
||||
|
||||
public static void WriteColor3(Attribute attr, Color3 value)
|
||||
{
|
||||
attr.WriteFloat(value.R);
|
||||
attr.WriteFloat(value.G);
|
||||
attr.WriteFloat(value.B);
|
||||
}
|
||||
|
||||
public bool ReadProperty(Property prop, XmlNode token)
|
||||
{
|
||||
bool success = true;
|
||||
float[] fields = new float[XmlFields.Length];
|
||||
|
||||
for (int i = 0; i < fields.Length; i++)
|
||||
{
|
||||
string key = XmlFields[i];
|
||||
|
||||
try
|
||||
{
|
||||
var coord = token[key];
|
||||
string text = coord?.InnerText;
|
||||
|
||||
if (text == null)
|
||||
{
|
||||
text = "0";
|
||||
success = false;
|
||||
}
|
||||
|
||||
fields[i] = Formatting.ParseFloat(text);
|
||||
}
|
||||
catch
|
||||
{
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (success)
|
||||
{
|
||||
float r = fields[0],
|
||||
g = fields[1],
|
||||
b = fields[2];
|
||||
|
||||
prop.Type = PropertyType.Color3;
|
||||
prop.Value = new Color3(r, g, b);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Try falling back to the Color3uint8 technique...
|
||||
var color3uint8 = XmlPropertyTokens.GetHandler<Color3uint8Token>();
|
||||
success = color3uint8.ReadProperty(prop, token);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
|
||||
{
|
||||
Color3 color = prop.CastValue<Color3>();
|
||||
float[] rgb = new float[3] { color.R, color.G, color.B };
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
string field = XmlFields[i];
|
||||
float value = rgb[i];
|
||||
|
||||
XmlElement channel = doc.CreateElement(field);
|
||||
channel.InnerText = value.ToInvariantString();
|
||||
|
||||
node.AppendChild(channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
43
Tokens/Color3uint8.cs
Normal file
43
Tokens/Color3uint8.cs
Normal file
@ -0,0 +1,43 @@
|
||||
using System.Xml;
|
||||
using RobloxFiles.DataTypes;
|
||||
using RobloxFiles.XmlFormat;
|
||||
using System.Diagnostics.Contracts;
|
||||
|
||||
namespace RobloxFiles.Tokens
|
||||
{
|
||||
public class Color3uint8Token : IXmlPropertyToken
|
||||
{
|
||||
public string XmlPropertyToken => "Color3uint8";
|
||||
|
||||
public bool ReadProperty(Property prop, XmlNode token)
|
||||
{
|
||||
if (XmlPropertyTokens.ReadPropertyGeneric(token, out uint value))
|
||||
{
|
||||
uint r = (value >> 16) & 0xFF;
|
||||
uint g = (value >> 8) & 0xFF;
|
||||
uint b = value & 0xFF;
|
||||
|
||||
Color3uint8 result = Color3.FromRGB(r, g, b);
|
||||
prop.Value = result;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
|
||||
{
|
||||
Color3uint8 color = prop?.CastValue<Color3uint8>();
|
||||
Contract.Requires(node != null);
|
||||
|
||||
|
||||
uint r = color.R,
|
||||
g = color.G,
|
||||
b = color.B;
|
||||
|
||||
uint rgb = (255u << 24) | (r << 16) | (g << 8) | b;
|
||||
node.InnerText = rgb.ToInvariantString();
|
||||
}
|
||||
}
|
||||
}
|
85
Tokens/ColorSequence.cs
Normal file
85
Tokens/ColorSequence.cs
Normal file
@ -0,0 +1,85 @@
|
||||
using System.Xml;
|
||||
using RobloxFiles.DataTypes;
|
||||
|
||||
namespace RobloxFiles.Tokens
|
||||
{
|
||||
public class ColorSequenceToken : IXmlPropertyToken, IAttributeToken<ColorSequence>
|
||||
{
|
||||
public string XmlPropertyToken => "ColorSequence";
|
||||
public AttributeType AttributeType => AttributeType.ColorSequence;
|
||||
|
||||
public bool ReadProperty(Property prop, XmlNode token)
|
||||
{
|
||||
string contents = token.InnerText.Trim();
|
||||
string[] buffer = contents.Split(' ');
|
||||
|
||||
int length = buffer.Length;
|
||||
bool valid = (length % 5 == 0);
|
||||
|
||||
if (valid)
|
||||
{
|
||||
try
|
||||
{
|
||||
var keypoints = new ColorSequenceKeypoint[length / 5];
|
||||
|
||||
for (int i = 0; i < length; i += 5)
|
||||
{
|
||||
float Time = Formatting.ParseFloat(buffer[i]);
|
||||
|
||||
float R = Formatting.ParseFloat(buffer[i + 1]);
|
||||
float G = Formatting.ParseFloat(buffer[i + 2]);
|
||||
float B = Formatting.ParseFloat(buffer[i + 3]);
|
||||
|
||||
Color3 Value = new Color3(R, G, B);
|
||||
keypoints[i / 5] = new ColorSequenceKeypoint(Time, Value);
|
||||
}
|
||||
|
||||
prop.Type = PropertyType.ColorSequence;
|
||||
prop.Value = new ColorSequence(keypoints);
|
||||
}
|
||||
catch
|
||||
{
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
|
||||
{
|
||||
ColorSequence value = prop.CastValue<ColorSequence>();
|
||||
node.InnerText = value.ToString() + ' ';
|
||||
}
|
||||
|
||||
public ColorSequence ReadAttribute(Attribute attr)
|
||||
{
|
||||
int numKeys = attr.ReadInt();
|
||||
var keypoints = new ColorSequenceKeypoint[numKeys];
|
||||
|
||||
for (int i = 0; i < numKeys; i++)
|
||||
{
|
||||
int envelope = attr.ReadInt();
|
||||
float time = attr.ReadFloat();
|
||||
|
||||
Color3 value = Color3Token.ReadColor3(attr);
|
||||
keypoints[i] = new ColorSequenceKeypoint(time, value, envelope);
|
||||
}
|
||||
|
||||
return new ColorSequence(keypoints);
|
||||
}
|
||||
|
||||
public void WriteAttribute(Attribute attr, ColorSequence value)
|
||||
{
|
||||
attr.WriteInt(value.Keypoints.Length);
|
||||
|
||||
foreach (var keypoint in value.Keypoints)
|
||||
{
|
||||
attr.WriteInt(keypoint.Envelope);
|
||||
attr.WriteFloat(keypoint.Time);
|
||||
|
||||
Color3Token.WriteColor3(attr, keypoint.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
66
Tokens/Content.cs
Normal file
66
Tokens/Content.cs
Normal file
@ -0,0 +1,66 @@
|
||||
using System;
|
||||
using System.Xml;
|
||||
|
||||
using RobloxFiles.DataTypes;
|
||||
|
||||
namespace RobloxFiles.Tokens
|
||||
{
|
||||
public class ContentToken : IXmlPropertyToken
|
||||
{
|
||||
public string XmlPropertyToken => "Content";
|
||||
|
||||
public bool ReadProperty(Property prop, XmlNode token)
|
||||
{
|
||||
string data = token.InnerText;
|
||||
prop.Value = new Content(data);
|
||||
prop.Type = PropertyType.String;
|
||||
|
||||
if (token.HasChildNodes)
|
||||
{
|
||||
XmlNode childNode = token.FirstChild;
|
||||
string contentType = childNode.Name;
|
||||
|
||||
if (contentType.StartsWith("binary") || contentType == "hash")
|
||||
{
|
||||
try
|
||||
{
|
||||
// Roblox technically doesn't support this anymore, but load it anyway :P
|
||||
byte[] buffer = Convert.FromBase64String(data);
|
||||
prop.RawBuffer = buffer;
|
||||
}
|
||||
catch
|
||||
{
|
||||
RobloxFile.LogError($"ContentToken: Got illegal base64 string: {data}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
|
||||
{
|
||||
string content = prop.CastValue<Content>();
|
||||
string type = "null";
|
||||
|
||||
if (prop.HasRawBuffer)
|
||||
type = "binary";
|
||||
else if (content.Length > 0)
|
||||
type = "url";
|
||||
|
||||
XmlElement contentType = doc.CreateElement(type);
|
||||
|
||||
if (type == "binary")
|
||||
{
|
||||
XmlCDataSection cdata = doc.CreateCDataSection(content);
|
||||
contentType.AppendChild(cdata);
|
||||
}
|
||||
else
|
||||
{
|
||||
contentType.InnerText = content;
|
||||
}
|
||||
|
||||
node.AppendChild(contentType);
|
||||
}
|
||||
}
|
||||
}
|
25
Tokens/Double.cs
Normal file
25
Tokens/Double.cs
Normal file
@ -0,0 +1,25 @@
|
||||
using System.Xml;
|
||||
using RobloxFiles.XmlFormat;
|
||||
|
||||
namespace RobloxFiles.Tokens
|
||||
{
|
||||
public class DoubleToken : IXmlPropertyToken, IAttributeToken<double>
|
||||
{
|
||||
public string XmlPropertyToken => "double";
|
||||
public AttributeType AttributeType => AttributeType.Double;
|
||||
|
||||
public double ReadAttribute(Attribute attr) => attr.ReadDouble();
|
||||
public void WriteAttribute(Attribute attr, double value) => attr.WriteDouble(value);
|
||||
|
||||
public bool ReadProperty(Property prop, XmlNode token)
|
||||
{
|
||||
return XmlPropertyTokens.ReadPropertyGeneric<double>(prop, PropertyType.Double, token);
|
||||
}
|
||||
|
||||
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
|
||||
{
|
||||
double value = prop.CastValue<double>();
|
||||
node.InnerText = value.ToInvariantString();
|
||||
}
|
||||
}
|
||||
}
|
51
Tokens/Enum.cs
Normal file
51
Tokens/Enum.cs
Normal file
@ -0,0 +1,51 @@
|
||||
using System;
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.Reflection;
|
||||
using System.Xml;
|
||||
|
||||
using RobloxFiles.XmlFormat;
|
||||
|
||||
namespace RobloxFiles.Tokens
|
||||
{
|
||||
public class EnumToken : IXmlPropertyToken
|
||||
{
|
||||
public string XmlPropertyToken => "token";
|
||||
|
||||
public bool ReadProperty(Property prop, XmlNode token)
|
||||
{
|
||||
Contract.Requires(prop != null);
|
||||
|
||||
if (XmlPropertyTokens.ReadPropertyGeneric(token, out uint value))
|
||||
{
|
||||
Instance inst = prop.Instance;
|
||||
Type instType = inst?.GetType();
|
||||
|
||||
FieldInfo info = instType.GetField(prop.Name, Property.BindingFlags);
|
||||
|
||||
if (info != null)
|
||||
{
|
||||
Type enumType = info.FieldType;
|
||||
string item = value.ToInvariantString();
|
||||
|
||||
prop.Type = PropertyType.Enum;
|
||||
prop.Value = Enum.Parse(enumType, item);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
|
||||
{
|
||||
Contract.Requires(prop != null && node != null);
|
||||
object rawValue = prop.Value;
|
||||
|
||||
int signed = (int)rawValue;
|
||||
uint value = (uint)signed;
|
||||
|
||||
node.InnerText = value.ToInvariantString();
|
||||
}
|
||||
}
|
||||
}
|
39
Tokens/Faces.cs
Normal file
39
Tokens/Faces.cs
Normal file
@ -0,0 +1,39 @@
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.Xml;
|
||||
|
||||
using RobloxFiles.DataTypes;
|
||||
using RobloxFiles.XmlFormat;
|
||||
|
||||
namespace RobloxFiles.Tokens
|
||||
{
|
||||
public class FacesToken : IXmlPropertyToken
|
||||
{
|
||||
public string XmlPropertyToken => "Faces";
|
||||
|
||||
public bool ReadProperty(Property prop, XmlNode token)
|
||||
{
|
||||
Contract.Requires(prop != null);
|
||||
|
||||
if (XmlPropertyTokens.ReadPropertyGeneric(token, out uint value))
|
||||
{
|
||||
Faces faces = (Faces)value;
|
||||
prop.Value = faces;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
|
||||
{
|
||||
Contract.Requires(prop != null && doc != null && node != null);
|
||||
|
||||
XmlElement faces = doc.CreateElement("faces");
|
||||
node.AppendChild(faces);
|
||||
|
||||
int value = prop.CastValue<int>();
|
||||
faces.InnerText = value.ToInvariantString();
|
||||
}
|
||||
}
|
||||
}
|
25
Tokens/Float.cs
Normal file
25
Tokens/Float.cs
Normal file
@ -0,0 +1,25 @@
|
||||
using System.Xml;
|
||||
using RobloxFiles.XmlFormat;
|
||||
|
||||
namespace RobloxFiles.Tokens
|
||||
{
|
||||
public class FloatToken : IXmlPropertyToken, IAttributeToken<float>
|
||||
{
|
||||
public string XmlPropertyToken => "float";
|
||||
public AttributeType AttributeType => AttributeType.Float;
|
||||
|
||||
public float ReadAttribute(Attribute attr) => attr.ReadFloat();
|
||||
public void WriteAttribute(Attribute attr, float value) => attr.WriteFloat(value);
|
||||
|
||||
public bool ReadProperty(Property prop, XmlNode token)
|
||||
{
|
||||
return XmlPropertyTokens.ReadPropertyGeneric<float>(prop, PropertyType.Float, token);
|
||||
}
|
||||
|
||||
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
|
||||
{
|
||||
float value = prop.CastValue<float>();
|
||||
node.InnerText = value.ToInvariantString();
|
||||
}
|
||||
}
|
||||
}
|
32
Tokens/Int.cs
Normal file
32
Tokens/Int.cs
Normal file
@ -0,0 +1,32 @@
|
||||
using System.Xml;
|
||||
using RobloxFiles.XmlFormat;
|
||||
|
||||
namespace RobloxFiles.Tokens
|
||||
{
|
||||
public class IntToken : IXmlPropertyToken
|
||||
{
|
||||
public string XmlPropertyToken => "int";
|
||||
|
||||
public bool ReadProperty(Property prop, XmlNode token)
|
||||
{
|
||||
// BrickColors are represented by ints, see if
|
||||
// we can infer when they should be a BrickColor.
|
||||
|
||||
if (prop.Name.Contains("Color") || prop.Instance.ClassName.Contains("Color"))
|
||||
{
|
||||
var brickColorToken = XmlPropertyTokens.GetHandler<BrickColorToken>();
|
||||
return brickColorToken.ReadProperty(prop, token);
|
||||
}
|
||||
else
|
||||
{
|
||||
return XmlPropertyTokens.ReadPropertyGeneric<int>(prop, PropertyType.Int, token);
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
|
||||
{
|
||||
int value = prop.CastValue<int>();
|
||||
node.InnerText = value.ToInvariantString();
|
||||
}
|
||||
}
|
||||
}
|
21
Tokens/Int64.cs
Normal file
21
Tokens/Int64.cs
Normal file
@ -0,0 +1,21 @@
|
||||
using System.Xml;
|
||||
using RobloxFiles.XmlFormat;
|
||||
|
||||
namespace RobloxFiles.Tokens
|
||||
{
|
||||
public class Int64Token : IXmlPropertyToken
|
||||
{
|
||||
public string XmlPropertyToken => "int64";
|
||||
|
||||
public bool ReadProperty(Property prop, XmlNode token)
|
||||
{
|
||||
return XmlPropertyTokens.ReadPropertyGeneric<long>(prop, PropertyType.Int64, token);
|
||||
}
|
||||
|
||||
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
|
||||
{
|
||||
long value = prop.CastValue<long>();
|
||||
node.InnerText = value.ToString();
|
||||
}
|
||||
}
|
||||
}
|
57
Tokens/NumberRange.cs
Normal file
57
Tokens/NumberRange.cs
Normal file
@ -0,0 +1,57 @@
|
||||
using System;
|
||||
using System.Xml;
|
||||
using RobloxFiles.DataTypes;
|
||||
|
||||
namespace RobloxFiles.Tokens
|
||||
{
|
||||
public class NumberRangeToken : IXmlPropertyToken, IAttributeToken<NumberRange>
|
||||
{
|
||||
public string XmlPropertyToken => "NumberRange";
|
||||
public AttributeType AttributeType => AttributeType.NumberRange;
|
||||
|
||||
public bool ReadProperty(Property prop, XmlNode token)
|
||||
{
|
||||
string contents = token.InnerText.Trim();
|
||||
string[] buffer = contents.Split(' ');
|
||||
bool valid = (buffer.Length == 2);
|
||||
|
||||
if (valid)
|
||||
{
|
||||
try
|
||||
{
|
||||
float min = Formatting.ParseFloat(buffer[0]);
|
||||
float max = Formatting.ParseFloat(buffer[1]);
|
||||
|
||||
prop.Type = PropertyType.NumberRange;
|
||||
prop.Value = new NumberRange(min, max);
|
||||
}
|
||||
catch
|
||||
{
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
|
||||
{
|
||||
NumberRange value = prop.CastValue<NumberRange>();
|
||||
node.InnerText = value.ToString() + ' ';
|
||||
}
|
||||
|
||||
public NumberRange ReadAttribute(Attribute attr)
|
||||
{
|
||||
float min = attr.ReadFloat();
|
||||
float max = attr.ReadFloat();
|
||||
|
||||
return new NumberRange(min, max);
|
||||
}
|
||||
|
||||
public void WriteAttribute(Attribute attr, NumberRange value)
|
||||
{
|
||||
attr.WriteFloat(value.Min);
|
||||
attr.WriteFloat(value.Max);
|
||||
}
|
||||
}
|
||||
}
|
81
Tokens/NumberSequence.cs
Normal file
81
Tokens/NumberSequence.cs
Normal file
@ -0,0 +1,81 @@
|
||||
using System.Xml;
|
||||
using RobloxFiles.DataTypes;
|
||||
|
||||
namespace RobloxFiles.Tokens
|
||||
{
|
||||
public class NumberSequenceToken : IXmlPropertyToken, IAttributeToken<NumberSequence>
|
||||
{
|
||||
public string XmlPropertyToken => "NumberSequence";
|
||||
public AttributeType AttributeType => AttributeType.NumberSequence;
|
||||
|
||||
public bool ReadProperty(Property prop, XmlNode token)
|
||||
{
|
||||
string contents = token.InnerText.Trim();
|
||||
string[] buffer = contents.Split(' ');
|
||||
|
||||
int length = buffer.Length;
|
||||
bool valid = (length % 3 == 0);
|
||||
|
||||
if (valid)
|
||||
{
|
||||
try
|
||||
{
|
||||
NumberSequenceKeypoint[] keypoints = new NumberSequenceKeypoint[length / 3];
|
||||
|
||||
for (int i = 0; i < length; i += 3)
|
||||
{
|
||||
float Time = Formatting.ParseFloat(buffer[ i ]);
|
||||
float Value = Formatting.ParseFloat(buffer[i + 1]);
|
||||
float Envelope = Formatting.ParseFloat(buffer[i + 2]);
|
||||
|
||||
keypoints[i / 3] = new NumberSequenceKeypoint(Time, Value, Envelope);
|
||||
}
|
||||
|
||||
prop.Type = PropertyType.NumberSequence;
|
||||
prop.Value = new NumberSequence(keypoints);
|
||||
}
|
||||
catch
|
||||
{
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
|
||||
{
|
||||
NumberSequence value = prop.CastValue<NumberSequence>();
|
||||
node.InnerText = value.ToString() + ' ';
|
||||
}
|
||||
|
||||
public NumberSequence ReadAttribute(Attribute attr)
|
||||
{
|
||||
int numKeys = attr.ReadInt();
|
||||
var keypoints = new NumberSequenceKeypoint[numKeys];
|
||||
|
||||
for (int i = 0; i < numKeys; i++)
|
||||
{
|
||||
float envelope = attr.ReadInt(),
|
||||
time = attr.ReadFloat(),
|
||||
value = attr.ReadFloat();
|
||||
|
||||
keypoints[i] = new NumberSequenceKeypoint(time, value, envelope);
|
||||
}
|
||||
|
||||
return new NumberSequence(keypoints);
|
||||
}
|
||||
|
||||
public void WriteAttribute(Attribute attr, NumberSequence value)
|
||||
{
|
||||
attr.WriteInt(value.Keypoints.Length);
|
||||
|
||||
foreach (var keypoint in value.Keypoints)
|
||||
{
|
||||
attr.WriteFloat(keypoint.Envelope);
|
||||
attr.WriteFloat(keypoint.Time);
|
||||
attr.WriteFloat(keypoint.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
87
Tokens/PhysicalProperties.cs
Normal file
87
Tokens/PhysicalProperties.cs
Normal file
@ -0,0 +1,87 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml;
|
||||
using RobloxFiles.DataTypes;
|
||||
|
||||
namespace RobloxFiles.Tokens
|
||||
{
|
||||
public class PhysicalPropertiesToken : IXmlPropertyToken
|
||||
{
|
||||
public string XmlPropertyToken => "PhysicalProperties";
|
||||
|
||||
private static Func<string, T> CreateReader<T>(Func<string, T> parse, XmlNode token) where T : struct
|
||||
{
|
||||
return new Func<string, T>(key =>
|
||||
{
|
||||
XmlElement node = token[key];
|
||||
return parse(node.InnerText);
|
||||
});
|
||||
}
|
||||
|
||||
public bool ReadProperty(Property prop, XmlNode token)
|
||||
{
|
||||
var readBool = CreateReader(bool.Parse, token);
|
||||
var readFloat = CreateReader(Formatting.ParseFloat, token);
|
||||
|
||||
try
|
||||
{
|
||||
bool custom = readBool("CustomPhysics");
|
||||
prop.Type = PropertyType.PhysicalProperties;
|
||||
|
||||
if (custom)
|
||||
{
|
||||
prop.Value = new PhysicalProperties
|
||||
(
|
||||
readFloat("Density"),
|
||||
readFloat("Friction"),
|
||||
readFloat("Elasticity"),
|
||||
readFloat("FrictionWeight"),
|
||||
readFloat("ElasticityWeight")
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
|
||||
{
|
||||
bool hasCustomPhysics = (prop.Value != null);
|
||||
|
||||
XmlElement customPhysics = doc.CreateElement("CustomPhysics");
|
||||
customPhysics.InnerText = hasCustomPhysics
|
||||
.ToString()
|
||||
.ToLower();
|
||||
|
||||
node.AppendChild(customPhysics);
|
||||
|
||||
if (hasCustomPhysics)
|
||||
{
|
||||
var customProps = prop.CastValue<PhysicalProperties>();
|
||||
|
||||
var data = new Dictionary<string, float>()
|
||||
{
|
||||
{ "Density", customProps.Density },
|
||||
{ "Friction", customProps.Friction },
|
||||
{ "Elasticity", customProps.Elasticity },
|
||||
{ "FrictionWeight", customProps.FrictionWeight },
|
||||
{ "ElasticityWeight", customProps.ElasticityWeight }
|
||||
};
|
||||
|
||||
foreach (string elementType in data.Keys)
|
||||
{
|
||||
float value = data[elementType];
|
||||
|
||||
XmlElement element = doc.CreateElement(elementType);
|
||||
element.InnerText = value.ToInvariantString();
|
||||
|
||||
node.AppendChild(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
47
Tokens/ProtectedString.cs
Normal file
47
Tokens/ProtectedString.cs
Normal file
@ -0,0 +1,47 @@
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
|
||||
using RobloxFiles.DataTypes;
|
||||
using RobloxFiles.XmlFormat;
|
||||
|
||||
namespace RobloxFiles.Tokens
|
||||
{
|
||||
public class ProtectedStringToken : IXmlPropertyToken
|
||||
{
|
||||
public string XmlPropertyToken => "ProtectedString";
|
||||
|
||||
public bool ReadProperty(Property prop, XmlNode token)
|
||||
{
|
||||
ProtectedString contents = token.InnerText;
|
||||
prop.Type = PropertyType.ProtectedString;
|
||||
prop.Value = contents;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
|
||||
{
|
||||
ProtectedString value = prop.CastValue<ProtectedString>();
|
||||
|
||||
if (value.IsCompiled)
|
||||
{
|
||||
var binary = XmlPropertyTokens.GetHandler<BinaryStringToken>();
|
||||
binary.WriteProperty(prop, doc, node);
|
||||
}
|
||||
else
|
||||
{
|
||||
string contents = Encoding.UTF8.GetString(value.RawBuffer);
|
||||
|
||||
if (contents.Contains("\r") || contents.Contains("\n"))
|
||||
{
|
||||
XmlCDataSection cdata = doc.CreateCDataSection(contents);
|
||||
node.AppendChild(cdata);
|
||||
}
|
||||
else
|
||||
{
|
||||
node.InnerText = contents;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
54
Tokens/Ray.cs
Normal file
54
Tokens/Ray.cs
Normal file
@ -0,0 +1,54 @@
|
||||
using System;
|
||||
using System.Xml;
|
||||
using RobloxFiles.DataTypes;
|
||||
|
||||
namespace RobloxFiles.Tokens
|
||||
{
|
||||
public class RayToken : IXmlPropertyToken
|
||||
{
|
||||
public string XmlPropertyToken => "Ray";
|
||||
private static readonly string[] Fields = new string[2] { "origin", "direction" };
|
||||
|
||||
public bool ReadProperty(Property prop, XmlNode token)
|
||||
{
|
||||
try
|
||||
{
|
||||
Vector3[] read = new Vector3[Fields.Length];
|
||||
|
||||
for (int i = 0; i < read.Length; i++)
|
||||
{
|
||||
string field = Fields[i];
|
||||
var fieldToken = token[field];
|
||||
read[i] = Vector3Token.ReadVector3(fieldToken);
|
||||
}
|
||||
|
||||
Vector3 origin = read[0],
|
||||
direction = read[1];
|
||||
|
||||
Ray ray = new Ray(origin, direction);
|
||||
prop.Type = PropertyType.Ray;
|
||||
prop.Value = ray;
|
||||
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
|
||||
{
|
||||
Ray ray = prop.CastValue<Ray>();
|
||||
|
||||
XmlElement origin = doc.CreateElement("origin");
|
||||
XmlElement direction = doc.CreateElement("direction");
|
||||
|
||||
Vector3Token.WriteVector3(doc, origin, ray.Origin);
|
||||
Vector3Token.WriteVector3(doc, direction, ray.Direction);
|
||||
|
||||
node.AppendChild(origin);
|
||||
node.AppendChild(direction);
|
||||
}
|
||||
}
|
||||
}
|
68
Tokens/Rect.cs
Normal file
68
Tokens/Rect.cs
Normal file
@ -0,0 +1,68 @@
|
||||
using System;
|
||||
using System.Xml;
|
||||
using RobloxFiles.DataTypes;
|
||||
|
||||
namespace RobloxFiles.Tokens
|
||||
{
|
||||
public class RectToken : IXmlPropertyToken, IAttributeToken<Rect>
|
||||
{
|
||||
public string XmlPropertyToken => "Rect2D";
|
||||
public AttributeType AttributeType => AttributeType.Rect;
|
||||
private static readonly string[] XmlFields = new string[2] { "min", "max" };
|
||||
|
||||
public bool ReadProperty(Property prop, XmlNode token)
|
||||
{
|
||||
try
|
||||
{
|
||||
Vector2[] read = new Vector2[XmlFields.Length];
|
||||
|
||||
for (int i = 0; i < read.Length; i++)
|
||||
{
|
||||
string field = XmlFields[i];
|
||||
var fieldToken = token[field];
|
||||
read[i] = Vector2Token.ReadVector2(fieldToken);
|
||||
}
|
||||
|
||||
Vector2 min = read[0],
|
||||
max = read[1];
|
||||
|
||||
Rect rect = new Rect(min, max);
|
||||
prop.Type = PropertyType.Rect;
|
||||
prop.Value = rect;
|
||||
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
|
||||
{
|
||||
Rect rect = prop.CastValue<Rect>();
|
||||
|
||||
XmlElement min = doc.CreateElement("min");
|
||||
Vector2Token.WriteVector2(doc, min, rect.Min);
|
||||
node.AppendChild(min);
|
||||
|
||||
XmlElement max = doc.CreateElement("max");
|
||||
Vector2Token.WriteVector2(doc, max, rect.Max);
|
||||
node.AppendChild(max);
|
||||
}
|
||||
|
||||
public Rect ReadAttribute(Attribute attr)
|
||||
{
|
||||
Vector2 min = Vector2Token.ReadVector2(attr);
|
||||
Vector2 max = Vector2Token.ReadVector2(attr);
|
||||
|
||||
return new Rect(min, max);
|
||||
}
|
||||
|
||||
public void WriteAttribute(Attribute attr, Rect value)
|
||||
{
|
||||
Vector2Token.WriteVector2(attr, value.Min);
|
||||
Vector2Token.WriteVector2(attr, value.Max);
|
||||
}
|
||||
}
|
||||
}
|
31
Tokens/Ref.cs
Normal file
31
Tokens/Ref.cs
Normal file
@ -0,0 +1,31 @@
|
||||
using System.Xml;
|
||||
|
||||
namespace RobloxFiles.Tokens
|
||||
{
|
||||
public class RefToken : IXmlPropertyToken
|
||||
{
|
||||
public string XmlPropertyToken => "Ref";
|
||||
|
||||
public bool ReadProperty(Property prop, XmlNode token)
|
||||
{
|
||||
string refId = token.InnerText;
|
||||
prop.Type = PropertyType.Ref;
|
||||
prop.XmlToken = refId;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
|
||||
{
|
||||
string result = "null";
|
||||
|
||||
if (prop.Value != null)
|
||||
{
|
||||
Instance inst = prop.CastValue<Instance>();
|
||||
result = inst.Referent;
|
||||
}
|
||||
|
||||
node.InnerText = result;
|
||||
}
|
||||
}
|
||||
}
|
35
Tokens/SharedString.cs
Normal file
35
Tokens/SharedString.cs
Normal file
@ -0,0 +1,35 @@
|
||||
using System.Xml;
|
||||
using RobloxFiles.DataTypes;
|
||||
|
||||
namespace RobloxFiles.Tokens
|
||||
{
|
||||
public class SharedStringToken : IXmlPropertyToken
|
||||
{
|
||||
public string XmlPropertyToken => "SharedString";
|
||||
|
||||
public bool ReadProperty(Property prop, XmlNode token)
|
||||
{
|
||||
string key = token.InnerText;
|
||||
prop.Type = PropertyType.SharedString;
|
||||
prop.Value = new SharedString(key);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
|
||||
{
|
||||
if (prop.Value is SharedString value)
|
||||
{
|
||||
string key = value.Key;
|
||||
|
||||
if (value.ComputedKey == null)
|
||||
{
|
||||
var newShared = SharedString.FromBuffer(value.SharedValue);
|
||||
key = newShared.ComputedKey;
|
||||
}
|
||||
|
||||
node.InnerText = key;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
37
Tokens/String.cs
Normal file
37
Tokens/String.cs
Normal file
@ -0,0 +1,37 @@
|
||||
using System.Xml;
|
||||
|
||||
namespace RobloxFiles.Tokens
|
||||
{
|
||||
public class StringToken : IXmlPropertyToken, IAttributeToken<string>
|
||||
{
|
||||
public string XmlPropertyToken => "string";
|
||||
public AttributeType AttributeType => AttributeType.String;
|
||||
|
||||
public string ReadAttribute(Attribute attr) => attr.ReadString();
|
||||
public void WriteAttribute(Attribute attr, string value) => attr.WriteString(value);
|
||||
|
||||
public bool ReadProperty(Property prop, XmlNode token)
|
||||
{
|
||||
string contents = token.InnerText;
|
||||
prop.Type = PropertyType.String;
|
||||
prop.Value = contents;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
|
||||
{
|
||||
string value = prop.Value.ToInvariantString();
|
||||
|
||||
if (value.Contains("\r") || value.Contains("\n"))
|
||||
{
|
||||
XmlCDataSection cdata = doc.CreateCDataSection(value);
|
||||
node.AppendChild(cdata);
|
||||
}
|
||||
else
|
||||
{
|
||||
node.InnerText = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
81
Tokens/UDim.cs
Normal file
81
Tokens/UDim.cs
Normal file
@ -0,0 +1,81 @@
|
||||
using System;
|
||||
using System.Xml;
|
||||
using RobloxFiles.DataTypes;
|
||||
|
||||
namespace RobloxFiles.Tokens
|
||||
{
|
||||
public class UDimToken : IXmlPropertyToken, IAttributeToken<UDim>
|
||||
{
|
||||
public string XmlPropertyToken => "UDim";
|
||||
public AttributeType AttributeType => AttributeType.UDim;
|
||||
|
||||
public UDim ReadAttribute(Attribute attr) => ReadUDim(attr);
|
||||
public void WriteAttribute(Attribute attr, UDim value) => WriteUDim(attr, value);
|
||||
|
||||
public static UDim ReadUDim(XmlNode token, string prefix = "")
|
||||
{
|
||||
try
|
||||
{
|
||||
XmlElement scaleToken = token[prefix + 'S'];
|
||||
float scale = Formatting.ParseFloat(scaleToken.InnerText);
|
||||
|
||||
XmlElement offsetToken = token[prefix + 'O'];
|
||||
int offset = int.Parse(offsetToken.InnerText);
|
||||
|
||||
return new UDim(scale, offset);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static void WriteUDim(XmlDocument doc, XmlNode node, UDim value, string prefix = "")
|
||||
{
|
||||
XmlElement scale = doc.CreateElement(prefix + 'S');
|
||||
scale.InnerText = value.Scale.ToInvariantString();
|
||||
node.AppendChild(scale);
|
||||
|
||||
XmlElement offset = doc.CreateElement(prefix + 'O');
|
||||
offset.InnerText = value.Offset.ToInvariantString();
|
||||
node.AppendChild(offset);
|
||||
}
|
||||
|
||||
public static UDim ReadUDim(Attribute attr)
|
||||
{
|
||||
float scale = attr.ReadFloat();
|
||||
int offset = attr.ReadInt();
|
||||
|
||||
return new UDim(scale, offset);
|
||||
}
|
||||
|
||||
public static void WriteUDim(Attribute attr, UDim value)
|
||||
{
|
||||
float scale = value.Scale;
|
||||
attr.WriteFloat(scale);
|
||||
|
||||
int offset = value.Offset;
|
||||
attr.WriteInt(offset);
|
||||
}
|
||||
|
||||
public bool ReadProperty(Property property, XmlNode token)
|
||||
{
|
||||
UDim result = ReadUDim(token);
|
||||
bool success = (result != null);
|
||||
|
||||
if (success)
|
||||
{
|
||||
property.Type = PropertyType.UDim;
|
||||
property.Value = result;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
|
||||
{
|
||||
UDim value = prop.CastValue<UDim>();
|
||||
WriteUDim(doc, node, value);
|
||||
}
|
||||
}
|
||||
}
|
52
Tokens/UDim2.cs
Normal file
52
Tokens/UDim2.cs
Normal file
@ -0,0 +1,52 @@
|
||||
using System.Xml;
|
||||
using RobloxFiles.DataTypes;
|
||||
|
||||
namespace RobloxFiles.Tokens
|
||||
{
|
||||
public class UDim2Token : IXmlPropertyToken, IAttributeToken<UDim2>
|
||||
{
|
||||
public string XmlPropertyToken => "UDim2";
|
||||
public AttributeType AttributeType => AttributeType.UDim2;
|
||||
|
||||
public UDim2 ReadAttribute(Attribute attr)
|
||||
{
|
||||
UDim x = UDimToken.ReadUDim(attr);
|
||||
UDim y = UDimToken.ReadUDim(attr);
|
||||
|
||||
return new UDim2(x, y);
|
||||
}
|
||||
|
||||
public void WriteAttribute(Attribute attr, UDim2 value)
|
||||
{
|
||||
UDimToken.WriteUDim(attr, value.X);
|
||||
UDimToken.WriteUDim(attr, value.Y);
|
||||
}
|
||||
|
||||
public bool ReadProperty(Property property, XmlNode token)
|
||||
{
|
||||
UDim xUDim = UDimToken.ReadUDim(token, "X");
|
||||
UDim yUDim = UDimToken.ReadUDim(token, "Y");
|
||||
|
||||
if (xUDim != null && yUDim != null)
|
||||
{
|
||||
property.Type = PropertyType.UDim2;
|
||||
property.Value = new UDim2(xUDim, yUDim);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
|
||||
{
|
||||
UDim2 value = prop.CastValue<UDim2>();
|
||||
|
||||
UDim xUDim = value.X;
|
||||
UDimToken.WriteUDim(doc, node, xUDim, "X");
|
||||
|
||||
UDim yUDim = value.Y;
|
||||
UDimToken.WriteUDim(doc, node, yUDim, "Y");
|
||||
}
|
||||
}
|
||||
}
|
83
Tokens/Vector2.cs
Normal file
83
Tokens/Vector2.cs
Normal file
@ -0,0 +1,83 @@
|
||||
using System.Xml;
|
||||
using RobloxFiles.DataTypes;
|
||||
|
||||
namespace RobloxFiles.Tokens
|
||||
{
|
||||
public class Vector2Token : IXmlPropertyToken, IAttributeToken<Vector2>
|
||||
{
|
||||
public string XmlPropertyToken => "Vector2";
|
||||
private static readonly string[] XmlCoords = new string[2] { "X", "Y" };
|
||||
|
||||
public AttributeType AttributeType => AttributeType.Vector2;
|
||||
public Vector2 ReadAttribute(Attribute attr) => ReadVector2(attr);
|
||||
public void WriteAttribute(Attribute attr, Vector2 value) => WriteVector2(attr, value);
|
||||
|
||||
public static Vector2 ReadVector2(XmlNode token)
|
||||
{
|
||||
float[] xy = new float[2];
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
string key = XmlCoords[i];
|
||||
|
||||
try
|
||||
{
|
||||
var coord = token[key];
|
||||
string text = coord.InnerText;
|
||||
xy[i] = Formatting.ParseFloat(text);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return new Vector2(xy);
|
||||
}
|
||||
|
||||
public static void WriteVector2(XmlDocument doc, XmlNode node, Vector2 value)
|
||||
{
|
||||
XmlElement x = doc.CreateElement("X");
|
||||
x.InnerText = value.X.ToInvariantString();
|
||||
node.AppendChild(x);
|
||||
|
||||
XmlElement y = doc.CreateElement("Y");
|
||||
y.InnerText = value.Y.ToInvariantString();
|
||||
node.AppendChild(y);
|
||||
}
|
||||
|
||||
public static Vector2 ReadVector2(Attribute attr)
|
||||
{
|
||||
float x = attr.ReadFloat(),
|
||||
y = attr.ReadFloat();
|
||||
|
||||
return new Vector2(x, y);
|
||||
}
|
||||
|
||||
public static void WriteVector2(Attribute attr, Vector2 value)
|
||||
{
|
||||
attr.WriteFloat(value.X);
|
||||
attr.WriteFloat(value.Y);
|
||||
}
|
||||
|
||||
public bool ReadProperty(Property property, XmlNode token)
|
||||
{
|
||||
Vector2 result = ReadVector2(token);
|
||||
bool success = (result != null);
|
||||
|
||||
if (success)
|
||||
{
|
||||
property.Type = PropertyType.Vector2;
|
||||
property.Value = result;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
|
||||
{
|
||||
Vector2 value = prop.CastValue<Vector2>();
|
||||
WriteVector2(doc, node, value);
|
||||
}
|
||||
}
|
||||
}
|
88
Tokens/Vector3.cs
Normal file
88
Tokens/Vector3.cs
Normal file
@ -0,0 +1,88 @@
|
||||
using System.Xml;
|
||||
using RobloxFiles.DataTypes;
|
||||
|
||||
namespace RobloxFiles.Tokens
|
||||
{
|
||||
public class Vector3Token : IXmlPropertyToken, IAttributeToken<Vector3>
|
||||
{
|
||||
public string XmlPropertyToken => "Vector3";
|
||||
private static readonly string[] XmlCoords = new string[3] { "X", "Y", "Z" };
|
||||
|
||||
public AttributeType AttributeType => AttributeType.Vector3;
|
||||
public Vector3 ReadAttribute(Attribute attr) => ReadVector3(attr);
|
||||
public void WriteAttribute(Attribute attr, Vector3 value) => WriteVector3(attr, value);
|
||||
|
||||
public static Vector3 ReadVector3(XmlNode token)
|
||||
{
|
||||
float[] xyz = new float[3];
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
string key = XmlCoords[i];
|
||||
|
||||
try
|
||||
{
|
||||
var coord = token[key];
|
||||
xyz[i] = Formatting.ParseFloat(coord.InnerText);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return new Vector3(xyz);
|
||||
}
|
||||
|
||||
public static void WriteVector3(XmlDocument doc, XmlNode node, Vector3 value)
|
||||
{
|
||||
XmlElement x = doc.CreateElement("X");
|
||||
x.InnerText = value.X.ToInvariantString();
|
||||
node.AppendChild(x);
|
||||
|
||||
XmlElement y = doc.CreateElement("Y");
|
||||
y.InnerText = value.Y.ToInvariantString();
|
||||
node.AppendChild(y);
|
||||
|
||||
XmlElement z = doc.CreateElement("Z");
|
||||
z.InnerText = value.Z.ToInvariantString();
|
||||
node.AppendChild(z);
|
||||
}
|
||||
|
||||
public static Vector3 ReadVector3(Attribute attr)
|
||||
{
|
||||
float x = attr.ReadFloat(),
|
||||
y = attr.ReadFloat(),
|
||||
z = attr.ReadFloat();
|
||||
|
||||
return new Vector3(x, y, z);
|
||||
}
|
||||
|
||||
public static void WriteVector3(Attribute attr, Vector3 value)
|
||||
{
|
||||
attr.WriteFloat(value.X);
|
||||
attr.WriteFloat(value.Y);
|
||||
attr.WriteFloat(value.Z);
|
||||
}
|
||||
|
||||
public bool ReadProperty(Property property, XmlNode token)
|
||||
{
|
||||
Vector3 result = ReadVector3(token);
|
||||
bool success = (result != null);
|
||||
|
||||
if (success)
|
||||
{
|
||||
property.Type = PropertyType.Vector3;
|
||||
property.Value = result;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
|
||||
{
|
||||
Vector3 value = prop.CastValue<Vector3>();
|
||||
WriteVector3(doc, node, value);
|
||||
}
|
||||
}
|
||||
}
|
57
Tokens/Vector3int16.cs
Normal file
57
Tokens/Vector3int16.cs
Normal file
@ -0,0 +1,57 @@
|
||||
using System.Xml;
|
||||
using RobloxFiles.DataTypes;
|
||||
|
||||
namespace RobloxFiles.Tokens
|
||||
{
|
||||
public class Vector3int16Token : IXmlPropertyToken
|
||||
{
|
||||
public string XmlPropertyToken => "Vector3int16";
|
||||
private static readonly string[] Coords = new string[3] { "X", "Y", "Z" };
|
||||
|
||||
public bool ReadProperty(Property property, XmlNode token)
|
||||
{
|
||||
short[] xyz = new short[3];
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
string key = Coords[i];
|
||||
|
||||
try
|
||||
{
|
||||
var coord = token[key];
|
||||
xyz[i] = short.Parse(coord.InnerText);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
short x = xyz[0],
|
||||
y = xyz[1],
|
||||
z = xyz[2];
|
||||
|
||||
property.Type = PropertyType.Vector3int16;
|
||||
property.Value = new Vector3int16(x, y, z);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
|
||||
{
|
||||
Vector3int16 value = prop.CastValue<Vector3int16>();
|
||||
|
||||
XmlElement x = doc.CreateElement("X");
|
||||
x.InnerText = value.X.ToString();
|
||||
node.AppendChild(x);
|
||||
|
||||
XmlElement y = doc.CreateElement("Y");
|
||||
y.InnerText = value.Y.ToString();
|
||||
node.AppendChild(y);
|
||||
|
||||
XmlElement z = doc.CreateElement("Z");
|
||||
z.InnerText = value.Z.ToString();
|
||||
node.AppendChild(z);
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user