Added support for saving XML files!
Support for binary files will be coming later.
This commit is contained in:
parent
45a84e34d0
commit
34642f5656
@ -100,5 +100,10 @@ namespace RobloxFiles.BinaryFormat
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteFile(Stream stream)
|
||||
{
|
||||
throw new NotImplementedException("Not implemented yet!");
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
@ -12,6 +13,8 @@ namespace RobloxFiles
|
||||
public interface IRobloxFile
|
||||
{
|
||||
Instance Contents { get; }
|
||||
|
||||
void ReadFile(byte[] buffer);
|
||||
void WriteFile(Stream stream);
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,8 @@ namespace RobloxFiles.XmlFormat
|
||||
public interface IXmlPropertyToken
|
||||
{
|
||||
string Token { get; }
|
||||
bool ReadToken(Property prop, XmlNode token);
|
||||
|
||||
bool ReadProperty(Property prop, XmlNode token);
|
||||
void WriteProperty(Property prop, XmlDocument doc, XmlNode node);
|
||||
}
|
||||
}
|
||||
|
@ -62,6 +62,11 @@ namespace RobloxFiles
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteFile(Stream stream)
|
||||
{
|
||||
InnerFile.WriteFile(stream);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a RobloxFile from a provided byte sequence that represents the file.
|
||||
/// </summary>
|
||||
|
@ -97,11 +97,13 @@
|
||||
<Compile Include="DataTypes\UDim2.cs" />
|
||||
<Compile Include="DataTypes\Vector2.cs" />
|
||||
<Compile Include="DataTypes\Vector3.cs" />
|
||||
<Compile Include="Utility\Formatting.cs" />
|
||||
<Compile Include="Utility\MaterialInfo.cs" />
|
||||
<Compile Include="Utility\Quaternion.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="XmlFormat\PropertyTokens\SharedString.cs" />
|
||||
<Compile Include="XmlFormat\PropertyTokens\Vector3int16.cs" />
|
||||
<Compile Include="XmlFormat\XmlDataWriter.cs" />
|
||||
<Compile Include="XmlFormat\XmlPropertyTokens.cs" />
|
||||
<Compile Include="XmlFormat\XmlDataReader.cs" />
|
||||
<Compile Include="XmlFormat\XmlRobloxFile.cs" />
|
||||
|
@ -12,11 +12,11 @@ namespace RobloxFiles
|
||||
public class Instance
|
||||
{
|
||||
/// <summary>The ClassName of this Instance.</summary>
|
||||
public readonly string ClassName;
|
||||
public string ClassName;
|
||||
|
||||
/// <summary>A list of properties that are defined under this Instance.</summary>
|
||||
public Dictionary<string, Property> Properties = new Dictionary<string, Property>();
|
||||
|
||||
|
||||
private List<Instance> Children = new List<Instance>();
|
||||
private Instance rawParent;
|
||||
|
||||
@ -24,6 +24,8 @@ namespace RobloxFiles
|
||||
public string Name => ReadProperty("Name", ClassName);
|
||||
public override string ToString() => Name;
|
||||
|
||||
internal string XmlReferent;
|
||||
|
||||
/// <summary>Creates an instance using the provided ClassName.</summary>
|
||||
/// <param name="className">The ClassName to use for this Instance.</param>
|
||||
public Instance(string className = "Instance")
|
||||
@ -292,7 +294,7 @@ namespace RobloxFiles
|
||||
/// This is used during the file loading procedure.
|
||||
/// </summary>
|
||||
/// <param name="prop">A reference to the property that will be added.</param>
|
||||
public void AddProperty(ref Property prop)
|
||||
internal void AddProperty(ref Property prop)
|
||||
{
|
||||
Properties.Add(prop.Name, prop);
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ namespace RobloxFiles
|
||||
PhysicalProperties,
|
||||
Color3uint8,
|
||||
Int64,
|
||||
SharedString
|
||||
SharedString,
|
||||
}
|
||||
|
||||
public class Property
|
||||
@ -44,7 +44,9 @@ namespace RobloxFiles
|
||||
public PropertyType Type;
|
||||
public object Value;
|
||||
|
||||
public string XmlToken = "";
|
||||
private byte[] RawBuffer = null;
|
||||
|
||||
public bool HasRawBuffer
|
||||
{
|
||||
get
|
||||
|
108
Utility/Formatting.cs
Normal file
108
Utility/Formatting.cs
Normal file
@ -0,0 +1,108 @@
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
|
||||
// This global class defines extension methods to numeric types
|
||||
// where I don't want system globalization to come into play.
|
||||
|
||||
internal static class Formatting
|
||||
{
|
||||
private static CultureInfo invariant => CultureInfo.InvariantCulture;
|
||||
|
||||
public static string ToInvariantString(this float value)
|
||||
{
|
||||
string result;
|
||||
|
||||
if (float.IsPositiveInfinity(value))
|
||||
result = "INF";
|
||||
else if (float.IsNegativeInfinity(value))
|
||||
result = "-INF";
|
||||
else if (float.IsNaN(value))
|
||||
result = "NAN";
|
||||
else
|
||||
result = value.ToString(invariant);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static string ToInvariantString(this double value)
|
||||
{
|
||||
string result;
|
||||
|
||||
if (double.IsPositiveInfinity(value))
|
||||
result = "INF";
|
||||
else if (double.IsNegativeInfinity(value))
|
||||
result = "-INF";
|
||||
else if (double.IsNaN(value))
|
||||
result = "NAN";
|
||||
else
|
||||
result = value.ToString(invariant);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static string ToInvariantString(this int value)
|
||||
{
|
||||
return value.ToString(invariant);
|
||||
}
|
||||
|
||||
public static string ToInvariantString(this object value)
|
||||
{
|
||||
if (value is float)
|
||||
{
|
||||
float f = (float)value;
|
||||
return f.ToInvariantString();
|
||||
}
|
||||
else if (value is double)
|
||||
{
|
||||
double d = (double)value;
|
||||
return d.ToInvariantString();
|
||||
}
|
||||
else if (value is int)
|
||||
{
|
||||
int i = (int)value;
|
||||
return i.ToInvariantString();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Unhandled
|
||||
return value.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public static float ParseFloat(string value)
|
||||
{
|
||||
float result;
|
||||
|
||||
if (value == "INF")
|
||||
result = float.PositiveInfinity;
|
||||
else if (value == "-INF")
|
||||
result = float.NegativeInfinity;
|
||||
else if (value == "NAN")
|
||||
result = float.NaN;
|
||||
else
|
||||
result = float.Parse(value, invariant);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static double ParseDouble(string value)
|
||||
{
|
||||
double result;
|
||||
|
||||
if (value == "INF")
|
||||
result = double.PositiveInfinity;
|
||||
else if (value == "-INF")
|
||||
result = double.NegativeInfinity;
|
||||
else if (value == "NAN")
|
||||
result = double.NaN;
|
||||
else
|
||||
result = double.Parse(value, invariant);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static int ParseInt(string s)
|
||||
{
|
||||
return int.Parse(s, invariant);
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
using System.Xml;
|
||||
using System;
|
||||
using System.Xml;
|
||||
using RobloxFiles.DataTypes;
|
||||
|
||||
namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
@ -6,10 +7,9 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
public class AxesToken : IXmlPropertyToken
|
||||
{
|
||||
public string Token => "Axes";
|
||||
|
||||
public bool ReadToken(Property prop, XmlNode token)
|
||||
public bool ReadProperty(Property prop, XmlNode token)
|
||||
{
|
||||
bool success = XmlPropertyTokens.ReadTokenGeneric<uint>(prop, PropertyType.Axes, token);
|
||||
bool success = XmlPropertyTokens.ReadPropertyGeneric<uint>(prop, PropertyType.Axes, token);
|
||||
|
||||
if (success)
|
||||
{
|
||||
@ -27,5 +27,14 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
|
||||
{
|
||||
XmlElement axes = doc.CreateElement("axes");
|
||||
node.AppendChild(axes);
|
||||
|
||||
int value = (int)prop.Value;
|
||||
axes.InnerText = value.ToInvariantString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,10 +7,10 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
{
|
||||
public string Token => "BinaryString";
|
||||
|
||||
public bool ReadToken(Property prop, XmlNode token)
|
||||
public bool ReadProperty(Property prop, XmlNode token)
|
||||
{
|
||||
// BinaryStrings are encoded in base64
|
||||
string base64 = token.InnerText;
|
||||
string base64 = token.InnerText.Replace("\n", "");
|
||||
prop.Type = PropertyType.String;
|
||||
prop.Value = base64;
|
||||
|
||||
@ -19,5 +19,28 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
|
||||
{
|
||||
byte[] data = prop.GetRawBuffer();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,9 +6,18 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
{
|
||||
public string Token => "bool";
|
||||
|
||||
public bool ReadToken(Property prop, XmlNode token)
|
||||
public bool ReadProperty(Property prop, XmlNode token)
|
||||
{
|
||||
return XmlPropertyTokens.ReadTokenGeneric<bool>(prop, PropertyType.Bool, 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System.Xml;
|
||||
using System;
|
||||
using System.Xml;
|
||||
using RobloxFiles.DataTypes;
|
||||
|
||||
namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
@ -10,16 +11,18 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
// 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 bool ReadToken(Property prop, XmlNode token)
|
||||
public bool ReadProperty(Property prop, XmlNode token)
|
||||
{
|
||||
bool success = XmlPropertyTokens.ReadTokenGeneric<int>(prop, PropertyType.BrickColor, token);
|
||||
bool success = XmlPropertyTokens.ReadPropertyGeneric<int>(prop, PropertyType.BrickColor, token);
|
||||
|
||||
if (success)
|
||||
{
|
||||
int value = (int)prop.Value;
|
||||
|
||||
try
|
||||
{
|
||||
BrickColor brickColor = BrickColor.FromNumber(value);
|
||||
prop.XmlToken = "BrickColor";
|
||||
prop.Value = brickColor;
|
||||
}
|
||||
catch
|
||||
@ -28,8 +31,19 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
|
||||
{
|
||||
BrickColor value = prop.Value as BrickColor;
|
||||
|
||||
XmlElement brickColor = doc.CreateElement("int");
|
||||
brickColor.InnerText = value.Number.ToInvariantString();
|
||||
|
||||
brickColor.SetAttribute("name", prop.Name);
|
||||
brickColor.AppendChild(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
try
|
||||
{
|
||||
var coord = token[key];
|
||||
components[i] = float.Parse(coord.InnerText);
|
||||
components[i] = Formatting.ParseFloat(coord.InnerText);
|
||||
}
|
||||
catch
|
||||
{
|
||||
@ -30,18 +30,35 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
return new CFrame(components);
|
||||
}
|
||||
|
||||
public bool ReadToken(Property property, XmlNode token)
|
||||
public bool ReadProperty(Property prop, XmlNode token)
|
||||
{
|
||||
CFrame result = ReadCFrame(token);
|
||||
bool success = (result != null);
|
||||
|
||||
if (success)
|
||||
{
|
||||
property.Type = PropertyType.CFrame;
|
||||
property.Value = result;
|
||||
prop.Type = PropertyType.CFrame;
|
||||
prop.Value = result;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
|
||||
{
|
||||
CFrame cf = prop.Value as 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
public string Token => "Color3";
|
||||
private string[] Fields = new string[3] { "R", "G", "B" };
|
||||
|
||||
public bool ReadToken(Property prop, XmlNode token)
|
||||
public bool ReadProperty(Property prop, XmlNode token)
|
||||
{
|
||||
bool success = true;
|
||||
float[] fields = new float[Fields.Length];
|
||||
@ -21,7 +21,7 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
try
|
||||
{
|
||||
var coord = token[key];
|
||||
fields[i] = XmlPropertyTokens.ParseFloat(coord.InnerText);
|
||||
fields[i] = Formatting.ParseFloat(coord.InnerText);
|
||||
}
|
||||
catch
|
||||
{
|
||||
@ -43,10 +43,35 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
{
|
||||
// Try falling back to the Color3uint8 technique...
|
||||
var color3uint8 = XmlPropertyTokens.GetHandler<Color3uint8Token>();
|
||||
success = color3uint8.ReadToken(prop, token);
|
||||
success = color3uint8.ReadProperty(prop, token);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
|
||||
{
|
||||
if (prop.Name == "Color3uint8")
|
||||
{
|
||||
var handler = XmlPropertyTokens.GetHandler<Color3uint8Token>();
|
||||
handler.WriteProperty(prop, doc, node);
|
||||
}
|
||||
else
|
||||
{
|
||||
Color3 color = prop.Value as Color3;
|
||||
float[] rgb = new float[3] { color.R, color.G, color.B };
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
string field = Fields[i];
|
||||
float value = rgb[i];
|
||||
|
||||
XmlElement channel = doc.CreateElement(field);
|
||||
channel.InnerText = value.ToInvariantString();
|
||||
|
||||
node.AppendChild(channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,9 +8,9 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
{
|
||||
public string Token => "Color3uint8";
|
||||
|
||||
public bool ReadToken(Property prop, XmlNode token)
|
||||
public bool ReadProperty(Property prop, XmlNode token)
|
||||
{
|
||||
bool success = XmlPropertyTokens.ReadTokenGeneric<uint>(prop, PropertyType.Color3, token);
|
||||
bool success = XmlPropertyTokens.ReadPropertyGeneric<uint>(prop, PropertyType.Color3, token);
|
||||
|
||||
if (success)
|
||||
{
|
||||
@ -25,5 +25,17 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
|
||||
{
|
||||
Color3 color = prop.Value as Color3;
|
||||
|
||||
uint r = (uint)(color.R * 256);
|
||||
uint g = (uint)(color.G * 256);
|
||||
uint b = (uint)(color.B * 256);
|
||||
|
||||
uint rgb = (255u << 24) | (r << 16) | (g << 8) | b;
|
||||
node.InnerText = rgb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
{
|
||||
public string Token => "ColorSequence";
|
||||
|
||||
public bool ReadToken(Property prop, XmlNode token)
|
||||
public bool ReadProperty(Property prop, XmlNode token)
|
||||
{
|
||||
string contents = token.InnerText.Trim();
|
||||
string[] buffer = contents.Split(' ');
|
||||
@ -23,11 +23,11 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
|
||||
for (int i = 0; i < length; i += 5)
|
||||
{
|
||||
float Time = float.Parse(buffer[i]);
|
||||
float Time = Formatting.ParseFloat(buffer[i]);
|
||||
|
||||
float R = float.Parse(buffer[i + 1]);
|
||||
float G = float.Parse(buffer[i + 2]);
|
||||
float B = float.Parse(buffer[i + 3]);
|
||||
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);
|
||||
@ -44,5 +44,10 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
|
||||
{
|
||||
node.InnerText = prop.Value.ToString() + ' ';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
{
|
||||
public string Token => "Content";
|
||||
|
||||
public bool ReadToken(Property prop, XmlNode token)
|
||||
public bool ReadProperty(Property prop, XmlNode token)
|
||||
{
|
||||
string content = token.InnerText;
|
||||
prop.Type = PropertyType.String;
|
||||
@ -28,5 +28,21 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
|
||||
{
|
||||
string content = prop.Value.ToString();
|
||||
string type = "null";
|
||||
|
||||
if (prop.HasRawBuffer)
|
||||
type = "binary";
|
||||
else if (content.Length > 0)
|
||||
type = "url";
|
||||
|
||||
XmlElement contentType = doc.CreateElement(type);
|
||||
contentType.InnerText = content;
|
||||
|
||||
node.AppendChild(contentType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,9 +6,14 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
{
|
||||
public string Token => "double";
|
||||
|
||||
public bool ReadToken(Property prop, XmlNode token)
|
||||
public bool ReadProperty(Property prop, XmlNode token)
|
||||
{
|
||||
return XmlPropertyTokens.ReadTokenGeneric<double>(prop, PropertyType.Double, token);
|
||||
return XmlPropertyTokens.ReadPropertyGeneric<double>(prop, PropertyType.Double, token);
|
||||
}
|
||||
|
||||
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
|
||||
{
|
||||
node.InnerText = prop.Value.ToInvariantString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,9 +6,14 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
{
|
||||
public string Token => "token";
|
||||
|
||||
public bool ReadToken(Property prop, XmlNode token)
|
||||
public bool ReadProperty(Property prop, XmlNode token)
|
||||
{
|
||||
return XmlPropertyTokens.ReadTokenGeneric<uint>(prop, PropertyType.Enum, token);
|
||||
return XmlPropertyTokens.ReadPropertyGeneric<uint>(prop, PropertyType.Enum, token);
|
||||
}
|
||||
|
||||
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
|
||||
{
|
||||
node.InnerText = prop.Value.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,9 +7,9 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
{
|
||||
public string Token => "Faces";
|
||||
|
||||
public bool ReadToken(Property prop, XmlNode token)
|
||||
public bool ReadProperty(Property prop, XmlNode token)
|
||||
{
|
||||
bool success = XmlPropertyTokens.ReadTokenGeneric<uint>(prop, PropertyType.Faces, token);
|
||||
bool success = XmlPropertyTokens.ReadPropertyGeneric<uint>(prop, PropertyType.Faces, token);
|
||||
|
||||
if (success)
|
||||
{
|
||||
@ -27,5 +27,14 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
|
||||
{
|
||||
XmlElement faces = doc.CreateElement("faces");
|
||||
node.AppendChild(faces);
|
||||
|
||||
int value = (int)prop.Value;
|
||||
faces.InnerText = value.ToInvariantString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,19 +6,14 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
{
|
||||
public string Token => "float";
|
||||
|
||||
public bool ReadToken(Property prop, XmlNode token)
|
||||
public bool ReadProperty(Property prop, XmlNode token)
|
||||
{
|
||||
try
|
||||
{
|
||||
float value = XmlPropertyTokens.ParseFloat(token.InnerText);
|
||||
prop.Type = PropertyType.Float;
|
||||
prop.Value = value;
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return XmlPropertyTokens.ReadPropertyGeneric<float>(prop, PropertyType.Float, token);
|
||||
}
|
||||
|
||||
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
|
||||
{
|
||||
node.InnerText = prop.Value.ToInvariantString();
|
||||
}
|
||||
}
|
||||
}
|
@ -6,7 +6,7 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
{
|
||||
public string Token => "int";
|
||||
|
||||
public bool ReadToken(Property prop, XmlNode token)
|
||||
public bool ReadProperty(Property prop, XmlNode token)
|
||||
{
|
||||
// BrickColors are represented by ints, see if
|
||||
// we can infer when they should be a BrickColor.
|
||||
@ -14,12 +14,19 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
if (prop.Name.Contains("Color") || prop.Instance.ClassName.Contains("Color"))
|
||||
{
|
||||
var brickColorToken = XmlPropertyTokens.GetHandler<BrickColorToken>();
|
||||
return brickColorToken.ReadToken(prop, token);
|
||||
return brickColorToken.ReadProperty(prop, token);
|
||||
}
|
||||
else
|
||||
{
|
||||
return XmlPropertyTokens.ReadTokenGeneric<int>(prop, PropertyType.Int, token);
|
||||
return XmlPropertyTokens.ReadPropertyGeneric<int>(prop, PropertyType.Int, token);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
|
||||
{
|
||||
node.InnerText = prop.Value.ToInvariantString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,9 +6,14 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
{
|
||||
public string Token => "int64";
|
||||
|
||||
public bool ReadToken(Property prop, XmlNode token)
|
||||
public bool ReadProperty(Property prop, XmlNode token)
|
||||
{
|
||||
return XmlPropertyTokens.ReadTokenGeneric<long>(prop, PropertyType.Int64, token);
|
||||
return XmlPropertyTokens.ReadPropertyGeneric<long>(prop, PropertyType.Int64, token);
|
||||
}
|
||||
|
||||
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
|
||||
{
|
||||
node.InnerText = prop.Value.ToString();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
using System.Xml;
|
||||
using System;
|
||||
using System.Xml;
|
||||
using RobloxFiles.DataTypes;
|
||||
|
||||
namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
@ -7,7 +8,7 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
{
|
||||
public string Token => "NumberRange";
|
||||
|
||||
public bool ReadToken(Property prop, XmlNode token)
|
||||
public bool ReadProperty(Property prop, XmlNode token)
|
||||
{
|
||||
string contents = token.InnerText.Trim();
|
||||
string[] buffer = contents.Split(' ');
|
||||
@ -17,8 +18,8 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
{
|
||||
try
|
||||
{
|
||||
float min = float.Parse(buffer[0]);
|
||||
float max = float.Parse(buffer[1]);
|
||||
float min = Formatting.ParseFloat(buffer[0]);
|
||||
float max = Formatting.ParseFloat(buffer[1]);
|
||||
|
||||
prop.Type = PropertyType.NumberRange;
|
||||
prop.Value = new NumberRange(min, max);
|
||||
@ -31,5 +32,10 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
|
||||
{
|
||||
node.InnerText = prop.Value.ToString() + ' ';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
{
|
||||
public string Token => "NumberSequence";
|
||||
|
||||
public bool ReadToken(Property prop, XmlNode token)
|
||||
public bool ReadProperty(Property prop, XmlNode token)
|
||||
{
|
||||
string contents = token.InnerText.Trim();
|
||||
string[] buffer = contents.Split(' ');
|
||||
@ -23,9 +23,9 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
|
||||
for (int i = 0; i < length; i += 3)
|
||||
{
|
||||
float Time = float.Parse(buffer[ i ]);
|
||||
float Value = float.Parse(buffer[i + 1]);
|
||||
float Envelope = float.Parse(buffer[i + 2]);
|
||||
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);
|
||||
}
|
||||
@ -41,5 +41,10 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
|
||||
{
|
||||
node.InnerText = prop.Value.ToString() + ' ';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml;
|
||||
using RobloxFiles.DataTypes;
|
||||
|
||||
@ -17,10 +18,10 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
});
|
||||
}
|
||||
|
||||
public bool ReadToken(Property prop, XmlNode token)
|
||||
public bool ReadProperty(Property prop, XmlNode token)
|
||||
{
|
||||
var readBool = createReader(bool.Parse, token);
|
||||
var readFloat = createReader(XmlPropertyTokens.ParseFloat, token);
|
||||
var readFloat = createReader(Formatting.ParseFloat, token);
|
||||
|
||||
try
|
||||
{
|
||||
@ -46,5 +47,40 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
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.Value as 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System.Xml;
|
||||
using System;
|
||||
using System.Xml;
|
||||
using RobloxFiles.DataTypes;
|
||||
|
||||
namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
@ -8,7 +9,7 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
public string Token => "Ray";
|
||||
private static string[] Fields = new string[2] { "origin", "direction" };
|
||||
|
||||
public bool ReadToken(Property prop, XmlNode token)
|
||||
public bool ReadProperty(Property prop, XmlNode token)
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -35,5 +36,18 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
|
||||
{
|
||||
Ray ray = prop.Value as Ray;
|
||||
|
||||
XmlElement origin = doc.CreateElement("origin");
|
||||
Vector3Token.WriteVector3(doc, origin, ray.Origin);
|
||||
node.AppendChild(origin);
|
||||
|
||||
XmlElement direction = doc.CreateElement("direction");
|
||||
Vector3Token.WriteVector3(doc, direction, ray.Direction);
|
||||
node.AppendChild(direction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System.Xml;
|
||||
using System;
|
||||
using System.Xml;
|
||||
using RobloxFiles.DataTypes;
|
||||
|
||||
namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
@ -8,7 +9,7 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
public string Token => "Rect2D";
|
||||
private static string[] Fields = new string[2] { "min", "max" };
|
||||
|
||||
public bool ReadToken(Property prop, XmlNode token)
|
||||
public bool ReadProperty(Property prop, XmlNode token)
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -35,5 +36,18 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
|
||||
{
|
||||
Rect rect = prop.Value as 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
{
|
||||
public string Token => "Ref";
|
||||
|
||||
public bool ReadToken(Property prop, XmlNode token)
|
||||
public bool ReadProperty(Property prop, XmlNode token)
|
||||
{
|
||||
string refId = token.InnerText;
|
||||
prop.Type = PropertyType.Ref;
|
||||
@ -14,5 +14,18 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
|
||||
{
|
||||
string result = "null";
|
||||
|
||||
if (prop.Value != null && prop.Value.ToString() != "null")
|
||||
{
|
||||
Instance inst = prop.Value as Instance;
|
||||
result = inst.XmlReferent;
|
||||
}
|
||||
|
||||
node.InnerText = result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System.Text;
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
|
||||
namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
@ -7,7 +8,7 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
{
|
||||
public string Token => "SharedString";
|
||||
|
||||
public bool ReadToken(Property prop, XmlNode token)
|
||||
public bool ReadProperty(Property prop, XmlNode token)
|
||||
{
|
||||
string contents = token.InnerText;
|
||||
prop.Type = PropertyType.SharedString;
|
||||
@ -15,5 +16,11 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
|
||||
{
|
||||
var BinaryStringToken = XmlPropertyTokens.GetHandler<BinaryStringToken>();
|
||||
BinaryStringToken.WriteProperty(prop, doc, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System.Text;
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
|
||||
namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
@ -7,16 +8,28 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
{
|
||||
public string Token => "string; ProtectedString";
|
||||
|
||||
public bool ReadToken(Property prop, XmlNode token)
|
||||
public bool ReadProperty(Property prop, XmlNode token)
|
||||
{
|
||||
string contents = token.InnerText;
|
||||
prop.Type = PropertyType.String;
|
||||
prop.Value = contents;
|
||||
|
||||
byte[] buffer = Encoding.UTF8.GetBytes(contents);
|
||||
prop.SetRawBuffer(buffer);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System.Xml;
|
||||
using System;
|
||||
using System.Xml;
|
||||
using RobloxFiles.DataTypes;
|
||||
|
||||
namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
@ -12,7 +13,7 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
try
|
||||
{
|
||||
XmlElement scaleToken = token[prefix + 'S'];
|
||||
float scale = XmlPropertyTokens.ParseFloat(scaleToken.InnerText);
|
||||
float scale = Formatting.ParseFloat(scaleToken.InnerText);
|
||||
|
||||
XmlElement offsetToken = token[prefix + 'O'];
|
||||
int offset = int.Parse(offsetToken.InnerText);
|
||||
@ -25,7 +26,18 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
}
|
||||
}
|
||||
|
||||
public bool ReadToken(Property property, XmlNode token)
|
||||
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 bool ReadProperty(Property property, XmlNode token)
|
||||
{
|
||||
UDim result = ReadUDim(token);
|
||||
bool success = (result != null);
|
||||
@ -38,5 +50,11 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
|
||||
{
|
||||
UDim value = prop.Value as UDim;
|
||||
WriteUDim(doc, node, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System.Xml;
|
||||
using System;
|
||||
using System.Xml;
|
||||
using RobloxFiles.DataTypes;
|
||||
|
||||
namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
@ -7,7 +8,7 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
{
|
||||
public string Token => "UDim2";
|
||||
|
||||
public bool ReadToken(Property property, XmlNode token)
|
||||
public bool ReadProperty(Property property, XmlNode token)
|
||||
{
|
||||
UDim xUDim = UDimToken.ReadUDim(token, "X");
|
||||
UDim yUDim = UDimToken.ReadUDim(token, "Y");
|
||||
@ -22,5 +23,16 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
|
||||
{
|
||||
UDim2 value = prop.Value as UDim2;
|
||||
|
||||
UDim xUDim = value.X;
|
||||
UDimToken.WriteUDim(doc, node, xUDim, "X");
|
||||
|
||||
UDim yUDim = value.Y;
|
||||
UDimToken.WriteUDim(doc, node, yUDim, "Y");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
{
|
||||
var coord = token[key];
|
||||
string text = coord.InnerText;
|
||||
xy[i] = XmlPropertyTokens.ParseFloat(text);
|
||||
xy[i] = Formatting.ParseFloat(text);
|
||||
}
|
||||
catch
|
||||
{
|
||||
@ -31,7 +31,18 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
return new Vector2(xy);
|
||||
}
|
||||
|
||||
public bool ReadToken(Property property, XmlNode token)
|
||||
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 bool ReadProperty(Property property, XmlNode token)
|
||||
{
|
||||
Vector2 result = ReadVector2(token);
|
||||
bool success = (result != null);
|
||||
@ -44,5 +55,11 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
|
||||
{
|
||||
Vector2 value = prop.Value as Vector2;
|
||||
WriteVector2(doc, node, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System.Xml;
|
||||
using System;
|
||||
using System.Xml;
|
||||
using RobloxFiles.DataTypes;
|
||||
|
||||
namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
@ -19,7 +20,7 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
try
|
||||
{
|
||||
var coord = token[key];
|
||||
xyz[i] = XmlPropertyTokens.ParseFloat(coord.InnerText);
|
||||
xyz[i] = Formatting.ParseFloat(coord.InnerText);
|
||||
}
|
||||
catch
|
||||
{
|
||||
@ -30,7 +31,22 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
return new Vector3(xyz);
|
||||
}
|
||||
|
||||
public bool ReadToken(Property property, XmlNode token)
|
||||
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 bool ReadProperty(Property property, XmlNode token)
|
||||
{
|
||||
Vector3 result = ReadVector3(token);
|
||||
bool success = (result != null);
|
||||
@ -43,5 +59,11 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
|
||||
{
|
||||
Vector3 value = prop.Value as Vector3;
|
||||
WriteVector3(doc, node, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
public string Token => "Vector3int16";
|
||||
private static string[] Coords = new string[3] { "X", "Y", "Z" };
|
||||
|
||||
public bool ReadToken(Property property, XmlNode token)
|
||||
public bool ReadProperty(Property property, XmlNode token)
|
||||
{
|
||||
short[] xyz = new short[3];
|
||||
|
||||
@ -36,5 +36,22 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
|
||||
{
|
||||
Vector3int16 value = prop.Value as 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -62,8 +62,9 @@ namespace RobloxFiles.XmlFormat
|
||||
Property prop = new Property();
|
||||
prop.Name = propName.InnerText;
|
||||
prop.Instance = instance;
|
||||
prop.XmlToken = propType;
|
||||
|
||||
if (!tokenHandler.ReadToken(prop, propNode))
|
||||
if (!tokenHandler.ReadProperty(prop, propNode))
|
||||
Console.WriteLine("Could not read property: " + prop.GetFullName() + '!');
|
||||
|
||||
instance.AddProperty(ref prop);
|
||||
@ -75,7 +76,7 @@ namespace RobloxFiles.XmlFormat
|
||||
}
|
||||
}
|
||||
|
||||
public static Instance ReadInstance(XmlNode instNode, XmlRobloxFile file = null)
|
||||
public static Instance ReadInstance(XmlNode instNode, XmlRobloxFile file)
|
||||
{
|
||||
var error = createErrorHandler("ReadInstance");
|
||||
|
||||
|
194
XmlFormat/XmlDataWriter.cs
Normal file
194
XmlFormat/XmlDataWriter.cs
Normal file
@ -0,0 +1,194 @@
|
||||
using System;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
|
||||
using RobloxFiles.XmlFormat.PropertyTokens;
|
||||
|
||||
namespace RobloxFiles.XmlFormat
|
||||
{
|
||||
public static class XmlDataWriter
|
||||
{
|
||||
public static XmlWriterSettings Settings = new XmlWriterSettings()
|
||||
{
|
||||
Indent = true,
|
||||
IndentChars = "\t",
|
||||
NewLineChars = "\r\n",
|
||||
Encoding = Encoding.UTF8,
|
||||
OmitXmlDeclaration = true,
|
||||
NamespaceHandling = NamespaceHandling.Default
|
||||
};
|
||||
|
||||
private static string CreateReferent()
|
||||
{
|
||||
Guid referentGuid = Guid.NewGuid();
|
||||
|
||||
string referent = "RBX" + referentGuid
|
||||
.ToString()
|
||||
.ToUpper();
|
||||
|
||||
return referent.Replace("-", "");
|
||||
}
|
||||
|
||||
private static string GetEnumName<T>(T item) where T : struct
|
||||
{
|
||||
return Enum.GetName(typeof(T), item);
|
||||
}
|
||||
|
||||
internal static void RecordInstances(XmlRobloxFile file, Instance inst)
|
||||
{
|
||||
foreach (Instance child in inst.GetChildren())
|
||||
RecordInstances(file, child);
|
||||
|
||||
string referent = CreateReferent();
|
||||
file.Instances.Add(referent, inst);
|
||||
inst.XmlReferent = referent;
|
||||
}
|
||||
|
||||
public static XmlElement CreateRobloxElement(XmlDocument doc)
|
||||
{
|
||||
XmlElement roblox = doc.CreateElement("roblox");
|
||||
doc.AppendChild(roblox);
|
||||
|
||||
roblox.SetAttribute("xmlns:xmime", "http://www.w3.org/2005/05/xmlmime");
|
||||
roblox.SetAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
|
||||
roblox.SetAttribute("xsi:noNamespaceSchemaLocation", "http://www.roblox.com/roblox.xsd");
|
||||
roblox.SetAttribute("version", "4");
|
||||
|
||||
XmlElement externalNull = doc.CreateElement("External");
|
||||
roblox.AppendChild(externalNull);
|
||||
externalNull.InnerText = "null";
|
||||
|
||||
XmlElement externalNil = doc.CreateElement("External");
|
||||
roblox.AppendChild(externalNil);
|
||||
externalNil.InnerText = "nil";
|
||||
|
||||
return roblox;
|
||||
}
|
||||
|
||||
public static XmlNode WriteProperty(Property prop, XmlDocument doc, XmlRobloxFile file)
|
||||
{
|
||||
string propType = prop.XmlToken;
|
||||
|
||||
if (prop.XmlToken.Length == 0)
|
||||
{
|
||||
propType = GetEnumName(prop.Type);
|
||||
|
||||
switch (prop.Type)
|
||||
{
|
||||
case PropertyType.CFrame:
|
||||
case PropertyType.Quaternion:
|
||||
propType = "CoordinateFrame";
|
||||
break;
|
||||
case PropertyType.Enum:
|
||||
propType = "token";
|
||||
break;
|
||||
case PropertyType.Rect:
|
||||
propType = "Rect2D";
|
||||
break;
|
||||
case PropertyType.Int:
|
||||
case PropertyType.Bool:
|
||||
case PropertyType.Float:
|
||||
case PropertyType.Int64:
|
||||
case PropertyType.Double:
|
||||
propType = propType.ToLower();
|
||||
break;
|
||||
case PropertyType.String:
|
||||
propType = (prop.HasRawBuffer ? "BinaryString" : "string");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
IXmlPropertyToken handler = XmlPropertyTokens.GetHandler(propType);
|
||||
|
||||
if (handler == null)
|
||||
{
|
||||
Console.WriteLine("XmlDataWriter.WriteProperty: No token handler found for property type: {0}", propType);
|
||||
return null;
|
||||
}
|
||||
|
||||
XmlElement propElement = doc.CreateElement(propType);
|
||||
propElement.SetAttribute("name", prop.Name);
|
||||
|
||||
XmlNode propNode = propElement;
|
||||
handler.WriteProperty(prop, doc, propNode);
|
||||
|
||||
if (propNode.ParentNode != null)
|
||||
{
|
||||
XmlNode newNode = propNode.ParentNode;
|
||||
newNode.RemoveChild(propNode);
|
||||
propNode = newNode;
|
||||
}
|
||||
|
||||
if (prop.Type == PropertyType.SharedString)
|
||||
{
|
||||
string data = prop.Value.ToString();
|
||||
byte[] buffer = Convert.FromBase64String(data);
|
||||
|
||||
using (MD5 md5 = MD5.Create())
|
||||
{
|
||||
byte[] hash = md5.ComputeHash(buffer);
|
||||
string key = Convert.ToBase64String(hash);
|
||||
|
||||
if (!file.SharedStrings.ContainsKey(key))
|
||||
file.SharedStrings.Add(key, data);
|
||||
|
||||
propNode.InnerText = key;
|
||||
}
|
||||
}
|
||||
|
||||
return propNode;
|
||||
}
|
||||
|
||||
public static XmlNode WriteInstance(Instance instance, XmlDocument doc, XmlRobloxFile file)
|
||||
{
|
||||
XmlElement instNode = doc.CreateElement("Item");
|
||||
instNode.SetAttribute("class", instance.ClassName);
|
||||
instNode.SetAttribute("referent", instance.XmlReferent);
|
||||
|
||||
XmlElement propsNode = doc.CreateElement("Properties");
|
||||
instNode.AppendChild(propsNode);
|
||||
|
||||
var props = instance.Properties;
|
||||
|
||||
foreach (string propName in props.Keys)
|
||||
{
|
||||
Property prop = props[propName];
|
||||
XmlNode propNode = WriteProperty(prop, doc, file);
|
||||
propsNode.AppendChild(propNode);
|
||||
}
|
||||
|
||||
foreach (Instance child in instance.GetChildren())
|
||||
{
|
||||
XmlNode childNode = WriteInstance(child, doc, file);
|
||||
instNode.AppendChild(childNode);
|
||||
}
|
||||
|
||||
return instNode;
|
||||
}
|
||||
|
||||
public static XmlNode WriteSharedStrings(XmlDocument doc, XmlRobloxFile file)
|
||||
{
|
||||
XmlElement sharedStrings = doc.CreateElement("SharedStrings");
|
||||
|
||||
var binaryWriter = XmlPropertyTokens.GetHandler<BinaryStringToken>();
|
||||
var bufferProp = new Property("SharedString", PropertyType.String);
|
||||
|
||||
foreach (string md5 in file.SharedStrings.Keys)
|
||||
{
|
||||
XmlElement sharedString = doc.CreateElement("SharedString");
|
||||
sharedString.SetAttribute("md5", md5);
|
||||
|
||||
string data = file.SharedStrings[md5];
|
||||
byte[] buffer = Convert.FromBase64String(data);
|
||||
|
||||
bufferProp.SetRawBuffer(buffer);
|
||||
binaryWriter.WriteProperty(bufferProp, doc, sharedString);
|
||||
|
||||
sharedStrings.AppendChild(sharedString);
|
||||
}
|
||||
|
||||
return sharedStrings;
|
||||
}
|
||||
}
|
||||
}
|
@ -4,6 +4,8 @@ using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
|
||||
using RobloxFiles;
|
||||
|
||||
namespace RobloxFiles.XmlFormat
|
||||
{
|
||||
public static class XmlPropertyTokens
|
||||
@ -35,17 +37,30 @@ namespace RobloxFiles.XmlFormat
|
||||
Handlers = tokenHandlers;
|
||||
}
|
||||
|
||||
public static bool ReadTokenGeneric<T>(Property prop, PropertyType propType, XmlNode token) where T : struct
|
||||
public static bool ReadPropertyGeneric<T>(Property prop, PropertyType propType, XmlNode token) where T : struct
|
||||
{
|
||||
try
|
||||
{
|
||||
Type resultType = typeof(T);
|
||||
TypeConverter converter = TypeDescriptor.GetConverter(resultType);
|
||||
string value = token.InnerText;
|
||||
|
||||
if (typeof(T) == typeof(int))
|
||||
prop.Value = Formatting.ParseInt(value);
|
||||
else if (typeof(T) == typeof(float))
|
||||
prop.Value = Formatting.ParseFloat(value);
|
||||
else if (typeof(T) == typeof(double))
|
||||
prop.Value = Formatting.ParseDouble(value);
|
||||
|
||||
if (prop.Value == null)
|
||||
{
|
||||
Type resultType = typeof(T);
|
||||
TypeConverter converter = TypeDescriptor.GetConverter(resultType);
|
||||
|
||||
object result = converter.ConvertFromString(token.InnerText);
|
||||
prop.Value = result;
|
||||
}
|
||||
|
||||
object result = converter.ConvertFromString(token.InnerText);
|
||||
prop.Type = propType;
|
||||
prop.Value = result;
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
@ -72,21 +87,5 @@ namespace RobloxFiles.XmlFormat
|
||||
|
||||
return (T)result;
|
||||
}
|
||||
|
||||
public static float ParseFloat(string value)
|
||||
{
|
||||
float result;
|
||||
|
||||
if (value == "INF")
|
||||
result = float.PositiveInfinity;
|
||||
else if (value == "-INF")
|
||||
result = float.NegativeInfinity;
|
||||
else if (value == "NAN")
|
||||
result = float.NaN;
|
||||
else
|
||||
result = float.Parse(value);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
@ -15,11 +16,15 @@ namespace RobloxFiles.XmlFormat
|
||||
|
||||
// Runtime Specific
|
||||
public readonly XmlDocument Root = new XmlDocument();
|
||||
|
||||
public Dictionary<string, Instance> Instances = new Dictionary<string, Instance>();
|
||||
public Dictionary<string, string> SharedStrings = new Dictionary<string, string>();
|
||||
|
||||
public void ReadFile(byte[] buffer)
|
||||
{
|
||||
Instances.Clear();
|
||||
SharedStrings.Clear();
|
||||
|
||||
try
|
||||
{
|
||||
string xml = Encoding.UTF8.GetString(buffer);
|
||||
@ -31,7 +36,7 @@ namespace RobloxFiles.XmlFormat
|
||||
}
|
||||
|
||||
XmlNode roblox = Root.FirstChild;
|
||||
|
||||
|
||||
if (roblox != null && roblox.Name == "roblox")
|
||||
{
|
||||
// Verify the version we are using.
|
||||
@ -108,5 +113,52 @@ namespace RobloxFiles.XmlFormat
|
||||
throw new Exception("XmlRobloxFile: No 'roblox' tag found!");
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteFile(Stream stream)
|
||||
{
|
||||
XmlDocument doc = new XmlDocument();
|
||||
XmlElement roblox = XmlDataWriter.CreateRobloxElement(doc);
|
||||
|
||||
Instances.Clear();
|
||||
SharedStrings.Clear();
|
||||
|
||||
Instance[] topLevelItems = Contents.GetChildren();
|
||||
|
||||
// First, record all of the instances.
|
||||
foreach (Instance inst in topLevelItems)
|
||||
XmlDataWriter.RecordInstances(this, inst);
|
||||
|
||||
// Now append them into the document.
|
||||
foreach (Instance inst in Contents.GetChildren())
|
||||
{
|
||||
XmlNode instNode = XmlDataWriter.WriteInstance(inst, doc, this);
|
||||
roblox.AppendChild(instNode);
|
||||
}
|
||||
|
||||
// Append the shared strings.
|
||||
if (SharedStrings.Count > 0)
|
||||
{
|
||||
XmlNode sharedStrings = XmlDataWriter.WriteSharedStrings(doc, this);
|
||||
roblox.AppendChild(sharedStrings);
|
||||
}
|
||||
|
||||
// Write the XML file.
|
||||
using (StringWriter buffer = new StringWriter())
|
||||
{
|
||||
XmlWriterSettings settings = XmlDataWriter.Settings;
|
||||
|
||||
using (XmlWriter xmlWriter = XmlWriter.Create(buffer, settings))
|
||||
doc.WriteContentTo(xmlWriter);
|
||||
|
||||
string result = buffer.ToString()
|
||||
.Replace("<![CDATA[]]>", "");
|
||||
|
||||
using (BinaryWriter writer = new BinaryWriter(stream))
|
||||
{
|
||||
byte[] data = Encoding.UTF8.GetBytes(result);
|
||||
writer.Write(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user