diff --git a/BinaryFormat/BinaryRobloxFile.cs b/BinaryFormat/BinaryRobloxFile.cs index ad4ac47..345f711 100644 --- a/BinaryFormat/BinaryRobloxFile.cs +++ b/BinaryFormat/BinaryRobloxFile.cs @@ -43,9 +43,9 @@ namespace RobloxFiles public BinaryRobloxFile() { - Name = "BinaryRobloxFile"; - ParentLocked = true; + Name = "Bin:"; Referent = "-1"; + ParentLocked = true; } protected override void ReadFile(byte[] contents) diff --git a/BinaryFormat/Chunks/PROP.cs b/BinaryFormat/Chunks/PROP.cs index 91d29e3..9a21c29 100644 --- a/BinaryFormat/Chunks/PROP.cs +++ b/BinaryFormat/Chunks/PROP.cs @@ -47,10 +47,9 @@ namespace RobloxFiles.BinaryFormat.Chunks INST inst = file.Classes[ClassIndex]; ClassName = inst.ClassName; - Property[] props = new Property[inst.NumInstances]; - var ids = inst.InstanceIds; int instCount = inst.NumInstances; + var props = new Property[inst.NumInstances]; for (int i = 0; i < instCount; i++) { @@ -71,8 +70,8 @@ namespace RobloxFiles.BinaryFormat.Chunks { for (int i = 0; i < instCount; i++) { - object result = read(i); - props[i].Value = result; + var prop = props[i]; + prop.Value = read(i); } }); diff --git a/DataTypes/BrickColor.cs b/DataTypes/BrickColor.cs index c005855..18a7830 100644 --- a/DataTypes/BrickColor.cs +++ b/DataTypes/BrickColor.cs @@ -18,10 +18,10 @@ namespace RobloxFiles.DataTypes public override string ToString() => Name; - private static List ByPalette; - private static Dictionary ByNumber; + private static readonly List ByPalette; + private static readonly Dictionary ByNumber; - private static Random RNG = new Random(); + private static readonly Random RNG = new Random(); private const string DefaultName = "Medium stone grey"; private const int DefaultNumber = 194; diff --git a/DataTypes/CFrame.cs b/DataTypes/CFrame.cs index 5d60d73..4af543e 100644 --- a/DataTypes/CFrame.cs +++ b/DataTypes/CFrame.cs @@ -161,7 +161,7 @@ namespace RobloxFiles.DataTypes m31 = comp[9]; m32 = comp[10]; m33 = comp[11]; } - private void initFromMatrix(Vector3 pos, Vector3 vX, Vector3 vY, Vector3 vZ = null) + private void InitFromMatrix(Vector3 pos, Vector3 vX, Vector3 vY, Vector3 vZ = null) { if (vZ == null) vZ = vX.Cross(vY); @@ -175,7 +175,7 @@ namespace RobloxFiles.DataTypes public CFrame(Vector3 pos, Vector3 vX, Vector3 vY, Vector3 vZ = null) { Contract.Requires(pos != null && vX != null && vY != null); - initFromMatrix(pos, vX, vY, vZ); + InitFromMatrix(pos, vX, vY, vZ); } internal CFrame(Attribute attr) @@ -194,7 +194,7 @@ namespace RobloxFiles.DataTypes NormalId yColumn = (NormalId)(orientId % 6); Vector3 vY = Vector3.FromNormalId(yColumn); - initFromMatrix(pos, vX, vY); + InitFromMatrix(pos, vX, vY); } else { @@ -202,7 +202,7 @@ namespace RobloxFiles.DataTypes vY = new Vector3(attr), vZ = new Vector3(attr); - initFromMatrix(pos, vX, vY, vZ); + InitFromMatrix(pos, vX, vY, vZ); } } diff --git a/DataTypes/Color3.cs b/DataTypes/Color3.cs index c5761ee..d7dda5f 100644 --- a/DataTypes/Color3.cs +++ b/DataTypes/Color3.cs @@ -44,9 +44,9 @@ namespace RobloxFiles.DataTypes internal Color3(Attribute attr) { - R = attr.readFloat(); - G = attr.readFloat(); - B = attr.readFloat(); + R = attr.ReadFloat(); + G = attr.ReadFloat(); + B = attr.ReadFloat(); } public static Color3 FromRGB(uint r = 0, uint g = 0, uint b = 0) diff --git a/DataTypes/ColorSequence.cs b/DataTypes/ColorSequence.cs index 1f3ea36..8a8fb8d 100644 --- a/DataTypes/ColorSequence.cs +++ b/DataTypes/ColorSequence.cs @@ -90,7 +90,7 @@ namespace RobloxFiles.DataTypes public ColorSequence(Attribute attr) { - int numKeys = attr.readInt(); + int numKeys = attr.ReadInt(); var keypoints = new ColorSequenceKeypoint[numKeys]; for (int i = 0; i < numKeys; i++) diff --git a/DataTypes/ColorSequenceKeypoint.cs b/DataTypes/ColorSequenceKeypoint.cs index b3498e1..cc64b9c 100644 --- a/DataTypes/ColorSequenceKeypoint.cs +++ b/DataTypes/ColorSequenceKeypoint.cs @@ -21,8 +21,8 @@ internal ColorSequenceKeypoint(Attribute attr) { - Envelope = attr.readInt(); - Time = attr.readFloat(); + Envelope = attr.ReadInt(); + Time = attr.ReadFloat(); Value = new Color3(attr); } diff --git a/DataTypes/Content.cs b/DataTypes/Content.cs index bab08d4..5499792 100644 --- a/DataTypes/Content.cs +++ b/DataTypes/Content.cs @@ -16,7 +16,7 @@ public static implicit operator string(Content content) { - return content.Url; + return content?.Url; } public static implicit operator Content(string url) diff --git a/DataTypes/NumberRange.cs b/DataTypes/NumberRange.cs index 420f120..37eb928 100644 --- a/DataTypes/NumberRange.cs +++ b/DataTypes/NumberRange.cs @@ -25,7 +25,7 @@ namespace RobloxFiles.DataTypes Max = max; } - internal NumberRange(Attribute attr) : this(attr.readFloat(), attr.readFloat()) + internal NumberRange(Attribute attr) : this(attr.ReadFloat(), attr.ReadFloat()) { } diff --git a/DataTypes/NumberSequence.cs b/DataTypes/NumberSequence.cs index 08bca67..09e5303 100644 --- a/DataTypes/NumberSequence.cs +++ b/DataTypes/NumberSequence.cs @@ -54,7 +54,7 @@ namespace RobloxFiles.DataTypes public NumberSequence(Attribute attr) { - int numKeys = attr.readInt(); + int numKeys = attr.ReadInt(); var keypoints = new NumberSequenceKeypoint[numKeys]; for (int i = 0; i < numKeys; i++) diff --git a/DataTypes/NumberSequenceKeypoint.cs b/DataTypes/NumberSequenceKeypoint.cs index 97751c9..8f04af3 100644 --- a/DataTypes/NumberSequenceKeypoint.cs +++ b/DataTypes/NumberSequenceKeypoint.cs @@ -20,9 +20,9 @@ internal NumberSequenceKeypoint(Attribute attr) { - Envelope = attr.readFloat(); - Time = attr.readFloat(); - Value = attr.readFloat(); + Envelope = attr.ReadFloat(); + Time = attr.ReadFloat(); + Value = attr.ReadFloat(); } public override int GetHashCode() diff --git a/DataTypes/PhysicalProperties.cs b/DataTypes/PhysicalProperties.cs index 871c6d6..48e90af 100644 --- a/DataTypes/PhysicalProperties.cs +++ b/DataTypes/PhysicalProperties.cs @@ -39,12 +39,12 @@ namespace RobloxFiles.DataTypes internal PhysicalProperties(Attribute attr) { - Density = attr.readFloat(); - Friction = attr.readFloat(); - Elasticity = attr.readFloat(); + Density = attr.ReadFloat(); + Friction = attr.ReadFloat(); + Elasticity = attr.ReadFloat(); - FrictionWeight = attr.readFloat(); - ElasticityWeight = attr.readFloat(); + FrictionWeight = attr.ReadFloat(); + ElasticityWeight = attr.ReadFloat(); } public override int GetHashCode() diff --git a/DataTypes/SharedString.cs b/DataTypes/SharedString.cs index bda010d..2727aaa 100644 --- a/DataTypes/SharedString.cs +++ b/DataTypes/SharedString.cs @@ -16,7 +16,7 @@ namespace RobloxFiles.DataTypes public class SharedString { - private static ConcurrentDictionary Lookup = new ConcurrentDictionary(); + private static readonly ConcurrentDictionary Lookup = new ConcurrentDictionary(); public string Key { get; internal set; } public string ComputedKey { get; internal set; } diff --git a/DataTypes/UDim.cs b/DataTypes/UDim.cs index addfce9..b69c62c 100644 --- a/DataTypes/UDim.cs +++ b/DataTypes/UDim.cs @@ -15,8 +15,8 @@ internal UDim(Attribute attr) { - Scale = attr.readFloat(); - Offset = attr.readInt(); + Scale = attr.ReadFloat(); + Offset = attr.ReadInt(); } public static UDim operator+(UDim a, UDim b) diff --git a/DataTypes/Vector2.cs b/DataTypes/Vector2.cs index 60ec2eb..307750e 100644 --- a/DataTypes/Vector2.cs +++ b/DataTypes/Vector2.cs @@ -37,8 +37,8 @@ namespace RobloxFiles.DataTypes internal Vector2(Attribute attr) { - X = attr.readFloat(); - Y = attr.readFloat(); + X = attr.ReadFloat(); + Y = attr.ReadFloat(); } private delegate Vector2 Operator(Vector2 a, Vector2 b); @@ -55,10 +55,10 @@ namespace RobloxFiles.DataTypes return upcast(numVec, vec); } - private static Operator add = new Operator((a, b) => new Vector2(a.X + b.X, a.Y + b.Y)); - private static Operator sub = new Operator((a, b) => new Vector2(a.X - b.X, a.Y - b.Y)); - private static Operator mul = new Operator((a, b) => new Vector2(a.X * b.X, a.Y * b.Y)); - private static Operator div = new Operator((a, b) => new Vector2(a.X / b.X, a.Y / b.Y)); + private static readonly Operator add = new Operator((a, b) => new Vector2(a.X + b.X, a.Y + b.Y)); + private static readonly Operator sub = new Operator((a, b) => new Vector2(a.X - b.X, a.Y - b.Y)); + private static readonly Operator mul = new Operator((a, b) => new Vector2(a.X * b.X, a.Y * b.Y)); + private static readonly Operator div = new Operator((a, b) => new Vector2(a.X / b.X, a.Y / b.Y)); public static Vector2 operator +(Vector2 a, Vector2 b) => add(a, b); public static Vector2 operator +(Vector2 v, float n) => upcastFloatOp(v, n, add); diff --git a/DataTypes/Vector2int16.cs b/DataTypes/Vector2int16.cs index 365d5df..6b91dea 100644 --- a/DataTypes/Vector2int16.cs +++ b/DataTypes/Vector2int16.cs @@ -39,10 +39,10 @@ namespace RobloxFiles.DataTypes return upcast(numVec, vec); } - private static Operator add = new Operator((a, b) => new Vector2int16(a.X + b.X, a.Y + b.Y)); - private static Operator sub = new Operator((a, b) => new Vector2int16(a.X - b.X, a.Y - b.Y)); - private static Operator mul = new Operator((a, b) => new Vector2int16(a.X * b.X, a.Y * b.Y)); - private static Operator div = new Operator((a, b) => + private static readonly Operator add = new Operator((a, b) => new Vector2int16(a.X + b.X, a.Y + b.Y)); + private static readonly Operator sub = new Operator((a, b) => new Vector2int16(a.X - b.X, a.Y - b.Y)); + private static readonly Operator mul = new Operator((a, b) => new Vector2int16(a.X * b.X, a.Y * b.Y)); + private static readonly Operator div = new Operator((a, b) => { if (b.X == 0 || b.Y == 0) throw new DivideByZeroException(); diff --git a/DataTypes/Vector3.cs b/DataTypes/Vector3.cs index 41fa15e..bc41d8f 100644 --- a/DataTypes/Vector3.cs +++ b/DataTypes/Vector3.cs @@ -41,9 +41,9 @@ namespace RobloxFiles.DataTypes internal Vector3(Attribute attr) { - X = attr.readFloat(); - Y = attr.readFloat(); - Z = attr.readFloat(); + X = attr.ReadFloat(); + Y = attr.ReadFloat(); + Z = attr.ReadFloat(); } public static Vector3 FromAxis(Axis axis) @@ -80,10 +80,10 @@ namespace RobloxFiles.DataTypes return upcast(numVec, vec); } - private static Operator add = new Operator((a, b) => new Vector3(a.X + b.X, a.Y + b.Y, a.Z + b.Z)); - private static Operator sub = new Operator((a, b) => new Vector3(a.X - b.X, a.Y - b.Y, a.Z - b.Z)); - private static Operator mul = new Operator((a, b) => new Vector3(a.X * b.X, a.Y * b.Y, a.Z * b.Z)); - private static Operator div = new Operator((a, b) => new Vector3(a.X / b.X, a.Y / b.Y, a.Z / b.Z)); + private static readonly Operator add = new Operator((a, b) => new Vector3(a.X + b.X, a.Y + b.Y, a.Z + b.Z)); + private static readonly Operator sub = new Operator((a, b) => new Vector3(a.X - b.X, a.Y - b.Y, a.Z - b.Z)); + private static readonly Operator mul = new Operator((a, b) => new Vector3(a.X * b.X, a.Y * b.Y, a.Z * b.Z)); + private static readonly Operator div = new Operator((a, b) => new Vector3(a.X / b.X, a.Y / b.Y, a.Z / b.Z)); public static Vector3 operator +(Vector3 a, Vector3 b) => add(a, b); public static Vector3 operator +(Vector3 v, float n) => upcastFloatOp(v, n, add); diff --git a/DataTypes/Vector3int16.cs b/DataTypes/Vector3int16.cs index 570bf19..6acdb35 100644 --- a/DataTypes/Vector3int16.cs +++ b/DataTypes/Vector3int16.cs @@ -46,10 +46,10 @@ namespace RobloxFiles.DataTypes return upcast(numVec, vec); } - private static Operator add = new Operator((a, b) => new Vector3int16(a.X + b.X, a.Y + b.Y, a.Z + b.Z)); - private static Operator sub = new Operator((a, b) => new Vector3int16(a.X - b.X, a.Y - b.Y, a.Z - b.Z)); - private static Operator mul = new Operator((a, b) => new Vector3int16(a.X * b.X, a.Y * b.Y, a.Z * b.Z)); - private static Operator div = new Operator((a, b) => + private static readonly Operator add = new Operator((a, b) => new Vector3int16(a.X + b.X, a.Y + b.Y, a.Z + b.Z)); + private static readonly Operator sub = new Operator((a, b) => new Vector3int16(a.X - b.X, a.Y - b.Y, a.Z - b.Z)); + private static readonly Operator mul = new Operator((a, b) => new Vector3int16(a.X * b.X, a.Y * b.Y, a.Z * b.Z)); + private static readonly Operator div = new Operator((a, b) => { if (b.X == 0 || b.Y == 0 || b.Z == 0) throw new DivideByZeroException(); diff --git a/RobloxFileFormat.dll b/RobloxFileFormat.dll index a3360c3..fd76518 100644 Binary files a/RobloxFileFormat.dll and b/RobloxFileFormat.dll differ diff --git a/Tree/Attributes.cs b/Tree/Attributes.cs index c019c21..6494dcc 100644 --- a/Tree/Attributes.cs +++ b/Tree/Attributes.cs @@ -56,17 +56,17 @@ namespace RobloxFiles internal BinaryReader reader; // internal BinaryWriter writer; - internal int readInt() => reader.ReadInt32(); + internal int ReadInt() => reader.ReadInt32(); internal byte readByte() => reader.ReadByte(); internal bool readBool() => reader.ReadBoolean(); internal short readShort() => reader.ReadInt16(); - internal float readFloat() => reader.ReadSingle(); - internal double readDouble() => reader.ReadDouble(); - internal string readString() => reader.ReadString(true); + internal float ReadFloat() => reader.ReadSingle(); + internal double ReadDouble() => reader.ReadDouble(); + internal string ReadString() => reader.ReadString(true); - internal Attribute[] readArray() + internal Attribute[] ReadArray() { - int count = readInt(); + int count = ReadInt(); var result = new Attribute[count]; for (int i = 0; i < count; i++) @@ -77,8 +77,8 @@ namespace RobloxFiles internal object readEnum() { - string name = readString(); - int value = readInt(); + string name = ReadString(); + int value = ReadInt(); try { @@ -107,22 +107,22 @@ namespace RobloxFiles case AttributeType.Null: break; case AttributeType.String: - Value = readString(); + Value = ReadString(); break; case AttributeType.Bool: Value = readBool(); break; case AttributeType.Int: - Value = readInt(); + Value = ReadInt(); break; case AttributeType.Float: - Value = readFloat(); + Value = ReadFloat(); break; case AttributeType.Double: - Value = readDouble(); + Value = ReadDouble(); break; case AttributeType.Array: - Value = readArray(); + Value = ReadArray(); break; case AttributeType.Dictionary: Value = new Attributes(reader); @@ -137,13 +137,13 @@ namespace RobloxFiles Value = new Ray(this); break; case AttributeType.Faces: - Value = (Faces)readInt(); + Value = (Faces)ReadInt(); break; case AttributeType.Axes: - Value = (Axes)readInt(); + Value = (Axes)ReadInt(); break; case AttributeType.BrickColor: - Value = (BrickColor)readInt(); + Value = (BrickColor)ReadInt(); break; case AttributeType.Color3: Value = new Color3(this); @@ -225,7 +225,7 @@ namespace RobloxFiles public class Attributes : Dictionary { - private void initialize(BinaryReader reader) + private void Initialize(BinaryReader reader) { Stream stream = reader.BaseStream; @@ -245,14 +245,14 @@ namespace RobloxFiles internal Attributes(BinaryReader reader) { - initialize(reader); + Initialize(reader); } internal Attributes(MemoryStream stream) { using (BinaryReader reader = new BinaryReader(stream)) { - initialize(reader); + Initialize(reader); } } diff --git a/Tree/Instance.cs b/Tree/Instance.cs index 4d7847d..2a1a836 100644 --- a/Tree/Instance.cs +++ b/Tree/Instance.cs @@ -24,7 +24,7 @@ namespace RobloxFiles public string ClassName => GetType().Name; /// Internal list of properties that are under this Instance. - private Dictionary props = new Dictionary(); + private readonly Dictionary props = new Dictionary(); /// A list of properties that are defined under this Instance. public IReadOnlyDictionary Properties => props; @@ -45,7 +45,7 @@ namespace RobloxFiles public override string ToString() => Name; /// A unique identifier for this instance when being serialized. - public string Referent { get; internal set; } + public string Referent { get; set; } /// Indicates whether the parent of this object is locked. public bool ParentLocked { get; internal set; } @@ -438,7 +438,7 @@ namespace RobloxFiles /// /// Returns a string describing the index traversal of this Instance, starting from its root ancestor. /// - public string GetFullName(string separator = ".") + public string GetFullName(string separator = "\\") { string fullName = Name; Instance at = Parent; @@ -512,6 +512,8 @@ namespace RobloxFiles if (field.GetCustomAttribute() != null) continue; + // A few specific edge case hacks. I wish these didn't need to exist :( + if (fieldName == "Archivable" || fieldName.EndsWith("k__BackingField")) continue; else if (fieldName == "Bevel_Roundness") diff --git a/Tree/Property.cs b/Tree/Property.cs index a1dc37d..6baa80e 100644 --- a/Tree/Property.cs +++ b/Tree/Property.cs @@ -125,10 +125,19 @@ namespace RobloxFiles return; } } + + if (RawValue is long) + Type = PropertyType.Int64; switch (Type) { case PropertyType.Int: + if (Value is long) + { + Type = PropertyType.Int64; + goto case PropertyType.Int64; + } + RawBuffer = BitConverter.GetBytes((int)Value); break; case PropertyType.Bool: @@ -307,7 +316,7 @@ namespace RobloxFiles string result = Name; if (Instance != null) - result = Instance.GetFullName() + '.' + result; + result = Instance.GetFullName() + "->" + result; return result; } @@ -329,8 +338,8 @@ namespace RobloxFiles if (typeof(T) == typeof(string)) result = Value?.ToString() ?? ""; - else if (Value is T) - result = (T)Value; + else if (Value is T typedValue) + result = typedValue; else result = default(T); diff --git a/XmlFormat/IO/XmlFileReader.cs b/XmlFormat/IO/XmlFileReader.cs index 9aa7ab0..5d76351 100644 --- a/XmlFormat/IO/XmlFileReader.cs +++ b/XmlFormat/IO/XmlFileReader.cs @@ -7,7 +7,7 @@ namespace RobloxFiles.XmlFormat { public static class XmlRobloxFileReader { - private static Func createErrorHandler(string label) + private static Func CreateErrorHandler(string label) { var errorHandler = new Func((message) => { @@ -20,7 +20,7 @@ namespace RobloxFiles.XmlFormat public static void ReadSharedStrings(XmlNode sharedStrings, XmlRobloxFile file) { - var error = createErrorHandler("ReadSharedStrings"); + var error = CreateErrorHandler("ReadSharedStrings"); if (sharedStrings.Name != "SharedStrings") throw error("Provided XmlNode's class should be 'SharedStrings'!"); @@ -56,7 +56,7 @@ namespace RobloxFiles.XmlFormat public static void ReadMetadata(XmlNode meta, XmlRobloxFile file) { - var error = createErrorHandler("ReadMetadata"); + var error = CreateErrorHandler("ReadMetadata"); if (meta.Name != "Meta") throw error("Provided XmlNode's class should be 'Meta'!"); @@ -74,7 +74,7 @@ namespace RobloxFiles.XmlFormat public static void ReadProperties(Instance instance, XmlNode propsNode) { - var error = createErrorHandler("ReadProperties"); + var error = CreateErrorHandler("ReadProperties"); if (propsNode.Name != "Properties") throw error("Provided XmlNode's class should be 'Properties'!"); @@ -121,7 +121,7 @@ namespace RobloxFiles.XmlFormat public static Instance ReadInstance(XmlNode instNode, XmlRobloxFile file) { - var error = createErrorHandler("ReadInstance"); + var error = CreateErrorHandler("ReadInstance"); // Process the instance itself if (instNode.Name != "Item") diff --git a/XmlFormat/IO/XmlFileWriter.cs b/XmlFormat/IO/XmlFileWriter.cs index 7f3f920..dffd385 100644 --- a/XmlFormat/IO/XmlFileWriter.cs +++ b/XmlFormat/IO/XmlFileWriter.cs @@ -161,29 +161,15 @@ namespace RobloxFiles.XmlFormat object a = DefaultProperty.Get(instance, prop); object b = prop.Value; - if (a is float) - { - float f0 = (float)a, - f1 = (float)b; - - isDefault = f0.FuzzyEquals(f1); - } - else if (a is double) - { - double d0 = (double)a, - d1 = (double)b; - + if (a is double d0 && b is double d1) isDefault = d0.FuzzyEquals(d1); - } + else if (a is float f0 && b is float f1) + isDefault = f0.FuzzyEquals(f1); else if (b != null) - { isDefault = b.Equals(a); - } else if (a == b) - { isDefault = true; - } - + if (!isDefault) { XmlNode propNode = WriteProperty(prop, doc, file); diff --git a/XmlFormat/XmlRobloxFile.cs b/XmlFormat/XmlRobloxFile.cs index 8ad56ae..ab8ce26 100644 --- a/XmlFormat/XmlRobloxFile.cs +++ b/XmlFormat/XmlRobloxFile.cs @@ -23,9 +23,9 @@ namespace RobloxFiles public XmlRobloxFile() { - Name = "XmlRobloxFile"; - ParentLocked = true; + Name = "Xml:"; Referent = "null"; + ParentLocked = true; } protected override void ReadFile(byte[] buffer)