using System; using System.Xml; namespace RobloxFiles.XmlFormat { public static class XmlRobloxFileReader { private static Func createErrorHandler(string label) { var errorHandler = new Func((message) => { string contents = $"XmlDataReader.{label}: {message}"; return new Exception(contents); }); return errorHandler; } public static void ReadSharedStrings(XmlNode sharedStrings, XmlRobloxFile file) { var error = createErrorHandler("ReadSharedStrings"); if (sharedStrings.Name != "SharedStrings") throw error("Provided XmlNode's class should be 'SharedStrings'!"); foreach (XmlNode sharedString in sharedStrings) { if (sharedString.Name == "SharedString") { XmlNode md5Node = sharedString.Attributes.GetNamedItem("md5"); if (md5Node == null) throw error("Got a SharedString without an 'md5' attribute!"); string key = md5Node.InnerText; string value = sharedString.InnerText.Replace("\n", ""); file.SharedStrings.Add(key, value); } } } public static void ReadProperties(Instance instance, XmlNode propsNode) { var error = createErrorHandler("ReadProperties"); if (propsNode.Name != "Properties") throw error("Provided XmlNode's class should be 'Properties'!"); foreach (XmlNode propNode in propsNode.ChildNodes) { string propType = propNode.Name; XmlNode propName = propNode.Attributes.GetNamedItem("name"); if (propName == null) throw error("Got a property node without a 'name' attribute!"); IXmlPropertyToken tokenHandler = XmlPropertyTokens.GetHandler(propType); if (tokenHandler != null) { Property prop = new Property() { Name = propName.InnerText, Instance = instance, XmlToken = propType }; if (!tokenHandler.ReadProperty(prop, propNode)) Console.WriteLine("Could not read property: " + prop.GetFullName() + '!'); instance.AddProperty(ref prop); } else { Console.WriteLine("No IXmlPropertyToken found for property type: " + propType + '!'); } } } public static Instance ReadInstance(XmlNode instNode, XmlRobloxFile file) { var error = createErrorHandler("ReadInstance"); // Process the instance itself if (instNode.Name != "Item") throw error("Provided XmlNode's name should be 'Item'!"); XmlNode classToken = instNode.Attributes.GetNamedItem("class"); if (classToken == null) throw error("Got an Item without a defined 'class' attribute!"); Instance inst = new Instance() { ClassName = classToken.InnerText }; // The 'referent' attribute is optional, but should be defined if a Ref property needs to link to this Instance. XmlNode refToken = instNode.Attributes.GetNamedItem("referent"); if (refToken != null && file != null) { string referent = refToken.InnerText; inst.XmlReferent = referent; if (file.Instances.ContainsKey(referent)) throw error("Got an Item with a duplicate 'referent' attribute!"); file.Instances.Add(referent, inst); } // Process the child nodes of this instance. foreach (XmlNode childNode in instNode.ChildNodes) { if (childNode.Name == "Properties") { ReadProperties(inst, childNode); } else if (childNode.Name == "Item") { Instance child = ReadInstance(childNode, file); child.Parent = inst; } } return inst; } } }