2019-01-30 06:36:56 +00:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
2019-05-17 12:08:06 +00:00
|
|
|
|
using System.IO;
|
2019-01-30 06:36:56 +00:00
|
|
|
|
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using System.Xml;
|
|
|
|
|
|
2019-06-30 22:01:19 +00:00
|
|
|
|
using RobloxFiles.DataTypes;
|
|
|
|
|
using RobloxFiles.XmlFormat;
|
|
|
|
|
|
|
|
|
|
namespace RobloxFiles
|
2019-01-30 06:36:56 +00:00
|
|
|
|
{
|
2019-05-19 04:44:51 +00:00
|
|
|
|
public class XmlRobloxFile : RobloxFile
|
2019-01-30 06:36:56 +00:00
|
|
|
|
{
|
2019-06-30 22:01:19 +00:00
|
|
|
|
public readonly XmlDocument XmlDocument = new XmlDocument();
|
2019-05-17 12:08:06 +00:00
|
|
|
|
|
2019-05-19 04:44:51 +00:00
|
|
|
|
internal Dictionary<string, Instance> Instances = new Dictionary<string, Instance>();
|
2019-06-30 22:01:19 +00:00
|
|
|
|
internal HashSet<string> SharedStrings = new HashSet<string>();
|
|
|
|
|
|
|
|
|
|
private Dictionary<string, string> RawMetadata = new Dictionary<string, string>();
|
|
|
|
|
public Dictionary<string, string> Metadata => RawMetadata;
|
2019-05-19 04:44:51 +00:00
|
|
|
|
|
2019-06-30 22:01:19 +00:00
|
|
|
|
public XmlRobloxFile()
|
2019-05-19 04:44:51 +00:00
|
|
|
|
{
|
|
|
|
|
Name = "XmlRobloxFile";
|
|
|
|
|
ParentLocked = true;
|
2019-06-30 22:01:19 +00:00
|
|
|
|
Referent = "null";
|
2019-05-19 04:44:51 +00:00
|
|
|
|
}
|
2019-01-30 06:36:56 +00:00
|
|
|
|
|
2019-05-19 04:44:51 +00:00
|
|
|
|
protected override void ReadFile(byte[] buffer)
|
2019-01-30 06:36:56 +00:00
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
string xml = Encoding.UTF8.GetString(buffer);
|
2020-08-17 05:33:59 +00:00
|
|
|
|
var settings = new XmlReaderSettings() { XmlResolver = null };
|
|
|
|
|
|
|
|
|
|
using (StringReader reader = new StringReader(xml))
|
|
|
|
|
{
|
|
|
|
|
XmlReader xmlReader = XmlReader.Create(reader, settings);
|
|
|
|
|
XmlDocument.Load(xmlReader);
|
|
|
|
|
xmlReader.Dispose();
|
|
|
|
|
}
|
2019-01-30 06:36:56 +00:00
|
|
|
|
}
|
2019-06-08 03:43:28 +00:00
|
|
|
|
catch
|
2019-01-30 06:36:56 +00:00
|
|
|
|
{
|
2019-02-01 18:40:39 +00:00
|
|
|
|
throw new Exception("XmlRobloxFile: Could not read provided buffer as XML!");
|
2019-01-30 06:36:56 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-06-30 22:01:19 +00:00
|
|
|
|
XmlNode roblox = XmlDocument.FirstChild;
|
2019-05-17 12:08:06 +00:00
|
|
|
|
|
2019-01-30 06:36:56 +00:00
|
|
|
|
if (roblox != null && roblox.Name == "roblox")
|
|
|
|
|
{
|
|
|
|
|
// Verify the version we are using.
|
|
|
|
|
XmlNode version = roblox.Attributes.GetNamedItem("version");
|
2020-08-17 05:33:59 +00:00
|
|
|
|
|
|
|
|
|
if (version == null || !int.TryParse(version.Value, out int schemaVersion))
|
2019-01-30 06:36:56 +00:00
|
|
|
|
throw new Exception("XmlRobloxFile: No version number defined!");
|
|
|
|
|
else if (schemaVersion < 4)
|
|
|
|
|
throw new Exception("XmlRobloxFile: Provided version must be at least 4!");
|
|
|
|
|
|
|
|
|
|
// Process the instances.
|
|
|
|
|
foreach (XmlNode child in roblox.ChildNodes)
|
|
|
|
|
{
|
|
|
|
|
if (child.Name == "Item")
|
|
|
|
|
{
|
2019-05-19 04:44:51 +00:00
|
|
|
|
Instance item = XmlRobloxFileReader.ReadInstance(child, this);
|
|
|
|
|
item.Parent = this;
|
2019-01-30 06:36:56 +00:00
|
|
|
|
}
|
2019-05-17 06:14:04 +00:00
|
|
|
|
else if (child.Name == "SharedStrings")
|
|
|
|
|
{
|
2019-05-19 04:44:51 +00:00
|
|
|
|
XmlRobloxFileReader.ReadSharedStrings(child, this);
|
2019-05-17 06:14:04 +00:00
|
|
|
|
}
|
2019-06-30 22:01:19 +00:00
|
|
|
|
else if (child.Name == "Meta")
|
|
|
|
|
{
|
|
|
|
|
XmlRobloxFileReader.ReadMetadata(child, this);
|
|
|
|
|
}
|
2019-01-30 06:36:56 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-05-17 06:14:04 +00:00
|
|
|
|
// Query the properties.
|
2019-05-19 04:44:51 +00:00
|
|
|
|
var allProps = Instances.Values
|
2019-01-30 06:36:56 +00:00
|
|
|
|
.SelectMany(inst => inst.Properties)
|
2019-05-17 06:14:04 +00:00
|
|
|
|
.Select(pair => pair.Value);
|
|
|
|
|
|
|
|
|
|
// Resolve referent properties.
|
2019-05-19 04:44:51 +00:00
|
|
|
|
var refProps = allProps.Where(prop => prop.Type == PropertyType.Ref);
|
2019-05-17 06:14:04 +00:00
|
|
|
|
|
2019-01-30 06:36:56 +00:00
|
|
|
|
foreach (Property refProp in refProps)
|
|
|
|
|
{
|
2019-06-30 22:01:19 +00:00
|
|
|
|
string refId = refProp.XmlToken;
|
|
|
|
|
refProp.XmlToken = "Ref";
|
2019-05-17 06:14:04 +00:00
|
|
|
|
|
2019-01-30 06:36:56 +00:00
|
|
|
|
if (Instances.ContainsKey(refId))
|
|
|
|
|
{
|
|
|
|
|
Instance refInst = Instances[refId];
|
|
|
|
|
refProp.Value = refInst;
|
|
|
|
|
}
|
|
|
|
|
else if (refId != "null")
|
|
|
|
|
{
|
2019-05-17 06:14:04 +00:00
|
|
|
|
string name = refProp.GetFullName();
|
|
|
|
|
Console.WriteLine("XmlRobloxFile: Could not resolve reference for {0}", name);
|
2019-05-25 23:45:54 +00:00
|
|
|
|
refProp.Value = null;
|
2019-05-17 06:14:04 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-30 22:01:19 +00:00
|
|
|
|
// Record shared strings.
|
2019-05-19 04:44:51 +00:00
|
|
|
|
var sharedProps = allProps.Where(prop => prop.Type == PropertyType.SharedString);
|
2019-05-17 06:14:04 +00:00
|
|
|
|
|
|
|
|
|
foreach (Property sharedProp in sharedProps)
|
|
|
|
|
{
|
2019-06-30 22:01:19 +00:00
|
|
|
|
SharedString shared = sharedProp.CastValue<SharedString>();
|
2020-07-13 01:19:30 +00:00
|
|
|
|
SharedStrings.Add(shared.Key);
|
2019-01-30 06:36:56 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2019-06-08 03:43:28 +00:00
|
|
|
|
throw new Exception("XmlRobloxFile: No 'roblox' element found!");
|
2019-01-30 06:36:56 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2019-05-17 12:08:06 +00:00
|
|
|
|
|
2019-05-19 04:44:51 +00:00
|
|
|
|
public override void Save(Stream stream)
|
2019-05-17 12:08:06 +00:00
|
|
|
|
{
|
|
|
|
|
XmlDocument doc = new XmlDocument();
|
2019-05-19 04:44:51 +00:00
|
|
|
|
|
|
|
|
|
XmlElement roblox = doc.CreateElement("roblox");
|
|
|
|
|
roblox.SetAttribute("version", "4");
|
|
|
|
|
doc.AppendChild(roblox);
|
2019-05-17 12:08:06 +00:00
|
|
|
|
|
|
|
|
|
Instances.Clear();
|
|
|
|
|
SharedStrings.Clear();
|
|
|
|
|
|
2019-06-30 22:01:19 +00:00
|
|
|
|
// First, append the metadata
|
|
|
|
|
foreach (string key in Metadata.Keys)
|
|
|
|
|
{
|
|
|
|
|
string value = Metadata[key];
|
|
|
|
|
|
|
|
|
|
XmlElement meta = doc.CreateElement("Meta");
|
|
|
|
|
meta.SetAttribute("name", key);
|
|
|
|
|
meta.InnerText = value;
|
|
|
|
|
|
|
|
|
|
roblox.AppendChild(meta);
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-19 04:44:51 +00:00
|
|
|
|
Instance[] children = GetChildren();
|
2019-05-17 12:08:06 +00:00
|
|
|
|
|
2019-06-30 22:01:19 +00:00
|
|
|
|
// Record all of the instances.
|
2019-05-19 04:44:51 +00:00
|
|
|
|
foreach (Instance inst in children)
|
|
|
|
|
XmlRobloxFileWriter.RecordInstances(this, inst);
|
2019-05-17 12:08:06 +00:00
|
|
|
|
|
|
|
|
|
// Now append them into the document.
|
2019-05-19 04:44:51 +00:00
|
|
|
|
foreach (Instance inst in children)
|
2019-05-17 12:08:06 +00:00
|
|
|
|
{
|
2019-06-30 22:01:19 +00:00
|
|
|
|
if (inst.Archivable)
|
|
|
|
|
{
|
|
|
|
|
XmlNode instNode = XmlRobloxFileWriter.WriteInstance(inst, doc, this);
|
|
|
|
|
roblox.AppendChild(instNode);
|
|
|
|
|
}
|
2019-05-17 12:08:06 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Append the shared strings.
|
|
|
|
|
if (SharedStrings.Count > 0)
|
|
|
|
|
{
|
2019-05-19 04:44:51 +00:00
|
|
|
|
XmlNode sharedStrings = XmlRobloxFileWriter.WriteSharedStrings(doc, this);
|
2019-05-17 12:08:06 +00:00
|
|
|
|
roblox.AppendChild(sharedStrings);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Write the XML file.
|
|
|
|
|
using (StringWriter buffer = new StringWriter())
|
|
|
|
|
{
|
2019-05-19 04:44:51 +00:00
|
|
|
|
XmlWriterSettings settings = XmlRobloxFileWriter.Settings;
|
2019-05-17 12:08:06 +00:00
|
|
|
|
|
|
|
|
|
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);
|
2019-05-25 23:45:54 +00:00
|
|
|
|
stream.SetLength(0);
|
2019-05-17 12:08:06 +00:00
|
|
|
|
writer.Write(data);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-01-30 06:36:56 +00:00
|
|
|
|
}
|
|
|
|
|
}
|