diff --git a/BinaryFormat/Chunks/INST.cs b/BinaryFormat/Chunks/INST.cs index 7bde3a6..c23b7e9 100644 --- a/BinaryFormat/Chunks/INST.cs +++ b/BinaryFormat/Chunks/INST.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Text; namespace RobloxFiles.BinaryFormat.Chunks { @@ -79,5 +80,18 @@ namespace RobloxFiles.BinaryFormat.Chunks } } } + + public void WriteInfo(StringBuilder builder) + { + builder.AppendLine($"- ClassIndex: {ClassIndex}"); + builder.AppendLine($"- ClassName: {ClassName}"); + builder.AppendLine($"- IsService: {IsService}"); + + if (IsService && RootedServices != null) + builder.AppendLine($"- RootedServices: `{string.Join(", ", RootedServices)}`"); + + builder.AppendLine($"- NumInstances: {NumInstances}"); + builder.AppendLine($"- InstanceIds: `{string.Join(", ", InstanceIds)}`"); + } } } diff --git a/BinaryFormat/Chunks/META.cs b/BinaryFormat/Chunks/META.cs index 9506a6c..9b5aca0 100644 --- a/BinaryFormat/Chunks/META.cs +++ b/BinaryFormat/Chunks/META.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Text; namespace RobloxFiles.BinaryFormat.Chunks { @@ -31,5 +32,18 @@ namespace RobloxFiles.BinaryFormat.Chunks writer.WriteString(pair.Value); } } + + public void WriteInfo(StringBuilder builder) + { + builder.AppendLine($"- NumEntries: {Data.Count}"); + + foreach (var pair in Data) + { + string key = pair.Key, + value = pair.Value; + + builder.AppendLine($" - {key}: {value}"); + } + } } } diff --git a/BinaryFormat/Chunks/PRNT.cs b/BinaryFormat/Chunks/PRNT.cs index 12e6819..8667576 100644 --- a/BinaryFormat/Chunks/PRNT.cs +++ b/BinaryFormat/Chunks/PRNT.cs @@ -1,15 +1,18 @@ using System; using System.Collections.Generic; +using System.Text; namespace RobloxFiles.BinaryFormat.Chunks { public class PRNT : IBinaryFileChunk { private const byte FORMAT = 0; + private BinaryRobloxFile File; public void Load(BinaryRobloxFileReader reader) { BinaryRobloxFile file = reader.File; + File = file; byte format = reader.ReadByte(); int idCount = reader.ReadInt32(); @@ -32,6 +35,9 @@ namespace RobloxFiles.BinaryFormat.Chunks public void Save(BinaryRobloxFileWriter writer) { + var file = writer.File; + File = file; + var postInstances = writer.PostInstances; var idCount = postInstances.Count; @@ -58,5 +64,29 @@ namespace RobloxFiles.BinaryFormat.Chunks writer.WriteInstanceIds(childIds); writer.WriteInstanceIds(parentIds); } + + public void WriteInfo(StringBuilder builder) + { + var childIds = new List(); + var parentIds = new List(); + + foreach (Instance inst in File.GetDescendants()) + { + Instance parent = inst.Parent; + + int childId = int.Parse(inst.Referent); + int parentId = -1; + + if (parent != null) + parentId = int.Parse(parent.Referent); + + childIds.Add(childId); + parentIds.Add(parentId); + } + + builder.AppendLine($"- Format: {FORMAT}"); + builder.AppendLine($"- ChildIds: {string.Join(", ", childIds)}"); + builder.AppendLine($"- ParentIds: {string.Join(", ", parentIds)}"); + } } } diff --git a/BinaryFormat/Chunks/PROP.cs b/BinaryFormat/Chunks/PROP.cs index a8f9536..91d29e3 100644 --- a/BinaryFormat/Chunks/PROP.cs +++ b/BinaryFormat/Chunks/PROP.cs @@ -6,12 +6,15 @@ using System.Text; using RobloxFiles.Enums; using RobloxFiles.DataTypes; +using RobloxFiles.Utility; + using System.Diagnostics; namespace RobloxFiles.BinaryFormat.Chunks { public class PROP : IBinaryFileChunk { + private BinaryRobloxFile File; public string Name { get; internal set; } public int ClassIndex { get; internal set; } @@ -33,6 +36,7 @@ namespace RobloxFiles.BinaryFormat.Chunks public void Load(BinaryRobloxFileReader reader) { BinaryRobloxFile file = reader.File; + File = file; ClassIndex = reader.ReadInt32(); Name = reader.ReadString(); @@ -87,28 +91,32 @@ namespace RobloxFiles.BinaryFormat.Chunks // Check if this is going to be casted as a BinaryString. // BinaryStrings should use a type of byte[] instead. - if (Name == "AttributesSerialize") - return buffer; - - Property prop = props[i]; - Instance instance = prop.Instance; - - Type instType = instance.GetType(); - FieldInfo field = instType.GetField(Name); - - if (field != null) + switch (Name) { - object result = value; - Type fieldType = field.FieldType; + case "Tags": + case "AttributesSerialize": + return buffer; + default: + { + Property prop = props[i]; + Instance instance = prop.Instance; - if (fieldType == typeof(byte[])) - result = buffer; + Type instType = instance.GetType(); + var member = ImplicitMember.Get(instType, Name); - return result; - } - else - { - return value; + if (member != null) + { + object result = value; + Type memberType = member.MemberType; + + if (memberType == typeof(byte[])) + result = buffer; + + return result; + } + + return value; + } } }); @@ -345,8 +353,8 @@ namespace RobloxFiles.BinaryFormat.Chunks try { - FieldInfo info = instType.GetField(Name, Property.BindingFlags); - return Enum.Parse(info.FieldType, value.ToInvariantString()); + var info = ImplicitMember.Get(instType, Name); + return Enum.Parse(info.MemberType, value.ToInvariantString()); } catch { @@ -362,12 +370,7 @@ namespace RobloxFiles.BinaryFormat.Chunks readProperties(i => { int instId = instIds[i]; - Instance result = null; - - if (instId >= 0) - result = file.Instances[instId]; - - return result; + return instId >= 0 ? file.Instances[instId] : null; }); break; @@ -540,6 +543,12 @@ namespace RobloxFiles.BinaryFormat.Chunks foreach (string propName in props.Keys) { + if (propName == "Archivable") + continue; + + if (propName.Contains("__")) + continue; + if (!propMap.ContainsKey(propName)) { Property prop = props[propName]; @@ -549,6 +558,7 @@ namespace RobloxFiles.BinaryFormat.Chunks Name = prop.Name, Type = prop.Type, + ClassName = inst.ClassName, ClassIndex = inst.ClassIndex }; @@ -563,6 +573,7 @@ namespace RobloxFiles.BinaryFormat.Chunks public void Save(BinaryRobloxFileWriter writer) { BinaryRobloxFile file = writer.File; + File = file; INST inst = file.Classes[ClassIndex]; var props = new List(); @@ -1054,5 +1065,40 @@ namespace RobloxFiles.BinaryFormat.Chunks default: break; } } + + public void WriteInfo(StringBuilder builder) + { + builder.AppendLine($"- Name: {Name}"); + builder.AppendLine($"- Type: {Type}"); + builder.AppendLine($"- TypeId: {TypeId}"); + builder.AppendLine($"- ClassName: {ClassName}"); + builder.AppendLine($"- ClassIndex: {ClassIndex}"); + + builder.AppendLine($"| InstanceId | Value |"); + builder.AppendLine($"|-----------:|---------------------------|"); + + INST inst = File.Classes[ClassIndex]; + + foreach (var instId in inst.InstanceIds) + { + Instance instance = File.Instances[instId]; + Property prop = instance?.GetProperty(Name); + + object value = prop?.Value; + string str = value?.ToInvariantString() ?? "null"; + + if (value is byte[]) + str = Convert.ToBase64String(value as byte[]); + + if (str.Length > 25) + str = str.Substring(0, 22) + "..."; + + str = str.Replace('\r', ' '); + str = str.Replace('\n', ' '); + + string row = string.Format("| {0, 10} | {1, -25} |", instId, str); + builder.AppendLine(row); + } + } } } diff --git a/BinaryFormat/Chunks/SIGN.cs b/BinaryFormat/Chunks/SIGN.cs index 7c0a51f..fbd0a95 100644 --- a/BinaryFormat/Chunks/SIGN.cs +++ b/BinaryFormat/Chunks/SIGN.cs @@ -1,4 +1,7 @@ -namespace RobloxFiles.BinaryFormat.Chunks +using System; +using System.Text; + +namespace RobloxFiles.BinaryFormat.Chunks { public struct Signature { @@ -52,5 +55,29 @@ writer.Write(signature.Data); } } + + public void WriteInfo(StringBuilder builder) + { + int numSignatures = Signatures.Length; + builder.AppendLine($"NumSignatures: {numSignatures}"); + + for (int i = 0; i < numSignatures; i++) + { + var signature = Signatures[i]; + builder.AppendLine($"## Signature {i}"); + + var version = signature.Version; + builder.AppendLine($"- Version: {version}"); + + var id = signature.Id; + builder.AppendLine($"- Id: {id}"); + + var length = signature.Length; + builder.AppendLine($"- Length: {length}"); + + var data = Convert.ToBase64String(signature.Data); + builder.AppendLine($"- Data: {data}"); + } + } } } diff --git a/BinaryFormat/Chunks/SSTR.cs b/BinaryFormat/Chunks/SSTR.cs index 38e1709..7875566 100644 --- a/BinaryFormat/Chunks/SSTR.cs +++ b/BinaryFormat/Chunks/SSTR.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using System.Text; + using RobloxFiles.DataTypes; namespace RobloxFiles.BinaryFormat.Chunks @@ -55,5 +57,19 @@ namespace RobloxFiles.BinaryFormat.Chunks writer.Write(buffer); } } + + public void WriteInfo(StringBuilder builder) + { + builder.AppendLine($"Format: {FORMAT}"); + builder.AppendLine($"NumStrings: {Lookup.Count}"); + + builder.AppendLine($"## Keys"); + + foreach (var pair in Lookup) + { + string key = pair.Key; + builder.AppendLine($"- `{key}`"); + } + } } } diff --git a/DataTypes/CFrame.cs b/DataTypes/CFrame.cs index 3aa776c..bc832c9 100644 --- a/DataTypes/CFrame.cs +++ b/DataTypes/CFrame.cs @@ -30,9 +30,13 @@ namespace RobloxFiles.DataTypes } } - public Vector3 UpVector => new Vector3( m21, m22, m23); - public Vector3 LookVector => new Vector3(-m31, -m32, -m33); - public Vector3 RightVector => new Vector3(m11, m12, m13); + public Vector3 RightVector => new Vector3( m11, m21, m31); + public Vector3 UpVector => new Vector3( m12, m22, m32); + public Vector3 LookVector => new Vector3(-m13, -m23, -m33); + + public Vector3 ColumnX => new Vector3(m11, m12, m13); + public Vector3 ColumnY => new Vector3(m21, m22, m23); + public Vector3 ColumnZ => new Vector3(m31, m32, m33); public CFrame() { @@ -43,8 +47,6 @@ namespace RobloxFiles.DataTypes public CFrame(Vector3 pos) { - Contract.Requires(pos != null); - m14 = pos.X; m24 = pos.Y; m34 = pos.Z; @@ -68,7 +70,7 @@ namespace RobloxFiles.DataTypes if (zAxis.Y < 0) { xAxis = new Vector3(0, 0, -1); - yAxis = new Vector3(1, 0, 0); + yAxis = new Vector3(1, 0, 0); zAxis = new Vector3(0, -1, 0); } else @@ -170,25 +172,6 @@ namespace RobloxFiles.DataTypes } } - public override bool Equals(object obj) - { - if (obj is CFrame) - { - CFrame other = obj as CFrame; - - float[] a = GetComponents(); - float[] b = other.GetComponents(); - - for (int i = 0; i < 12; i++) - if (!a[i].FuzzyEquals(b[i])) - return false; - - return true; - } - - return base.Equals(obj); - } - public static CFrame operator +(CFrame a, Vector3 b) { float[] ac = a.GetComponents(); @@ -226,7 +209,6 @@ namespace RobloxFiles.DataTypes m21 = ac[6], m22 = ac[7], m23 = ac[8], m31 = ac[9], m32 = ac[10], m33 = ac[11]; - var up = new Vector3(m12, m22, m32); var back = new Vector3(m13, m23, m33); var right = new Vector3(m11, m21, m31); @@ -279,8 +261,7 @@ namespace RobloxFiles.DataTypes float cosAng = (float)Math.Cos(theta); float sinAng = (float)Math.Sin(theta); - return axis * cosAng + axis.Dot(unit) * unit * - (1 - cosAng) + unit.Cross(axis) * sinAng; + return axis * cosAng + axis.Dot(unit) * unit * (1 - cosAng) + unit.Cross(axis) * sinAng; } public CFrame Inverse() @@ -324,9 +305,9 @@ namespace RobloxFiles.DataTypes public static CFrame FromAxisAngle(Vector3 axis, float theta) { - Vector3 r = VectorAxisAngle(axis, Vector3.Right, theta); Vector3 u = VectorAxisAngle(axis, Vector3.Up, theta); Vector3 b = VectorAxisAngle(axis, Vector3.Back, theta); + Vector3 r = VectorAxisAngle(axis, Vector3.Right, theta); return new CFrame(0, 0, 0, r.X, u.X, b.X, r.Y, u.Y, b.Y, r.Z, u.Z, b.Z); } @@ -347,24 +328,18 @@ namespace RobloxFiles.DataTypes public CFrame Lerp(CFrame other, float t) { - if (t == 0.0f) - { + if (t == 0f) return this; - } - else if (t == 1.0f) - { + else if (t == 1f) return other; - } - else - { - Quaternion q1 = new Quaternion(this); - Quaternion q2 = new Quaternion(other); + + var q1 = new Quaternion(this); + var q2 = new Quaternion(other); - CFrame rot = q1.Slerp(q2, t).ToCFrame(); - Vector3 pos = Position.Lerp(other.Position, t); + CFrame rot = q1.Slerp(q2, t).ToCFrame(); + Vector3 pos = Position.Lerp(other.Position, t); - return new CFrame(pos) * rot; - } + return new CFrame(pos) * rot; } public CFrame ToWorldSpace(CFrame cf2) @@ -422,12 +397,12 @@ namespace RobloxFiles.DataTypes { float t = Math.Abs(matrix[i]); - if (t.FuzzyEquals(1)) + if (t.FuzzyEquals(1, 1e-8f)) { // Approximately ±1 sum1++; } - else if (t.FuzzyEquals(0)) + else if (t.FuzzyEquals(0, 1e-8f)) { // Approximately ±0 sum0++; @@ -450,8 +425,8 @@ namespace RobloxFiles.DataTypes if (!IsAxisAligned()) return -1; - int xNormal = RightVector.ToNormalId(); - int yNormal = UpVector.ToNormalId(); + int xNormal = ColumnX.ToNormalId(); + int yNormal = ColumnY.ToNormalId(); int orientId = (6 * xNormal) + yNormal; @@ -461,4 +436,4 @@ namespace RobloxFiles.DataTypes return orientId; } } -} +} \ No newline at end of file diff --git a/Interfaces/IBinaryFileChunk.cs b/Interfaces/IBinaryFileChunk.cs index 0a02908..9b6df91 100644 --- a/Interfaces/IBinaryFileChunk.cs +++ b/Interfaces/IBinaryFileChunk.cs @@ -1,8 +1,12 @@ -namespace RobloxFiles.BinaryFormat +using System.IO; +using System.Text; + +namespace RobloxFiles.BinaryFormat { public interface IBinaryFileChunk { void Load(BinaryRobloxFileReader reader); void Save(BinaryRobloxFileWriter writer); + void WriteInfo(StringBuilder builder); } } diff --git a/RobloxFile.cs b/RobloxFile.cs index cbb0bdc..852cd09 100644 --- a/RobloxFile.cs +++ b/RobloxFile.cs @@ -42,7 +42,6 @@ namespace RobloxFiles } } - string lead = Encoding.UTF8.GetString(buffer, 0, 100); throw new Exception("Unrecognized header!"); } diff --git a/RobloxFileFormat.dll b/RobloxFileFormat.dll index 2a55268..e1f395f 100644 Binary files a/RobloxFileFormat.dll and b/RobloxFileFormat.dll differ diff --git a/XmlFormat/XmlRobloxFile.cs b/XmlFormat/XmlRobloxFile.cs index f9a932b..25cf721 100644 --- a/XmlFormat/XmlRobloxFile.cs +++ b/XmlFormat/XmlRobloxFile.cs @@ -18,8 +18,7 @@ namespace RobloxFiles internal Dictionary Instances = new Dictionary(); internal HashSet SharedStrings = new HashSet(); - private Dictionary RawMetadata = new Dictionary(); - public Dictionary Metadata => RawMetadata; + public Dictionary Metadata { get; private set; } = new Dictionary(); internal int RefCounter = 0; public XmlRobloxFile()