General Misc Patches
This commit is contained in:
parent
297426bdb5
commit
f4899b4ce6
@ -29,8 +29,8 @@ namespace RobloxFiles.BinaryFormat.Chunks
|
||||
byte[] data = reader.ReadBuffer();
|
||||
SharedString value = SharedString.FromBuffer(data);
|
||||
|
||||
Lookup.Add(key, id);
|
||||
Strings.Add(id, value);
|
||||
Lookup[key] = id;
|
||||
Strings[id] = value;
|
||||
}
|
||||
|
||||
file.SSTR = this;
|
||||
|
@ -170,6 +170,25 @@ 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();
|
||||
|
@ -14,6 +14,15 @@ namespace RobloxFiles.DataTypes
|
||||
B = b;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
int r = R.GetHashCode(),
|
||||
g = G.GetHashCode(),
|
||||
b = B.GetHashCode();
|
||||
|
||||
return (r ^ g ^ b);
|
||||
}
|
||||
|
||||
internal Color3(Attribute attr)
|
||||
{
|
||||
R = attr.readFloat();
|
||||
|
@ -16,6 +16,11 @@
|
||||
B = b;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return (R << 24) | (G << 8) | B;
|
||||
}
|
||||
|
||||
public static implicit operator Color3(Color3uint8 color)
|
||||
{
|
||||
float r = color.R / 255f;
|
||||
|
@ -21,10 +21,11 @@ namespace RobloxFiles.DataTypes
|
||||
|
||||
public ColorSequence(Color3 c0, Color3 c1)
|
||||
{
|
||||
ColorSequenceKeypoint a = new ColorSequenceKeypoint(0, c0);
|
||||
ColorSequenceKeypoint b = new ColorSequenceKeypoint(1, c1);
|
||||
|
||||
Keypoints = new ColorSequenceKeypoint[2] { a, b };
|
||||
Keypoints = new ColorSequenceKeypoint[2]
|
||||
{
|
||||
new ColorSequenceKeypoint(0, c0),
|
||||
new ColorSequenceKeypoint(1, c1)
|
||||
};
|
||||
}
|
||||
|
||||
public ColorSequence(ColorSequenceKeypoint[] keypoints)
|
||||
|
@ -3,12 +3,13 @@
|
||||
public class ColorSequenceKeypoint
|
||||
{
|
||||
public readonly float Time;
|
||||
public readonly Color3 Value;
|
||||
public readonly Color3uint8 Value;
|
||||
public readonly int Envelope;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Time} {Value.R} {Value.G} {Value.B} {Envelope}";
|
||||
Color3 Color = Value;
|
||||
return $"{Time} {Color.R} {Color.G} {Color.B} {Envelope}";
|
||||
}
|
||||
|
||||
public ColorSequenceKeypoint(float time, Color3 value, int envelope = 0)
|
||||
|
@ -59,7 +59,7 @@ namespace RobloxFiles.DataTypes
|
||||
|
||||
public static SharedString FromBuffer(byte[] buffer)
|
||||
{
|
||||
return new SharedString(buffer);
|
||||
return new SharedString(buffer ?? Array.Empty<byte>());
|
||||
}
|
||||
|
||||
public static SharedString FromString(string value)
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using RobloxFiles.Enums;
|
||||
|
||||
namespace RobloxFiles.DataTypes
|
||||
@ -65,6 +66,36 @@ namespace RobloxFiles.DataTypes
|
||||
return new Vector3(coords);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is Vector3)
|
||||
{
|
||||
Vector3 other = obj as Vector3;
|
||||
|
||||
if (!X.FuzzyEquals(other.X))
|
||||
return false;
|
||||
|
||||
if (!Y.FuzzyEquals(other.Y))
|
||||
return false;
|
||||
|
||||
if (!Z.FuzzyEquals(other.Z))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.Equals(obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
int x = X.GetHashCode(),
|
||||
y = Y.GetHashCode(),
|
||||
z = Z.GetHashCode();
|
||||
|
||||
return x ^ y ^ z;
|
||||
}
|
||||
|
||||
private delegate Vector3 Operator(Vector3 a, Vector3 b);
|
||||
|
||||
private static Vector3 upcastFloatOp(Vector3 vec, float num, Operator upcast)
|
||||
|
@ -2251,7 +2251,9 @@ namespace RobloxFiles
|
||||
{
|
||||
public ModelLevelOfDetail LevelOfDetail = ModelLevelOfDetail.Automatic;
|
||||
public CFrame ModelInPrimary = new CFrame();
|
||||
public CFrame ModelMeshCFrame = new CFrame();
|
||||
public byte[] ModelMeshData = Array.Empty<byte>();
|
||||
public Vector3 ModelMeshSize = new Vector3();
|
||||
public BasePart PrimaryPart;
|
||||
}
|
||||
|
||||
|
@ -121,6 +121,7 @@
|
||||
<Compile Include="DataTypes\UDim2.cs" />
|
||||
<Compile Include="DataTypes\Vector2.cs" />
|
||||
<Compile Include="DataTypes\Vector3.cs" />
|
||||
<Compile Include="Utility\DefaultProperty.cs" />
|
||||
<Compile Include="Utility\Formatting.cs" />
|
||||
<Compile Include="Utility\FontUtility.cs" />
|
||||
<Compile Include="Utility\ImplicitMember.cs" />
|
||||
|
Binary file not shown.
@ -530,11 +530,44 @@ namespace RobloxFiles
|
||||
if (fieldName.EndsWith("_"))
|
||||
fieldName = instType.Name;
|
||||
|
||||
string xmlToken = fieldType.Name;
|
||||
|
||||
if (fieldType.IsEnum)
|
||||
xmlToken = "token";
|
||||
|
||||
switch (xmlToken)
|
||||
{
|
||||
case "String":
|
||||
case "Double":
|
||||
xmlToken = xmlToken.ToLowerInvariant();
|
||||
break;
|
||||
case "Boolean":
|
||||
xmlToken = "bool";
|
||||
break;
|
||||
case "Single":
|
||||
xmlToken = "float";
|
||||
break;
|
||||
case "Int32":
|
||||
xmlToken = "int";
|
||||
break;
|
||||
case "Int64":
|
||||
xmlToken = "int64";
|
||||
break;
|
||||
case "Rect":
|
||||
xmlToken = "Rect2D";
|
||||
break;
|
||||
case "CFrame":
|
||||
xmlToken = "CoordinateFrame";
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
if (!props.ContainsKey(fieldName))
|
||||
{
|
||||
Property newProp = new Property()
|
||||
{
|
||||
Value = field.GetValue(this),
|
||||
XmlToken = xmlToken,
|
||||
Name = fieldName,
|
||||
Type = propType,
|
||||
Instance = this
|
||||
@ -546,6 +579,7 @@ namespace RobloxFiles
|
||||
{
|
||||
Property prop = props[fieldName];
|
||||
prop.Value = field.GetValue(this);
|
||||
prop.XmlToken = xmlToken;
|
||||
prop.Type = propType;
|
||||
}
|
||||
}
|
||||
|
@ -100,35 +100,45 @@ namespace RobloxFiles
|
||||
|
||||
private void ImproviseRawBuffer()
|
||||
{
|
||||
if (RawValue is SharedString)
|
||||
{
|
||||
var sharedString = CastValue<SharedString>();
|
||||
RawBuffer = sharedString.SharedValue;
|
||||
return;
|
||||
}
|
||||
else if (RawValue is ProtectedString)
|
||||
{
|
||||
var protectedString = CastValue<ProtectedString>();
|
||||
RawBuffer = protectedString.RawBuffer;
|
||||
return;
|
||||
}
|
||||
else if (RawValue is byte[])
|
||||
if (RawValue is byte[])
|
||||
{
|
||||
RawBuffer = RawValue as byte[];
|
||||
return;
|
||||
}
|
||||
|
||||
if (RawValue is SharedString)
|
||||
{
|
||||
var sharedString = CastValue<SharedString>();
|
||||
|
||||
if (sharedString != null)
|
||||
{
|
||||
RawBuffer = sharedString.SharedValue;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (RawValue is ProtectedString)
|
||||
{
|
||||
var protectedString = CastValue<ProtectedString>();
|
||||
|
||||
if (protectedString != null)
|
||||
{
|
||||
RawBuffer = protectedString.RawBuffer;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
switch (Type)
|
||||
{
|
||||
case PropertyType.Int:
|
||||
RawBuffer = BitConverter.GetBytes((int)Value);
|
||||
break;
|
||||
case PropertyType.Int64:
|
||||
RawBuffer = BitConverter.GetBytes((long)Value);
|
||||
break;
|
||||
case PropertyType.Bool:
|
||||
RawBuffer = BitConverter.GetBytes((bool)Value);
|
||||
break;
|
||||
case PropertyType.Int64:
|
||||
RawBuffer = BitConverter.GetBytes((long)Value);
|
||||
break;
|
||||
case PropertyType.Float:
|
||||
RawBuffer = BitConverter.GetBytes((float)Value);
|
||||
break;
|
||||
|
63
Utility/DefaultProperty.cs
Normal file
63
Utility/DefaultProperty.cs
Normal file
@ -0,0 +1,63 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace RobloxFiles.Utility
|
||||
{
|
||||
static class DefaultProperty
|
||||
{
|
||||
private static Dictionary<string, Instance> ClassMap;
|
||||
private static HashSet<Instance> Refreshed = new HashSet<Instance>();
|
||||
|
||||
static DefaultProperty()
|
||||
{
|
||||
var Instance = typeof(Instance);
|
||||
var assembly = Assembly.GetExecutingAssembly();
|
||||
|
||||
var classes = assembly.GetTypes()
|
||||
.Where(type => !type.IsAbstract && Instance.IsAssignableFrom(type))
|
||||
.Select(type => Activator.CreateInstance(type))
|
||||
.Cast<Instance>();
|
||||
|
||||
ClassMap = classes.ToDictionary(inst => inst.ClassName);
|
||||
}
|
||||
|
||||
public static object Get(string className, string propName)
|
||||
{
|
||||
if (!ClassMap.ContainsKey(className))
|
||||
return null;
|
||||
|
||||
Instance inst = ClassMap[className];
|
||||
|
||||
if (!Refreshed.Contains(inst))
|
||||
{
|
||||
inst.RefreshProperties();
|
||||
Refreshed.Add(inst);
|
||||
}
|
||||
|
||||
var props = inst.Properties;
|
||||
|
||||
if (!props.ContainsKey(propName))
|
||||
return null;
|
||||
|
||||
var prop = props[propName];
|
||||
return prop.Value;
|
||||
}
|
||||
|
||||
public static object Get(Instance inst, string propName)
|
||||
{
|
||||
return Get(inst.ClassName, propName);
|
||||
}
|
||||
|
||||
public static object Get(Instance inst, Property prop)
|
||||
{
|
||||
return Get(inst.ClassName, prop.Name);
|
||||
}
|
||||
|
||||
public static object Get(string className, Property prop)
|
||||
{
|
||||
return Get(className, prop.Name);
|
||||
}
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@ using System.Text;
|
||||
using System.Xml;
|
||||
|
||||
using RobloxFiles.DataTypes;
|
||||
using RobloxFiles.Utility;
|
||||
using RobloxFiles.XmlFormat.PropertyTokens;
|
||||
|
||||
namespace RobloxFiles.XmlFormat
|
||||
@ -41,12 +42,11 @@ namespace RobloxFiles.XmlFormat
|
||||
|
||||
internal static void RecordInstances(XmlRobloxFile file, Instance inst)
|
||||
{
|
||||
inst.Referent = "RBX" + file.RefCounter++;
|
||||
|
||||
foreach (Instance child in inst.GetChildren())
|
||||
RecordInstances(file, child);
|
||||
|
||||
if (inst.Referent == null || inst.Referent.Length < 35)
|
||||
inst.Referent = CreateReferent();
|
||||
|
||||
file.Instances.Add(inst.Referent, inst);
|
||||
}
|
||||
|
||||
@ -100,15 +100,21 @@ namespace RobloxFiles.XmlFormat
|
||||
|
||||
if (prop.Type == PropertyType.SharedString)
|
||||
{
|
||||
SharedString value = prop.CastValue<SharedString>();
|
||||
|
||||
if (value.ComputedKey == null)
|
||||
SharedString str = prop.CastValue<SharedString>();
|
||||
|
||||
if (str == null)
|
||||
{
|
||||
var newShared = SharedString.FromBuffer(value.SharedValue);
|
||||
value.Key = newShared.ComputedKey;
|
||||
byte[] value = prop.CastValue<byte[]>();
|
||||
str = SharedString.FromBuffer(value);
|
||||
}
|
||||
|
||||
if (str.ComputedKey == null)
|
||||
{
|
||||
var newShared = SharedString.FromBuffer(str.SharedValue);
|
||||
str.Key = newShared.ComputedKey;
|
||||
}
|
||||
|
||||
file.SharedStrings.Add(value.Key);
|
||||
file.SharedStrings.Add(str.Key);
|
||||
}
|
||||
|
||||
XmlElement propElement = doc.CreateElement(propType);
|
||||
@ -148,12 +154,43 @@ namespace RobloxFiles.XmlFormat
|
||||
foreach (string propName in orderedKeys)
|
||||
{
|
||||
Property prop = props[propName];
|
||||
XmlNode propNode = WriteProperty(prop, doc, file);
|
||||
bool isDefault = false;
|
||||
|
||||
if (propNode == null)
|
||||
continue;
|
||||
object a = DefaultProperty.Get(instance, prop);
|
||||
object b = prop.Value;
|
||||
|
||||
propsNode.AppendChild(propNode);
|
||||
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;
|
||||
|
||||
isDefault = d0.FuzzyEquals(d1);
|
||||
}
|
||||
else if (b != null)
|
||||
{
|
||||
isDefault = b.Equals(a);
|
||||
}
|
||||
else if (a == b)
|
||||
{
|
||||
isDefault = true;
|
||||
}
|
||||
|
||||
if (!isDefault)
|
||||
{
|
||||
XmlNode propNode = WriteProperty(prop, doc, file);
|
||||
|
||||
if (propNode == null)
|
||||
continue;
|
||||
|
||||
propsNode.AppendChild(propNode);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Instance child in instance.GetChildren())
|
||||
|
@ -18,16 +18,20 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
|
||||
|
||||
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
|
||||
{
|
||||
var value = prop.CastValue<SharedString>();
|
||||
string key = value.Key;
|
||||
var value = prop.Value as SharedString;
|
||||
|
||||
if (value.ComputedKey == null)
|
||||
if (value != null)
|
||||
{
|
||||
var newShared = SharedString.FromBuffer(value.SharedValue);
|
||||
key = newShared.ComputedKey;
|
||||
}
|
||||
string key = value.Key;
|
||||
|
||||
node.InnerText = key;
|
||||
if (value.ComputedKey == null)
|
||||
{
|
||||
var newShared = SharedString.FromBuffer(value.SharedValue);
|
||||
key = newShared.ComputedKey;
|
||||
}
|
||||
|
||||
node.InnerText = key;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ namespace RobloxFiles
|
||||
|
||||
private Dictionary<string, string> RawMetadata = new Dictionary<string, string>();
|
||||
public Dictionary<string, string> Metadata => RawMetadata;
|
||||
internal int RefCounter = 0;
|
||||
|
||||
public XmlRobloxFile()
|
||||
{
|
||||
@ -121,11 +122,12 @@ namespace RobloxFiles
|
||||
public override void Save(Stream stream)
|
||||
{
|
||||
XmlDocument doc = new XmlDocument();
|
||||
|
||||
|
||||
XmlElement roblox = doc.CreateElement("roblox");
|
||||
roblox.SetAttribute("version", "4");
|
||||
doc.AppendChild(roblox);
|
||||
|
||||
|
||||
RefCounter = 0;
|
||||
Instances.Clear();
|
||||
SharedStrings.Clear();
|
||||
|
||||
|
@ -4,5 +4,6 @@
|
||||
<package id="Fody" version="6.2.0" targetFramework="net472" developmentDependency="true" />
|
||||
<package id="Konscious.Security.Cryptography.Blake2" version="1.0.9" targetFramework="net472" />
|
||||
<package id="lz4net" version="1.0.15.93" targetFramework="net452" />
|
||||
<package id="Newtonsoft.Json" version="12.0.3" targetFramework="net472" />
|
||||
<package id="System.Numerics.Vectors" version="4.5.0" targetFramework="net472" />
|
||||
</packages>
|
Loading…
Reference in New Issue
Block a user