Brought spec up to date, improvements to stability

This commit is contained in:
CloneTrooper1019
2020-07-12 20:19:30 -05:00
parent 7359b6efb7
commit 57fd3f8a25
29 changed files with 551 additions and 245 deletions

View File

@ -11,7 +11,7 @@ namespace RobloxFiles.XmlFormat
{
var errorHandler = new Func<string, Exception>((message) =>
{
string contents = $"XmlDataReader.{label}: {message}";
string contents = $"XmlRobloxFileReader.{label}: {message}";
return new Exception(contents);
});
@ -29,19 +29,25 @@ namespace RobloxFiles.XmlFormat
{
if (sharedString.Name == "SharedString")
{
XmlNode md5Node = sharedString.Attributes.GetNamedItem("md5");
XmlNode hashNode = sharedString.Attributes.GetNamedItem("md5");
if (md5Node == null)
if (hashNode == null)
throw error("Got a SharedString without an 'md5' attribute!");
string key = md5Node.InnerText;
string key = hashNode.InnerText;
string value = sharedString.InnerText.Replace("\n", "");
byte[] buffer = Convert.FromBase64String(value);
SharedString record = SharedString.FromBase64(value);
byte[] hash = Convert.FromBase64String(key);
var record = SharedString.FromBase64(value);
if (record.MD5_Key != key)
throw error("The provided md5 hash did not match with the md5 hash computed for the value!");
if (hash.Length != 16)
throw error($"SharedString base64 key '{key}' must decode to byte[16]!");
if (key != record.Key)
{
SharedString.Register(key, record.SharedValue);
record.Key = key;
}
file.SharedStrings.Add(key);
}

View File

@ -1,4 +1,5 @@
using System;
using System.Linq;
using System.Text;
using System.Xml;
@ -47,6 +48,9 @@ namespace RobloxFiles.XmlFormat
public static XmlNode WriteProperty(Property prop, XmlDocument doc, XmlRobloxFile file)
{
if (prop.Name == "Archivable")
return null;
string propType = prop.XmlToken;
if (propType == null)
@ -78,6 +82,7 @@ namespace RobloxFiles.XmlFormat
case PropertyType.String:
propType = (prop.HasRawBuffer ? "BinaryString" : "string");
break;
default: break;
}
}
@ -89,6 +94,19 @@ namespace RobloxFiles.XmlFormat
return null;
}
if (prop.Type == PropertyType.SharedString)
{
SharedString value = prop.CastValue<SharedString>();
if (value.ComputedKey == null)
{
var newShared = SharedString.FromBuffer(value.SharedValue);
value.Key = newShared.ComputedKey;
}
file.SharedStrings.Add(value.Key);
}
XmlElement propElement = doc.CreateElement(propType);
propElement.SetAttribute("name", prop.Name);
@ -102,12 +120,6 @@ namespace RobloxFiles.XmlFormat
propNode = newNode;
}
if (prop.Type == PropertyType.SharedString)
{
SharedString value = prop.CastValue<SharedString>();
file.SharedStrings.Add(value.MD5_Key);
}
return propNode;
}
@ -124,11 +136,19 @@ namespace RobloxFiles.XmlFormat
instNode.AppendChild(propsNode);
var props = instance.RefreshProperties();
var orderedKeys = props
.OrderBy(pair => pair.Key)
.Select(pair => pair.Key);
foreach (string propName in props.Keys)
foreach (string propName in orderedKeys)
{
Property prop = props[propName];
XmlNode propNode = WriteProperty(prop, doc, file);
if (propNode == null)
continue;
propsNode.AppendChild(propNode);
}
@ -151,12 +171,12 @@ namespace RobloxFiles.XmlFormat
var binaryWriter = XmlPropertyTokens.GetHandler<BinaryStringToken>();
var binaryBuffer = new Property("SharedString", PropertyType.String);
foreach (string md5 in file.SharedStrings)
foreach (string key in file.SharedStrings)
{
XmlElement sharedString = doc.CreateElement("SharedString");
sharedString.SetAttribute("md5", md5);
sharedString.SetAttribute("md5", key);
binaryBuffer.RawBuffer = SharedString.FindRecord(md5);
binaryBuffer.RawBuffer = SharedString.Find(key);
binaryWriter.WriteProperty(binaryBuffer, doc, sharedString);
sharedStrings.AppendChild(sharedString);

View File

@ -1,4 +1,5 @@
using System.Xml;
using System.Text;
using System.Xml;
using RobloxFiles.DataTypes;
namespace RobloxFiles.XmlFormat.PropertyTokens
@ -10,7 +11,7 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
public bool ReadProperty(Property prop, XmlNode token)
{
ProtectedString contents = token.InnerText;
prop.Type = PropertyType.String;
prop.Type = PropertyType.ProtectedString;
prop.Value = contents;
return true;
@ -18,16 +19,26 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
{
string value = prop.CastValue<ProtectedString>();
ProtectedString value = prop.CastValue<ProtectedString>();
if (value.Contains("\r") || value.Contains("\n"))
if (value.IsCompiled)
{
XmlCDataSection cdata = doc.CreateCDataSection(value);
node.AppendChild(cdata);
var binary = XmlPropertyTokens.GetHandler<BinaryStringToken>();
binary.WriteProperty(prop, doc, node);
}
else
{
node.InnerText = value;
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;
}
}
}
}

View File

@ -9,17 +9,25 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
public bool ReadProperty(Property prop, XmlNode token)
{
string md5 = token.InnerText;
string key = token.InnerText;
prop.Type = PropertyType.SharedString;
prop.Value = new SharedString(md5);
prop.Value = new SharedString(key);
return true;
}
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
{
SharedString value = prop.CastValue<SharedString>();
node.InnerText = value.MD5_Key;
var value = prop.CastValue<SharedString>();
string key = value.Key;
if (value.ComputedKey == null)
{
var newShared = SharedString.FromBuffer(value.SharedValue);
key = newShared.ComputedKey;
}
node.InnerText = key;
}
}
}

View File

@ -103,7 +103,7 @@ namespace RobloxFiles
foreach (Property sharedProp in sharedProps)
{
SharedString shared = sharedProp.CastValue<SharedString>();
SharedStrings.Add(shared.MD5_Key);
SharedStrings.Add(shared.Key);
}
}
else