diff --git a/BinaryFormat/BinaryFileChunk.cs b/BinaryFormat/BinaryFileChunk.cs
index 400cb12..60d5a0a 100644
--- a/BinaryFormat/BinaryFileChunk.cs
+++ b/BinaryFormat/BinaryFileChunk.cs
@@ -35,9 +35,8 @@ namespace RobloxFiles.BinaryFormat
public override string ToString()
{
string chunkType = ChunkType.Replace('\0', ' ');
- int bytes = (HasCompressedData ? CompressedSize : Size);
- return $"'{chunkType}' Chunk ({bytes} bytes)";
+ return $"'{chunkType}' Chunk ({Size} bytes) [{Handler?.ToString()}]";
}
public BinaryRobloxFileChunk(BinaryRobloxFileReader reader)
diff --git a/BinaryFormat/BinaryRobloxFile.cs b/BinaryFormat/BinaryRobloxFile.cs
index ac43750..ec278fa 100644
--- a/BinaryFormat/BinaryRobloxFile.cs
+++ b/BinaryFormat/BinaryRobloxFile.cs
@@ -4,9 +4,11 @@ using System.IO;
using System.Linq;
using System.Text;
+using RobloxFiles.BinaryFormat;
using RobloxFiles.BinaryFormat.Chunks;
+using RobloxFiles.DataTypes;
-namespace RobloxFiles.BinaryFormat
+namespace RobloxFiles
{
public class BinaryRobloxFile : RobloxFile
{
@@ -14,7 +16,7 @@ namespace RobloxFiles.BinaryFormat
public const string MagicHeader = " GetType().Name;
public Instance[] Instances;
- public INST[] Types;
+ public INST[] Classes;
internal META META = null;
internal SSTR SSTR = null;
@@ -32,9 +34,9 @@ namespace RobloxFiles.BinaryFormat
public Dictionary Metadata => META?.Data;
public bool HasSharedStrings => (SSTR != null);
- public IReadOnlyDictionary SharedStrings => SSTR?.Strings;
+ public IReadOnlyDictionary SharedStrings => SSTR?.Strings;
- internal BinaryRobloxFile()
+ public BinaryRobloxFile()
{
Name = "BinaryRobloxFile";
ParentLocked = true;
@@ -55,14 +57,14 @@ namespace RobloxFiles.BinaryFormat
// Read header data.
Version = reader.ReadUInt16();
- NumTypes = reader.ReadUInt32();
+ NumClasses = reader.ReadUInt32();
NumInstances = reader.ReadUInt32();
Reserved = reader.ReadInt64();
// Begin reading the file chunks.
bool reading = true;
- Types = new INST[NumTypes];
+ Classes = new INST[NumClasses];
Instances = new Instance[NumInstances];
while (reading)
@@ -92,6 +94,7 @@ namespace RobloxFiles.BinaryFormat
handler = new SSTR();
break;
case "END\0":
+ Chunks.Add(chunk);
reading = false;
break;
default:
@@ -129,42 +132,35 @@ namespace RobloxFiles.BinaryFormat
Chunks.Clear();
NumInstances = 0;
- NumTypes = 0;
+ NumClasses = 0;
SSTR = null;
- // Record all instances and types.
+ // Recursively capture all instances and classes.
writer.RecordInstances(Children);
- // Apply the type values.
- INST.ApplyTypeMap(writer);
+ // Apply the recorded instances and classes.
+ writer.ApplyClassMap();
// Write the INST chunks.
- foreach (INST type in Types)
- {
- var instChunk = type.SaveAsChunk(writer);
- Chunks.Add(instChunk);
- }
+ foreach (INST inst in Classes)
+ writer.SaveChunk(inst);
// Write the PROP chunks.
- foreach (INST type in Types)
+ foreach (INST inst in Classes)
{
- Dictionary props = PROP.CollectProperties(writer, type);
+ Dictionary props = PROP.CollectProperties(writer, inst);
foreach (string propName in props.Keys)
{
PROP prop = props[propName];
-
- var chunk = prop.SaveAsChunk(writer);
- Chunks.Add(chunk);
+ writer.SaveChunk(prop);
}
}
// Write the PRNT chunk.
PRNT parents = new PRNT();
-
- var parentChunk = parents.SaveAsChunk(writer);
- Chunks.Add(parentChunk);
-
+ writer.SaveChunk(parents);
+
// Write the SSTR chunk.
if (HasSharedStrings)
{
@@ -180,15 +176,12 @@ namespace RobloxFiles.BinaryFormat
}
// Write the END_ chunk.
- writer.StartWritingChunk("END\0");
- writer.WriteString("", true);
-
- var endChunk = writer.FinishWritingChunk(false);
+ var endChunk = writer.WriteEndChunk();
Chunks.Add(endChunk);
}
//////////////////////////////////////////////////////////////////////////
- // Write the chunks with the header & footer data
+ // Write the chunk buffers with the header data
//////////////////////////////////////////////////////////////////////////
using (BinaryWriter writer = new BinaryWriter(stream))
@@ -196,20 +189,15 @@ namespace RobloxFiles.BinaryFormat
stream.Position = 0;
stream.SetLength(0);
- byte[] magicHeader = MagicHeader
+ writer.Write(MagicHeader
.Select(ch => (byte)ch)
- .ToArray();
-
- writer.Write(magicHeader);
+ .ToArray());
writer.Write(Version);
- writer.Write(NumTypes);
+ writer.Write(NumClasses);
writer.Write(NumInstances);
+ writer.Write(Reserved);
- // Write the 8 reserved-bytes.
- writer.Write(0L);
-
- // Write all of the chunks.
foreach (BinaryRobloxFileChunk chunk in Chunks)
{
if (chunk.HasWriteBuffer)
diff --git a/BinaryFormat/Chunks/INST.cs b/BinaryFormat/Chunks/INST.cs
index fff8620..f89535b 100644
--- a/BinaryFormat/Chunks/INST.cs
+++ b/BinaryFormat/Chunks/INST.cs
@@ -1,12 +1,12 @@
-using System.Collections.Generic;
-using System.Linq;
+using System;
+using System.Collections.Generic;
namespace RobloxFiles.BinaryFormat.Chunks
{
public class INST : IBinaryFileChunk
{
- public int TypeIndex { get; internal set; }
- public string TypeName { get; internal set; }
+ public int ClassIndex { get; internal set; }
+ public string ClassName { get; internal set; }
public bool IsService { get; internal set; }
public List RootedServices { get; internal set; }
@@ -14,17 +14,14 @@ namespace RobloxFiles.BinaryFormat.Chunks
public int NumInstances { get; internal set; }
public List InstanceIds { get; internal set; }
- public override string ToString()
- {
- return TypeName;
- }
-
+ public override string ToString() => ClassName;
+
public void LoadFromReader(BinaryRobloxFileReader reader)
{
BinaryRobloxFile file = reader.File;
- TypeIndex = reader.ReadInt32();
- TypeName = reader.ReadString();
+ ClassIndex = reader.ReadInt32();
+ ClassName = reader.ReadString();
IsService = reader.ReadBoolean();
NumInstances = reader.ReadInt32();
@@ -44,32 +41,30 @@ namespace RobloxFiles.BinaryFormat.Chunks
for (int i = 0; i < NumInstances; i++)
{
int instId = InstanceIds[i];
+ Type instType = Type.GetType($"RobloxFiles.{ClassName}") ?? typeof(Instance);
- var inst = new Instance()
- {
- ClassName = TypeName,
- IsService = IsService,
- Referent = instId.ToString()
- };
-
+ var inst = Activator.CreateInstance(instType) as Instance;
+ inst.Referent = instId.ToString();
+ inst.IsService = IsService;
+
if (IsService)
{
- bool rooted = RootedServices[i];
- inst.IsRootedService = rooted;
+ bool isRooted = RootedServices[i];
+ inst.Parent = (isRooted ? file : null);
}
file.Instances[instId] = inst;
}
- file.Types[TypeIndex] = this;
+ file.Classes[ClassIndex] = this;
}
public BinaryRobloxFileChunk SaveAsChunk(BinaryRobloxFileWriter writer)
{
writer.StartWritingChunk(this);
- writer.Write(TypeIndex);
- writer.WriteString(TypeName);
+ writer.Write(ClassIndex);
+ writer.WriteString(ClassName);
writer.Write(IsService);
writer.Write(NumInstances);
@@ -82,30 +77,13 @@ namespace RobloxFiles.BinaryFormat.Chunks
foreach (int instId in InstanceIds)
{
Instance service = file.Instances[instId];
- writer.Write(service.IsRootedService);
+ bool isRooted = (service.Parent == file);
+
+ writer.Write(isRooted);
}
}
return writer.FinishWritingChunk();
}
-
- internal static void ApplyTypeMap(BinaryRobloxFileWriter writer)
- {
- BinaryRobloxFile file = writer.File;
- file.Instances = writer.Instances.ToArray();
-
- var types = writer.TypeMap
- .OrderBy(type => type.Key)
- .Select(type => type.Value)
- .ToArray();
-
- for (int i = 0; i < types.Length; i++, file.NumTypes++)
- {
- INST type = types[i];
- type.TypeIndex = i;
- }
-
- file.Types = types;
- }
}
}
diff --git a/BinaryFormat/Chunks/PRNT.cs b/BinaryFormat/Chunks/PRNT.cs
index 4b6fd4a..23ba0f6 100644
--- a/BinaryFormat/Chunks/PRNT.cs
+++ b/BinaryFormat/Chunks/PRNT.cs
@@ -27,21 +27,21 @@ namespace RobloxFiles.BinaryFormat.Chunks
Instance child = file.Instances[childId];
child.Parent = (parentId >= 0 ? file.Instances[parentId] : file);
+ child.ParentLocked = child.IsService;
}
}
public BinaryRobloxFileChunk SaveAsChunk(BinaryRobloxFileWriter writer)
{
- BinaryRobloxFile file = writer.File;
writer.StartWritingChunk(this);
Format = 0;
- NumRelations = file.Instances.Length;
+ NumRelations = 0;
ChildrenIds = new List();
ParentIds = new List();
- foreach (Instance inst in file.Instances)
+ foreach (Instance inst in writer.PostInstances)
{
Instance parent = inst.Parent;
@@ -53,6 +53,8 @@ namespace RobloxFiles.BinaryFormat.Chunks
ChildrenIds.Add(childId);
ParentIds.Add(parentId);
+
+ NumRelations++;
}
writer.Write(Format);
diff --git a/BinaryFormat/Chunks/PROP.cs b/BinaryFormat/Chunks/PROP.cs
index 9021c02..4aa0102 100644
--- a/BinaryFormat/Chunks/PROP.cs
+++ b/BinaryFormat/Chunks/PROP.cs
@@ -1,50 +1,62 @@
using System;
using System.Collections.Generic;
using System.Linq;
-using System.Security.Cryptography;
+using System.Reflection;
+using System.Text;
using RobloxFiles.Enums;
using RobloxFiles.DataTypes;
using RobloxFiles.Utility;
-using System.Text;
namespace RobloxFiles.BinaryFormat.Chunks
{
public class PROP : IBinaryFileChunk
{
public string Name { get; internal set; }
- public int TypeIndex { get; internal set; }
+
+ public int ClassIndex { get; internal set; }
+ public string ClassName { get; private set; }
public PropertyType Type { get; internal set; }
+
public byte TypeId
{
get { return (byte)Type; }
internal set { Type = (PropertyType)value; }
}
-
+
+ public override string ToString()
+ {
+ return $"{Type} {ClassName}.{Name}";
+ }
+
public void LoadFromReader(BinaryRobloxFileReader reader)
{
BinaryRobloxFile file = reader.File;
- TypeIndex = reader.ReadInt32();
+ ClassIndex = reader.ReadInt32();
Name = reader.ReadString();
- TypeId = reader.ReadByte();
- INST type = file.Types[TypeIndex];
- Property[] props = new Property[type.NumInstances];
+ byte propType = reader.ReadByte();
+ Type = (PropertyType)propType;
+
+ INST inst = file.Classes[ClassIndex];
+ ClassName = inst.ClassName;
- var ids = type.InstanceIds;
- int instCount = type.NumInstances;
+ Property[] props = new Property[inst.NumInstances];
+
+ var ids = inst.InstanceIds;
+ int instCount = inst.NumInstances;
for (int i = 0; i < instCount; i++)
{
int id = ids[i];
- Instance inst = file.Instances[id];
+ Instance instance = file.Instances[id];
- Property prop = new Property(inst, this);
+ Property prop = new Property(instance, this);
props[i] = prop;
- inst.AddProperty(ref prop);
+ instance.AddProperty(ref prop);
}
// Setup some short-hand functions for actions used during the read procedure.
@@ -66,14 +78,36 @@ namespace RobloxFiles.BinaryFormat.Chunks
case PropertyType.String:
readProperties(i =>
{
- string result = reader.ReadString();
-
+ string value = reader.ReadString();
+
// Leave an access point for the original byte sequence, in case this is a BinaryString.
// This will allow the developer to read the sequence without any mangling from C# strings.
byte[] buffer = reader.GetLastStringBuffer();
props[i].RawBuffer = buffer;
- return result;
+ // Check if this is going to be casted as a BinaryString.
+ // BinaryStrings should use a type of byte[] instead.
+
+ Property prop = props[i];
+ Instance instance = prop.Instance;
+
+ Type instType = instance.GetType();
+ FieldInfo field = instType.GetField(Name);
+
+ if (field != null)
+ {
+ object result = value;
+ Type fieldType = field.FieldType;
+
+ if (fieldType == typeof(byte[]))
+ result = buffer;
+
+ return result;
+ }
+ else
+ {
+ return value;
+ }
});
break;
@@ -213,15 +247,16 @@ namespace RobloxFiles.BinaryFormat.Chunks
case PropertyType.Quaternion:
// Temporarily load the rotation matrices into their properties.
// We'll update them to CFrames once we iterate over the position data.
+ float[][] matrices = new float[instCount][];
- readProperties(i =>
+ for (int i = 0; i < instCount; i++)
{
- byte b_OrientId = reader.ReadByte();
+ byte rawOrientId = reader.ReadByte();
- if (b_OrientId > 0)
+ if (rawOrientId > 0)
{
// Make sure this value is in a safe range.
- int orientId = (b_OrientId - 1) % 36;
+ int orientId = (rawOrientId - 1) % 36;
NormalId xColumn = (NormalId)(orientId / 6);
Vector3 R0 = Vector3.FromNormalId(xColumn);
@@ -232,8 +267,8 @@ namespace RobloxFiles.BinaryFormat.Chunks
// Compute R2 using the cross product of R0 and R1.
Vector3 R2 = R0.Cross(R1);
- // Generate the rotation matrix and return it.
- return new float[9]
+ // Generate the rotation matrix.
+ matrices[i] = new float[9]
{
R0.X, R0.Y, R0.Z,
R1.X, R1.Y, R1.Z,
@@ -247,8 +282,7 @@ namespace RobloxFiles.BinaryFormat.Chunks
Quaternion quaternion = new Quaternion(qx, qy, qz, qw);
var rotation = quaternion.ToCFrame();
-
- return rotation.GetComponents();
+ matrices[i] = rotation.GetComponents();
}
else
{
@@ -260,9 +294,9 @@ namespace RobloxFiles.BinaryFormat.Chunks
matrix[m] = value;
}
- return matrix;
+ matrices[i] = matrix;
}
- });
+ }
float[] CFrame_X = readFloats(),
CFrame_Y = readFloats(),
@@ -270,25 +304,54 @@ namespace RobloxFiles.BinaryFormat.Chunks
readProperties(i =>
{
- float[] matrix = props[i].Value as float[];
+ float[] matrix = matrices[i];
float x = CFrame_X[i],
y = CFrame_Y[i],
z = CFrame_Z[i];
- float[] position = new float[3] { x, y, z };
- float[] components = position.Concat(matrix).ToArray();
+ float[] components;
+
+ if (matrix.Length == 12)
+ {
+ matrix[0] = x;
+ matrix[1] = y;
+ matrix[2] = z;
+
+ components = matrix;
+ }
+ else
+ {
+ float[] position = new float[3] { x, y, z };
+ components = position.Concat(matrix).ToArray();
+ }
return new CFrame(components);
});
break;
case PropertyType.Enum:
- // TODO: I want to map these values to actual Roblox enums, but I'll have to add an
- // interpreter for the JSON API Dump to do it properly.
-
uint[] enums = reader.ReadUInts(instCount);
- readProperties(i => enums[i]);
+
+ readProperties(i =>
+ {
+ Property prop = props[i];
+ Instance instance = prop.Instance;
+
+ Type instType = instance.GetType();
+ uint value = enums[i];
+
+ try
+ {
+ FieldInfo info = instType.GetField(Name, Property.BindingFlags);
+ return Enum.Parse(info.FieldType, value.ToInvariantString());
+ }
+ catch
+ {
+ Console.WriteLine($"Enum cast failed for {inst.ClassName}.{Name} using value {value}!");
+ return value;
+ }
+ });
break;
case PropertyType.Ref:
@@ -345,9 +408,9 @@ namespace RobloxFiles.BinaryFormat.Chunks
B = reader.ReadFloat();
Color3 Value = new Color3(R, G, B);
- byte[] Reserved = reader.ReadBytes(4);
+ int Envelope = reader.ReadInt32();
- keypoints[key] = new ColorSequenceKeypoint(Time, Value, Reserved);
+ keypoints[key] = new ColorSequenceKeypoint(Time, Value, Envelope);
}
return new ColorSequence(keypoints);
@@ -415,7 +478,8 @@ namespace RobloxFiles.BinaryFormat.Chunks
g = Color3uint8_G[i],
b = Color3uint8_B[i];
- return Color3.FromRGB(r, g, b);
+ Color3uint8 result = Color3.FromRGB(r, g, b);
+ return result;
});
break;
@@ -434,6 +498,7 @@ namespace RobloxFiles.BinaryFormat.Chunks
readProperties(i =>
{
uint key = SharedKeys[i];
+
return file.SharedStrings[key];
});
@@ -455,11 +520,9 @@ namespace RobloxFiles.BinaryFormat.Chunks
foreach (int instId in inst.InstanceIds)
{
Instance instance = file.Instances[instId];
+ var props = instance.RefreshProperties();
- var props = instance.Properties;
- var propNames = props.Keys;
-
- foreach (string propName in propNames)
+ foreach (string propName in props.Keys)
{
if (!propMap.ContainsKey(propName))
{
@@ -469,7 +532,8 @@ namespace RobloxFiles.BinaryFormat.Chunks
{
Name = prop.Name,
Type = prop.Type,
- TypeIndex = inst.TypeIndex
+
+ ClassIndex = inst.ClassIndex
};
propMap.Add(propName, propChunk);
@@ -484,13 +548,13 @@ namespace RobloxFiles.BinaryFormat.Chunks
{
BinaryRobloxFile file = writer.File;
- INST inst = file.Types[TypeIndex];
+ INST inst = file.Classes[ClassIndex];
var props = new List();
foreach (int instId in inst.InstanceIds)
{
Instance instance = file.Instances[instId];
- Property prop = instance.GetProperty(Name);
+ Property prop = instance.Properties[Name];
if (prop == null)
throw new Exception($"Property {Name} must be defined in {instance.GetFullName()}!");
@@ -502,7 +566,7 @@ namespace RobloxFiles.BinaryFormat.Chunks
}
writer.StartWritingChunk(this);
- writer.Write(TypeIndex);
+ writer.Write(ClassIndex);
writer.WriteString(Name);
writer.Write(TypeId);
@@ -526,7 +590,12 @@ namespace RobloxFiles.BinaryFormat.Chunks
break;
case PropertyType.Bool:
- props.ForEach(prop => prop.WriteValue());
+ props.ForEach(prop =>
+ {
+ bool value = prop.CastValue();
+ writer.Write(value);
+ });
+
break;
case PropertyType.Int:
var ints = new List();
@@ -737,7 +806,15 @@ namespace RobloxFiles.BinaryFormat.Chunks
props.ForEach(prop =>
{
- uint value = prop.CastValue();
+ if (prop.Value is uint)
+ {
+ uint raw = prop.CastValue();
+ Enums.Add(raw);
+ return;
+ }
+
+ int signed = (int)prop.Value;
+ uint value = (uint)signed;
Enums.Add(value);
});
@@ -805,7 +882,7 @@ namespace RobloxFiles.BinaryFormat.Chunks
writer.Write(color.G);
writer.Write(color.B);
- writer.Write(0);
+ writer.Write(keyPoint.Envelope);
}
});
@@ -873,21 +950,20 @@ namespace RobloxFiles.BinaryFormat.Chunks
props.ForEach(prop =>
{
- Color3 value = prop.CastValue();
-
- byte r = (byte)(value.R * 255);
- Color3uint8_R.Add(r);
-
- byte g = (byte)(value.G * 255);
- Color3uint8_G.Add(g);
-
- byte b = (byte)(value.B * 255);
- Color3uint8_B.Add(b);
+ Color3uint8 value = prop.CastValue();
+ Color3uint8_R.Add(value.R);
+ Color3uint8_G.Add(value.G);
+ Color3uint8_B.Add(value.B);
});
- writer.Write(Color3uint8_R.ToArray());
- writer.Write(Color3uint8_G.ToArray());
- writer.Write(Color3uint8_B.ToArray());
+ byte[] rBuffer = Color3uint8_R.ToArray();
+ writer.Write(rBuffer);
+
+ byte[] gBuffer = Color3uint8_G.ToArray();
+ writer.Write(gBuffer);
+
+ byte[] bBuffer = Color3uint8_B.ToArray();
+ writer.Write(bBuffer);
break;
case PropertyType.Int64:
@@ -918,27 +994,18 @@ namespace RobloxFiles.BinaryFormat.Chunks
props.ForEach(prop =>
{
- uint sharedKey = 0;
-
- string value = prop.CastValue();
- byte[] buffer = Encoding.UTF8.GetBytes(value);
-
- using (MD5 md5 = MD5.Create())
+ SharedString shared = prop.CastValue();
+ string key = shared.MD5_Key;
+
+ if (!sstr.Lookup.ContainsKey(key))
{
- byte[] hash = md5.ComputeHash(buffer);
- string key = Convert.ToBase64String(hash);
-
- if (!sstr.Lookup.ContainsKey(key))
- {
- uint id = (uint)(sstr.NumHashes++);
- sstr.Strings.Add(id, value);
- sstr.Lookup.Add(key, id);
- }
-
- sharedKey = sstr.Lookup[key];
+ uint id = (uint)(sstr.NumHashes++);
+ sstr.Strings.Add(id, shared);
+ sstr.Lookup.Add(key, id);
}
- sharedKeys.Add(sharedKey);
+ uint hashId = sstr.Lookup[key];
+ sharedKeys.Add(hashId);
});
writer.WriteInterleaved(sharedKeys);
diff --git a/BinaryFormat/Chunks/SSTR.cs b/BinaryFormat/Chunks/SSTR.cs
index 54f02ea..32c5bee 100644
--- a/BinaryFormat/Chunks/SSTR.cs
+++ b/BinaryFormat/Chunks/SSTR.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using RobloxFiles.DataTypes;
namespace RobloxFiles.BinaryFormat.Chunks
{
@@ -9,7 +10,7 @@ namespace RobloxFiles.BinaryFormat.Chunks
public int NumHashes;
public Dictionary Lookup = new Dictionary();
- public Dictionary Strings = new Dictionary();
+ public Dictionary Strings = new Dictionary();
public void LoadFromReader(BinaryRobloxFileReader reader)
{
@@ -25,10 +26,8 @@ namespace RobloxFiles.BinaryFormat.Chunks
int length = reader.ReadInt32();
byte[] data = reader.ReadBytes(length);
- string key = Convert.ToBase64String(md5);
- string value = Convert.ToBase64String(data);
-
- Lookup.Add(key, id);
+ SharedString value = SharedString.FromBuffer(data);
+ Lookup.Add(value.MD5_Key, id);
Strings.Add(id, value);
}
@@ -49,8 +48,8 @@ namespace RobloxFiles.BinaryFormat.Chunks
byte[] md5 = Convert.FromBase64String(key);
writer.Write(md5);
- string value = Strings[pair.Value];
- byte[] buffer = Convert.FromBase64String(value);
+ SharedString value = Strings[pair.Value];
+ byte[] buffer = SharedString.FindRecord(value.MD5_Key);
writer.Write(buffer.Length);
writer.Write(buffer);
diff --git a/BinaryFormat/IO/BinaryFileWriter.cs b/BinaryFormat/IO/BinaryFileWriter.cs
index 6c6efbc..e350527 100644
--- a/BinaryFormat/IO/BinaryFileWriter.cs
+++ b/BinaryFormat/IO/BinaryFileWriter.cs
@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
+using System.Globalization;
using System.IO;
+using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
@@ -16,10 +18,15 @@ namespace RobloxFiles.BinaryFormat
public string ChunkType { get; private set; }
public long ChunkStart { get; private set; }
- public Dictionary TypeMap;
+ public Dictionary ClassMap;
public readonly BinaryRobloxFile File;
+
+ // Instances in parent->child order
public List Instances;
+ // Instances in child->parent order
+ public List PostInstances;
+
public BinaryRobloxFileWriter(BinaryRobloxFile file, Stream workBuffer = null) : base(workBuffer ?? new MemoryStream())
{
File = file;
@@ -28,7 +35,9 @@ namespace RobloxFiles.BinaryFormat
ChunkType = "";
Instances = new List();
- TypeMap = new Dictionary();
+ PostInstances = new List();
+
+ ClassMap = new Dictionary();
}
private static byte[] GetBytes(T value, int bufferSize, IntPtr converter)
@@ -144,36 +153,64 @@ namespace RobloxFiles.BinaryFormat
{
foreach (Instance instance in instances)
{
- int instId = (int)(File.NumInstances++);
+ if (!instance.Archivable)
+ continue;
+ int instId = (int)(File.NumInstances++);
instance.Referent = instId.ToString();
Instances.Add(instance);
string className = instance.ClassName;
INST inst = null;
- if (!TypeMap.ContainsKey(className))
+ if (!ClassMap.ContainsKey(className))
{
inst = new INST()
{
- TypeName = className,
+ ClassName = className,
InstanceIds = new List(),
IsService = instance.IsService
};
- TypeMap.Add(className, inst);
+ ClassMap.Add(className, inst);
}
else
{
- inst = TypeMap[className];
+ inst = ClassMap[className];
}
inst.NumInstances++;
inst.InstanceIds.Add(instId);
RecordInstances(instance.GetChildren());
+ PostInstances.Add(instance);
}
}
+
+ internal void ApplyClassMap()
+ {
+ File.Instances = Instances.ToArray();
+
+ var classNames = ClassMap
+ .Select(type => type.Key)
+ .ToList();
+
+ classNames.Sort(StringComparer.Ordinal);
+
+ var classes = classNames
+ .Select(className => ClassMap[className])
+ .ToArray();
+
+ for (int i = 0; i < classes.Length; i++, File.NumClasses++)
+ {
+ string className = classNames[i];
+
+ INST inst = ClassMap[className];
+ inst.ClassIndex = i;
+ }
+
+ File.Classes = classes;
+ }
// Marks that we are writing a chunk.
public bool StartWritingChunk(string chunkType)
@@ -236,5 +273,19 @@ namespace RobloxFiles.BinaryFormat
return chunk;
}
+
+ public void SaveChunk(IBinaryFileChunk handler)
+ {
+ var chunk = handler.SaveAsChunk(this);
+ File.Chunks.Add(chunk);
+ }
+
+ public BinaryRobloxFileChunk WriteEndChunk()
+ {
+ StartWritingChunk("END\0");
+ WriteString("", true);
+
+ return FinishWritingChunk(false);
+ }
}
}
diff --git a/DataTypes/CFrame.cs b/DataTypes/CFrame.cs
index bd4295b..74903f2 100644
--- a/DataTypes/CFrame.cs
+++ b/DataTypes/CFrame.cs
@@ -14,11 +14,23 @@ namespace RobloxFiles.DataTypes
public float Y => m24;
public float Z => m34;
- public Vector3 Position => new Vector3(X, Y, Z);
+ public Vector3 Position
+ {
+ get
+ {
+ return new Vector3(X, Y, Z);
+ }
+ set
+ {
+ m14 = value.X;
+ m24 = value.Y;
+ m34 = value.Z;
+ }
+ }
- public Vector3 LookVector => new Vector3(-m13, -m23, -m33);
- public Vector3 RightVector => new Vector3( m11, m21, m31);
- public Vector3 UpVector => new Vector3( m12, m22, m32);
+ public Vector3 RightVector => new Vector3( m11, m12, m13);
+ public Vector3 UpVector => new Vector3( m21, m22, m23);
+ public Vector3 LookVector => new Vector3(-m31, -m32, -m33);
public CFrame()
{
@@ -41,7 +53,6 @@ namespace RobloxFiles.DataTypes
m34 = nz;
}
-
public CFrame(Vector3 eye, Vector3 look)
{
Vector3 zAxis = (eye - look).Unit;
@@ -127,9 +138,9 @@ namespace RobloxFiles.DataTypes
{
float[] ac = a.GetComponents();
- float x = ac[0], y = ac[1], z = ac[2],
- m11 = ac[3], m12 = ac[4], m13 = ac[5],
- m21 = ac[6], m22 = ac[7], m23 = ac[8],
+ float x = ac[0], y = ac[1], z = ac[2],
+ m11 = ac[3], m12 = ac[4], m13 = ac[5],
+ m21 = ac[6], m22 = ac[7], m23 = ac[8],
m31 = ac[9], m32 = ac[10], m33 = ac[11];
return new CFrame(x - b.X, y - b.Y, z - b.Z, m11, m12, m13, m21, m22, m23, m31, m32, m33);
@@ -138,9 +149,10 @@ namespace RobloxFiles.DataTypes
public static Vector3 operator *(CFrame a, Vector3 b)
{
float[] ac = a.GetComponents();
- float x = ac[0], y = ac[1], z = ac[2],
- m11 = ac[3], m12 = ac[4], m13 = ac[5],
- m21 = ac[6], m22 = ac[7], m23 = ac[8],
+
+ float x = ac[0], y = ac[1], z = ac[2],
+ m11 = ac[3], m12 = ac[4], m13 = ac[5],
+ m21 = ac[6], m22 = ac[7], m23 = ac[8],
m31 = ac[9], m32 = ac[10], m33 = ac[11];
Vector3 right = new Vector3(m11, m21, m31);
@@ -154,14 +166,14 @@ namespace RobloxFiles.DataTypes
float[] ac = a.GetComponents();
float[] bc = b.GetComponents();
- float a14 = ac[0], a24 = ac[1], a34 = ac[2],
- a11 = ac[3], a12 = ac[4], a13 = ac[5],
- a21 = ac[6], a22 = ac[7], a23 = ac[8],
+ float a14 = ac[0], a24 = ac[1], a34 = ac[2],
+ a11 = ac[3], a12 = ac[4], a13 = ac[5],
+ a21 = ac[6], a22 = ac[7], a23 = ac[8],
a31 = ac[9], a32 = ac[10], a33 = ac[11];
- float b14 = bc[0], b24 = bc[1], b34 = bc[2],
- b11 = bc[3], b12 = bc[4], b13 = bc[5],
- b21 = bc[6], b22 = bc[7], b23 = bc[8],
+ float b14 = bc[0], b24 = bc[1], b34 = bc[2],
+ b11 = bc[3], b12 = bc[4], b13 = bc[5],
+ b21 = bc[6], b22 = bc[7], b23 = bc[8],
b31 = bc[9], b32 = bc[10], b33 = bc[11];
float n11 = a11 * b11 + a12 * b21 + a13 * b31 + a14 * m41;
@@ -345,14 +357,14 @@ namespace RobloxFiles.DataTypes
for (int i = 3; i < 12; i++)
{
- float t = matrix[i];
+ float t = Math.Abs(matrix[i]);
- if (Math.Abs(t - 1f) < 10e-5f)
+ if (t.FuzzyEquals(1))
{
// Approximately ±1
sum1++;
}
- else if (Math.Abs(t) < 10e-5f)
+ else if (t.FuzzyEquals(0))
{
// Approximately ±0
sum0++;
@@ -364,10 +376,10 @@ namespace RobloxFiles.DataTypes
private static bool IsLegalOrientId(int orientId)
{
- int xNormalAbs = (orientId / 6) % 3;
- int yNormalAbs = orientId % 3;
+ int xOrientId = (orientId / 6) % 3;
+ int yOrientId = orientId % 3;
- return (xNormalAbs != yNormalAbs);
+ return (xOrientId != yOrientId);
}
public int GetOrientId()
diff --git a/DataTypes/Color3uint8.cs b/DataTypes/Color3uint8.cs
new file mode 100644
index 0000000..a436a52
--- /dev/null
+++ b/DataTypes/Color3uint8.cs
@@ -0,0 +1,41 @@
+namespace RobloxFiles.DataTypes
+{
+ ///
+ /// Color3uint8 functions as an interconvertible storage medium for Color3 types.
+ /// It is used by property types that want their Color3 value encoded with bytes instead of floats.
+ ///
+ public class Color3uint8
+ {
+ public readonly byte R, G, B;
+
+ public Color3uint8(byte r = 0, byte g = 0, byte b = 0)
+ {
+ R = r;
+ G = g;
+ B = b;
+ }
+
+ public override string ToString()
+ {
+ return string.Join(", ", R, G, B);
+ }
+
+ public static implicit operator Color3(Color3uint8 color)
+ {
+ float r = color.R / 255f;
+ float g = color.G / 255f;
+ float b = color.B / 255f;
+
+ return new Color3(r, g, b);
+ }
+
+ public static implicit operator Color3uint8(Color3 color)
+ {
+ byte r = (byte)(color.R * 255);
+ byte g = (byte)(color.G * 255);
+ byte b = (byte)(color.B * 255);
+
+ return new Color3uint8(r, g, b);
+ }
+ }
+}
diff --git a/DataTypes/ColorSequence.cs b/DataTypes/ColorSequence.cs
index b2575ad..9609e64 100644
--- a/DataTypes/ColorSequence.cs
+++ b/DataTypes/ColorSequence.cs
@@ -6,12 +6,8 @@ namespace RobloxFiles.DataTypes
{
public readonly ColorSequenceKeypoint[] Keypoints;
- public ColorSequence(Color3 c)
+ public ColorSequence(Color3 c) : this(c, c)
{
- ColorSequenceKeypoint a = new ColorSequenceKeypoint(0, c);
- ColorSequenceKeypoint b = new ColorSequenceKeypoint(1, c);
-
- Keypoints = new ColorSequenceKeypoint[2] { a, b };
}
public ColorSequence(Color3 c0, Color3 c1)
@@ -35,10 +31,13 @@ namespace RobloxFiles.DataTypes
if (keypoints[key - 1].Time > keypoints[key].Time)
throw new Exception("ColorSequence: all keypoints must be ordered by time");
- if (Math.Abs(keypoints[0].Time) >= 10e-5f)
+ var first = keypoints[0];
+ var last = keypoints[numKeys - 1];
+
+ if (!first.Time.FuzzyEquals(0))
throw new Exception("ColorSequence must start at time=0.0");
- if (Math.Abs(keypoints[numKeys - 1].Time - 1f) >= 10e-5f)
+ if (!last.Time.FuzzyEquals(1))
throw new Exception("ColorSequence must end at time=1.0");
Keypoints = keypoints;
diff --git a/DataTypes/ColorSequenceKeypoint.cs b/DataTypes/ColorSequenceKeypoint.cs
index 4c007cd..04570d6 100644
--- a/DataTypes/ColorSequenceKeypoint.cs
+++ b/DataTypes/ColorSequenceKeypoint.cs
@@ -4,18 +4,18 @@
{
public readonly float Time;
public readonly Color3 Value;
- public readonly byte[] Reserved;
+ public readonly int Envelope;
- public ColorSequenceKeypoint(float time, Color3 value, byte[] reserved = null)
+ public ColorSequenceKeypoint(float time, Color3 value, int envelope = 0)
{
Time = time;
Value = value;
- Reserved = reserved;
+ Envelope = envelope;
}
public override string ToString()
{
- return string.Join(" ", Time, Value.R, Value.G, Value.B, 0);
+ return string.Join(" ", Time, Value.R, Value.G, Value.B, Envelope);
}
}
}
diff --git a/DataTypes/Content.cs b/DataTypes/Content.cs
new file mode 100644
index 0000000..4923be4
--- /dev/null
+++ b/DataTypes/Content.cs
@@ -0,0 +1,32 @@
+namespace RobloxFiles.DataTypes
+{
+ ///
+ /// Content is a type used by most url-based XML properties.
+ /// Here, it only exists as a wrapper class for strings.
+ ///
+ public class Content
+ {
+ // TODO: Maybe introduce constraints to the value?
+ public readonly string Data;
+
+ public override string ToString()
+ {
+ return Data;
+ }
+
+ public Content(string data)
+ {
+ Data = data;
+ }
+
+ public static implicit operator string(Content content)
+ {
+ return content.Data;
+ }
+
+ public static implicit operator Content(string data)
+ {
+ return new Content(data);
+ }
+ }
+}
diff --git a/DataTypes/NumberRange.cs b/DataTypes/NumberRange.cs
index 0c9d359..a95b210 100644
--- a/DataTypes/NumberRange.cs
+++ b/DataTypes/NumberRange.cs
@@ -7,6 +7,12 @@ namespace RobloxFiles.DataTypes
public readonly float Min;
public readonly float Max;
+ public NumberRange(float num)
+ {
+ Min = num;
+ Max = num;
+ }
+
public NumberRange(float min = 0, float max = 0)
{
if (max - min < 0)
diff --git a/DataTypes/NumberSequence.cs b/DataTypes/NumberSequence.cs
index 846d178..f789c0f 100644
--- a/DataTypes/NumberSequence.cs
+++ b/DataTypes/NumberSequence.cs
@@ -35,10 +35,13 @@ namespace RobloxFiles.DataTypes
if (keypoints[key - 1].Time > keypoints[key].Time)
throw new Exception("NumberSequence: all keypoints must be ordered by time");
- if (Math.Abs(keypoints[0].Time) >= 10e-5f)
+ var first = keypoints[0];
+ var last = keypoints[numKeys - 1];
+
+ if (!first.Time.FuzzyEquals(0))
throw new Exception("NumberSequence must start at time=0.0");
- if (Math.Abs(keypoints[numKeys - 1].Time - 1f) >= 10e-5f)
+ if (!last.Time.FuzzyEquals(1))
throw new Exception("NumberSequence must end at time=1.0");
Keypoints = keypoints;
diff --git a/DataTypes/ProtectedString.cs b/DataTypes/ProtectedString.cs
new file mode 100644
index 0000000..de6e8cd
--- /dev/null
+++ b/DataTypes/ProtectedString.cs
@@ -0,0 +1,31 @@
+namespace RobloxFiles.DataTypes
+{
+ ///
+ /// ProtectedString is a type used by some of the XML properties.
+ /// Here, it only exists as a wrapper class for strings.
+ ///
+ public class ProtectedString
+ {
+ public readonly string Value;
+
+ public override string ToString()
+ {
+ return Value;
+ }
+
+ public ProtectedString(string value)
+ {
+ Value = value;
+ }
+
+ public static implicit operator string(ProtectedString protectedString)
+ {
+ return protectedString.Value;
+ }
+
+ public static implicit operator ProtectedString(string value)
+ {
+ return new ProtectedString(value);
+ }
+ }
+}
diff --git a/DataTypes/Quaternion.cs b/DataTypes/Quaternion.cs
new file mode 100644
index 0000000..608027c
--- /dev/null
+++ b/DataTypes/Quaternion.cs
@@ -0,0 +1,208 @@
+using System;
+using RobloxFiles.DataTypes;
+
+namespace RobloxFiles.Utility
+{
+ ///
+ /// Quaternion is a utility used by the CFrame DataType to handle rotation interpolation.
+ /// It can be used as an independent Quaternion implementation if you so please!
+ ///
+ public class Quaternion
+ {
+ public readonly float X, Y, Z, W;
+
+ public float Magnitude
+ {
+ get
+ {
+ float squared = Dot(this);
+ double magnitude = Math.Sqrt(squared);
+
+ return (float)magnitude;
+ }
+ }
+
+ public Quaternion(float x, float y, float z, float w)
+ {
+ X = x;
+ Y = y;
+ Z = z;
+ W = w;
+ }
+
+ public Quaternion(Vector3 qv, float qw)
+ {
+ X = qv.X;
+ Y = qv.Y;
+ Z = qv.Z;
+ W = qw;
+ }
+
+ public Quaternion(CFrame cf)
+ {
+ CFrame matrix = (cf - cf.Position);
+ float[] ac = cf.GetComponents();
+
+ float m11 = ac[3], m12 = ac[4], m13 = ac[5],
+ m21 = ac[6], m22 = ac[7], m23 = ac[8],
+ m31 = ac[9], m32 = ac[10], m33 = ac[11];
+
+ float trace = m11 + m22 + m33;
+
+ if (trace > 0)
+ {
+ float s = (float)Math.Sqrt(1 + trace);
+ float r = 0.5f / s;
+
+ W = s * 0.5f;
+ X = (m32 - m23) * r;
+ Y = (m13 - m31) * r;
+ Z = (m21 - m12) * r;
+ }
+ else
+ {
+ float big = Math.Max(Math.Max(m11, m22), m33);
+
+ if (big == m11)
+ {
+ float s = (float)Math.Sqrt(1 + m11 - m22 - m33);
+ float r = 0.5f / s;
+
+ W = (m32 - m23) * r;
+ X = 0.5f * s;
+ Y = (m21 + m12) * r;
+ Z = (m13 + m31) * r;
+ }
+ else if (big == m22)
+ {
+ float s = (float)Math.Sqrt(1 - m11 + m22 - m33);
+ float r = 0.5f / s;
+
+ W = (m13 - m31) * r;
+ X = (m21 + m12) * r;
+ Y = 0.5f * s;
+ Z = (m32 + m23) * r;
+ }
+ else if (big == m33)
+ {
+ float s = (float)Math.Sqrt(1 - m11 - m22 + m33);
+ float r = 0.5f / s;
+
+ W = (m21 - m12) * r;
+ X = (m13 + m31) * r;
+ Y = (m32 + m23) * r;
+ Z = 0.5f * s;
+ }
+ }
+ }
+
+ public float Dot(Quaternion other)
+ {
+ return (X * other.X) + (Y * other.Y) + (Z * other.Z) + (W * other.W);
+ }
+
+ public Quaternion Lerp(Quaternion other, float alpha)
+ {
+ Quaternion result = this * (1.0f - alpha) + other * alpha;
+ return result / result.Magnitude;
+ }
+
+ public Quaternion Slerp(Quaternion other, float alpha)
+ {
+ float cosAng = Dot(other);
+
+ if (cosAng < 0)
+ {
+ other = -other;
+ cosAng = -cosAng;
+ }
+
+ double ang = Math.Acos(cosAng);
+
+ if (ang >= 0.05f)
+ {
+ float scale0 = (float)Math.Sin((1.0f - alpha) * ang);
+ float scale1 = (float)Math.Sin(alpha * ang);
+ float denom = (float)Math.Sin(ang);
+
+ return ((this * scale0) + (other * scale1)) / denom;
+ }
+ else
+ {
+ return Lerp(other, alpha);
+ }
+ }
+
+ public CFrame ToCFrame()
+ {
+ float xc = X * 2f;
+ float yc = Y * 2f;
+ float zc = Z * 2f;
+
+ float xx = X * xc;
+ float xy = X * yc;
+ float xz = X * zc;
+
+ float wx = W * xc;
+ float wy = W * yc;
+ float wz = W * zc;
+
+ float yy = Y * yc;
+ float yz = Y * zc;
+ float zz = Z * zc;
+
+ return new CFrame
+ (
+ 0, 0, 0,
+
+ 1f - (yy + zz),
+ xy - wz,
+ xz + wy,
+
+ xy + wz,
+ 1f - (xx + zz),
+ yz - wx,
+
+ xz - wy,
+ yz + wx,
+ 1f - (xx + yy)
+ );
+ }
+
+ public static Quaternion operator +(Quaternion a, Quaternion b)
+ {
+ return new Quaternion(a.X + b.X, a.Y + b.Y, a.Z + b.Z, a.W + b.W);
+ }
+
+ public static Quaternion operator -(Quaternion a, Quaternion b)
+ {
+ return new Quaternion(a.X - b.X, a.Y - b.Y, a.Z - b.Z, a.W - b.W);
+ }
+
+ public static Quaternion operator *(Quaternion a, float f)
+ {
+ return new Quaternion(a.X * f, a.Y * f, a.Z * f, a.W * f);
+ }
+
+ public static Quaternion operator /(Quaternion a, float f)
+ {
+ return new Quaternion(a.X / f, a.Y / f, a.Z / f, a.W / f);
+ }
+
+ public static Quaternion operator -(Quaternion a)
+ {
+ return new Quaternion(-a.X, -a.Y, -a.Z, -a.W);
+ }
+
+ public static Quaternion operator *(Quaternion a, Quaternion b)
+ {
+ Vector3 v1 = new Vector3(a.X, a.Y, a.Z);
+ float s1 = a.W;
+
+ Vector3 v2 = new Vector3(b.X, b.Y, b.Z);
+ float s2 = b.W;
+
+ return new Quaternion(s1 * v2 + s2 * v1 + v1.Cross(v2), s1 * s2 - v1.Dot(v2));
+ }
+ }
+}
diff --git a/DataTypes/Ray.cs b/DataTypes/Ray.cs
index 1496ee5..11ae42d 100644
--- a/DataTypes/Ray.cs
+++ b/DataTypes/Ray.cs
@@ -20,10 +20,10 @@
}
}
- public Ray(Vector3 origin, Vector3 direction)
+ public Ray(Vector3 origin = null, Vector3 direction = null)
{
- Origin = origin;
- Direction = direction;
+ Origin = origin ?? new Vector3();
+ Direction = direction ?? new Vector3();
}
public override string ToString()
diff --git a/DataTypes/SharedString.cs b/DataTypes/SharedString.cs
new file mode 100644
index 0000000..98a17ae
--- /dev/null
+++ b/DataTypes/SharedString.cs
@@ -0,0 +1,62 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Security.Cryptography;
+
+namespace RobloxFiles.DataTypes
+{
+ public class SharedString
+ {
+ private static Dictionary Records = new Dictionary();
+ public readonly string MD5_Key;
+
+ public byte[] SharedValue => FindRecord(MD5_Key);
+ public override string ToString() => $"MD5 Key: {MD5_Key}";
+
+ internal SharedString(string md5)
+ {
+ MD5_Key = md5;
+ }
+
+ private SharedString(byte[] buffer)
+ {
+ using (MD5 md5 = MD5.Create())
+ {
+ byte[] hash = md5.ComputeHash(buffer);
+ MD5_Key = Convert.ToBase64String(hash);
+ }
+
+ if (Records.ContainsKey(MD5_Key))
+ return;
+
+ Records.Add(MD5_Key, buffer);
+ }
+
+ public static byte[] FindRecord(string key)
+ {
+ byte[] result = null;
+
+ if (Records.ContainsKey(key))
+ result = Records[key];
+
+ return result;
+ }
+
+ public static SharedString FromBuffer(byte[] buffer)
+ {
+ return new SharedString(buffer);
+ }
+
+ public static SharedString FromString(string value)
+ {
+ byte[] buffer = Encoding.UTF8.GetBytes(value);
+ return new SharedString(buffer);
+ }
+
+ public static SharedString FromBase64(string base64)
+ {
+ byte[] buffer = Convert.FromBase64String(base64);
+ return new SharedString(buffer);
+ }
+ }
+}
diff --git a/DataTypes/Vector2.cs b/DataTypes/Vector2.cs
index c58b5df..ddb8289 100644
--- a/DataTypes/Vector2.cs
+++ b/DataTypes/Vector2.cs
@@ -28,7 +28,7 @@ namespace RobloxFiles.DataTypes
Y = y;
}
- public Vector2(float[] coords)
+ internal Vector2(float[] coords)
{
X = coords.Length > 0 ? coords[0] : 0;
Y = coords.Length > 1 ? coords[1] : 0;
@@ -69,6 +69,11 @@ namespace RobloxFiles.DataTypes
public static Vector2 operator /(Vector2 v, float n) => upcastFloatOp(v, n, div);
public static Vector2 operator /(float n, Vector2 v) => upcastFloatOp(n, v, div);
+ public static Vector2 operator -(Vector2 v)
+ {
+ return new Vector2(-v.X, -v.Y);
+ }
+
public static Vector2 Zero => new Vector2(0, 0);
public override string ToString()
diff --git a/DataTypes/Vector3.cs b/DataTypes/Vector3.cs
index 7aa0d76..65fe164 100644
--- a/DataTypes/Vector3.cs
+++ b/DataTypes/Vector3.cs
@@ -30,7 +30,7 @@ namespace RobloxFiles.DataTypes
Z = z;
}
- public Vector3(float[] coords)
+ internal Vector3(float[] coords)
{
X = coords.Length > 0 ? coords[0] : 0;
Y = coords.Length > 1 ? coords[1] : 0;
@@ -92,6 +92,11 @@ namespace RobloxFiles.DataTypes
public static Vector3 operator /(Vector3 v, float n) => upcastFloatOp(v, n, div);
public static Vector3 operator /(float n, Vector3 v) => upcastFloatOp(n, v, div);
+ public static Vector3 operator -(Vector3 v)
+ {
+ return new Vector3(-v.X, -v.Y, -v.Z);
+ }
+
public static Vector3 Zero => new Vector3(0, 0, 0);
public static Vector3 Right => new Vector3(1, 0, 0);
public static Vector3 Up => new Vector3(0, 1, 0);
@@ -141,7 +146,7 @@ namespace RobloxFiles.DataTypes
float dotProd = normal.Dot(this);
- if (Math.Abs(dotProd - 1f) < 10e-5f)
+ if (dotProd.FuzzyEquals(1))
{
result = i;
break;
diff --git a/Generated/Classes.cs b/Generated/Classes.cs
new file mode 100644
index 0000000..d6ae6be
--- /dev/null
+++ b/Generated/Classes.cs
@@ -0,0 +1,3130 @@
+// Auto-generated list of creatable Roblox classes.
+// Updated as of 0.391.0.313677
+
+using System;
+
+using RobloxFiles.DataTypes;
+using RobloxFiles.Enums;
+using RobloxFiles.Utility;
+
+namespace RobloxFiles
+{
+ public class ABTestService : Instance
+ {
+ public ABTestService()
+ {
+ IsService = true;
+ }
+ }
+
+ public class Accoutrement : Instance
+ {
+ public CFrame AttachmentPoint = new CFrame();
+ }
+
+ public class Accessory : Accoutrement
+ {
+ }
+
+ public class Hat : Accoutrement
+ {
+ }
+
+ public class AdService : Instance
+ {
+ public AdService()
+ {
+ IsService = true;
+ }
+ }
+
+ public class AdvancedDragger : Instance
+ {
+ }
+
+ public class AnalyticsService : Instance
+ {
+ public AnalyticsService()
+ {
+ IsService = true;
+ }
+
+ public string ApiKey = "";
+ }
+
+ public class Animation : Instance
+ {
+ public Content AnimationId = "";
+ }
+
+ public class AnimationController : Instance
+ {
+ }
+
+ public class Animator : Instance
+ {
+ }
+
+ public class AssetService : Instance
+ {
+ public AssetService()
+ {
+ IsService = true;
+ }
+ }
+
+ public class Attachment : Instance
+ {
+ public CFrame CFrame = new CFrame();
+ public bool Visible = false;
+ }
+
+ public class Backpack : Instance
+ {
+ }
+
+ public abstract class BackpackItem : Instance
+ {
+ public Content TextureId = "";
+ }
+
+ public class HopperBin : BackpackItem
+ {
+ public bool Active = false;
+ public BinType BinType = BinType.Script;
+ }
+
+ public class Tool : BackpackItem
+ {
+ public bool CanBeDropped = true;
+ public bool Enabled = true;
+ public CFrame Grip = new CFrame();
+ public bool ManualActivationOnly = false;
+ public bool RequiresHandle = true;
+ public string ToolTip = "";
+ }
+
+ public class Flag : Tool
+ {
+ public BrickColor TeamColor = BrickColor.FromNumber(194);
+ }
+
+ public class BadgeService : Instance
+ {
+ public BadgeService()
+ {
+ IsService = true;
+ }
+ }
+
+ public abstract class BasePlayerGui : Instance
+ {
+ }
+
+ public class CoreGui : BasePlayerGui
+ {
+ public CoreGui()
+ {
+ IsService = true;
+ }
+
+ public GuiObject SelectionImageObject = null;
+ }
+
+ public class StarterGui : BasePlayerGui
+ {
+ public StarterGui()
+ {
+ IsService = true;
+ }
+
+ [Obsolete]
+ public bool ResetPlayerGuiOnSpawn = true;
+
+ public ScreenOrientation ScreenOrientation = ScreenOrientation.LandscapeSensor;
+ public bool ShowDevelopmentGui = true;
+ }
+
+ public class Beam : Instance
+ {
+ public Attachment Attachment0 = null;
+ public Attachment Attachment1 = null;
+ public ColorSequence Color = new ColorSequence(new Color3(1, 1, 1));
+ public float CurveSize0 = 0;
+ public float CurveSize1 = 0;
+ public bool Enabled = true;
+ public bool FaceCamera = false;
+ public float LightEmission = 0;
+ public float LightInfluence = 0;
+ public int Segments = 10;
+ public Content Texture = "";
+ public float TextureLength = 1;
+ public TextureMode TextureMode = TextureMode.Stretch;
+ public float TextureSpeed = 1;
+ public NumberSequence Transparency = new NumberSequence(0.5f);
+ public float Width0 = 1;
+ public float Width1 = 1;
+ public float ZOffset = 0;
+ }
+
+ public class BindableEvent : Instance
+ {
+ }
+
+ public class BindableFunction : Instance
+ {
+ }
+
+ public abstract class BodyMover : Instance
+ {
+ }
+
+ public class BodyAngularVelocity : BodyMover
+ {
+ public Vector3 AngularVelocity = new Vector3(0, 2, 0);
+ public Vector3 MaxTorque = new Vector3(4000, 4000, 4000);
+ public float P = 1250;
+
+ [Obsolete]
+ public Vector3 angularvelocity
+ {
+ get { return AngularVelocity; }
+ set { AngularVelocity = value; }
+ }
+
+ [Obsolete]
+ public Vector3 maxTorque
+ {
+ get { return MaxTorque; }
+ set { MaxTorque = value; }
+ }
+ }
+
+ public class BodyForce : BodyMover
+ {
+ public Vector3 Force = new Vector3(0, 1, 0);
+
+ [Obsolete]
+ public Vector3 force
+ {
+ get { return Force; }
+ set { Force = value; }
+ }
+ }
+
+ public class BodyGyro : BodyMover
+ {
+ public CFrame CFrame = new CFrame();
+ public float D = 500;
+ public Vector3 MaxTorque = new Vector3(400000, 0, 400000);
+ public float P = 3000;
+
+ [Obsolete]
+ public CFrame cframe
+ {
+ get { return CFrame; }
+ set { CFrame = value; }
+ }
+
+ [Obsolete]
+ public Vector3 maxTorque
+ {
+ get { return MaxTorque; }
+ set { MaxTorque = value; }
+ }
+ }
+
+ public class BodyPosition : BodyMover
+ {
+ public float D = 1250;
+ public Vector3 MaxForce = new Vector3(4000, 4000, 4000);
+ public float P = 10000;
+ public Vector3 Position = new Vector3(0, 50, 0);
+
+ [Obsolete]
+ public Vector3 maxForce
+ {
+ get { return MaxForce; }
+ set { MaxForce = value; }
+ }
+
+ [Obsolete]
+ public Vector3 position
+ {
+ get { return Position; }
+ set { Position = value; }
+ }
+ }
+
+ public class BodyThrust : BodyMover
+ {
+ public Vector3 Force = new Vector3(0, 1, 0);
+ public Vector3 Location = new Vector3();
+
+ [Obsolete]
+ public Vector3 force
+ {
+ get { return Force; }
+ set { Force = value; }
+ }
+
+ [Obsolete]
+ public Vector3 location
+ {
+ get { return Location; }
+ set { Location = value; }
+ }
+ }
+
+ public class BodyVelocity : BodyMover
+ {
+ public Vector3 MaxForce = new Vector3(4000, 4000, 4000);
+ public float P = 1250;
+ public Vector3 Velocity = new Vector3(0, 2, 0);
+
+ [Obsolete]
+ public Vector3 maxForce
+ {
+ get { return MaxForce; }
+ set { MaxForce = value; }
+ }
+
+ [Obsolete]
+ public Vector3 velocity
+ {
+ get { return Velocity; }
+ set { Velocity = value; }
+ }
+ }
+
+ public class RocketPropulsion : BodyMover
+ {
+ public float CartoonFactor = 0.7f;
+ public float MaxSpeed = 30;
+ public float MaxThrust = 4000;
+ public Vector3 MaxTorque = new Vector3(400000, 400000, 0);
+ public BasePart Target = null;
+ public Vector3 TargetOffset = new Vector3();
+ public float TargetRadius = 4;
+ public float ThrustD = 0.001f;
+ public float ThrustP = 5;
+ public float TurnD = 500;
+ public float TurnP = 3000;
+ }
+
+ public abstract class CacheableContentProvider : Instance
+ {
+ public CacheableContentProvider()
+ {
+ IsService = true;
+ }
+ }
+
+ public class MeshContentProvider : CacheableContentProvider
+ {
+ public MeshContentProvider()
+ {
+ IsService = true;
+ }
+ }
+
+ public class SolidModelContentProvider : CacheableContentProvider
+ {
+ public SolidModelContentProvider()
+ {
+ IsService = true;
+ }
+ }
+
+ public class Camera : Instance
+ {
+ public CFrame CFrame = new CFrame(0, 20, 20, 1, 0, 0, 0, 0.70711f, 0.70711f, 0, -0.70711f, 0.70711f);
+ public Instance CameraSubject = null;
+ public CameraType CameraType = CameraType.Fixed;
+
+ [Obsolete]
+ public CFrame CoordinateFrame
+ {
+ get { return CFrame; }
+ set { CFrame = value; }
+ }
+
+ public float FieldOfView = 70;
+ public CFrame Focus = new CFrame(0, 0, -5);
+ public bool HeadLocked = true;
+ public float HeadScale = 1;
+
+ [Obsolete]
+ public CFrame focus
+ {
+ get { return Focus; }
+ set { Focus = value; }
+ }
+ }
+
+ public class ChangeHistoryService : Instance
+ {
+ public ChangeHistoryService()
+ {
+ IsService = true;
+ }
+ }
+
+ public abstract class CharacterAppearance : Instance
+ {
+ }
+
+ public class BodyColors : CharacterAppearance
+ {
+ public BrickColor HeadColor
+ {
+ get { return BrickColor.FromColor3(HeadColor3); }
+ set { HeadColor3 = value.Color; }
+ }
+
+ public Color3 HeadColor3 = Color3.FromRGB(253, 234, 141);
+
+ public BrickColor LeftArmColor
+ {
+ get { return BrickColor.FromColor3(LeftArmColor3); }
+ set { LeftArmColor3 = value.Color; }
+ }
+
+ public Color3 LeftArmColor3 = Color3.FromRGB(253, 234, 141);
+
+ public BrickColor LeftLegColor
+ {
+ get { return BrickColor.FromColor3(LeftLegColor3); }
+ set { LeftLegColor3 = value.Color; }
+ }
+
+ public Color3 LeftLegColor3 = Color3.FromRGB(13, 105, 172);
+
+ public BrickColor RightArmColor
+ {
+ get { return BrickColor.FromColor3(RightArmColor3); }
+ set { RightArmColor3 = value.Color; }
+ }
+
+ public Color3 RightArmColor3 = Color3.FromRGB(253, 234, 141);
+
+ public BrickColor RightLegColor
+ {
+ get { return BrickColor.FromColor3(RightLegColor3); }
+ set { RightLegColor3 = value.Color; }
+ }
+
+ public Color3 RightLegColor3 = Color3.FromRGB(13, 105, 172);
+
+ public BrickColor TorsoColor
+ {
+ get { return BrickColor.FromColor3(TorsoColor3); }
+ set { TorsoColor3 = value.Color; }
+ }
+
+ public Color3 TorsoColor3 = Color3.FromRGB(40, 127, 71);
+ }
+
+ public class CharacterMesh : CharacterAppearance
+ {
+ public long BaseTextureId = 0;
+ public BodyPart BodyPart = BodyPart.Head;
+ public long MeshId = 0;
+ public long OverlayTextureId = 0;
+ }
+
+ public abstract class Clothing : CharacterAppearance
+ {
+ public Color3 Color3 = new Color3(1, 1, 1);
+ }
+
+ public class Pants : Clothing
+ {
+ public Content PantsTemplate = "";
+ }
+
+ public class Shirt : Clothing
+ {
+ public Content ShirtTemplate = "";
+ }
+
+ public class ShirtGraphic : CharacterAppearance
+ {
+ public Color3 Color3 = new Color3(1, 1, 1);
+ public Content Graphic = "";
+ }
+
+ public class Skin : CharacterAppearance
+ {
+ public BrickColor SkinColor = BrickColor.FromNumber(226);
+ }
+
+ public class Chat : Instance
+ {
+ public Chat()
+ {
+ IsService = true;
+ }
+
+ public bool BubbleChatEnabled = false;
+ public bool LoadDefaultChat = true;
+ }
+
+ public class ClickDetector : Instance
+ {
+ public Content CursorIcon = "";
+ public float MaxActivationDistance = 32;
+ }
+
+ public class CollectionService : Instance
+ {
+ public CollectionService()
+ {
+ IsService = true;
+ }
+ }
+
+ public class Configuration : Instance
+ {
+ }
+
+ public abstract class Constraint : Instance
+ {
+ public Attachment Attachment0 = null;
+ public Attachment Attachment1 = null;
+ public BrickColor Color = BrickColor.FromNumber(23);
+ public bool Enabled = true;
+ public bool Visible = false;
+ }
+
+ public class AlignOrientation : Constraint
+ {
+ public AlignType AlignType = AlignType.Parallel;
+ public float MaxAngularVelocity = float.MaxValue;
+ public float MaxTorque = 10000;
+ public bool PrimaryAxisOnly = false;
+ public bool ReactionTorqueEnabled = false;
+ public float Responsiveness = 10;
+ public bool RigidityEnabled = false;
+ }
+
+ public class AlignPosition : Constraint
+ {
+ public bool ApplyAtCenterOfMass = false;
+ public float MaxForce = 10000;
+ public float MaxVelocity = float.MaxValue;
+ public bool ReactionForceEnabled = false;
+ public float Responsiveness = 10;
+ public bool RigidityEnabled = false;
+ }
+
+ public class AngularVelocity : Constraint
+ {
+ public Vector3 AngularVelocity_ = new Vector3();
+ public float MaxTorque = 0;
+ public ActuatorRelativeTo RelativeTo = ActuatorRelativeTo.World;
+ }
+
+ public class BallSocketConstraint : Constraint
+ {
+ public bool LimitsEnabled = false;
+ public float Radius = 0.15f;
+ public float Restitution = 0;
+ public bool TwistLimitsEnabled = false;
+ public float TwistLowerAngle = -45;
+ public float TwistUpperAngle = 45;
+ public float UpperAngle = 45;
+ }
+
+ public class HingeConstraint : Constraint
+ {
+ public ActuatorType ActuatorType = ActuatorType.None;
+ public float AngularSpeed = 0;
+ public float AngularVelocity = 0;
+ public bool LimitsEnabled = false;
+ public float LowerAngle = -45;
+ public float MotorMaxAcceleration = float.MaxValue;
+ public float MotorMaxTorque = 0;
+ public float Radius = 0.15f;
+ public float Restitution = 0;
+ public float ServoMaxTorque = 0;
+ public float TargetAngle = 0;
+ public float UpperAngle = 45;
+ }
+
+ public class LineForce : Constraint
+ {
+ public bool ApplyAtCenterOfMass = false;
+ public bool InverseSquareLaw = false;
+ public float Magnitude = 1000;
+ public float MaxForce = float.MaxValue;
+ public bool ReactionForceEnabled = false;
+ }
+
+ public class RodConstraint : Constraint
+ {
+ public float Length = 5;
+ public float Thickness = 0.1f;
+ }
+
+ public class RopeConstraint : Constraint
+ {
+ public float Length = 5;
+ public float Restitution = 0;
+ public float Thickness = 0.1f;
+ }
+
+ public abstract class SlidingBallConstraint : Constraint
+ {
+ public ActuatorType ActuatorType = ActuatorType.None;
+ public bool LimitsEnabled = false;
+ public float LowerLimit = 0;
+ public float MotorMaxAcceleration = float.MaxValue;
+ public float MotorMaxForce = 0;
+ public float Restitution = 0;
+ public float ServoMaxForce = 0;
+ public float Size = 0.15f;
+ public float Speed = 0;
+ public float TargetPosition = 0;
+ public float UpperLimit = 5;
+ public float Velocity = 0;
+ }
+
+ public class CylindricalConstraint : SlidingBallConstraint
+ {
+ public ActuatorType AngularActuatorType = ActuatorType.None;
+ public bool AngularLimitsEnabled = false;
+ public float AngularRestitution = 0;
+ public float AngularSpeed = 0;
+ public float AngularVelocity = 0;
+ public float InclinationAngle = 0;
+ public float LowerAngle = -45;
+ public float MotorMaxAngularAcceleration = float.MaxValue;
+ public float MotorMaxTorque = 0;
+ public bool RotationAxisVisible = false;
+ public float ServoMaxTorque = 0;
+ public float TargetAngle = 0;
+ public float UpperAngle = 45;
+ }
+
+ public class PrismaticConstraint : SlidingBallConstraint
+ {
+ }
+
+ public class SpringConstraint : Constraint
+ {
+ public float Coils = 3;
+ public float Damping = 0;
+ public float FreeLength = 1;
+ public bool LimitsEnabled = false;
+ public float MaxForce = float.MaxValue;
+ public float MaxLength = 5;
+ public float MinLength = 0;
+ public float Radius = 0.4f;
+ public float Stiffness = 0;
+ public float Thickness = 0.1f;
+ }
+
+ public class Torque : Constraint
+ {
+ public ActuatorRelativeTo RelativeTo = ActuatorRelativeTo.Attachment0;
+ public Vector3 Torque_ = new Vector3();
+ }
+
+ public class VectorForce : Constraint
+ {
+ public bool ApplyAtCenterOfMass = false;
+ public Vector3 Force = new Vector3(1000, 0, 0);
+ public ActuatorRelativeTo RelativeTo = ActuatorRelativeTo.Attachment0;
+ }
+
+ public class ContentProvider : Instance
+ {
+ public ContentProvider()
+ {
+ IsService = true;
+ }
+
+ }
+
+ public class ContextActionService : Instance
+ {
+ public ContextActionService()
+ {
+ IsService = true;
+ }
+ }
+
+ public abstract class Controller : Instance
+ {
+ }
+
+ public class HumanoidController : Controller
+ {
+ }
+
+ public class SkateboardController : Controller
+ {
+ }
+
+ public class VehicleController : Controller
+ {
+ }
+
+ public class ControllerService : Instance
+ {
+ public ControllerService()
+ {
+ IsService = true;
+ }
+ }
+
+ public class CookiesService : Instance
+ {
+ public CookiesService()
+ {
+ IsService = true;
+ }
+ }
+
+ public class CorePackages : Instance
+ {
+ public CorePackages()
+ {
+ IsService = true;
+ }
+ }
+
+ public class CoreScriptSyncService : Instance
+ {
+ public CoreScriptSyncService()
+ {
+ IsService = true;
+ }
+ }
+
+ public class CustomEvent : Instance
+ {
+ }
+
+ public class CustomEventReceiver : Instance
+ {
+ public Instance Source = null;
+ }
+
+ public abstract class DataModelMesh : Instance
+ {
+ public LevelOfDetailSetting LODX = LevelOfDetailSetting.High;
+ public LevelOfDetailSetting LODY = LevelOfDetailSetting.High;
+ public Vector3 Offset = new Vector3();
+ public Vector3 Scale = new Vector3(1, 1, 1);
+ public Vector3 VertexColor = new Vector3(1, 1, 1);
+ }
+
+ public abstract class BevelMesh : DataModelMesh
+ {
+ }
+
+ public class BlockMesh : BevelMesh
+ {
+ }
+
+ public class CylinderMesh : BevelMesh
+ {
+ }
+
+ public class FileMesh : DataModelMesh
+ {
+ public Content MeshId = "";
+ public Content TextureId = "";
+ }
+
+ public class SpecialMesh : FileMesh
+ {
+ public MeshType MeshType = MeshType.Head;
+ }
+
+ public class DataStoreService : Instance
+ {
+ public DataStoreService()
+ {
+ IsService = true;
+ }
+
+ public bool AutomaticRetry = true;
+
+ [Obsolete]
+ public bool LegacyNamingScheme = false;
+ }
+
+ public class Debris : Instance
+ {
+ public Debris()
+ {
+ IsService = true;
+ }
+
+ [Obsolete]
+ public int MaxItems = 1000;
+ }
+
+ public class DebuggerWatch : Instance
+ {
+ public string Expression = "";
+ }
+
+ public class Dialog : Instance
+ {
+ public DialogBehaviorType BehaviorType = DialogBehaviorType.SinglePlayer;
+ public float ConversationDistance = 25;
+ public bool GoodbyeChoiceActive = true;
+ public string GoodbyeDialog = "";
+ public string InitialPrompt = "";
+ public DialogPurpose Purpose = DialogPurpose.Help;
+ public DialogTone Tone = DialogTone.Neutral;
+ public float TriggerDistance = 0;
+ public Vector3 TriggerOffset = new Vector3();
+ }
+
+ public class DialogChoice : Instance
+ {
+ public bool GoodbyeChoiceActive = true;
+ public string GoodbyeDialog = "";
+ public string ResponseDialog = "";
+ public string UserDialog = "";
+ }
+
+ public class Dragger : Instance
+ {
+ }
+
+ public class Explosion : Instance
+ {
+ public float BlastPressure = 500000;
+ public float BlastRadius = 4;
+ public float DestroyJointRadiusPercent = 1;
+ public ExplosionType ExplosionType = ExplosionType.Craters;
+ public Vector3 Position = new Vector3();
+ public bool Visible = true;
+ }
+
+ public abstract class FaceInstance : Instance
+ {
+ public NormalId Face = NormalId.Front;
+ }
+
+ public class Decal : FaceInstance
+ {
+ public Color3 Color3 = new Color3(1, 1, 1);
+
+ [Obsolete]
+ public float Shiny = 20;
+
+ [Obsolete]
+ public float Specular = 0;
+
+ public Content Texture = "";
+ public float Transparency = 0;
+ }
+
+ public class Texture : Decal
+ {
+ public float OffsetStudsU = 0;
+ public float OffsetStudsV = 0;
+ public float StudsPerTileU = 2;
+ public float StudsPerTileV = 2;
+ }
+
+ public abstract class Feature : Instance
+ {
+ public NormalId FaceId = NormalId.Right;
+ public InOut InOut = InOut.Center;
+ public LeftRight LeftRight = LeftRight.Center;
+ public TopBottom TopBottom = TopBottom.Center;
+ }
+
+ public class Hole : Feature
+ {
+ }
+
+ public class MotorFeature : Feature
+ {
+ }
+
+ public class Fire : Instance
+ {
+ public Color3 Color = Color3.FromRGB(236, 139, 70);
+ public bool Enabled = true;
+
+ public float Heat
+ {
+ get { return heat_xml; }
+ set { heat_xml = value; }
+ }
+
+ public Color3 SecondaryColor = Color3.FromRGB(139, 80, 55);
+
+ public float Size
+ {
+ get { return size_xml; }
+ set { size_xml = value; }
+ }
+
+ public float heat_xml = 9;
+
+ [Obsolete]
+ public float size
+ {
+ get { return Size; }
+ set { Size = value; }
+ }
+
+ public float size_xml = 5;
+ }
+
+ public class FlyweightService : Instance
+ {
+ public FlyweightService()
+ {
+ IsService = true;
+ }
+ }
+
+ public class CSGDictionaryService : FlyweightService
+ {
+ public CSGDictionaryService()
+ {
+ IsService = true;
+ }
+ }
+
+ public class NonReplicatedCSGDictionaryService : FlyweightService
+ {
+ public NonReplicatedCSGDictionaryService()
+ {
+ IsService = true;
+ }
+ }
+
+ public class Folder : Instance
+ {
+ }
+
+ public class ForceField : Instance
+ {
+ public bool Visible = true;
+ }
+
+ public class FriendService : Instance
+ {
+ public FriendService()
+ {
+ IsService = true;
+ }
+ }
+
+ public class FunctionalTest : Instance
+ {
+ public string Description = "?";
+ }
+
+ public class GamePassService : Instance
+ {
+ public GamePassService()
+ {
+ IsService = true;
+ }
+ }
+
+ public class GamepadService : Instance
+ {
+ public GamepadService()
+ {
+ IsService = true;
+ }
+ }
+
+ public class Geometry : Instance
+ {
+ public Geometry()
+ {
+ IsService = true;
+ }
+ }
+
+ public class GroupService : Instance
+ {
+ public GroupService()
+ {
+ IsService = true;
+ }
+ }
+
+ public abstract class GuiBase : Instance
+ {
+ }
+
+ public abstract class GuiBase2d : GuiBase
+ {
+ public bool AutoLocalize = true;
+
+ [Obsolete]
+ public bool Localize
+ {
+ get { return AutoLocalize; }
+ set { AutoLocalize = value; }
+ }
+
+ public LocalizationTable RootLocalizationTable = null;
+ }
+
+ public abstract class GuiObject : GuiBase2d
+ {
+ public bool Active = false;
+ public Vector2 AnchorPoint = new Vector2();
+
+ [Obsolete]
+ public BrickColor BackgroundColor
+ {
+ get { return BrickColor.FromColor3(BackgroundColor3); }
+ set { BackgroundColor3 = value.Color; }
+ }
+
+ public Color3 BackgroundColor3 = Color3.FromRGB(163, 162, 165);
+ public float BackgroundTransparency = 0;
+
+ [Obsolete]
+ public BrickColor BorderColor
+ {
+ get { return BrickColor.FromColor3(BorderColor3); }
+ set { BorderColor3 = value.Color; }
+ }
+
+ public Color3 BorderColor3 = Color3.FromRGB(27, 42, 53);
+ public int BorderSizePixel = 1;
+ public bool ClipsDescendants = false;
+
+ [Obsolete]
+ public bool Draggable = false;
+
+ public int LayoutOrder = 0;
+ public GuiObject NextSelectionDown = null;
+ public GuiObject NextSelectionLeft = null;
+ public GuiObject NextSelectionRight = null;
+ public GuiObject NextSelectionUp = null;
+ public UDim2 Position = new UDim2();
+ public float Rotation = 0;
+ public bool Selectable = false;
+ public GuiObject SelectionImageObject = null;
+ public UDim2 Size = new UDim2();
+ public SizeConstraint SizeConstraint = SizeConstraint.RelativeXY;
+
+ public float Transparency
+ {
+ get { return BackgroundTransparency; }
+ set { BackgroundTransparency = value; }
+ }
+
+ public bool Visible = true;
+ public int ZIndex = 1;
+ }
+
+ public class Frame : GuiObject
+ {
+ public FrameStyle Style = FrameStyle.Custom;
+ }
+
+ public abstract class GuiButton : GuiObject
+ {
+ public bool AutoButtonColor = true;
+ public bool Modal = false;
+ public bool Selected = false;
+ public ButtonStyle Style = ButtonStyle.Custom;
+ }
+
+ public class ImageButton : GuiButton
+ {
+ public Content HoverImage = "";
+ public Content Image = "";
+ public Color3 ImageColor3 = new Color3(1, 1, 1);
+ public Vector2 ImageRectOffset = new Vector2();
+ public Vector2 ImageRectSize = new Vector2();
+ public float ImageTransparency = 0;
+ public Content PressedImage = "";
+ public ScaleType ScaleType = ScaleType.Stretch;
+ public Rect SliceCenter = new Rect(new Vector2(), new Vector2());
+ public float SliceScale = 1;
+ public UDim2 TileSize = new UDim2(1, 0, 1, 0);
+ }
+
+ public class TextButton : GuiButton
+ {
+ public Font Font = Font.Legacy;
+
+ [Obsolete]
+ public FontSize FontSize
+ {
+ get { return FontUtility.GetFontSize(TextSize); }
+ set { TextSize = FontUtility.GetFontSize(value); }
+ }
+
+ public float LineHeight = 1;
+ public string Text = "Button";
+
+ [Obsolete]
+ public BrickColor TextColor
+ {
+ get { return BrickColor.FromColor3(TextColor3); }
+ set { TextColor3 = value.Color; }
+ }
+
+ public Color3 TextColor3 = Color3.FromRGB(27, 42, 53);
+ public bool TextScaled = false;
+ public float TextSize = 8;
+ public Color3 TextStrokeColor3 = new Color3();
+ public float TextStrokeTransparency = 1;
+ public float TextTransparency = 0;
+ public TextTruncate TextTruncate = TextTruncate.None;
+
+ [Obsolete]
+ public bool TextWrap
+ {
+ get { return TextWrapped; }
+ set { TextWrapped = value; }
+ }
+
+ public bool TextWrapped = false;
+ public TextXAlignment TextXAlignment = TextXAlignment.Center;
+ public TextYAlignment TextYAlignment = TextYAlignment.Center;
+ }
+
+ public abstract class GuiLabel : GuiObject
+ {
+ }
+
+ public class ImageLabel : GuiLabel
+ {
+ public Content Image = "";
+ public Color3 ImageColor3 = new Color3(1, 1, 1);
+ public Vector2 ImageRectOffset = new Vector2();
+ public Vector2 ImageRectSize = new Vector2();
+ public float ImageTransparency = 0;
+ public ScaleType ScaleType = ScaleType.Stretch;
+ public Rect SliceCenter = new Rect(new Vector2(), new Vector2());
+ public float SliceScale = 1;
+ public UDim2 TileSize = new UDim2(1, 0, 1, 0);
+ }
+
+ public class TextLabel : GuiLabel
+ {
+ public Font Font = Font.Legacy;
+
+ [Obsolete]
+ public FontSize FontSize
+ {
+ get { return FontUtility.GetFontSize(TextSize); }
+ set { TextSize = FontUtility.GetFontSize(value); }
+ }
+
+ public float LineHeight = 1;
+ public string Text = "Label";
+
+ [Obsolete]
+ public BrickColor TextColor
+ {
+ get { return BrickColor.FromColor3(TextColor3); }
+ set { TextColor3 = value.Color; }
+ }
+
+ public Color3 TextColor3 = Color3.FromRGB(27, 42, 53);
+ public bool TextScaled = false;
+ public float TextSize = 8;
+ public Color3 TextStrokeColor3 = new Color3();
+ public float TextStrokeTransparency = 1;
+ public float TextTransparency = 0;
+ public TextTruncate TextTruncate = TextTruncate.None;
+
+ [Obsolete]
+ public bool TextWrap
+ {
+ get { return TextWrapped; }
+ set { TextWrapped = value; }
+ }
+
+ public bool TextWrapped = false;
+ public TextXAlignment TextXAlignment = TextXAlignment.Center;
+ public TextYAlignment TextYAlignment = TextYAlignment.Center;
+ }
+
+ public class ScrollingFrame : GuiObject
+ {
+ public Content BottomImage = "rbxasset://textures/ui/Scroll/scroll-bottom.png";
+ public Vector2 CanvasPosition = new Vector2();
+ public UDim2 CanvasSize = new UDim2(0, 0, 2, 0);
+ public ElasticBehavior ElasticBehavior = ElasticBehavior.WhenScrollable;
+ public ScrollBarInset HorizontalScrollBarInset = ScrollBarInset.None;
+ public Content MidImage = "rbxasset://textures/ui/Scroll/scroll-middle.png";
+ public Color3 ScrollBarImageColor3 = new Color3(1, 1, 1);
+ public float ScrollBarImageTransparency = 0;
+ public int ScrollBarThickness = 12;
+ public ScrollingDirection ScrollingDirection = ScrollingDirection.XY;
+ public bool ScrollingEnabled = true;
+ public Content TopImage = "rbxasset://textures/ui/Scroll/scroll-top.png";
+ public ScrollBarInset VerticalScrollBarInset = ScrollBarInset.None;
+ public VerticalScrollBarPosition VerticalScrollBarPosition = VerticalScrollBarPosition.Right;
+ }
+
+ public class TextBox : GuiObject
+ {
+ public bool ClearTextOnFocus = true;
+ public Font Font = Font.Legacy;
+
+ [Obsolete]
+ public FontSize FontSize
+ {
+ get { return FontUtility.GetFontSize(TextSize); }
+ set { TextSize = FontUtility.GetFontSize(value); }
+ }
+
+ public bool IsPassword = false; // [Load-only]
+ public float LineHeight = 1;
+ public bool ManualFocusRelease = false; // [Load-only]
+ public bool MultiLine = false;
+ public bool OverlayNativeInput = false; // [Load-only]
+ public Color3 PlaceholderColor3 = Color3.FromRGB(178, 178, 178);
+ public string PlaceholderText = "";
+ public bool ShowNativeInput = true;
+ public string Text = "TextBox";
+
+ [Obsolete]
+ public BrickColor TextColor
+ {
+ get { return BrickColor.FromColor3(TextColor3); }
+ set { TextColor3 = value.Color; }
+ }
+
+ public Color3 TextColor3 = Color3.FromRGB(27, 42, 53);
+ public bool TextEditable = true;
+ public bool TextScaled = false;
+ public float TextSize = 8;
+ public Color3 TextStrokeColor3 = new Color3();
+ public float TextStrokeTransparency = 1;
+ public float TextTransparency = 0;
+ public TextTruncate TextTruncate = TextTruncate.None;
+
+ [Obsolete]
+ public bool TextWrap
+ {
+ get { return TextWrapped; }
+ set { TextWrapped = value; }
+ }
+
+ public bool TextWrapped = false;
+ public TextXAlignment TextXAlignment = TextXAlignment.Center;
+ public TextYAlignment TextYAlignment = TextYAlignment.Center;
+ }
+
+ public class ViewportFrame : GuiObject
+ {
+ public Color3 Ambient = Color3.FromRGB(200, 200, 200);
+ public CFrame CameraCFrame = new CFrame();
+ public float CameraFieldOfView = 70;
+ public Color3 ImageColor3 = new Color3(1, 1, 1);
+ public float ImageTransparency = 0;
+ public Color3 LightColor = Color3.FromRGB(140, 140, 140);
+ public Vector3 LightDirection = new Vector3(-1, -1, -1);
+ }
+
+ public abstract class LayerCollector : GuiBase2d
+ {
+ public bool Enabled = true;
+ public bool ResetOnSpawn = true;
+ public ZIndexBehavior ZIndexBehavior = ZIndexBehavior.Global;
+ }
+
+ public class BillboardGui : LayerCollector
+ {
+ public bool Active = false;
+ public Instance Adornee = null;
+ public bool AlwaysOnTop = false;
+ public bool ClipsDescendants = false;
+ public float DistanceLowerLimit = 0;
+ public float DistanceStep = 0;
+ public float DistanceUpperLimit = -1;
+ public Vector3 ExtentsOffset = new Vector3();
+ public Vector3 ExtentsOffsetWorldSpace = new Vector3();
+ public float LightInfluence = 0;
+ public float MaxDistance = float.MaxValue;
+ public Instance PlayerToHideFrom = null;
+ public UDim2 Size = new UDim2();
+ public Vector2 SizeOffset = new Vector2();
+ public Vector3 StudsOffset = new Vector3();
+ public Vector3 StudsOffsetWorldSpace = new Vector3();
+ }
+
+ public class ScreenGui : LayerCollector
+ {
+ public int DisplayOrder = 0;
+ public bool IgnoreGuiInset = false;
+ }
+
+ public class GuiMain : ScreenGui
+ {
+ }
+
+ public class SurfaceGui : LayerCollector
+ {
+ public bool Active = true;
+ public Instance Adornee = null;
+ public bool AlwaysOnTop = false;
+ public Vector2 CanvasSize = new Vector2(800, 600);
+ public bool ClipsDescendants = false;
+ public NormalId Face = NormalId.Front;
+ public float LightInfluence = 0;
+ public float PixelsPerStud = 50;
+ public SurfaceGuiSizingMode SizingMode = SurfaceGuiSizingMode.FixedSize;
+ public float ToolPunchThroughDistance = 0;
+ public float ZOffset = 0;
+ }
+
+ public abstract class GuiBase3d : GuiBase
+ {
+ [Obsolete]
+ public BrickColor Color
+ {
+ get { return BrickColor.FromColor3(Color3); }
+ set { Color3 = value.Color; }
+ }
+
+ public Color3 Color3 = Color3.FromRGB(13, 105, 172);
+ public float Transparency = 0;
+ public bool Visible = true;
+ }
+
+ public class FloorWire : GuiBase3d
+ {
+ public float CycleOffset = 0;
+ public BasePart From = null;
+ public float StudsBetweenTextures = 4;
+ public Content Texture = "";
+ public Vector2 TextureSize = new Vector2(1, 1);
+ public BasePart To = null;
+ public float Velocity = 2;
+ public float WireRadius = 0.0625f;
+ }
+
+ public abstract class PVAdornment : GuiBase3d
+ {
+ public PVInstance Adornee = null;
+ }
+
+ public abstract class HandleAdornment : PVAdornment
+ {
+ public bool AlwaysOnTop = false;
+ public CFrame CFrame = new CFrame();
+ public Vector3 SizeRelativeOffset = new Vector3();
+ public int ZIndex = -1;
+ }
+
+ public class BoxHandleAdornment : HandleAdornment
+ {
+ public Vector3 Size = new Vector3(1, 1, 1);
+ }
+
+ public class ConeHandleAdornment : HandleAdornment
+ {
+ public float Height = 2;
+ public float Radius = 0.5f;
+ }
+
+ public class CylinderHandleAdornment : HandleAdornment
+ {
+ public float Height = 1;
+ public float Radius = 1;
+ }
+
+ public class ImageHandleAdornment : HandleAdornment
+ {
+ public Content Image = "rbxasset://textures/SurfacesDefault.png";
+ public Vector2 Size = new Vector2(1, 1);
+ }
+
+ public class LineHandleAdornment : HandleAdornment
+ {
+ public float Length = 5;
+ public float Thickness = 1;
+ }
+
+ public class SphereHandleAdornment : HandleAdornment
+ {
+ public float Radius = 1;
+ }
+
+ public class ParabolaAdornment : PVAdornment
+ {
+ }
+
+ public class SelectionBox : PVAdornment
+ {
+ public float LineThickness = 0.15f;
+
+ [Obsolete]
+ public BrickColor SurfaceColor
+ {
+ get { return BrickColor.FromColor3(SurfaceColor3); }
+ set { SurfaceColor3 = value.Color; }
+ }
+
+ public Color3 SurfaceColor3 = Color3.FromRGB(13, 105, 172);
+ public float SurfaceTransparency = 1;
+ }
+
+ public class SelectionSphere : PVAdornment
+ {
+ [Obsolete]
+ public BrickColor SurfaceColor
+ {
+ get { return BrickColor.FromColor3(SurfaceColor3); }
+ set { SurfaceColor3 = value.Color; }
+ }
+
+ public Color3 SurfaceColor3 = Color3.FromRGB(13, 105, 172);
+ public float SurfaceTransparency = 1;
+ }
+
+ public abstract class PartAdornment : GuiBase3d
+ {
+ public BasePart Adornee = null;
+ }
+
+ public abstract class HandlesBase : PartAdornment
+ {
+ }
+
+ public class ArcHandles : HandlesBase
+ {
+ public Axes Axes = (Axes)7;
+ }
+
+ public class Handles : HandlesBase
+ {
+ public Faces Faces = (Faces)63;
+ public HandlesStyle Style = HandlesStyle.Resize;
+ }
+
+ public class SurfaceSelection : PartAdornment
+ {
+ public NormalId TargetSurface = NormalId.Right;
+ }
+
+ public abstract class SelectionLasso : GuiBase3d
+ {
+ public Humanoid Humanoid = null;
+ }
+
+ public class SelectionPartLasso : SelectionLasso
+ {
+ public BasePart Part = null;
+ }
+
+ public class SelectionPointLasso : SelectionLasso
+ {
+ public Vector3 Point = new Vector3();
+ }
+
+ public class GuiService : Instance
+ {
+ public GuiService()
+ {
+ IsService = true;
+ }
+
+ public bool AutoSelectGuiEnabled = true;
+ public bool CoreGuiNavigationEnabled = true;
+ public bool GuiNavigationEnabled = true;
+ public GuiObject SelectedCoreObject = null;
+ public GuiObject SelectedObject = null;
+ }
+
+ public class HapticService : Instance
+ {
+ public HapticService()
+ {
+ IsService = true;
+ }
+ }
+
+ public class HttpRbxApiService : Instance
+ {
+ public HttpRbxApiService()
+ {
+ IsService = true;
+ }
+ }
+
+ public class HttpService : Instance
+ {
+ public HttpService()
+ {
+ IsService = true;
+ }
+
+ public bool HttpEnabled = false;
+ }
+
+ public class Humanoid : Instance
+ {
+ public bool AutoJumpEnabled = true;
+ public bool AutoRotate = true;
+ public bool AutomaticScalingEnabled = true;
+ public bool BreakJointsOnDeath = true;
+ public HumanoidCollisionType CollisionType = HumanoidCollisionType.OuterBox;
+ public HumanoidDisplayDistanceType DisplayDistanceType = HumanoidDisplayDistanceType.Viewer;
+
+ public float Health
+ {
+ get { return Health_XML; }
+ set { Health_XML = value; }
+ }
+
+ public float HealthDisplayDistance = 100;
+ public HumanoidHealthDisplayType HealthDisplayType = HumanoidHealthDisplayType.DisplayWhenDamaged;
+ public float Health_XML = 100;
+ public float HipHeight = 0;
+ public Vector3 InternalBodyScale = new Vector3(1, 1, 1);
+ public float InternalHeadScale = 1;
+ public float JumpHeight = 7.2f;
+ public float JumpPower = 50;
+ public float MaxHealth = 100;
+ public float MaxSlopeAngle = 89;
+ public float NameDisplayDistance = 100;
+ public NameOcclusion NameOcclusion = NameOcclusion.OccludeAll;
+ public HumanoidRigType RigType = HumanoidRigType.R6;
+ public bool UseJumpPower = true;
+ public float WalkSpeed = 16;
+
+ [Obsolete]
+ public float maxHealth
+ {
+ get { return MaxHealth; }
+ set { MaxHealth = value; }
+ }
+ }
+
+ public class HumanoidDescription : Instance
+ {
+ public string BackAccessory = "";
+ public float BodyTypeScale = 0.3f;
+ public long ClimbAnimation = 0;
+ public float DepthScale = 1;
+ public string EmotesDataInternal = "";
+ public string EquippedEmotesDataInternal = "";
+ public long Face = 0;
+ public string FaceAccessory = "";
+ public long FallAnimation = 0;
+ public string FrontAccessory = "";
+ public long GraphicTShirt = 0;
+ public string HairAccessory = "";
+ public string HatAccessory = "";
+ public long Head = 0;
+ public Color3 HeadColor = new Color3();
+ public float HeadScale = 1;
+ public float HeightScale = 1;
+ public long IdleAnimation = 0;
+ public long JumpAnimation = 0;
+ public long LeftArm = 0;
+ public Color3 LeftArmColor = new Color3();
+ public long LeftLeg = 0;
+ public Color3 LeftLegColor = new Color3();
+ public string NeckAccessory = "";
+ public long Pants = 0;
+ public float ProportionScale = 1;
+ public long RightArm = 0;
+ public Color3 RightArmColor = new Color3();
+ public long RightLeg = 0;
+ public Color3 RightLegColor = new Color3();
+ public long RunAnimation = 0;
+ public long Shirt = 0;
+ public string ShouldersAccessory = "";
+ public long SwimAnimation = 0;
+ public long Torso = 0;
+ public Color3 TorsoColor = new Color3();
+ public string WaistAccessory = "";
+ public long WalkAnimation = 0;
+ public float WidthScale = 1;
+ }
+
+ public class InsertService : Instance
+ {
+ public InsertService()
+ {
+ IsService = true;
+ }
+
+ public bool AllowClientInsertModels = false;
+
+ [Obsolete]
+ public bool AllowInsertFreeModels = false;
+ }
+
+ public abstract class JointInstance : Instance
+ {
+ public CFrame C0 = new CFrame();
+ public CFrame C1 = new CFrame();
+ public bool IsAutoJoint = true;
+ public BasePart Part0 = null;
+ public BasePart Part1 = null;
+ }
+
+ public abstract class DynamicRotate : JointInstance
+ {
+ public float BaseAngle = 0;
+ }
+
+ public class RotateP : DynamicRotate
+ {
+ }
+
+ public class RotateV : DynamicRotate
+ {
+ }
+
+ public class Glue : JointInstance
+ {
+ public Vector3 F0 = new Vector3();
+ public Vector3 F1 = new Vector3();
+ public Vector3 F2 = new Vector3();
+ public Vector3 F3 = new Vector3();
+ }
+
+ public abstract class ManualSurfaceJointInstance : JointInstance
+ {
+ public int Surface0 = -1;
+ public int Surface1 = -1;
+ }
+
+ public class ManualGlue : ManualSurfaceJointInstance
+ {
+ }
+
+ public class ManualWeld : ManualSurfaceJointInstance
+ {
+ }
+
+ public class Motor : JointInstance
+ {
+ public float CurrentAngle = 0; // [Load-only]
+ public float DesiredAngle = 0;
+ public float MaxVelocity = 0;
+ }
+
+ public class Motor6D : Motor
+ {
+ }
+
+ public class Rotate : JointInstance
+ {
+ }
+
+ public class Snap : JointInstance
+ {
+ }
+
+ public class VelocityMotor : JointInstance
+ {
+ public float CurrentAngle = 0;
+ public float DesiredAngle = 0;
+ public Hole Hole = null;
+ public float MaxVelocity = 0;
+ }
+
+ public class Weld : JointInstance
+ {
+ }
+
+ public class JointsService : Instance
+ {
+ public JointsService()
+ {
+ IsService = true;
+ }
+ }
+
+ public class KeyboardService : Instance
+ {
+ public KeyboardService()
+ {
+ IsService = true;
+ }
+ }
+
+ public class Keyframe : Instance
+ {
+ public float Time = 0;
+ }
+
+ public class KeyframeMarker : Instance
+ {
+ public string Value = "";
+ }
+
+ public class KeyframeSequence : Instance
+ {
+ public float AuthoredHipHeight = 2;
+ public bool Loop = true;
+ public AnimationPriority Priority = AnimationPriority.Action;
+ }
+
+ public class KeyframeSequenceProvider : Instance
+ {
+ public KeyframeSequenceProvider()
+ {
+ IsService = true;
+ }
+ }
+
+ public abstract class Light : Instance
+ {
+ public float Brightness = 1;
+ public Color3 Color = new Color3(1, 1, 1);
+ public bool Enabled = true;
+ public bool Shadows = false;
+ }
+
+ public class PointLight : Light
+ {
+ public float Range = 8;
+ }
+
+ public class SpotLight : Light
+ {
+ public float Angle = 90;
+ public NormalId Face = NormalId.Front;
+ public float Range = 16;
+ }
+
+ public class SurfaceLight : Light
+ {
+ public float Angle = 90;
+ public NormalId Face = NormalId.Front;
+ public float Range = 16;
+ }
+
+ public class Lighting : Instance
+ {
+ public Lighting()
+ {
+ IsService = true;
+ }
+
+ public Color3 Ambient = new Color3();
+ public float Brightness = 2;
+ public Color3 ColorShift_Bottom = new Color3();
+ public Color3 ColorShift_Top = new Color3();
+ public float ExposureCompensation = 0;
+ public Color3 FogColor = Color3.FromRGB(192, 192, 192);
+ public float FogEnd = 100000;
+ public float FogStart = 0;
+ public float GeographicLatitude = 41.7333f;
+ public bool GlobalShadows = true;
+ public bool LegacyOutlines = false;
+ public Color3 OutdoorAmbient = Color3.FromRGB(128, 128, 128);
+
+ public bool Outlines
+ {
+ get { return LegacyOutlines; }
+ set { LegacyOutlines = value; }
+ }
+
+ [Obsolete]
+ public Color3 ShadowColor = Color3.FromRGB(178, 178, 183);
+
+ public float ShadowSoftness = 0.5f;
+ public Technology Technology = Technology.Compatibility;
+ public string TimeOfDay = "14:00:00";
+ }
+
+ public abstract class LocalStorageService : Instance
+ {
+ public LocalStorageService()
+ {
+ IsService = true;
+ }
+ }
+
+ public class LocalizationService : Instance
+ {
+ public LocalizationService()
+ {
+ IsService = true;
+ }
+
+ }
+
+ public class LocalizationTable : Instance
+ {
+ public string Contents = "[]";
+
+ [Obsolete]
+ public string DevelopmentLanguage
+ {
+ get { return SourceLocaleId; }
+ set { SourceLocaleId = value; }
+ }
+
+ public string SourceLocaleId = "en-us";
+ }
+
+ public class LogService : Instance
+ {
+ public LogService()
+ {
+ IsService = true;
+ }
+ }
+
+ public abstract class LuaSourceContainer : Instance
+ {
+ }
+
+ public abstract class BaseScript : LuaSourceContainer
+ {
+ public bool Disabled = false;
+ public Content LinkedSource = "";
+ }
+
+ public class Script : BaseScript
+ {
+ public ProtectedString Source = "";
+ }
+
+ public class LocalScript : Script
+ {
+ }
+
+ public class ModuleScript : LuaSourceContainer
+ {
+ public Content LinkedSource = "";
+ public ProtectedString Source = "";
+ }
+
+ public class LuaWebService : Instance
+ {
+ public LuaWebService()
+ {
+ IsService = true;
+ }
+ }
+
+ public class MarketplaceService : Instance
+ {
+ public MarketplaceService()
+ {
+ IsService = true;
+ }
+ }
+
+ public class Message : Instance
+ {
+ public string Text = "";
+ }
+
+ public class Hint : Message
+ {
+ }
+
+ public class MessagingService : Instance
+ {
+ public MessagingService()
+ {
+ IsService = true;
+ }
+ }
+
+ public class MouseService : Instance
+ {
+ public MouseService()
+ {
+ IsService = true;
+ }
+ }
+
+ public abstract class NetworkPeer : Instance
+ {
+ }
+
+ public class NoCollisionConstraint : Instance
+ {
+ public bool Enabled = true;
+ public BasePart Part0 = null;
+ public BasePart Part1 = null;
+ }
+
+ public class NotificationService : Instance
+ {
+ public NotificationService()
+ {
+ IsService = true;
+ }
+
+ }
+
+ public abstract class PVInstance : Instance
+ {
+ }
+
+ public abstract class BasePart : PVInstance
+ {
+ public bool Anchored = false;
+ public float BackParamA = -0.5f;
+ public float BackParamB = 0.5f;
+ public SurfaceType BackSurface = SurfaceType.Smooth;
+ public InputType BackSurfaceInput = InputType.NoInput;
+ public float BottomParamA = -0.5f;
+ public float BottomParamB = 0.5f;
+ public SurfaceType BottomSurface = SurfaceType.Smooth;
+ public InputType BottomSurfaceInput = InputType.NoInput;
+
+ public BrickColor BrickColor
+ {
+ get { return BrickColor.FromColor3(Color); }
+ set { Color = value.Color; }
+ }
+
+ public CFrame CFrame = new CFrame();
+ public bool CanCollide = true;
+ public bool CastShadow = true;
+ public int CollisionGroupId = 0;
+
+ public Color3 Color
+ {
+ get { return Color3uint8; }
+ set { Color3uint8 = value; }
+ }
+
+ public Color3uint8 Color3uint8 = Color3.FromRGB(163, 162, 165);
+ public PhysicalProperties CustomPhysicalProperties = null;
+
+ [Obsolete]
+ public float Elasticity = 0.5f;
+
+ [Obsolete]
+ public float Friction = 0.3f;
+
+ public float FrontParamA = -0.5f;
+ public float FrontParamB = 0.5f;
+ public SurfaceType FrontSurface = SurfaceType.Smooth;
+ public InputType FrontSurfaceInput = InputType.NoInput;
+ public float LeftParamA = -0.5f;
+ public float LeftParamB = 0.5f;
+ public SurfaceType LeftSurface = SurfaceType.Smooth;
+ public InputType LeftSurfaceInput = InputType.NoInput;
+ public bool Locked = false;
+ public bool Massless = false;
+ public Material Material = Material.Plastic;
+
+ public Vector3 Position
+ {
+ get { return CFrame.Position; }
+ set { CFrame.Position = value; }
+ }
+
+ public float Reflectance = 0;
+ public float RightParamA = -0.5f;
+ public float RightParamB = 0.5f;
+ public SurfaceType RightSurface = SurfaceType.Smooth;
+ public InputType RightSurfaceInput = InputType.NoInput;
+ public int RootPriority = 0;
+ public Vector3 RotVelocity = new Vector3();
+
+ public Vector3 Size
+ {
+ get { return size; }
+ set { size = value; }
+ }
+
+ public float TopParamA = -0.5f;
+ public float TopParamB = 0.5f;
+ public SurfaceType TopSurface = SurfaceType.Smooth;
+ public InputType TopSurfaceInput = InputType.NoInput;
+ public float Transparency = 0;
+ public Vector3 Velocity = new Vector3();
+
+ [Obsolete]
+ public BrickColor brickColor
+ {
+ get { return BrickColor; }
+ set { BrickColor = value; }
+ }
+
+ public Vector3 size = new Vector3(4, 1.2f, 2);
+ }
+
+ public class CornerWedgePart : BasePart
+ {
+ }
+
+ public abstract class FormFactorPart : BasePart
+ {
+ [Obsolete]
+ public FormFactor FormFactor
+ {
+ get { return formFactorRaw; }
+ set { formFactorRaw = value; }
+ }
+
+ [Obsolete]
+ public FormFactor formFactor
+ {
+ get { return FormFactor; }
+ set { FormFactor = value; }
+ }
+
+ public FormFactor formFactorRaw = FormFactor.Brick;
+ }
+
+ public class Part : FormFactorPart
+ {
+ public PartType Shape
+ {
+ get { return shape; }
+ set { shape = value; }
+ }
+
+ public PartType shape = PartType.Block;
+ }
+
+ public class FlagStand : Part
+ {
+ public BrickColor TeamColor = BrickColor.FromNumber(194);
+ }
+
+ public class Seat : Part
+ {
+ public bool Disabled = false;
+ }
+
+ public class SkateboardPlatform : Part
+ {
+ public int Steer = 0;
+ public bool StickyWheels = true;
+ public int Throttle = 0;
+ }
+
+ public class SpawnLocation : Part
+ {
+ public bool AllowTeamChangeOnTouch = false;
+ public int Duration = 10;
+ public bool Enabled = true;
+ public bool Neutral = true;
+ public BrickColor TeamColor = BrickColor.FromNumber(194);
+ }
+
+ public class WedgePart : FormFactorPart
+ {
+ }
+
+ public class Terrain : BasePart
+ {
+ public string ClusterGrid = "";
+ public string ClusterGridV2 = "";
+ public byte[] ClusterGridV3 = new byte[0];
+ public byte[] MaterialColors = Convert.FromBase64String("AAAAAAAAan8/P39rf2Y/ilY+j35fi21PZmxvZbDqw8faiVpHOi4kHh4lZlw76JxKc3trhHtagcLgc4RKxr21zq2UlJSM");
+ public byte[] PhysicsGrid = Convert.FromBase64String("AgMAAAAAAAAAAAAAAAA=");
+ public byte[] SmoothGrid = Convert.FromBase64String("AQU=");
+ public Color3 WaterColor = Color3.FromRGB(12, 84, 92);
+ public float WaterReflectance = 1;
+ public float WaterTransparency = 0.3f;
+ public float WaterWaveSize = 0.15f;
+ public float WaterWaveSpeed = 10;
+ }
+
+ public abstract class TriangleMeshPart : BasePart
+ {
+ public Vector3 InitialSize = new Vector3(1, 1, 1);
+ public byte[] LODData = new byte[0];
+ public SharedString PhysicalConfigData = SharedString.FromBase64("1B2M2Y8AsgTpgAmY7PhCfg==");
+ public byte[] PhysicsData = new byte[0];
+ }
+
+ public class MeshPart : TriangleMeshPart
+ {
+ [Obsolete]
+ public Content MeshID
+ {
+ get { return MeshId; }
+ set { MeshId = value; }
+ }
+
+ public Content MeshId = "";
+ public RenderFidelity RenderFidelity = RenderFidelity.Precise;
+ public Content TextureID = "";
+ }
+
+ public class PartOperation : TriangleMeshPart
+ {
+ public Content AssetId = "";
+ public byte[] ChildData = new byte[0];
+ public byte[] MeshData = new byte[0];
+ public RenderFidelity RenderFidelity = RenderFidelity.Precise;
+ public bool UsePartColor = false;
+ }
+
+ public class NegateOperation : PartOperation
+ {
+ }
+
+ public class UnionOperation : PartOperation
+ {
+ }
+
+ public class TrussPart : BasePart
+ {
+ public Style Style
+ {
+ get { return style; }
+ set { style = value; }
+ }
+
+ public Style style = Style.AlternatingSupports;
+ }
+
+ public class VehicleSeat : BasePart
+ {
+ public bool Disabled = false;
+ public bool HeadsUpDisplay = true;
+ public float MaxSpeed = 25;
+ public int Steer = 0;
+ public float SteerFloat = 0;
+ public int Throttle = 0;
+ public float ThrottleFloat = 0;
+ public float Torque = 10;
+ public float TurnSpeed = 1;
+ }
+
+ public class Model : PVInstance
+ {
+ public CFrame ModelInPrimary = new CFrame();
+ public BasePart PrimaryPart = null;
+ }
+
+ public class Workspace : Model
+ {
+ public Workspace()
+ {
+ IsService = true;
+ }
+
+ public bool AllowThirdPartySales = false;
+ public AutoJointsMode AutoJointsMode = AutoJointsMode.Default;
+ public string CollisionGroups = "Default^0^1";
+ public Camera CurrentCamera = null;
+ public double DistributedGameTime = 0;
+ public bool ExplicitAutoJoints = true;
+ public float FallenPartsDestroyHeight = -500;
+ public bool FilteringEnabled = true;
+ public float Gravity = 196.2f;
+ public bool StreamingEnabled = false;
+ public int StreamingMinRadius = 64;
+ public StreamingPauseMode StreamingPauseMode = StreamingPauseMode.Default;
+ public int StreamingTargetRadius = 1024;
+ public bool TerrainWeldsFixed = true;
+ }
+
+ public class PackageService : Instance
+ {
+ public PackageService()
+ {
+ IsService = true;
+ }
+ }
+
+ public class PartOperationAsset : Instance
+ {
+ public byte[] ChildData = new byte[0];
+ public byte[] MeshData = new byte[0];
+ }
+
+ public class ParticleEmitter : Instance
+ {
+ public Vector3 Acceleration = new Vector3();
+ public ColorSequence Color = new ColorSequence(new Color3(1, 1, 1));
+ public float Drag = 0;
+ public NormalId EmissionDirection = NormalId.Top;
+ public bool Enabled = true;
+ public NumberRange Lifetime = new NumberRange(5, 10);
+ public float LightEmission = 0;
+ public float LightInfluence = 0;
+ public bool LockedToPart = false;
+ public float Rate = 20;
+ public NumberRange RotSpeed = new NumberRange(0);
+ public NumberRange Rotation = new NumberRange(0);
+ public NumberSequence Size = new NumberSequence(1);
+ public NumberRange Speed = new NumberRange(5);
+ public Vector2 SpreadAngle = new Vector2();
+ public Content Texture = "rbxasset://textures/particles/sparkles_main.dds";
+ public NumberSequence Transparency = new NumberSequence(0);
+ public float VelocityInheritance = 0;
+
+ [Obsolete]
+ public float VelocitySpread
+ {
+ get { return SpreadAngle.X; }
+ set { SpreadAngle = new Vector2(value, value); }
+ }
+
+ public float ZOffset = 0;
+ }
+
+ public class PathfindingService : Instance
+ {
+ public PathfindingService()
+ {
+ IsService = true;
+ }
+
+ [Obsolete]
+ public float EmptyCutoff = 0;
+ }
+
+ public class PhysicsService : Instance
+ {
+ public PhysicsService()
+ {
+ IsService = true;
+ }
+ }
+
+ public class Players : Instance
+ {
+ public Players()
+ {
+ IsService = true;
+ }
+
+ public bool CharacterAutoLoads = true;
+ public int MaxPlayersInternal = 16;
+ public int PreferredPlayersInternal = 0;
+ public float RespawnTime = 5;
+ }
+
+ public class PluginAction : Instance
+ {
+ public bool Enabled = true; // [Load-only]
+ }
+
+ public class PluginGuiService : Instance
+ {
+ public PluginGuiService()
+ {
+ IsService = true;
+ }
+ }
+
+ public class PointsService : Instance
+ {
+ public PointsService()
+ {
+ IsService = true;
+ }
+ }
+
+ public class Pose : Instance
+ {
+ public CFrame CFrame = new CFrame();
+ public PoseEasingDirection EasingDirection = PoseEasingDirection.In;
+ public PoseEasingStyle EasingStyle = PoseEasingStyle.Linear;
+
+ [Obsolete]
+ public float MaskWeight = 0;
+
+ public float Weight = 1;
+ }
+
+ public abstract class PostEffect : Instance
+ {
+ public bool Enabled = true;
+ }
+
+ public class BloomEffect : PostEffect
+ {
+ public float Intensity = 0.4f;
+ public float Size = 24;
+ public float Threshold = 0.95f;
+ }
+
+ public class BlurEffect : PostEffect
+ {
+ public float Size = 24;
+ }
+
+ public class ColorCorrectionEffect : PostEffect
+ {
+ public float Brightness = 0;
+ public float Contrast = 0;
+ public float Saturation = 0;
+ public Color3 TintColor = new Color3(1, 1, 1);
+ }
+
+ public class SunRaysEffect : PostEffect
+ {
+ public float Intensity = 0.25f;
+ public float Spread = 1;
+ }
+
+ public class RbxAnalyticsService : Instance
+ {
+ public RbxAnalyticsService()
+ {
+ IsService = true;
+ }
+ }
+
+ public class ReflectionMetadata : Instance
+ {
+ }
+
+ public class ReflectionMetadataCallbacks : Instance
+ {
+ }
+
+ public class ReflectionMetadataClasses : Instance
+ {
+ }
+
+ public class ReflectionMetadataEnums : Instance
+ {
+ }
+
+ public class ReflectionMetadataEvents : Instance
+ {
+ }
+
+ public class ReflectionMetadataFunctions : Instance
+ {
+ }
+
+ public abstract class ReflectionMetadataItem : Instance
+ {
+ public bool Browsable = true;
+ public string ClassCategory = "";
+ public bool ClientOnly = false;
+ public string Constraint = "";
+ public bool Deprecated = false;
+ public bool EditingDisabled = false;
+ public bool IsBackend = false;
+ public string ScriptContext = "";
+ public bool ServerOnly = false;
+ public double UIMaximum = 0;
+ public double UIMinimum = 0;
+ public double UINumTicks = 0;
+ public string summary = "";
+ }
+
+ public class ReflectionMetadataClass : ReflectionMetadataItem
+ {
+ public int ExplorerImageIndex = 0;
+ public int ExplorerOrder = 2147483647;
+ public bool Insertable = true;
+ public string PreferredParent = "";
+ }
+
+ public class ReflectionMetadataEnum : ReflectionMetadataItem
+ {
+ }
+
+ public class ReflectionMetadataEnumItem : ReflectionMetadataItem
+ {
+ }
+
+ public class ReflectionMetadataMember : ReflectionMetadataItem
+ {
+ }
+
+ public class ReflectionMetadataProperties : Instance
+ {
+ }
+
+ public class ReflectionMetadataYieldFunctions : Instance
+ {
+ }
+
+ public class RemoteEvent : Instance
+ {
+ }
+
+ public class RemoteFunction : Instance
+ {
+ }
+
+ public class RenderingTest : Instance
+ {
+ public CFrame CFrame = new CFrame();
+ public int ComparisonDiffThreshold = 10;
+ public RenderingTestComparisonMethod ComparisonMethod = RenderingTestComparisonMethod.psnr;
+ public float ComparisonPsnrThreshold = 50;
+ public string Description = "";
+ public float FieldOfView = 70;
+ public int QualityLevel = 21;
+ public bool ShouldSkip = false;
+ public string Ticket = "";
+ }
+
+ public class ReplicatedFirst : Instance
+ {
+ public ReplicatedFirst()
+ {
+ IsService = true;
+ }
+ }
+
+ public class ReplicatedStorage : Instance
+ {
+ public ReplicatedStorage()
+ {
+ IsService = true;
+ }
+ }
+
+ public class RobloxPluginGuiService : Instance
+ {
+ public RobloxPluginGuiService()
+ {
+ IsService = true;
+ }
+ }
+
+ public class RobloxReplicatedStorage : Instance
+ {
+ public RobloxReplicatedStorage()
+ {
+ IsService = true;
+ }
+ }
+
+ public class RunService : Instance
+ {
+ public RunService()
+ {
+ IsService = true;
+ }
+ }
+
+ public class RuntimeScriptService : Instance
+ {
+ public RuntimeScriptService()
+ {
+ IsService = true;
+ }
+ }
+
+ public class ScriptContext : Instance
+ {
+ public ScriptContext()
+ {
+ IsService = true;
+ }
+ }
+
+ public class ScriptService : Instance
+ {
+ public ScriptService()
+ {
+ IsService = true;
+ }
+ }
+
+ public class Selection : Instance
+ {
+ public Selection()
+ {
+ IsService = true;
+ }
+ }
+
+ public class ServerScriptService : Instance
+ {
+ public ServerScriptService()
+ {
+ IsService = true;
+ }
+
+ public bool LoadStringEnabled = false;
+ }
+
+ public class ServerStorage : Instance
+ {
+ public ServerStorage()
+ {
+ IsService = true;
+ }
+ }
+
+ public class Sky : Instance
+ {
+ public bool CelestialBodiesShown = true;
+ public float MoonAngularSize = 11;
+ public Content MoonTextureId = "rbxasset://sky/moon.jpg";
+ public Content SkyboxBk = "rbxasset://textures/sky/sky512_bk.tex";
+ public Content SkyboxDn = "rbxasset://textures/sky/sky512_dn.tex";
+ public Content SkyboxFt = "rbxasset://textures/sky/sky512_ft.tex";
+ public Content SkyboxLf = "rbxasset://textures/sky/sky512_lf.tex";
+ public Content SkyboxRt = "rbxasset://textures/sky/sky512_rt.tex";
+ public Content SkyboxUp = "rbxasset://textures/sky/sky512_up.tex";
+ public int StarCount = 3000;
+ public float SunAngularSize = 21;
+ public Content SunTextureId = "rbxasset://sky/sun.jpg";
+ }
+
+ public class Smoke : Instance
+ {
+ public Color3 Color = new Color3(1, 1, 1);
+ public bool Enabled = true;
+
+ public float Opacity
+ {
+ get { return opacity_xml; }
+ set { opacity_xml = value; }
+ }
+
+ public float RiseVelocity
+ {
+ get { return riseVelocity_xml; }
+ set { riseVelocity_xml = value; }
+ }
+
+ public float Size
+ {
+ get { return size_xml; }
+ set { size_xml = value; }
+ }
+
+ public float opacity_xml = 0.5f;
+ public float riseVelocity_xml = 1;
+ public float size_xml = 1;
+ }
+
+ public class SocialService : Instance
+ {
+ public SocialService()
+ {
+ IsService = true;
+ }
+ }
+
+ public class Sound : Instance
+ {
+ public float EmitterSize
+ {
+ get { return xmlRead_MinDistance_3; }
+ set { xmlRead_MinDistance_3 = value; }
+ }
+
+ public bool Looped = false;
+
+ public float MaxDistance
+ {
+ get { return xmlRead_MaxDistance_3; }
+ set { xmlRead_MaxDistance_3 = value; }
+ }
+
+ [Obsolete]
+ public float MinDistance
+ {
+ get { return EmitterSize; }
+ set { EmitterSize = value; }
+ }
+
+ [Obsolete]
+ public float Pitch
+ {
+ get { return PlaybackSpeed; }
+ set { PlaybackSpeed = value; }
+ }
+
+ public bool PlayOnRemove = false;
+ public float PlaybackSpeed = 1;
+ public bool Playing = false;
+ public RollOffMode RollOffMode = RollOffMode.Inverse;
+ public SoundGroup SoundGroup = null;
+ public Content SoundId = "";
+ public double TimePosition = 0;
+ public float Volume = 0.5f;
+ public float xmlRead_MaxDistance_3 = 10000;
+ public float xmlRead_MinDistance_3 = 10;
+ }
+
+ public abstract class SoundEffect : Instance
+ {
+ public bool Enabled = true;
+ public int Priority = 0;
+ }
+
+ public class ChorusSoundEffect : SoundEffect
+ {
+ public float Depth = 0.15f;
+ public float Mix = 0.5f;
+ public float Rate = 0.5f;
+ }
+
+ public class CompressorSoundEffect : SoundEffect
+ {
+ public float Attack = 0.1f;
+ public float GainMakeup = 0;
+ public float Ratio = 40;
+ public float Release = 0.1f;
+ public Instance SideChain = null;
+ public float Threshold = -40;
+ }
+
+ public class DistortionSoundEffect : SoundEffect
+ {
+ public float Level = 0.75f;
+ }
+
+ public class EchoSoundEffect : SoundEffect
+ {
+ public float Delay = 1;
+ public float DryLevel = 0;
+ public float Feedback = 0.5f;
+ public float WetLevel = 0;
+ }
+
+ public class EqualizerSoundEffect : SoundEffect
+ {
+ public float HighGain = 0;
+ public float LowGain = -20;
+ public float MidGain = -10;
+ }
+
+ public class FlangeSoundEffect : SoundEffect
+ {
+ public float Depth = 0.45f;
+ public float Mix = 0.85f;
+ public float Rate = 5;
+ }
+
+ public class PitchShiftSoundEffect : SoundEffect
+ {
+ public float Octave = 1.25f;
+ }
+
+ public class ReverbSoundEffect : SoundEffect
+ {
+ public float DecayTime = 1.5f;
+ public float Density = 1;
+ public float Diffusion = 1;
+ public float DryLevel = -6;
+ public float WetLevel = 0;
+ }
+
+ public class TremoloSoundEffect : SoundEffect
+ {
+ public float Depth = 1;
+ public float Duty = 0.5f;
+ public float Frequency = 5;
+ }
+
+ public class SoundGroup : Instance
+ {
+ public float Volume = 0.5f;
+ }
+
+ public class SoundService : Instance
+ {
+ public SoundService()
+ {
+ IsService = true;
+ }
+
+ public ReverbType AmbientReverb = ReverbType.NoReverb;
+ public float DistanceFactor = 3.33f;
+ public float DopplerScale = 1;
+ public bool RespectFilteringEnabled = true;
+ public float RolloffScale = 1;
+ }
+
+ public class Sparkles : Instance
+ {
+ public Color3 Color
+ {
+ get { return SparkleColor; }
+ set { SparkleColor = value; }
+ }
+
+ public bool Enabled = true;
+ public Color3 SparkleColor = Color3.FromRGB(144, 25, 255);
+ }
+
+ public class StarterGear : Instance
+ {
+ }
+
+ public class StarterPack : Instance
+ {
+ public StarterPack()
+ {
+ IsService = true;
+ }
+ }
+
+ public class StarterPlayer : Instance
+ {
+ public StarterPlayer()
+ {
+ IsService = true;
+ }
+
+ public bool AllowCustomAnimations = true;
+ public bool AutoJumpEnabled = true;
+ public float CameraMaxZoomDistance = 400;
+ public float CameraMinZoomDistance = 0.5f;
+ public CameraMode CameraMode = CameraMode.Classic;
+ public float CharacterJumpHeight = 7.2f;
+ public float CharacterJumpPower = 50;
+ public float CharacterMaxSlopeAngle = 89;
+ public bool CharacterUseJumpPower = true;
+ public float CharacterWalkSpeed = 16;
+ public DevCameraOcclusionMode DevCameraOcclusionMode = DevCameraOcclusionMode.Zoom;
+ public DevComputerCameraMovementMode DevComputerCameraMovementMode = DevComputerCameraMovementMode.UserChoice;
+ public DevComputerMovementMode DevComputerMovementMode = DevComputerMovementMode.UserChoice;
+ public DevTouchCameraMovementMode DevTouchCameraMovementMode = DevTouchCameraMovementMode.UserChoice;
+ public DevTouchMovementMode DevTouchMovementMode = DevTouchMovementMode.UserChoice;
+ public bool EnableMouseLockOption = true;
+ public long GameSettingsAssetIDFace = 0;
+ public long GameSettingsAssetIDHead = 0;
+ public long GameSettingsAssetIDLeftArm = 0;
+ public long GameSettingsAssetIDLeftLeg = 0;
+ public long GameSettingsAssetIDPants = 0;
+ public long GameSettingsAssetIDRightArm = 0;
+ public long GameSettingsAssetIDRightLeg = 0;
+ public long GameSettingsAssetIDShirt = 0;
+ public long GameSettingsAssetIDTeeShirt = 0;
+ public long GameSettingsAssetIDTorso = 0;
+ public GameAvatarType GameSettingsAvatar = GameAvatarType.R15;
+ public R15CollisionType GameSettingsR15Collision = R15CollisionType.OuterBox;
+ public NumberRange GameSettingsScaleRangeBodyType = new NumberRange(0, 1);
+ public NumberRange GameSettingsScaleRangeHead = new NumberRange(0.95f, 1);
+ public NumberRange GameSettingsScaleRangeHeight = new NumberRange(0.9f, 1.05f);
+ public NumberRange GameSettingsScaleRangeProportion = new NumberRange(0, 1);
+ public NumberRange GameSettingsScaleRangeWidth = new NumberRange(0.7f, 1);
+ public float HealthDisplayDistance = 100;
+ public bool LoadCharacterAppearance = true;
+ public float NameDisplayDistance = 100;
+ public bool UserEmotesEnabled = true;
+ }
+
+ public class StarterPlayerScripts : Instance
+ {
+ }
+
+ public class StarterCharacterScripts : StarterPlayerScripts
+ {
+ }
+
+ public class Stats : Instance
+ {
+ public Stats()
+ {
+ IsService = true;
+ }
+
+ }
+
+ public class StudioData : Instance
+ {
+ public StudioData()
+ {
+ IsService = true;
+ }
+
+ public long SrcPlaceId = 0;
+ public long SrcUniverseId = 0;
+ }
+
+ public class StudioService : Instance
+ {
+ public StudioService()
+ {
+ IsService = true;
+ }
+
+ }
+
+ public class Team : Instance
+ {
+ public bool AutoAssignable = true;
+
+ [Obsolete]
+ public bool AutoColorCharacters = true;
+
+ [Obsolete]
+ public int Score = 0;
+
+ public BrickColor TeamColor = BrickColor.FromNumber(1);
+ }
+
+ public class Teams : Instance
+ {
+ public Teams()
+ {
+ IsService = true;
+ }
+ }
+
+ public class TeleportService : Instance
+ {
+ public TeleportService()
+ {
+ IsService = true;
+ }
+
+ [Obsolete]
+ public bool CustomizedTeleportUI = false;
+ }
+
+ public class TerrainRegion : Instance
+ {
+ public Vector3int16 ExtentsMax = new Vector3int16();
+ public Vector3int16 ExtentsMin = new Vector3int16();
+ public byte[] GridV3 = new byte[0];
+ public byte[] SmoothGrid = Convert.FromBase64String("AQU=");
+ }
+
+ public class TestService : Instance
+ {
+ public TestService()
+ {
+ IsService = true;
+ }
+
+ public bool AutoRuns = true;
+ public string Description = "";
+ public bool ExecuteWithStudioRun = false;
+ public bool Is30FpsThrottleEnabled = true;
+ public bool IsPhysicsEnvironmentalThrottled = true;
+ public bool IsSleepAllowed = true;
+ public int NumberOfPlayers = 0;
+ public double SimulateSecondsLag = 0;
+ public double Timeout = 10;
+ }
+
+ public class TextService : Instance
+ {
+ public TextService()
+ {
+ IsService = true;
+ }
+ }
+
+ public class TimerService : Instance
+ {
+ public TimerService()
+ {
+ IsService = true;
+ }
+ }
+
+ public class TouchInputService : Instance
+ {
+ public TouchInputService()
+ {
+ IsService = true;
+ }
+ }
+
+ public class Trail : Instance
+ {
+ public Attachment Attachment0 = null;
+ public Attachment Attachment1 = null;
+ public ColorSequence Color = new ColorSequence(new Color3(1, 1, 1));
+ public bool Enabled = true;
+ public bool FaceCamera = false;
+ public float Lifetime = 2;
+ public float LightEmission = 0;
+ public float LightInfluence = 0;
+ public float MaxLength = 0;
+ public float MinLength = 0.1f;
+ public Content Texture = "";
+ public float TextureLength = 1;
+ public TextureMode TextureMode = TextureMode.Stretch;
+ public NumberSequence Transparency = new NumberSequence(0.5f);
+ public NumberSequence WidthScale = new NumberSequence(1);
+ }
+
+ public abstract class TweenBase : Instance
+ {
+ }
+
+ public class Tween : TweenBase
+ {
+ }
+
+ public class TweenService : Instance
+ {
+ public TweenService()
+ {
+ IsService = true;
+ }
+ }
+
+ public abstract class UIBase : Instance
+ {
+ }
+
+ public abstract class UIComponent : UIBase
+ {
+ }
+
+ public abstract class UIConstraint : UIComponent
+ {
+ }
+
+ public class UIAspectRatioConstraint : UIConstraint
+ {
+ public float AspectRatio = 1;
+ public AspectType AspectType = AspectType.FitWithinMaxSize;
+ public DominantAxis DominantAxis = DominantAxis.Width;
+ }
+
+ public class UISizeConstraint : UIConstraint
+ {
+ public Vector2 MaxSize = new Vector2(float.MaxValue, float.MaxValue);
+ public Vector2 MinSize = new Vector2();
+ }
+
+ public class UITextSizeConstraint : UIConstraint
+ {
+ public int MaxTextSize = 100;
+ public int MinTextSize = 1;
+ }
+
+ public abstract class UILayout : UIComponent
+ {
+ }
+
+ public abstract class UIGridStyleLayout : UILayout
+ {
+ public FillDirection FillDirection = FillDirection.Horizontal;
+ public HorizontalAlignment HorizontalAlignment = HorizontalAlignment.Left;
+ public SortOrder SortOrder = SortOrder.Name;
+ public VerticalAlignment VerticalAlignment = VerticalAlignment.Top;
+ }
+
+ public class UIGridLayout : UIGridStyleLayout
+ {
+ public UDim2 CellPadding = new UDim2(0, 5, 0, 5);
+ public UDim2 CellSize = new UDim2(0, 100, 0, 100);
+ public int FillDirectionMaxCells = 0;
+ public StartCorner StartCorner = StartCorner.TopLeft;
+ }
+
+ public class UIInlineLayout : UIGridStyleLayout
+ {
+ public InlineAlignment InlineAlignment = InlineAlignment.Center;
+ public UDim2 InlinePadding = new UDim2();
+ }
+
+ public class UIListLayout : UIGridStyleLayout
+ {
+ public UDim Padding = new UDim();
+ }
+
+ public class UIPageLayout : UIGridStyleLayout
+ {
+ public bool Animated = true;
+ public bool Circular = false;
+ public EasingDirection EasingDirection = EasingDirection.Out;
+ public EasingStyle EasingStyle = EasingStyle.Back;
+ public bool GamepadInputEnabled = true;
+ public UDim Padding = new UDim();
+ public bool ScrollWheelInputEnabled = true;
+ public bool TouchInputEnabled = true;
+ public float TweenTime = 1;
+ }
+
+ public class UITableLayout : UIGridStyleLayout
+ {
+ public bool FillEmptySpaceColumns = false;
+ public bool FillEmptySpaceRows = false;
+ public TableMajorAxis MajorAxis = TableMajorAxis.RowMajor;
+ public UDim2 Padding = new UDim2();
+ }
+
+ public class UIPadding : UIComponent
+ {
+ public UDim PaddingBottom = new UDim();
+ public UDim PaddingLeft = new UDim();
+ public UDim PaddingRight = new UDim();
+ public UDim PaddingTop = new UDim();
+ }
+
+ public class UIScale : UIComponent
+ {
+ public float Scale = 1;
+ }
+
+ public class UserInputService : Instance
+ {
+ public UserInputService()
+ {
+ IsService = true;
+ }
+
+ public bool LegacyInputEventsEnabled = true;
+ public MouseBehavior MouseBehavior = MouseBehavior.Default;
+ public float MouseDeltaSensitivity = 1; // [Load-only]
+ public bool MouseIconEnabled = true;
+ }
+
+ public class VRService : Instance
+ {
+ public VRService()
+ {
+ IsService = true;
+ }
+
+ }
+
+ public abstract class ValueBase : Instance
+ {
+ }
+
+ public class BinaryStringValue : ValueBase
+ {
+ public byte[] Value = new byte[0];
+ }
+
+ public class BoolValue : ValueBase
+ {
+ public bool Value = false;
+ }
+
+ public class BrickColorValue : ValueBase
+ {
+ public BrickColor Value = BrickColor.FromNumber(194);
+ }
+
+ public class CFrameValue : ValueBase
+ {
+ public CFrame Value = new CFrame();
+ }
+
+ public class Color3Value : ValueBase
+ {
+ public Color3 Value = new Color3();
+ }
+
+ public class DoubleConstrainedValue : ValueBase
+ {
+ public double ConstrainedValue
+ {
+ get { return Value; }
+ set { Value = value; }
+ }
+
+ public double MaxValue = 1;
+ public double MinValue = 0;
+ public double Value = 0; // [Load-only]
+ }
+
+ public class IntConstrainedValue : ValueBase
+ {
+ public long ConstrainedValue
+ {
+ get { return Value; }
+ set { Value = value; }
+ }
+
+ public long MaxValue = 10;
+ public long MinValue = 0;
+ public long Value = 0; // [Load-only]
+ }
+
+ public class IntValue : ValueBase
+ {
+ public long Value = 0;
+ }
+
+ public class NumberValue : ValueBase
+ {
+ public double Value = 0;
+ }
+
+ public class ObjectValue : ValueBase
+ {
+ public Instance Value = null;
+ }
+
+ public class RayValue : ValueBase
+ {
+ public Ray Value = new Ray();
+ }
+
+ public class StringValue : ValueBase
+ {
+ public string Value = "";
+ }
+
+ public class Vector3Value : ValueBase
+ {
+ public Vector3 Value = new Vector3();
+ }
+
+ public class VersionControlService : Instance
+ {
+ public VersionControlService()
+ {
+ IsService = true;
+ }
+ }
+
+ public class VirtualInputManager : Instance
+ {
+ public VirtualInputManager()
+ {
+ IsService = true;
+ }
+
+ public string AdditionalLuaState = ""; // [Load-only]
+ }
+
+ public class VirtualUser : Instance
+ {
+ public VirtualUser()
+ {
+ IsService = true;
+ }
+ }
+
+ public class Visit : Instance
+ {
+ public Visit()
+ {
+ IsService = true;
+ }
+ }
+
+ public class WeldConstraint : Instance
+ {
+ public CFrame CFrame0 = new CFrame();
+ public CFrame CFrame1 = new CFrame();
+ public bool Enabled = true;
+
+ public BasePart Part0
+ {
+ get { return Part0Internal; }
+ set { Part0Internal = value; }
+ }
+
+ public BasePart Part0Internal = null;
+
+ public BasePart Part1
+ {
+ get { return Part1Internal; }
+ set { Part1Internal = value; }
+ }
+
+ public BasePart Part1Internal = null;
+ }
+}
diff --git a/Tree/Enums.cs b/Generated/Enums.cs
similarity index 95%
rename from Tree/Enums.cs
rename to Generated/Enums.cs
index 78a40b8..ff35304 100644
--- a/Tree/Enums.cs
+++ b/Generated/Enums.cs
@@ -1,5 +1,5 @@
-// This is an auto-generated list of all available enums on Roblox!
-// Updated as of 0.370.0.274702
+// Auto-generated list of Roblox enums.
+// Updated as of 0.392.0.316618
namespace RobloxFiles.Enums
{
@@ -26,6 +26,12 @@ namespace RobloxFiles.Enums
Servo
}
+ public enum AlignType
+ {
+ Parallel,
+ Perpendicular
+ }
+
public enum AnimationPriority
{
Idle,
@@ -55,9 +61,15 @@ namespace RobloxFiles.Enums
ScaleWithParentSize
}
+ public enum AssetFetchStatus
+ {
+ Success,
+ Failure
+ }
+
public enum AssetType
{
- Image,
+ Image = 1,
TeeShirt,
Audio,
Mesh,
@@ -99,7 +111,8 @@ namespace RobloxFiles.Enums
WalkAnimation,
PoseAnimation,
EarAccessory,
- EyeAccessory
+ EyeAccessory,
+ EmoteAnimation = 61
}
public enum AutoJointsMode
@@ -113,7 +126,8 @@ namespace RobloxFiles.Enums
{
Friend,
Chat,
- Emote
+ Emote,
+ InspectMenu
}
public enum AvatarJointPositionType
@@ -169,6 +183,14 @@ namespace RobloxFiles.Enums
Unknown = 17
}
+ public enum BreakReason
+ {
+ Other,
+ Error,
+ SpecialBreakpoint,
+ UserBreakpoint
+ }
+
public enum Button
{
Dismount = 8,
@@ -250,7 +272,7 @@ namespace RobloxFiles.Enums
public enum CenterDialogType
{
- UnsolicitedDialog,
+ UnsolicitedDialog = 1,
PlayerInitiatedDialog,
ModalDialog,
QuitDialog
@@ -258,7 +280,7 @@ namespace RobloxFiles.Enums
public enum ChatCallbackType
{
- OnCreatingChatWindow,
+ OnCreatingChatWindow = 1,
OnClientSendingMessage,
OnClientFormattingMessage,
OnServerReceivingMessage = 17
@@ -342,6 +364,8 @@ namespace RobloxFiles.Enums
DisconnectIdle,
DisconnectRaknetErrors,
DisconnectWrongVersion,
+ DisconnectBySecurityPolicy,
+ DisconnectBlockedIP,
PlacelaunchErrors = 512,
PlacelaunchDisabled = 515,
PlacelaunchError,
@@ -376,10 +400,9 @@ namespace RobloxFiles.Enums
public enum ContextActionPriority
{
Low = 1000,
+ Default = 2000,
Medium = 2000,
- High = 3000,
-
- Default = Medium
+ High = 3000
}
public enum ContextActionResult
@@ -400,7 +423,8 @@ namespace RobloxFiles.Enums
Health,
Backpack,
Chat,
- All
+ All,
+ EmotesMenu
}
public enum CreatorType
@@ -423,12 +447,6 @@ namespace RobloxFiles.Enums
Follow
}
- public enum DEPRECATED_DebuggerDataModelPreference
- {
- Server,
- Client
- }
-
public enum DataStoreRequestType
{
GetAsync,
@@ -506,6 +524,14 @@ namespace RobloxFiles.Enums
Navigation
}
+ public enum DeviceType
+ {
+ Unknown,
+ Desktop,
+ Tablet,
+ Phone
+ }
+
public enum DialogBehaviorType
{
SinglePlayer,
@@ -735,7 +761,7 @@ namespace RobloxFiles.Enums
public enum GraphicsMode
{
- Automatic,
+ Automatic = 1,
Direct3D11,
Direct3D9,
OpenGL,
@@ -811,6 +837,12 @@ namespace RobloxFiles.Enums
Localization = 24
}
+ public enum HumanoidCollisionType
+ {
+ OuterBox,
+ InnerBox
+ }
+
public enum HumanoidDisplayDistanceType
{
Viewer,
@@ -875,6 +907,13 @@ namespace RobloxFiles.Enums
Float
}
+ public enum InlineAlignment
+ {
+ Bottom,
+ Center,
+ Top
+ }
+
public enum InputType
{
NoInput,
@@ -891,7 +930,7 @@ namespace RobloxFiles.Enums
public enum JointType
{
- Weld,
+ Weld = 1,
Snap = 3,
Rotate = 7,
RotateP,
@@ -1169,6 +1208,13 @@ namespace RobloxFiles.Enums
Default
}
+ public enum LanguagePreference
+ {
+ SystemDefault,
+ English,
+ SimplifiedChinese
+ }
+
public enum LeftRight
{
Left,
@@ -1244,6 +1290,7 @@ namespace RobloxFiles.Enums
Ice = 1536,
Glacier = 1552,
Glass = 1568,
+ ForceField = 1584,
Air = 1792,
Water = 2048
}
@@ -1253,7 +1300,8 @@ namespace RobloxFiles.Enums
None,
BuildersClub,
TurboBuildersClub,
- OutrageousBuildersClub
+ OutrageousBuildersClub,
+ Premium
}
public enum MeshType
@@ -1619,7 +1667,7 @@ namespace RobloxFiles.Enums
public enum ScrollingDirection
{
- X,
+ X = 1,
Y,
XY = 4
}
@@ -1688,6 +1736,13 @@ namespace RobloxFiles.Enums
Confusion
}
+ public enum StreamingPauseMode
+ {
+ Default,
+ Disabled,
+ ClientPhysicsPause
+ }
+
public enum StudioStyleGuideColor
{
MainBackground,
@@ -1773,7 +1828,15 @@ namespace RobloxFiles.Enums
CheckedFieldIndicator,
HeaderSection,
Midlight,
- StatusBar
+ StatusBar,
+ DialogButton,
+ DialogButtonText,
+ DialogButtonBorder,
+ DialogMainButton,
+ DialogMainButtonText,
+ Merge3HighlightOriginal,
+ Merge3HighlightMine,
+ Merge3HighlightTheirs
}
public enum StudioStyleGuideModifier
@@ -1800,6 +1863,12 @@ namespace RobloxFiles.Enums
Motor
}
+ public enum SurfaceGuiSizingMode
+ {
+ FixedSize,
+ PixelsPerStud
+ }
+
public enum SurfaceType
{
Smooth,
@@ -1832,7 +1901,9 @@ namespace RobloxFiles.Enums
public enum Technology
{
Legacy,
- Voxel
+ Voxel,
+ Compatibility,
+ ShadowMap
}
public enum TeleportResult
@@ -1865,7 +1936,7 @@ namespace RobloxFiles.Enums
public enum TextFilterContext
{
- PublicChat,
+ PublicChat = 1,
PrivateChat
}
@@ -2038,6 +2109,7 @@ namespace RobloxFiles.Enums
Gamepad7,
Gamepad8,
TextInput,
+ InputMethod,
None
}
@@ -2115,4 +2187,4 @@ namespace RobloxFiles.Enums
Global,
Sibling
}
-}
\ No newline at end of file
+}
diff --git a/Plugins/GenerateApiDump.rbxm b/Plugins/GenerateApiDump.rbxm
new file mode 100644
index 0000000..8f53365
Binary files /dev/null and b/Plugins/GenerateApiDump.rbxm differ
diff --git a/Plugins/GenerateApiDump/ApiPlugin.server.lua b/Plugins/GenerateApiDump/ApiPlugin.server.lua
new file mode 100644
index 0000000..cbc3efd
--- /dev/null
+++ b/Plugins/GenerateApiDump/ApiPlugin.server.lua
@@ -0,0 +1,607 @@
+local HttpService = game:GetService("HttpService")
+local ServerStorage = game:GetService("ServerStorage")
+local StarterPlayer = game:GetService("StarterPlayer")
+local StudioService = game:GetService("StudioService")
+
+local classes = {}
+local outStream = ""
+local stackLevel = 0
+
+local singletons =
+{
+ Terrain = workspace:WaitForChild("Terrain");
+ StarterPlayerScripts = StarterPlayer:WaitForChild("StarterPlayerScripts");
+ StarterCharacterScripts = StarterPlayer:WaitForChild("StarterCharacterScripts");
+}
+
+local isCoreScript = pcall(function ()
+ local restricted = game:GetService("RobloxPluginGuiService")
+ return tostring(restricted)
+end)
+
+local function write(formatString, ...)
+ local tabs = string.rep(' ', stackLevel * 4)
+ local fmt = formatString or ""
+
+ local value = tabs .. fmt:format(...)
+ outStream = outStream .. value
+end
+
+local function writeLine(formatString, ...)
+ if not formatString then
+ outStream = outStream .. '\n'
+ return
+ end
+
+ write(formatString .. '\n', ...)
+end
+
+local function openStack()
+ writeLine('{')
+ stackLevel = stackLevel + 1
+end
+
+local function closeStack()
+ stackLevel = stackLevel - 1
+ writeLine('}')
+end
+
+local function clearStream()
+ stackLevel = 0
+ outStream = ""
+end
+
+local function exportStream(label)
+ local results = outStream:gsub("\n\n\n", "\n\n")
+
+ if plugin then
+ local export = Instance.new("Script")
+ export.Archivable = false
+ export.Source = results
+ export.Name = label
+
+ plugin:OpenScript(export)
+ end
+
+ if isCoreScript then
+ StudioService:CopyToClipboard(results)
+ elseif not plugin then
+ warn(label)
+ print(results)
+ end
+end
+
+local function getTags(object)
+ local tags = {}
+
+ if object.Tags ~= nil then
+ for _,tag in pairs(object.Tags) do
+ tags[tag] = true
+ end
+ end
+
+ if object.Name == "Terrain" then
+ tags.NotCreatable = nil
+ end
+
+ return tags
+end
+
+local function upcastInheritance(class, root)
+ local superClass = classes[class.Superclass]
+
+ if not superClass then
+ return
+ end
+
+ if not root then
+ root = class
+ end
+
+ if not superClass.Inherited then
+ superClass.Inherited = root
+ end
+
+ upcastInheritance(superClass, root)
+end
+
+local function canCreateClass(class)
+ local tags = getTags(class)
+ local canCreate = true
+
+ if tags.NotCreatable then
+ canCreate = false
+ end
+
+ if tags.Service then
+ canCreate = true
+ end
+
+ if tags.Settings then
+ canCreate = false
+ end
+
+ if singletons[class.Name] then
+ canCreate = true
+ end
+
+ return canCreate
+end
+
+local function collectProperties(class)
+ local propMap = {}
+
+ for _,member in ipairs(class.Members) do
+ if member.MemberType == "Property" then
+ local propName = member.Name
+ propMap[propName] = member
+ end
+ end
+
+ return propMap
+end
+
+local function createProperty(propName, propType)
+ local category = "DataType";
+ local name = propType
+
+ if propType:find(':') then
+ local data = string.split(propType, ':')
+ category = data[1]
+ name = data[2]
+ end
+
+ return
+ {
+ Name = propName;
+
+ Serialization =
+ {
+ CanSave = true;
+ CanLoad = true;
+ };
+
+ ValueType =
+ {
+ Category = category;
+ Name = name;
+ };
+
+ Security = "None";
+ }
+end
+
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Formatting
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local formatting = require(script.Parent.Formatting)
+
+local formatLinks =
+{
+ ["int"] = "Int";
+ ["nil"] = "Null";
+ ["long"] = "Int";
+
+ ["float"] = "Float";
+ ["byte[]"] = "Bytes";
+ ["double"] = "Double";
+
+ ["string"] = "String";
+ ["Content"] = "String";
+ ["Instance"] = "Null";
+
+ ["Color3uint8"] = "Color3";
+ ["ProtectedString"] = "String";
+}
+
+local function getFormatFunction(valueType)
+ if not formatting[valueType] then
+ valueType = formatLinks[valueType]
+ end
+
+ return formatting[valueType]
+end
+
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Property Patches
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local patches = require(script.Parent.PropertyPatches)
+local patchIndex = {}
+
+function patchIndex:__index(key)
+ if not rawget(self, key) then
+ rawset(self, key, {})
+ end
+
+ return self[key]
+end
+
+local function getPatches(className)
+ local classPatches = patches[className]
+ return setmetatable(classPatches, patchIndex)
+end
+
+setmetatable(patches, patchIndex)
+
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Main
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+local baseUrl = "https://raw.githubusercontent.com/CloneTrooper1019/Roblox-Client-Tracker/roblox/"
+local toolbar, classButton, enumButton
+
+if plugin then
+ toolbar = plugin:CreateToolbar("C# API Dump")
+
+ classButton = toolbar:CreateButton(
+ "Dump Classes",
+ "Generates a C# dump of Roblox's Class API.",
+ "rbxasset://textures/Icon_Stream_Off@2x.png"
+ )
+
+ enumButton = toolbar:CreateButton(
+ "Dump Enums",
+ "Generates a C# dump of Roblox's Enum API.",
+ "rbxasset://textures/Icon_Stream_Off@2x.png"
+ )
+end
+
+local function getAsync(url)
+ local enabled
+
+ if isCoreScript then
+ enabled = HttpService:GetHttpEnabled()
+ HttpService:SetHttpEnabled(true)
+ end
+
+ local result = HttpService:GetAsync(url)
+
+ if isCoreScript then
+ HttpService:SetHttpEnabled(enabled)
+ end
+
+ return result
+end
+
+local function generateClasses()
+ local version = getAsync(baseUrl .. "version.txt")
+
+ local apiDump = getAsync(baseUrl .. "API-Dump.json")
+ apiDump = HttpService:JSONDecode(apiDump)
+
+ local classNames = {}
+ classes = {}
+
+ for _,class in ipairs(apiDump.Classes) do
+ local className = class.Name
+ local superClass = classes[class.Superclass]
+
+ if singletons[className] then
+ class.Singleton = true
+ class.Object = singletons[className]
+ end
+
+ if superClass and canCreateClass(class) then
+ local classTags = getTags(class)
+
+ if classTags.Service then
+ pcall(function ()
+ if not className:find("Network") then
+ class.Object = game:GetService(className)
+ end
+ end)
+ elseif not classTags.NotCreatable then
+ pcall(function ()
+ class.Object = Instance.new(className)
+
+ if ServerStorage:FindFirstChild("DumpFolder") then
+ class.Object.Name = className
+ class.Object.Parent = ServerStorage.DumpFolder
+ end
+ end)
+ end
+
+ upcastInheritance(class)
+ end
+
+ classes[className] = class
+ table.insert(classNames, className)
+ end
+
+ outStream = ""
+
+ writeLine("// Auto-generated list of creatable Roblox classes.")
+ writeLine("// Updated as of %s", version)
+ writeLine()
+
+ writeLine("using System;")
+ writeLine()
+
+ writeLine("using RobloxFiles.DataTypes;")
+ writeLine("using RobloxFiles.Enums;")
+ writeLine("using RobloxFiles.Utility;")
+ writeLine()
+
+ writeLine("namespace RobloxFiles")
+ openStack()
+
+ for i,className in ipairs(classNames) do
+ local class = classes[className]
+ local classTags = getTags(class)
+
+ local registerClass = canCreateClass(class)
+
+ if class.Inherited then
+ registerClass = true
+ end
+
+ if class.Name == "Instance" or class.Name == "Studio" then
+ registerClass = false
+ end
+
+ local object = class.Object
+
+ if not object then
+ if class.Inherited then
+ object = class.Inherited.Object
+ elseif singletons[className] then
+ object = singletons[className]
+ else
+ registerClass = false
+ end
+ end
+
+ if registerClass then
+ local objectType
+
+ if classTags.NotCreatable and class.Inherited and not class.Singleton then
+ objectType = "abstract class"
+ else
+ objectType = "class"
+ end
+
+ writeLine("public %s %s : %s", objectType, className, class.Superclass)
+ openStack()
+
+ local classPatches = getPatches(className)
+ local redirectProps = classPatches.Redirect
+
+ local propMap = collectProperties(class)
+ local propNames = {}
+
+ for _,propName in pairs(classPatches.Remove) do
+ propMap[propName] = nil
+ end
+
+ for propName in pairs(propMap) do
+ table.insert(propNames, propName)
+ end
+
+ for propName, propType in pairs(classPatches.Add) do
+ if not propMap[propName] then
+ propMap[propName] = createProperty(propName, propType)
+ table.insert(propNames, propName)
+ else
+ propMap[propName].Serialization.CanLoad = true
+ end
+ end
+
+ local firstLine = true
+ table.sort(propNames)
+
+
+ if classTags.Service then
+ writeLine("public %s()", className)
+ openStack()
+
+ writeLine("IsService = true;")
+ closeStack()
+
+ if #propNames > 0 then
+ writeLine()
+ end
+ end
+
+ for i, propName in ipairs(propNames) do
+ local prop = propMap[propName]
+
+ local serial = prop.Serialization
+ local valueType = prop.ValueType.Name
+
+ if serial.CanLoad then
+ local propTags = getTags(prop)
+
+ local redirect = redirectProps[propName]
+ local name = propName
+ local default = ""
+
+ if propName == className then
+ name = name .. '_'
+ end
+
+ if valueType == "int64" then
+ valueType = "long"
+ elseif valueType == "BinaryString" then
+ valueType = "byte[]"
+ end
+
+ local first = name:sub(1, 1)
+
+ if first == first:lower() then
+ local pascal = first:upper() .. name:sub(2)
+ if propMap[pascal] ~= nil and propTags.Deprecated then
+ redirect = pascal
+ end
+ end
+
+ if redirect then
+ local get, set
+
+ if typeof(redirect) == "string" then
+ get = redirect
+ set = redirect .. " = value"
+ else
+ get = redirect.Get
+ set = redirect.Set
+ end
+
+ if not firstLine then
+ writeLine()
+ end
+
+ if propTags.Deprecated then
+ writeLine("[Obsolete]")
+ end
+
+ writeLine("public %s %s", valueType, name)
+
+ openStack()
+ writeLine("get { return %s; }", get)
+ writeLine("set { %s; }", set)
+ closeStack()
+
+ if (i ~= #propNames) then
+ writeLine()
+ end
+ else
+ local value = classPatches.Defaults[propName]
+ local gotValue = (value ~= nil)
+
+ if not gotValue then
+ gotValue, value = pcall(function ()
+ return object[propName]
+ end)
+ end
+
+ local comment = " // Default missing!"
+ local category = prop.ValueType.Category
+
+ if gotValue then
+ local category = prop.ValueType.Category
+ local formatFunc = getFormatFunction(valueType)
+
+ if not formatFunc then
+ local literal = typeof(value)
+ formatFunc = getFormatFunction(literal)
+ end
+
+ if not formatFunc then
+ formatFunc = tostring
+ end
+
+ local result
+
+ if typeof(formatFunc) == "string" then
+ result = formatFunc
+ else
+ result = formatFunc(value)
+ end
+
+ if not serial.CanSave and not propTags.Deprecated then
+ comment = " // [Load-only]"
+ else
+ comment = ""
+ end
+
+ default = " = " .. result
+ end
+
+ if propTags.Deprecated then
+ if not firstLine then
+ writeLine()
+ end
+
+ writeLine("[Obsolete]")
+ end
+
+ if category == "Class" then
+ default = " = null"
+ comment = ""
+ end
+
+ writeLine("public %s %s%s;%s", valueType, name, default, comment)
+
+ if propTags.Deprecated and i ~= #propNames then
+ writeLine()
+ end
+ end
+
+ firstLine = false
+ end
+ end
+
+ closeStack()
+
+ if (i ~= #classNames) then
+ writeLine()
+ end
+ end
+ end
+
+ closeStack()
+ exportStream("Classes")
+end
+
+local function generateEnums()
+ local version = getfenv().version():gsub("%. ", ".")
+ clearStream()
+
+ writeLine("// Auto-generated list of Roblox enums.")
+ writeLine("// Updated as of %s", version)
+ writeLine()
+
+ writeLine("namespace RobloxFiles.Enums")
+ openStack()
+
+ local enums = Enum:GetEnums()
+
+ for i, enum in ipairs(enums) do
+ writeLine("public enum %s", tostring(enum))
+ openStack()
+
+ local enumItems = enum:GetEnumItems()
+ local lastValue = -1
+
+ table.sort(enumItems, function (a, b)
+ return a.Value < b.Value
+ end)
+
+ for i, enumItem in ipairs(enumItems) do
+ local text = ""
+ local comma = ','
+
+ local name = enumItem.Name
+ local value = enumItem.Value
+
+ if (value - lastValue) ~= 1 then
+ text = " = " .. value;
+ end
+
+ if i == #enumItems then
+ comma = ""
+ end
+
+ lastValue = value
+ writeLine("%s%s%s", name, text, comma)
+ end
+
+ closeStack()
+
+ if i ~= #enums then
+ writeLine()
+ end
+ end
+
+ closeStack()
+ exportStream("Enums")
+end
+
+if plugin then
+ classButton.Click:Connect(generateClasses)
+ enumButton.Click:Connect(generateEnums)
+else
+ generateClasses()
+ generateEnums()
+end
\ No newline at end of file
diff --git a/Plugins/GenerateApiDump/Formatting.lua b/Plugins/GenerateApiDump/Formatting.lua
new file mode 100644
index 0000000..47b442a
--- /dev/null
+++ b/Plugins/GenerateApiDump/Formatting.lua
@@ -0,0 +1,273 @@
+local Format = {}
+
+function Format.Null(value)
+ return "null"
+end
+
+function Format.Bytes(value)
+ if #value > 0 then
+ local fmt = "Convert.FromBase64String(%q)"
+ return fmt:format(value)
+ else
+ return "new byte[0]"
+ end
+end
+
+function Format.String(value)
+ return string.format("%q", value)
+end
+
+function Format.Int(value)
+ return string.format("%i", value)
+end
+
+function Format.Number(value)
+ local int = math.floor(value)
+
+ if math.abs(value - int) < 0.001 then
+ return Format.Int(int)
+ end
+
+ local result = string.format("%.5f", value)
+ result = result:gsub("%.?0+$", "")
+
+ return result
+end
+
+function Format.Double(value)
+ local result = Format.Number(value)
+
+ if result == "inf" then
+ return "double.MaxValue"
+ elseif result == "-inf" then
+ return "double.MinValue"
+ else
+ return result
+ end
+end
+
+function Format.Float(value)
+ local result = Format.Number(value)
+
+ if result == "inf" then
+ return "float.MaxValue"
+ elseif result == "-inf" then
+ return "float.MinValue"
+ else
+ if result:find("%.") then
+ result = result .. 'f'
+ end
+
+ return result
+ end
+end
+
+function Format.Flags(flag, enum)
+ local value = 0
+
+ for _,item in pairs(enum:GetEnumItems()) do
+ if flag[item.Name] then
+ value = value + (2 ^ item.Value)
+ end
+ end
+
+ return value
+end
+
+function Format.Axes(axes)
+ return "(Axes)" .. Format.Flags(axes, Enum.Axis)
+end
+
+function Format.Faces(faces)
+ return "(Faces)" .. Format.Flags(faces, Enum.NormalId)
+end
+
+function Format.EnumItem(item)
+ local enum = tostring(item.EnumType)
+ return enum .. '.' .. item.Name
+end
+
+function Format.BrickColor(brickColor)
+ local fmt = "BrickColor.FromNumber(%i)"
+ return fmt:format(brickColor.Number)
+end
+
+function Format.Color3(color)
+ if color == Color3.new() then
+ return "new Color3()"
+ end
+
+ local r = Format.Float(color.r)
+ local g = Format.Float(color.g)
+ local b = Format.Float(color.b)
+
+ local fmt = "%s(%s, %s, %s)";
+ local constructor = "new Color3";
+
+ if string.find(r .. g .. b, 'f') then
+ r = Format.Int(color.r * 255)
+ g = Format.Int(color.g * 255)
+ b = Format.Int(color.b * 255)
+
+ constructor = "Color3.FromRGB"
+ end
+
+ return fmt:format(constructor, r, g, b)
+end
+
+function Format.UDim(udim)
+ if udim == UDim.new() then
+ return "new UDim()"
+ end
+
+ local scale = Format.Float(udim.Scale)
+ local offset = Format.Int(udim.Offset)
+
+ local fmt = "new UDim(%s, %s)"
+ return fmt:format(scale, offset)
+end
+
+function Format.UDim2(udim2)
+ if udim2 == UDim2.new() then
+ return "new UDim2()"
+ end
+
+ local xScale = Format.Float(udim2.X.Scale)
+ local yScale = Format.Float(udim2.Y.Scale)
+
+ local xOffset = Format.Int(udim2.X.Offset)
+ local yOffset = Format.Int(udim2.Y.Offset)
+
+ local fmt = "new UDim2(%s, %s, %s, %s)"
+ return fmt:format(xScale, xOffset, yScale, yOffset)
+end
+
+function Format.Vector2(v2)
+ if v2.Magnitude < 0.001 then
+ return "new Vector2()"
+ end
+
+ local x = Format.Float(v2.X)
+ local y = Format.Float(v2.Y)
+
+ local fmt = "new Vector2(%s, %s)"
+ return fmt:format(x, y)
+end
+
+function Format.Vector3(v3)
+ if v3.Magnitude < 0.001 then
+ return "new Vector3()"
+ end
+
+ local x = Format.Float(v3.X)
+ local y = Format.Float(v3.Y)
+ local z = Format.Float(v3.Z)
+
+ local fmt = "new Vector3(%s, %s, %s)"
+ return fmt:format(x, y, z)
+end
+
+function Format.CFrame(cf)
+ local blankCF = CFrame.new()
+
+ if cf == blankCF then
+ return "new CFrame()"
+ end
+
+ local rot = cf - cf.p
+
+ if rot == blankCF then
+ local fmt = "new CFrame(%s, %s, %s)"
+
+ local x = Format.Float(cf.X)
+ local y = Format.Float(cf.Y)
+ local z = Format.Float(cf.Z)
+
+ return fmt:format(x, y, z)
+ else
+ local comp = { cf:GetComponents() }
+
+ for i = 1,12 do
+ comp[i] = Format.Float(comp[i])
+ end
+
+ local fmt = "new CFrame(%s)"
+ local matrix = table.concat(comp, ", ")
+
+ return fmt:format(matrix)
+ end
+end
+
+function Format.NumberRange(nr)
+ local min = nr.Min
+ local max = nr.Max
+
+ local fmt = "new NumberRange(%s)"
+ local value = Format.Float(min)
+
+ if min ~= max then
+ value = value .. ", " .. Format.Float(max)
+ end
+
+ return fmt:format(value)
+end
+
+function Format.Ray(ray)
+ if ray == Ray.new() then
+ return "new Ray()"
+ end
+
+ local fmt = "new Ray(%s, %s)"
+
+ local origin = Format.Vector3(ray.Origin)
+ local direction = Format.Vector3(ray.Direction)
+
+ return fmt:format(origin, direction)
+end
+
+function Format.Rect(rect)
+ local fmt = "new Rect(%s, %s)"
+
+ local min = Format.Vector2(rect.Min)
+ local max = Format.Vector2(rect.Max)
+
+ return fmt:format(min, max)
+end
+
+function Format.ColorSequence(cs)
+ local csKey = cs.Keypoints[1]
+
+ local fmt = "new ColorSequence(%s)"
+ local value = Format.Color3(csKey.Value)
+
+ return fmt:format(value)
+end
+
+function Format.NumberSequence(ns)
+ local nsKey = ns.Keypoints[1]
+
+ local fmt = "new NumberSequence(%s)"
+ local value = Format.Float(nsKey.Value)
+
+ return fmt:format(value)
+end
+
+function Format.Vector3int16(v3)
+ if v3 == Vector3int16.new() then
+ return "new Vector3int16()"
+ end
+
+ local x = Format.Int(v3.X)
+ local y = Format.Int(v3.Y)
+ local z = Format.Int(v3.Z)
+
+ local fmt = "new Vector3int16(%s, %s, %s)"
+ return fmt:format(x, y, z)
+end
+
+function Format.SharedString(str)
+ local fmt = "SharedString.FromBase64(%q)"
+ return fmt:format(str)
+end
+
+return Format
\ No newline at end of file
diff --git a/Plugins/GenerateApiDump/PropertyPatches.lua b/Plugins/GenerateApiDump/PropertyPatches.lua
new file mode 100644
index 0000000..1f6ee6f
--- /dev/null
+++ b/Plugins/GenerateApiDump/PropertyPatches.lua
@@ -0,0 +1,674 @@
+local function UseColor3(propName)
+ return
+ {
+ Get = "BrickColor.FromColor3(" .. propName .. ')';
+ Set = propName .. " = value.Color";
+ }
+end
+
+local GuiTextMixIn =
+{
+ Redirect =
+ {
+ FontSize =
+ {
+ Get = "FontUtility.GetFontSize(TextSize)";
+ Set = "TextSize = FontUtility.GetFontSize(value)";
+ };
+
+ TextColor = UseColor3("TextColor3");
+ TextWrap = "TextWrapped";
+ };
+}
+
+return
+{
+ Accoutrement =
+ {
+ Remove =
+ {
+ "AttachmentUp";
+ "AttachmentPos";
+ "AttachmentRight";
+ "AttachmentForward";
+ };
+ };
+
+ AnalyticsService =
+ {
+ Defaults = { ApiKey = "" }
+ };
+
+ Attachment =
+ {
+ Remove =
+ {
+ "Axis";
+ "Orientation";
+ "Position";
+ "SecondaryAxis";
+ "WorldAxis";
+ "WorldCFrame";
+ "WorldOrientation";
+ "WorldPosition";
+ "WorldSecondaryAxis";
+ };
+ };
+
+ BasePart =
+ {
+ Add =
+ {
+ Color3uint8 = "Color3uint8";
+ size = "Vector3";
+ };
+
+ Redirect =
+ {
+ Position = "CFrame.Position";
+ BrickColor = UseColor3("Color");
+ Color = "Color3uint8";
+ Size = "size";
+ };
+
+ Defaults =
+ {
+ Color3uint8 = Color3.fromRGB(163, 162, 165);
+ size = Vector3.new(4, 1.2, 2);
+ };
+
+ Remove =
+ {
+ "Orientation";
+ "Rotation";
+ }
+ };
+
+ BinaryStringValue =
+ {
+ Add =
+ {
+ Value = "BinaryString";
+ };
+
+ Defaults =
+ {
+ Value = "";
+ };
+ };
+
+ BodyColors =
+ {
+ Redirect =
+ {
+ HeadColor = UseColor3("HeadColor3");
+ LeftArmColor = UseColor3("LeftArmColor3");
+ RightArmColor = UseColor3("RightArmColor3");
+ LeftLegColor = UseColor3("LeftLegColor3");
+ RightLegColor = UseColor3("RightLegColor3");
+ TorsoColor = UseColor3("TorsoColor3");
+ }
+ };
+
+ BodyAngularVelocity =
+ {
+ Redirect = { angularvelocity = "AngularVelocity" };
+ };
+
+ BodyGyro =
+ {
+ Redirect = { cframe = "CFrame" };
+ };
+
+ Camera =
+ {
+ Redirect = { CoordinateFrame = "CFrame" }
+ };
+
+ DataModelMesh =
+ {
+ Add =
+ {
+ LODX = "Enum:LevelOfDetailSetting";
+ LODY = "Enum:LevelOfDetailSetting";
+ };
+
+ Defaults =
+ {
+ LODX = Enum.LevelOfDetailSetting.High;
+ LODY = Enum.LevelOfDetailSetting.High;
+ };
+ };
+
+ DataStoreService =
+ {
+ Defaults =
+ {
+ AutomaticRetry = true;
+ LegacyNamingScheme = false;
+ }
+ };
+
+ DebuggerWatch =
+ {
+ Defaults = { Expression = "" };
+ };
+
+ DoubleConstrainedValue =
+ {
+ Redirect = { ConstrainedValue = "Value" }
+ };
+
+ Fire =
+ {
+ Add =
+ {
+ heat_xml = "float";
+ size_xml = "float";
+ };
+
+ Defaults =
+ {
+ heat_xml = 9;
+ size_xml = 5;
+ };
+
+ Redirect =
+ {
+ Heat = "heat_xml";
+ Size = "size_xml";
+ };
+ };
+
+ FormFactorPart =
+ {
+ Add =
+ {
+ formFactorRaw = "Enum:FormFactor";
+ };
+
+ Defaults =
+ {
+ formFactorRaw = Enum.FormFactor.Brick;
+ };
+
+ Redirect =
+ {
+ FormFactor = "formFactorRaw";
+ };
+ };
+
+ GuiBase2d =
+ {
+ Redirect = { Localize = "AutoLocalize" }
+ };
+
+ GuiBase3d =
+ {
+ Redirect = { Color = UseColor3("Color3") }
+ };
+
+ GuiObject =
+ {
+ Redirect =
+ {
+ BackgroundColor = UseColor3("BackgroundColor3");
+ BorderColor = UseColor3("BorderColor3");
+ Transparency = "BackgroundTransparency";
+ }
+ };
+
+ HttpService =
+ {
+ Defaults = { HttpEnabled = false }
+ };
+
+ Humanoid =
+ {
+ Add =
+ {
+ Health_XML = "float";
+ InternalHeadScale = "float";
+ InternalBodyScale = "Vector3";
+ };
+
+ Defaults =
+ {
+ Health_XML = 100;
+ InternalHeadScale = 1;
+ InternalBodyScale = Vector3.new(1, 1, 1);
+ };
+
+ Redirect =
+ {
+ Health = "Health_XML";
+ };
+
+ Remove =
+ {
+ "Jump";
+ "Torso";
+ "LeftLeg";
+ "RightLeg";
+ };
+ };
+
+ HumanoidDescription =
+ {
+ Add =
+ {
+ EmotesDataInternal = "string";
+ EquippedEmotesDataInternal = "string";
+ };
+
+ Defaults =
+ {
+ EmotesDataInternal = "";
+ EquippedEmotesDataInternal = "";
+ };
+ };
+
+ InsertService =
+ {
+ Add = { AllowClientInsertModels = "bool" };
+ Defaults = { AllowClientInsertModels = false };
+ };
+
+ IntConstrainedValue =
+ {
+ Redirect = { ConstrainedValue = "Value" }
+ };
+
+ JointInstance =
+ {
+ Add = { IsAutoJoint = "bool" };
+ Defaults = { IsAutoJoint = true };
+ };
+
+ Lighting =
+ {
+ Add =
+ {
+ Technology = "Enum:Technology";
+ };
+
+ Defaults =
+ {
+ LegacyOutlines = false;
+ Technology = Enum.Technology.Compatibility;
+ };
+
+ Redirect =
+ {
+ Outlines = "LegacyOutlines";
+ };
+
+ Remove =
+ {
+ "ClockTime";
+ };
+ };
+
+ LocalizationService =
+ {
+ Remove =
+ {
+ "ForcePlayModeGameLocaleId";
+ "ForcePlayModeRobloxLocaleId";
+ "RobloxForcePlayModeGameLocaleId";
+ "RobloxForcePlayModeRobloxLocaleId";
+ }
+ };
+
+ LocalizationTable =
+ {
+ Add = { Contents = "string" };
+ Defaults = { Contents = "[]" };
+
+ Redirect =
+ {
+ DevelopmentLanguage = "SourceLocaleId";
+ }
+ };
+
+ ManualSurfaceJointInstance =
+ {
+ Add =
+ {
+ Surface0 = "int";
+ Surface1 = "int";
+ };
+
+ Defaults =
+ {
+ Surface0 = -1;
+ Surface1 = -1;
+ }
+ };
+
+ MeshPart =
+ {
+ Redirect = { MeshID = "MeshId" }
+ };
+
+ Model =
+ {
+ Add = { ModelInPrimary = "CFrame" };
+ Defaults = { ModelInPrimary = CFrame.new() };
+ };
+
+ NotificationService =
+ {
+ Remove = {"SelectedTheme"}
+ };
+
+ Part =
+ {
+ Add = { shape = "Enum:PartType" };
+ Redirect = { Shape = "shape" };
+ };
+
+ ParticleEmitter =
+ {
+ Redirect =
+ {
+ VelocitySpread =
+ {
+ Get = "SpreadAngle.X";
+ Set = "SpreadAngle = new Vector2(value, value)";
+ }
+ }
+ };
+
+ PartOperation =
+ {
+ Add =
+ {
+ AssetId = "Content";
+ ChildData = "BinaryString";
+ MeshData = "BinaryString";
+ };
+
+ Defaults =
+ {
+ AssetId = "";
+ ChildData = "";
+ MeshData = "";
+ };
+ };
+
+ PartOperationAsset =
+ {
+ Add =
+ {
+ ChildData = "BinaryString";
+ MeshData = "BinaryString";
+ };
+
+ Defaults =
+ {
+ ChildData = "";
+ MeshData = "";
+ };
+ };
+
+ Players =
+ {
+ Defaults =
+ {
+ MaxPlayersInternal = 16;
+ PreferredPlayersInternal = 0;
+ }
+ };
+
+ RenderingTest =
+ {
+ Remove =
+ {
+ "Position";
+ "Orientation";
+ };
+ };
+
+ ScriptContext =
+ {
+ Remove = { "ScriptsDisabled" }
+ };
+
+ SelectionBox =
+ {
+ Redirect = { SurfaceColor = UseColor3("SurfaceColor3") }
+ };
+
+ SelectionSphere =
+ {
+ Redirect = { SurfaceColor = UseColor3("SurfaceColor3") }
+ };
+
+ ServerScriptService =
+ {
+ Defaults = { LoadStringEnabled = false }
+ };
+
+ Smoke =
+ {
+ Add =
+ {
+ size_xml = "float";
+ opacity_xml = "float";
+ riseVelocity_xml = "float";
+ };
+
+ Defaults =
+ {
+ size_xml = 1;
+ opacity_xml = 0.5;
+ riseVelocity_xml = 1;
+ };
+
+ Redirect =
+ {
+ Size = "size_xml";
+ Opacity = "opacity_xml";
+ RiseVelocity = "riseVelocity_xml";
+ };
+ };
+
+ Sound =
+ {
+ Add =
+ {
+ MaxDistance = "float"; -- ?!
+
+ xmlRead_MaxDistance_3 = "float";
+ xmlRead_MinDistance_3 = "float";
+ };
+
+ Defaults =
+ {
+ xmlRead_MinDistance_3 = 10;
+ xmlRead_MaxDistance_3 = 10000;
+ };
+
+ Redirect =
+ {
+ EmitterSize = "xmlRead_MinDistance_3";
+ MaxDistance = "xmlRead_MaxDistance_3";
+
+ MinDistance = "EmitterSize";
+ Pitch = "PlaybackSpeed";
+ };
+ };
+
+ Sparkles =
+ {
+ Redirect = { Color = "SparkleColor" };
+ };
+
+ StudioData =
+ {
+ Defaults =
+ {
+ SrcPlaceId = 0;
+ SrcUniverseId = 0;
+ };
+ };
+
+ TextBox = GuiTextMixIn;
+ TextLabel = GuiTextMixIn;
+ TextButton = GuiTextMixIn;
+
+ Terrain =
+ {
+ Add =
+ {
+ ClusterGrid = "string";
+ ClusterGridV2 = "string";
+ ClusterGridV3 = "BinaryString";
+
+ SmoothGrid = "BinaryString";
+ PhysicsGrid = "BinaryString";
+ };
+
+ Defaults =
+ {
+ ClusterGrid = "";
+ ClusterGridV2 = "";
+ ClusterGridV3 = "";
+
+ SmoothGrid = "AQU=";
+ PhysicsGrid = "AgMAAAAAAAAAAAAAAAA=";
+ MaterialColors = "AAAAAAAAan8/P39rf2Y/ilY+j35fi21PZmxvZbDqw8faiVpHOi4kHh4lZlw76JxKc3trhHtagcLgc4RKxr21zq2UlJSM";
+ };
+ };
+
+ TerrainRegion =
+ {
+ Add =
+ {
+ ExtentsMax = "Vector3int16";
+ ExtentsMin = "Vector3int16";
+
+ GridV3 = "BinaryString";
+ SmoothGrid = "BinaryString";
+ };
+
+ Defaults =
+ {
+ ExtentsMax = Vector3int16.new();
+ ExtentsMin = Vector3int16.new();
+
+ GridV3 = "";
+ SmoothGrid = "AQU=";
+ };
+ };
+
+ Tool =
+ {
+ Remove =
+ {
+ "GripForward";
+ "GripPos";
+ "GripRight";
+ "GripUp";
+ };
+ };
+
+ TriangleMeshPart =
+ {
+ Add =
+ {
+ InitialSize = "Vector3";
+ LODData = "BinaryString";
+ PhysicsData = "BinaryString";
+ PhysicalConfigData = "SharedString";
+ };
+
+ Defaults =
+ {
+ LODData = "";
+ PhysicsData = "";
+ InitialSize = Vector3.new(1, 1, 1);
+ PhysicalConfigData = "1B2M2Y8AsgTpgAmY7PhCfg==";
+ };
+ };
+
+ TrussPart =
+ {
+ Add = { style = "Enum:Style" };
+ Redirect = { Style = "style" };
+ };
+
+ ViewportFrame =
+ {
+ Add =
+ {
+ CameraCFrame = "CFrame";
+ CameraFieldOfView = "float";
+ };
+
+ Defaults =
+ {
+ CameraCFrame = CFrame.new();
+ CameraFieldOfView = 70;
+ };
+
+ Remove = {"CurrentCamera"};
+ };
+
+ WeldConstraint =
+ {
+ Add =
+ {
+ Part0Internal = "Class:BasePart";
+ Part1Internal = "Class:BasePart";
+
+ CFrame0 = "CFrame";
+ CFrame1 = "CFrame";
+ };
+
+ Defaults =
+ {
+ CFrame0 = CFrame.new();
+ CFrame1 = CFrame.new();
+
+ Part0 = Instance.new("Part");
+ Part1 = Instance.new("Part");
+ };
+
+ Redirect =
+ {
+ Part0 = "Part0Internal";
+ Part1 = "Part1Internal";
+ };
+ };
+
+ Workspace =
+ {
+ Add =
+ {
+ AutoJointsMode = "Enum:AutoJointsMode";
+ CollisionGroups = "string";
+ ExplicitAutoJoints = "bool";
+
+ StreamingMinRadius = "int";
+ StreamingTargetRadius = "int";
+ StreamingPauseMode = "Enum:StreamingPauseMode";
+
+ TerrainWeldsFixed = "bool";
+ };
+
+ Defaults =
+ {
+ AutoJointsMode = Enum.AutoJointsMode.Default;
+ CollisionGroups = "Default^0^1";
+ ExplicitAutoJoints = true;
+
+ StreamingMinRadius = 64;
+ StreamingTargetRadius = 1024;
+ StreamingPauseMode = Enum.StreamingPauseMode.Default;
+
+ TerrainWeldsFixed = true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/RobloxFile.cs b/RobloxFile.cs
index 2e8d6b6..051b84e 100644
--- a/RobloxFile.cs
+++ b/RobloxFile.cs
@@ -3,9 +3,6 @@ using System.IO;
using System.Text;
using System.Threading.Tasks;
-using RobloxFiles.BinaryFormat;
-using RobloxFiles.XmlFormat;
-
namespace RobloxFiles
{
///
diff --git a/RobloxFileFormat.csproj b/RobloxFileFormat.csproj
index 05f162e..45536c0 100644
--- a/RobloxFileFormat.csproj
+++ b/RobloxFileFormat.csproj
@@ -71,9 +71,14 @@
+
+
+
+
-
+
+
@@ -89,7 +94,6 @@
-
@@ -100,41 +104,43 @@
+
-
+
-
-
+
+
+
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Tree/Instance.cs b/Tree/Instance.cs
index e282d6e..1eb3ddf 100644
--- a/Tree/Instance.cs
+++ b/Tree/Instance.cs
@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Reflection;
+using System.Text;
namespace RobloxFiles
{
@@ -11,16 +13,32 @@ namespace RobloxFiles
public class Instance
{
+ public Instance()
+ {
+ Name = ClassName;
+ }
+
/// The ClassName of this Instance.
- public string ClassName;
+ public string ClassName => GetType().Name;
+
+ /// Internal list of Properties that are under this Instance.
+ private Dictionary props = new Dictionary();
/// A list of properties that are defined under this Instance.
- private Dictionary props = new Dictionary();
public IReadOnlyDictionary Properties => props;
- protected List Children = new List();
- private Instance parent;
+ /// The raw list of children for this Instance.
+ internal List Children = new List();
+ /// Raw value of the Instance's parent.
+ private Instance RawParent;
+
+ /// The name of this Instance.
+ public string Name;
+
+ /// Indicates whether this Instance should be serialized.
+ public bool Archivable = true;
+
/// The name of this Instance, if a Name property is defined.
public override string ToString() => Name;
@@ -30,14 +48,56 @@ namespace RobloxFiles
/// Indicates whether the parent of this object is locked.
public bool ParentLocked { get; internal set; }
- /// Indicates whether this Instance is marked as a Service in the binary file format.
+ /// Indicates whether this Instance is a Service.
public bool IsService { get; internal set; }
- /// If this instance is a service, this indicates whether the service should be loaded via GetService when Roblox loads the place file.
- public bool IsRootedService { get; internal set; }
+ /// Raw list of CollectionService tags assigned to this Instance.
+ private List RawTags = new List();
- /// Indicates whether this object should be serialized.
- public bool Archivable = true;
+ /// A list of CollectionService tags assigned to this Instance.
+ public List Tags => RawTags;
+
+ ///
+ /// Internal format of the Instance's CollectionService tags.
+ /// Property objects will look to this member for serializing the Tags property.
+ ///
+ internal byte[] SerializedTags
+ {
+ get
+ {
+ string fullString = string.Join("\0", Tags.ToArray());
+
+ byte[] buffer = fullString.ToCharArray()
+ .Select(ch => (byte)ch)
+ .ToArray();
+
+ return buffer;
+ }
+ set
+ {
+ int length = value.Length;
+
+ List buffer = new List();
+ Tags.Clear();
+
+ for (int i = 0; i < length; i++)
+ {
+ byte id = value[i];
+
+ if (id != 0)
+ buffer.Add(id);
+
+ if (id == 0 || i == (length - 1))
+ {
+ byte[] data = buffer.ToArray();
+ buffer.Clear();
+
+ string tag = Encoding.UTF8.GetString(data);
+ Tags.Add(tag);
+ }
+ }
+ }
+ }
/// Returns true if this Instance is an ancestor to the provided Instance.
/// The instance whose descendance will be tested against this Instance.
@@ -61,26 +121,20 @@ namespace RobloxFiles
return ancestor.IsAncestorOf(this);
}
- public string Name
+ ///
+ /// Returns true if the provided instance inherits from the provided instance type.
+ ///
+ public bool IsA() where T : Instance
{
- get
- {
- Property propName = GetProperty("Name");
-
- if (propName == null)
- SetProperty("Name", "Instance");
-
- return propName.Value.ToString();
- }
- set
- {
- SetProperty("Name", value);
- }
+ Type myType = GetType();
+ Type classType = typeof(T);
+ return classType.IsAssignableFrom(myType);
}
///
/// The parent of this Instance, or null if the instance is the root of a tree.
/// Setting the value of this property will throw an exception if:
+ /// - The parent is currently locked.
/// - The value is set to itself.
/// - The value is a descendant of the Instance.
///
@@ -88,7 +142,7 @@ namespace RobloxFiles
{
get
{
- return parent;
+ return RawParent;
}
set
{
@@ -101,11 +155,10 @@ namespace RobloxFiles
if (Parent == this)
throw new Exception("Attempt to set parent to self.");
- if (parent != null)
- parent.Children.Remove(this);
+ RawParent?.Children.Remove(this);
+ value?.Children.Add(this);
- value.Children.Add(this);
- parent = value;
+ RawParent = value;
}
}
@@ -143,10 +196,14 @@ namespace RobloxFiles
///
/// The Name of the Instance to find.
/// Indicates if we should search descendants as well.
- public Instance FindFirstChild(string name, bool recursive = false)
+ public T FindFirstChild(string name, bool recursive = false) where T : Instance
{
- Instance result = null;
- var query = Children.Where((child) => name == child.Name);
+ T result = null;
+
+ var query = Children
+ .Where(child => child is T)
+ .Where(child => name == child.Name)
+ .Cast();
if (query.Count() > 0)
{
@@ -156,7 +213,7 @@ namespace RobloxFiles
{
foreach (Instance child in Children)
{
- Instance found = child.FindFirstChild(name, true);
+ T found = child.FindFirstChild(name, true);
if (found != null)
{
@@ -169,6 +226,37 @@ namespace RobloxFiles
return result;
}
+ ///
+ /// Returns the first child of this Instance whose Name is the provided string name.
+ /// If the instance is not found, this returns null.
+ ///
+ /// The Name of the Instance to find.
+ /// Indicates if we should search descendants as well.
+ public Instance FindFirstChild(string name, bool recursive = false)
+ {
+ return FindFirstChild(name, recursive);
+ }
+
+ ///
+ /// Returns the first ancestor of this Instance whose Name is the provided string name.
+ /// If the instance is not found, this returns null.
+ ///
+ /// The Name of the Instance to find.
+ public T FindFirstAncestor(string name) where T : Instance
+ {
+ Instance ancestor = Parent;
+
+ while (ancestor != null)
+ {
+ if (ancestor is T && ancestor.Name == name)
+ return (T)ancestor;
+
+ ancestor = ancestor.Parent;
+ }
+
+ return null;
+ }
+
///
/// Returns the first ancestor of this Instance whose Name is the provided string name.
/// If the instance is not found, this returns null.
@@ -176,17 +264,7 @@ namespace RobloxFiles
/// The Name of the Instance to find.
public Instance FindFirstAncestor(string name)
{
- Instance ancestor = Parent;
-
- while (ancestor != null)
- {
- if (ancestor.Name == name)
- break;
-
- ancestor = ancestor.Parent;
- }
-
- return ancestor;
+ return FindFirstAncestor(name);
}
///
@@ -194,18 +272,45 @@ namespace RobloxFiles
/// If the instance is not found, this returns null.
///
/// The Name of the Instance to find.
- public Instance FindFirstAncestorOfClass(string className)
+ public T FindFirstAncestorOfClass() where T : Instance
{
+ Type classType = typeof(T);
+ string className = classType.Name;
+
Instance ancestor = Parent;
while (ancestor != null)
{
- if (ancestor.ClassName == className)
- break;
-
+ if (ancestor is T)
+ return (T)ancestor;
+
ancestor = ancestor.Parent;
}
+ return null;
+ }
+
+ ///
+ /// Returns the first ancestor of this Instance which derives from the provided type T.
+ /// If the instance is not found, this returns null.
+ ///
+ /// The Name of the Instance to find.
+ public T FindFirstAncestorWhichIsA() where T : Instance
+ {
+ T ancestor = null;
+ Instance check = Parent;
+
+ while (check != null)
+ {
+ if (check.IsA())
+ {
+ ancestor = (T)check;
+ break;
+ }
+
+ check = check.Parent;
+ }
+
return ancestor;
}
@@ -214,13 +319,65 @@ namespace RobloxFiles
/// If the instance is not found, this returns null.
///
/// The ClassName of the Instance to find.
- public Instance FindFirstChildOfClass(string className, bool recursive = false)
+ public T FindFirstChildOfClass(bool recursive = false) where T : Instance
{
- Instance result = null;
- var query = Children.Where((child) => className == child.ClassName);
+ var query = Children
+ .Where(child => child is T)
+ .Cast();
+
+ T result = null;
+
+ if (query.Count() > 0)
+ {
+ result = query.First();
+ }
+ else if (recursive)
+ {
+ foreach (Instance child in Children)
+ {
+ T found = child.FindFirstChildOfClass(true);
+
+ if (found != null)
+ {
+ result = found;
+ break;
+ }
+ }
+ }
+
+ return result;
+ }
+
+ ///
+ /// Returns the first child of this Instance which derives from the provided type T.
+ /// If the instance is not found, this returns null.
+ ///
+ /// Whether this should search descendants as well.
+ public T FindFirstChildWhichIsA(bool recursive = false) where T : Instance
+ {
+ var query = Children
+ .Where(child => child.IsA())
+ .Cast();
+
+ T result = null;
if (query.Count() > 0)
+ {
result = query.First();
+ }
+ else if (recursive)
+ {
+ foreach (Instance child in Children)
+ {
+ T found = child.FindFirstChildWhichIsA(true);
+
+ if (found != null)
+ {
+ result = found;
+ break;
+ }
+ }
+ }
return result;
}
@@ -243,123 +400,18 @@ namespace RobloxFiles
}
///
- /// Returns a Property object if a property with the specified name is defined in this Instance.
+ /// Returns a Property object whose name is the provided string name.
///
public Property GetProperty(string name)
{
Property result = null;
- if (Properties.ContainsKey(name))
- result = Properties[name];
+ if (props.ContainsKey(name))
+ result = props[name];
return result;
}
-
- ///
- /// Finds or creates a property with the specified name, and sets its value to the provided object.
- /// Returns the property object that had its value set, if the value is not null.
- ///
- public Property SetProperty(string name, object value, PropertyType? preferType = null)
- {
- Property prop = GetProperty(name) ?? new Property()
- {
- Type = preferType ?? PropertyType.Unknown,
- Name = name
- };
-
- if (preferType == null)
- {
- object oldValue = prop.Value;
-
- Type oldType = oldValue?.GetType();
- Type newType = value?.GetType();
-
- if (oldType != newType)
- {
- if (value == null)
- {
- RemoveProperty(name);
- return prop;
- }
-
- string typeName = newType.Name;
-
- if (value is Instance)
- typeName = "Ref";
- else if (value is int)
- typeName = "Int";
- else if (value is long)
- typeName = "Int64";
-
- Enum.TryParse(typeName, out prop.Type);
- }
- }
-
- prop.Value = value;
-
- if (prop.Instance == null)
- AddProperty(ref prop);
-
- return prop;
- }
-
- ///
- /// Looks for a property with the specified property name, and returns its value as an object.
- /// The resulting value may be null if the property is not serialized.
- /// You can use the templated ReadProperty overload to fetch it as a specific type with a default value provided.
- ///
- /// The name of the property to be fetched from this Instance.
- /// An object reference to the value of the specified property, if it exists.
- public object ReadProperty(string propertyName)
- {
- Property property = GetProperty(propertyName);
- return property?.Value;
- }
-
- ///
- /// Looks for a property with the specified property name, and returns it as the specified type.
- /// If it cannot be converted, the provided nullFallback value will be returned instead.
- ///
- /// The value type to convert to when finding the specified property name.
- /// The name of the property to be fetched from this Instance.
- /// A fallback value to be returned if casting to T fails, or the property is not found.
- ///
- public T ReadProperty(string propertyName, T nullFallback)
- {
- try
- {
- object result = ReadProperty(propertyName);
- return (T)result;
- }
- catch
- {
- return nullFallback;
- }
- }
-
- ///
- /// Looks for a property with the specified property name. If found, it will try to set the value of the referenced outValue to its value.
- /// Returns true if the property was found and its value was casted to the referenced outValue.
- /// If it returns false, the outValue will not have its value set.
- ///
- /// The value type to convert to when finding the specified property name.
- /// The name of the property to be fetched from this Instance.
- /// The value to write to if the property can be casted to T correctly.
- public bool TryReadProperty(string propertyName, ref T outValue)
- {
- try
- {
- object result = ReadProperty(propertyName);
- outValue = (T)result;
-
- return true;
- }
- catch
- {
- return false;
- }
- }
-
+
///
/// Adds a property by reference to this Instance's property list.
///
@@ -368,16 +420,8 @@ namespace RobloxFiles
{
prop.Instance = this;
- if (prop.Name == "Name")
- {
- Property nameProp = GetProperty("Name");
-
- if (nameProp != null)
- {
- nameProp.Value = prop.Value;
- return;
- }
- }
+ if (props.ContainsKey(prop.Name))
+ props.Remove(prop.Name);
props.Add(prop.Name, prop);
}
@@ -387,14 +431,69 @@ namespace RobloxFiles
///
/// The name of the property to be removed.
/// True if a property with the provided name was removed.
- public bool RemoveProperty(string name)
+ internal bool RemoveProperty(string name)
{
- Property prop = GetProperty(name);
-
- if (prop != null)
+ if (props.ContainsKey(name))
+ {
+ Property prop = Properties[name];
prop.Instance = null;
+ }
return props.Remove(name);
}
+
+ ///
+ /// Ensures that all serializable properties of this Instance have
+ /// a registered Property object with the correct PropertyType.
+ ///
+ internal IReadOnlyDictionary RefreshProperties()
+ {
+ Type instType = GetType();
+ FieldInfo[] fields = instType.GetFields(Property.BindingFlags);
+
+ foreach (FieldInfo field in fields)
+ {
+ string fieldName = field.Name;
+ Type fieldType = field.FieldType;
+
+ if (field.GetCustomAttribute() != null)
+ continue;
+
+ if (Property.Types.ContainsKey(fieldType))
+ {
+ if (fieldName.EndsWith("_"))
+ fieldName = instType.Name;
+
+ if (!props.ContainsKey(fieldName))
+ {
+ Property newProp = new Property()
+ {
+ Type = Property.Types[fieldType],
+ Value = field.GetValue(this),
+ Name = fieldName,
+ Instance = this
+ };
+
+ AddProperty(ref newProp);
+ }
+ else
+ {
+ Property prop = props[fieldName];
+ prop.Value = field.GetValue(this);
+ prop.Type = Property.Types[fieldType];
+ }
+ }
+ }
+
+ Property tags = GetProperty("Tags");
+
+ if (tags == null)
+ {
+ tags = new Property("Tags", PropertyType.String);
+ AddProperty(ref tags);
+ }
+
+ return Properties;
+ }
}
}
\ No newline at end of file
diff --git a/Tree/Property.cs b/Tree/Property.cs
index 707d62b..01398c9 100644
--- a/Tree/Property.cs
+++ b/Tree/Property.cs
@@ -1,8 +1,13 @@
using System;
+using System.Collections.Generic;
+using System.Reflection;
using RobloxFiles.BinaryFormat;
using RobloxFiles.BinaryFormat.Chunks;
+using RobloxFiles.DataTypes;
+using RobloxFiles.Utility;
+
namespace RobloxFiles
{
public enum PropertyType
@@ -22,8 +27,7 @@ namespace RobloxFiles
Color3,
Vector2,
Vector3,
- Vector2int16,
- CFrame,
+ CFrame = 16,
Quaternion,
Enum,
Ref,
@@ -48,11 +52,57 @@ namespace RobloxFiles
public string XmlToken = "";
public byte[] RawBuffer;
- internal BinaryRobloxFileWriter CurrentWriter;
internal object RawValue;
+ internal BinaryRobloxFileWriter CurrentWriter;
+
+ internal static BindingFlags BindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy | BindingFlags.IgnoreCase;
+
+ public static IReadOnlyDictionary Types = new Dictionary()
+ {
+ { typeof(Axes), PropertyType.Axes },
+ { typeof(Faces), PropertyType.Faces },
+
+ { typeof(int), PropertyType.Int },
+ { typeof(bool), PropertyType.Bool },
+ { typeof(long), PropertyType.Int64 },
+ { typeof(float), PropertyType.Float },
+ { typeof(double), PropertyType.Double },
+ { typeof(string), PropertyType.String },
+
+ { typeof(Ray), PropertyType.Ray },
+ { typeof(Rect), PropertyType.Rect },
+ { typeof(UDim), PropertyType.UDim },
+ { typeof(UDim2), PropertyType.UDim2 },
+ { typeof(CFrame), PropertyType.CFrame },
+ { typeof(Color3), PropertyType.Color3 },
+ { typeof(Vector2), PropertyType.Vector2 },
+ { typeof(Vector3), PropertyType.Vector3 },
+
+ { typeof(BrickColor), PropertyType.BrickColor },
+ { typeof(Quaternion), PropertyType.Quaternion },
+ { typeof(NumberRange), PropertyType.NumberRange },
+ { typeof(SharedString), PropertyType.SharedString },
+ { typeof(Vector3int16), PropertyType.Vector3int16 },
+ { typeof(ColorSequence), PropertyType.ColorSequence },
+ { typeof(NumberSequence), PropertyType.NumberSequence },
+
+ { typeof(PhysicalProperties), PropertyType.PhysicalProperties },
+ };
private void ImproviseRawBuffer()
{
+ if (RawValue is byte[])
+ {
+ RawBuffer = RawValue as byte[];
+ return;
+ }
+ else if (RawValue is SharedString)
+ {
+ var sharedString = CastValue();
+ RawBuffer = Convert.FromBase64String(sharedString.MD5_Key);
+ return;
+ }
+
switch (Type)
{
case PropertyType.Int:
@@ -70,21 +120,85 @@ namespace RobloxFiles
case PropertyType.Double:
RawBuffer = BitConverter.GetBytes((double)Value);
break;
- case PropertyType.SharedString:
- RawBuffer = Convert.FromBase64String((string)Value);
- break;
//
}
}
+ private string ImplicitName
+ {
+ get
+ {
+ if (Instance != null)
+ {
+ Type instType = Instance.GetType();
+ string typeName = instType.Name;
+
+ if (typeName == Name)
+ {
+ var implicitName = Name + '_';
+ return implicitName;
+ }
+ }
+
+ return Name;
+ }
+ }
+
public object Value
{
get
{
+ if (Instance != null)
+ {
+ if (Name == "Tags")
+ {
+ byte[] data = Instance.SerializedTags;
+ RawValue = data;
+ }
+ else
+ {
+ FieldInfo field = Instance.GetType()
+ .GetField(ImplicitName, BindingFlags);
+
+ if (field != null)
+ {
+ object value = field.GetValue(Instance);
+ RawValue = value;
+ }
+ else
+ {
+ Console.WriteLine($"RobloxFiles.Property - No defined field for {Instance.ClassName}.{Name}");
+ }
+ }
+ }
+
return RawValue;
}
set
{
+ if (Instance != null)
+ {
+ if (Name == "Tags" && value is byte[])
+ {
+ byte[] data = value as byte[];
+ Instance.SerializedTags = data;
+ }
+ else
+ {
+ FieldInfo field = Instance.GetType()
+ .GetField(ImplicitName, BindingFlags);
+
+ try
+ {
+ field?.SetValue(Instance, value);
+ }
+ catch
+ {
+ Console.WriteLine($"RobloxFiles.Property - Failed to cast value {value} into property {Instance.ClassName}.{Name}");
+ }
+ }
+ }
+
RawValue = value;
RawBuffer = null;
@@ -96,11 +210,9 @@ namespace RobloxFiles
{
get
{
+ // Improvise what the buffer should be if this is a primitive.
if (RawBuffer == null && Value != null)
- {
- // Improvise what the buffer should be if this is a primitive.
ImproviseRawBuffer();
- }
return (RawBuffer != null);
}
diff --git a/Utility/FontUtility.cs b/Utility/FontUtility.cs
new file mode 100644
index 0000000..6fcd18e
--- /dev/null
+++ b/Utility/FontUtility.cs
@@ -0,0 +1,66 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+using RobloxFiles.Enums;
+
+namespace RobloxFiles.Utility
+{
+ public static class FontUtility
+ {
+ public static IReadOnlyDictionary FontSizes = new Dictionary()
+ {
+ { 8, FontSize.Size8 },
+ { 9, FontSize.Size9 },
+ { 10, FontSize.Size10 },
+ { 11, FontSize.Size11 },
+ { 12, FontSize.Size12 },
+ { 14, FontSize.Size14 },
+ { 18, FontSize.Size18 },
+ { 24, FontSize.Size24 },
+ { 28, FontSize.Size28 },
+ { 32, FontSize.Size32 },
+ { 36, FontSize.Size36 },
+ { 42, FontSize.Size42 },
+ { 48, FontSize.Size48 },
+ { 60, FontSize.Size60 },
+ { 96, FontSize.Size96 },
+ };
+
+ private static Dictionary IntToFontSize = new Dictionary();
+
+ public static FontSize GetFontSize(int fontSize)
+ {
+ if (fontSize > 60)
+ return FontSize.Size96;
+
+ if (FontSizes.ContainsKey(fontSize))
+ return FontSizes[fontSize];
+
+ FontSize closest = FontSizes
+ .Where(pair => pair.Key <= fontSize)
+ .Select(pair => pair.Value)
+ .Last();
+
+ return closest;
+ }
+
+ public static FontSize GetFontSize(float size)
+ {
+ int fontSize = (int)size;
+ return GetFontSize(fontSize);
+ }
+
+ public static int GetFontSize(FontSize fontSize)
+ {
+ int value = FontSizes
+ .Where(pair => pair.Value == fontSize)
+ .Select(pair => pair.Key)
+ .First();
+
+ return value;
+ }
+ }
+}
diff --git a/Utility/Formatting.cs b/Utility/Formatting.cs
index d9c680b..4a7ddec 100644
--- a/Utility/Formatting.cs
+++ b/Utility/Formatting.cs
@@ -1,8 +1,5 @@
-using System.Globalization;
-using System.Linq;
-
-// This global class defines extension methods to numeric types
-// where I don't want system globalization to come into play.
+using System;
+using System.Globalization;
internal static class Formatting
{
@@ -105,4 +102,14 @@ internal static class Formatting
{
return int.Parse(s, invariant);
}
+
+ public static bool FuzzyEquals(this float a, float b, float epsilon = 10e-5f)
+ {
+ return Math.Abs(a - b) < epsilon;
+ }
+
+ public static bool FuzzyEquals(this double a, double b, double epsilon = 10e-5)
+ {
+ return Math.Abs(a - b) < epsilon;
+ }
}
\ No newline at end of file
diff --git a/Utility/MaterialInfo.cs b/Utility/MaterialInfo.cs
index f0b5882..d78a57e 100644
--- a/Utility/MaterialInfo.cs
+++ b/Utility/MaterialInfo.cs
@@ -25,6 +25,7 @@ namespace RobloxFiles.Utility
{Material.DiamondPlate, 7.85f},
{Material.Fabric, 0.70f},
{Material.Foil, 2.70f},
+ {Material.ForceField, 2.40f},
{Material.Glacier, 0.92f},
{Material.Glass, 2.40f},
{Material.Granite, 2.69f},
@@ -68,6 +69,7 @@ namespace RobloxFiles.Utility
{Material.DiamondPlate, 0.25f},
{Material.Fabric, 0.05f},
{Material.Foil, 0.25f},
+ {Material.ForceField, 0.20f},
{Material.Glacier, 0.15f},
{Material.Glass, 0.20f},
{Material.Granite, 0.20f},
@@ -111,6 +113,7 @@ namespace RobloxFiles.Utility
{Material.DiamondPlate, 0.35f},
{Material.Fabric, 0.35f},
{Material.Foil, 0.40f},
+ {Material.ForceField, 0.25f},
{Material.Glacier, 0.05f},
{Material.Glass, 0.25f},
{Material.Granite, 0.40f},
diff --git a/XmlFormat/IO/XmlFileReader.cs b/XmlFormat/IO/XmlFileReader.cs
index bc5b8ed..377dc4c 100644
--- a/XmlFormat/IO/XmlFileReader.cs
+++ b/XmlFormat/IO/XmlFileReader.cs
@@ -1,6 +1,8 @@
using System;
using System.Xml;
+using RobloxFiles.DataTypes;
+
namespace RobloxFiles.XmlFormat
{
public static class XmlRobloxFileReader
@@ -35,11 +37,35 @@ namespace RobloxFiles.XmlFormat
string key = md5Node.InnerText;
string value = sharedString.InnerText.Replace("\n", "");
- file.SharedStrings.Add(key, value);
+ byte[] buffer = Convert.FromBase64String(value);
+ SharedString 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!");
+
+ file.SharedStrings.Add(key);
}
}
}
+ public static void ReadMetadata(XmlNode meta, XmlRobloxFile file)
+ {
+ var error = createErrorHandler("ReadMetadata");
+
+ if (meta.Name != "Meta")
+ throw error("Provided XmlNode's class should be 'Meta'!");
+
+ XmlNode propName = meta.Attributes.GetNamedItem("name");
+
+ if (propName == null)
+ throw error("Got a Meta node without a 'name' attribute!");
+
+ string key = propName.InnerText;
+ string value = meta.InnerText;
+
+ file.Metadata[key] = value;
+ }
+
public static void ReadProperties(Instance instance, XmlNode propsNode)
{
var error = createErrorHandler("ReadProperties");
@@ -53,7 +79,12 @@ namespace RobloxFiles.XmlFormat
XmlNode propName = propNode.Attributes.GetNamedItem("name");
if (propName == null)
+ {
+ if (propNode.Name == "Item")
+ continue;
+
throw error("Got a property node without a 'name' attribute!");
+ }
IXmlPropertyToken tokenHandler = XmlPropertyTokens.GetHandler(propType);
@@ -90,8 +121,12 @@ namespace RobloxFiles.XmlFormat
if (classToken == null)
throw error("Got an Item without a defined 'class' attribute!");
- Instance inst = new Instance() { ClassName = classToken.InnerText };
+ string className = classToken.InnerText;
+
+ Type instType = Type.GetType($"RobloxFiles.{className}") ?? typeof(Instance);
+ Instance inst = Activator.CreateInstance(instType) as Instance;
+
// 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");
diff --git a/XmlFormat/IO/XmlFileWriter.cs b/XmlFormat/IO/XmlFileWriter.cs
index fafb4c6..e5e2ebe 100644
--- a/XmlFormat/IO/XmlFileWriter.cs
+++ b/XmlFormat/IO/XmlFileWriter.cs
@@ -1,9 +1,8 @@
using System;
-using System.Linq;
-using System.Security.Cryptography;
using System.Text;
using System.Xml;
+using RobloxFiles.DataTypes;
using RobloxFiles.XmlFormat.PropertyTokens;
namespace RobloxFiles.XmlFormat
@@ -40,7 +39,7 @@ namespace RobloxFiles.XmlFormat
foreach (Instance child in inst.GetChildren())
RecordInstances(file, child);
- if (inst.Referent.Length < 35)
+ if (inst.Referent == null || inst.Referent.Length < 35)
inst.Referent = CreateReferent();
file.Instances.Add(inst.Referent, inst);
@@ -102,19 +101,8 @@ namespace RobloxFiles.XmlFormat
if (prop.Type == PropertyType.SharedString)
{
- string data = prop.Value.ToString();
- byte[] buffer = Convert.FromBase64String(data);
-
- using (MD5 md5 = MD5.Create())
- {
- byte[] hash = md5.ComputeHash(buffer);
- string key = Convert.ToBase64String(hash);
-
- if (!file.SharedStrings.ContainsKey(key))
- file.SharedStrings.Add(key, data);
-
- propNode.InnerText = key;
- }
+ SharedString value = prop.CastValue();
+ file.SharedStrings.Add(value.MD5_Key);
}
return propNode;
@@ -122,6 +110,9 @@ namespace RobloxFiles.XmlFormat
public static XmlNode WriteInstance(Instance instance, XmlDocument doc, XmlRobloxFile file)
{
+ if (!instance.Archivable)
+ return null;
+
XmlElement instNode = doc.CreateElement("Item");
instNode.SetAttribute("class", instance.ClassName);
instNode.SetAttribute("referent", instance.Referent);
@@ -129,7 +120,7 @@ namespace RobloxFiles.XmlFormat
XmlElement propsNode = doc.CreateElement("Properties");
instNode.AppendChild(propsNode);
- var props = instance.Properties;
+ var props = instance.RefreshProperties();
foreach (string propName in props.Keys)
{
@@ -140,8 +131,11 @@ namespace RobloxFiles.XmlFormat
foreach (Instance child in instance.GetChildren())
{
- XmlNode childNode = WriteInstance(child, doc, file);
- instNode.AppendChild(childNode);
+ if (child.Archivable)
+ {
+ XmlNode childNode = WriteInstance(child, doc, file);
+ instNode.AppendChild(childNode);
+ }
}
return instNode;
@@ -152,18 +146,15 @@ namespace RobloxFiles.XmlFormat
XmlElement sharedStrings = doc.CreateElement("SharedStrings");
var binaryWriter = XmlPropertyTokens.GetHandler();
- var bufferProp = new Property("SharedString", PropertyType.String);
+ var binaryBuffer = new Property("SharedString", PropertyType.String);
- foreach (string md5 in file.SharedStrings.Keys)
+ foreach (string md5 in file.SharedStrings)
{
XmlElement sharedString = doc.CreateElement("SharedString");
sharedString.SetAttribute("md5", md5);
- string data = file.SharedStrings[md5];
- byte[] buffer = Convert.FromBase64String(data);
-
- bufferProp.RawBuffer = buffer;
- binaryWriter.WriteProperty(bufferProp, doc, sharedString);
+ binaryBuffer.RawBuffer = SharedString.FindRecord(md5);
+ binaryWriter.WriteProperty(binaryBuffer, doc, sharedString);
sharedStrings.AppendChild(sharedString);
}
diff --git a/XmlFormat/PropertyTokens/Tokens/Enum.cs b/XmlFormat/PropertyTokens/Tokens/Enum.cs
deleted file mode 100644
index aef6d4b..0000000
--- a/XmlFormat/PropertyTokens/Tokens/Enum.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using System.Xml;
-
-namespace RobloxFiles.XmlFormat.PropertyTokens
-{
- public class EnumToken : IXmlPropertyToken
- {
- public string Token => "token";
-
- public bool ReadProperty(Property prop, XmlNode token)
- {
- return XmlPropertyTokens.ReadPropertyGeneric(prop, PropertyType.Enum, token);
- }
-
- public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
- {
- node.InnerText = prop.Value.ToString();
- }
- }
-}
diff --git a/XmlFormat/PropertyTokens/Tokens/Axes.cs b/XmlFormat/Tokens/Axes.cs
similarity index 51%
rename from XmlFormat/PropertyTokens/Tokens/Axes.cs
rename to XmlFormat/Tokens/Axes.cs
index 844363e..c43509b 100644
--- a/XmlFormat/PropertyTokens/Tokens/Axes.cs
+++ b/XmlFormat/Tokens/Axes.cs
@@ -1,5 +1,4 @@
-using System;
-using System.Xml;
+using System.Xml;
using RobloxFiles.DataTypes;
namespace RobloxFiles.XmlFormat.PropertyTokens
@@ -7,25 +6,20 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
public class AxesToken : IXmlPropertyToken
{
public string Token => "Axes";
+
public bool ReadProperty(Property prop, XmlNode token)
{
- bool success = XmlPropertyTokens.ReadPropertyGeneric(prop, PropertyType.Axes, token);
+ uint value;
- if (success)
+ if (XmlPropertyTokens.ReadPropertyGeneric(token, out value))
{
- uint value = (uint)prop.Value;
- try
- {
- Axes axes = (Axes)value;
- prop.Value = axes;
- }
- catch
- {
- success = false;
- }
+ Axes axes = (Axes)value;
+ prop.Value = axes;
+
+ return true;
}
- return success;
+ return false;
}
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
@@ -33,7 +27,7 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
XmlElement axes = doc.CreateElement("axes");
node.AppendChild(axes);
- int value = (int)prop.Value;
+ int value = prop.CastValue();
axes.InnerText = value.ToInvariantString();
}
}
diff --git a/XmlFormat/PropertyTokens/Tokens/BinaryString.cs b/XmlFormat/Tokens/BinaryString.cs
similarity index 94%
rename from XmlFormat/PropertyTokens/Tokens/BinaryString.cs
rename to XmlFormat/Tokens/BinaryString.cs
index ad5cd15..7aa88a2 100644
--- a/XmlFormat/PropertyTokens/Tokens/BinaryString.cs
+++ b/XmlFormat/Tokens/BinaryString.cs
@@ -11,9 +11,9 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
{
// BinaryStrings are encoded in base64
string base64 = token.InnerText.Replace("\n", "");
+ prop.Value = Convert.FromBase64String(base64);
prop.Type = PropertyType.String;
- prop.Value = base64;
-
+
byte[] buffer = Convert.FromBase64String(base64);
prop.RawBuffer = buffer;
diff --git a/XmlFormat/PropertyTokens/Tokens/Boolean.cs b/XmlFormat/Tokens/Boolean.cs
similarity index 100%
rename from XmlFormat/PropertyTokens/Tokens/Boolean.cs
rename to XmlFormat/Tokens/Boolean.cs
diff --git a/XmlFormat/PropertyTokens/Tokens/BrickColor.cs b/XmlFormat/Tokens/BrickColor.cs
similarity index 60%
rename from XmlFormat/PropertyTokens/Tokens/BrickColor.cs
rename to XmlFormat/Tokens/BrickColor.cs
index 3e811cf..c509c6a 100644
--- a/XmlFormat/PropertyTokens/Tokens/BrickColor.cs
+++ b/XmlFormat/Tokens/BrickColor.cs
@@ -13,31 +13,23 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
public bool ReadProperty(Property prop, XmlNode token)
{
- bool success = XmlPropertyTokens.ReadPropertyGeneric(prop, PropertyType.BrickColor, token);
+ int value;
- if (success)
+ if (XmlPropertyTokens.ReadPropertyGeneric(token, out value))
{
- int value = (int)prop.Value;
+ BrickColor brickColor = BrickColor.FromNumber(value);
+ prop.XmlToken = "BrickColor";
+ prop.Value = brickColor;
- try
- {
- BrickColor brickColor = BrickColor.FromNumber(value);
- prop.XmlToken = "BrickColor";
- prop.Value = brickColor;
- }
- catch
- {
- // Invalid BrickColor Id?
- success = false;
- }
+ return true;
}
- return success;
+ return false;
}
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
{
- BrickColor value = prop.Value as BrickColor;
+ BrickColor value = prop.CastValue();
XmlElement brickColor = doc.CreateElement("int");
brickColor.InnerText = value.Number.ToInvariantString();
diff --git a/XmlFormat/PropertyTokens/Tokens/CFrame.cs b/XmlFormat/Tokens/CFrame.cs
similarity index 97%
rename from XmlFormat/PropertyTokens/Tokens/CFrame.cs
rename to XmlFormat/Tokens/CFrame.cs
index 0d46dd8..416c7f4 100644
--- a/XmlFormat/PropertyTokens/Tokens/CFrame.cs
+++ b/XmlFormat/Tokens/CFrame.cs
@@ -46,7 +46,7 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
{
- CFrame cf = prop.Value as CFrame;
+ CFrame cf = prop.CastValue();
float[] components = cf.GetComponents();
for (int i = 0; i < 12; i++)
diff --git a/XmlFormat/PropertyTokens/Tokens/Color3.cs b/XmlFormat/Tokens/Color3.cs
similarity index 69%
rename from XmlFormat/PropertyTokens/Tokens/Color3.cs
rename to XmlFormat/Tokens/Color3.cs
index 9ada037..62462b3 100644
--- a/XmlFormat/PropertyTokens/Tokens/Color3.cs
+++ b/XmlFormat/Tokens/Color3.cs
@@ -51,26 +51,18 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
{
- if (prop.Name == "Color3uint8")
+ Color3 color = prop.CastValue();
+ float[] rgb = new float[3] { color.R, color.G, color.B };
+
+ for (int i = 0; i < 3; i++)
{
- var handler = XmlPropertyTokens.GetHandler();
- handler.WriteProperty(prop, doc, node);
- }
- else
- {
- Color3 color = prop.Value as Color3;
- float[] rgb = new float[3] { color.R, color.G, color.B };
+ string field = Fields[i];
+ float value = rgb[i];
- for (int i = 0; i < 3; i++)
- {
- string field = Fields[i];
- float value = rgb[i];
+ XmlElement channel = doc.CreateElement(field);
+ channel.InnerText = value.ToInvariantString();
- XmlElement channel = doc.CreateElement(field);
- channel.InnerText = value.ToInvariantString();
-
- node.AppendChild(channel);
- }
+ node.AppendChild(channel);
}
}
}
diff --git a/XmlFormat/PropertyTokens/Tokens/Color3uint8.cs b/XmlFormat/Tokens/Color3uint8.cs
similarity index 58%
rename from XmlFormat/PropertyTokens/Tokens/Color3uint8.cs
rename to XmlFormat/Tokens/Color3uint8.cs
index c1ad0fc..3b3ce05 100644
--- a/XmlFormat/PropertyTokens/Tokens/Color3uint8.cs
+++ b/XmlFormat/Tokens/Color3uint8.cs
@@ -1,5 +1,4 @@
-using System;
-using System.Xml;
+using System.Xml;
using RobloxFiles.DataTypes;
namespace RobloxFiles.XmlFormat.PropertyTokens
@@ -10,29 +9,30 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
public bool ReadProperty(Property prop, XmlNode token)
{
- bool success = XmlPropertyTokens.ReadPropertyGeneric(prop, PropertyType.Color3, token);
+ uint value;
- if (success)
+ if (XmlPropertyTokens.ReadPropertyGeneric(token, out value))
{
- uint value = (uint)prop.Value;
-
uint r = (value >> 16) & 0xFF;
uint g = (value >> 8) & 0xFF;
uint b = value & 0xFF;
- prop.Value = Color3.FromRGB(r, g, b);
+ Color3uint8 result = Color3.FromRGB(r, g, b);
+ prop.Value = result;
+
+ return true;
}
- return success;
+ return false;
}
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
{
- Color3 color = prop.Value as Color3;
+ Color3uint8 color = prop.CastValue();
- uint r = (uint)(color.R * 256);
- uint g = (uint)(color.G * 256);
- uint b = (uint)(color.B * 256);
+ uint r = color.R,
+ g = color.G,
+ b = color.B;
uint rgb = (255u << 24) | (r << 16) | (g << 8) | b;
node.InnerText = rgb.ToString();
diff --git a/XmlFormat/PropertyTokens/Tokens/ColorSequence.cs b/XmlFormat/Tokens/ColorSequence.cs
similarity index 92%
rename from XmlFormat/PropertyTokens/Tokens/ColorSequence.cs
rename to XmlFormat/Tokens/ColorSequence.cs
index 50bac61..424a884 100644
--- a/XmlFormat/PropertyTokens/Tokens/ColorSequence.cs
+++ b/XmlFormat/Tokens/ColorSequence.cs
@@ -47,7 +47,8 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
{
- node.InnerText = prop.Value.ToString() + ' ';
+ ColorSequence value = prop.CastValue();
+ node.InnerText = value.ToString() + ' ';
}
}
}
diff --git a/XmlFormat/PropertyTokens/Tokens/Content.cs b/XmlFormat/Tokens/Content.cs
similarity index 88%
rename from XmlFormat/PropertyTokens/Tokens/Content.cs
rename to XmlFormat/Tokens/Content.cs
index fad23e3..1933bac 100644
--- a/XmlFormat/PropertyTokens/Tokens/Content.cs
+++ b/XmlFormat/Tokens/Content.cs
@@ -1,6 +1,8 @@
using System;
using System.Xml;
+using RobloxFiles.DataTypes;
+
namespace RobloxFiles.XmlFormat.PropertyTokens
{
public class ContentToken : IXmlPropertyToken
@@ -9,9 +11,9 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
public bool ReadProperty(Property prop, XmlNode token)
{
- string content = token.InnerText;
+ string data = token.InnerText;
+ prop.Value = new Content(data);
prop.Type = PropertyType.String;
- prop.Value = content;
if (token.HasChildNodes)
{
@@ -23,12 +25,12 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
try
{
// Roblox technically doesn't support this anymore, but load it anyway :P
- byte[] buffer = Convert.FromBase64String(content);
+ byte[] buffer = Convert.FromBase64String(data);
prop.RawBuffer = buffer;
}
catch
{
- Console.WriteLine("ContentToken: Got illegal base64 string: {0}", content);
+ Console.WriteLine("ContentToken: Got illegal base64 string: {0}", data);
}
}
}
@@ -38,7 +40,7 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
{
- string content = prop.Value.ToString();
+ string content = prop.CastValue();
string type = "null";
if (prop.HasRawBuffer)
diff --git a/XmlFormat/PropertyTokens/Tokens/Double.cs b/XmlFormat/Tokens/Double.cs
similarity index 80%
rename from XmlFormat/PropertyTokens/Tokens/Double.cs
rename to XmlFormat/Tokens/Double.cs
index dc6face..2eda661 100644
--- a/XmlFormat/PropertyTokens/Tokens/Double.cs
+++ b/XmlFormat/Tokens/Double.cs
@@ -13,7 +13,8 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
{
- node.InnerText = prop.Value.ToInvariantString();
+ double value = prop.CastValue();
+ node.InnerText = value.ToInvariantString();
}
}
}
diff --git a/XmlFormat/Tokens/Enum.cs b/XmlFormat/Tokens/Enum.cs
new file mode 100644
index 0000000..b9f8536
--- /dev/null
+++ b/XmlFormat/Tokens/Enum.cs
@@ -0,0 +1,48 @@
+using System;
+using System.Reflection;
+using System.Xml;
+
+namespace RobloxFiles.XmlFormat.PropertyTokens
+{
+ public class EnumToken : IXmlPropertyToken
+ {
+ public string Token => "token";
+
+ public bool ReadProperty(Property prop, XmlNode token)
+ {
+ uint value;
+
+ if (XmlPropertyTokens.ReadPropertyGeneric(token, out value))
+ {
+ Instance inst = prop.Instance;
+ Type instType = inst?.GetType();
+
+ FieldInfo info = instType.GetField(prop.Name, Property.BindingFlags);
+
+ if (info != null)
+ {
+ Type enumType = info.FieldType;
+ string item = value.ToInvariantString();
+
+ prop.Type = PropertyType.Enum;
+ prop.Value = Enum.Parse(enumType, item);
+
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
+ {
+ object rawValue = prop.Value;
+ Type valueType = rawValue.GetType();
+
+ int signed = (int)rawValue;
+ uint value = (uint)signed;
+
+ node.InnerText = value.ToString();
+ }
+ }
+}
diff --git a/XmlFormat/PropertyTokens/Tokens/Faces.cs b/XmlFormat/Tokens/Faces.cs
similarity index 54%
rename from XmlFormat/PropertyTokens/Tokens/Faces.cs
rename to XmlFormat/Tokens/Faces.cs
index 2ab9963..f29db4b 100644
--- a/XmlFormat/PropertyTokens/Tokens/Faces.cs
+++ b/XmlFormat/Tokens/Faces.cs
@@ -9,23 +9,17 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
public bool ReadProperty(Property prop, XmlNode token)
{
- bool success = XmlPropertyTokens.ReadPropertyGeneric(prop, PropertyType.Faces, token);
+ uint value;
- if (success)
+ if (XmlPropertyTokens.ReadPropertyGeneric(token, out value))
{
- uint value = (uint)prop.Value;
- try
- {
- Faces faces = (Faces)value;
- prop.Value = faces;
- }
- catch
- {
- success = false;
- }
+ Faces faces = (Faces)value;
+ prop.Value = faces;
+
+ return true;
}
- return success;
+ return false;
}
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
@@ -33,7 +27,7 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
XmlElement faces = doc.CreateElement("faces");
node.AppendChild(faces);
- int value = (int)prop.Value;
+ int value = prop.CastValue();
faces.InnerText = value.ToInvariantString();
}
}
diff --git a/XmlFormat/PropertyTokens/Tokens/Float.cs b/XmlFormat/Tokens/Float.cs
similarity index 80%
rename from XmlFormat/PropertyTokens/Tokens/Float.cs
rename to XmlFormat/Tokens/Float.cs
index 31caa3c..1899943 100644
--- a/XmlFormat/PropertyTokens/Tokens/Float.cs
+++ b/XmlFormat/Tokens/Float.cs
@@ -13,7 +13,8 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
{
- node.InnerText = prop.Value.ToInvariantString();
+ float value = prop.CastValue();
+ node.InnerText = value.ToInvariantString();
}
}
}
\ No newline at end of file
diff --git a/XmlFormat/PropertyTokens/Tokens/Int.cs b/XmlFormat/Tokens/Int.cs
similarity index 89%
rename from XmlFormat/PropertyTokens/Tokens/Int.cs
rename to XmlFormat/Tokens/Int.cs
index 2223036..fcf0637 100644
--- a/XmlFormat/PropertyTokens/Tokens/Int.cs
+++ b/XmlFormat/Tokens/Int.cs
@@ -24,7 +24,8 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
{
- node.InnerText = prop.Value.ToInvariantString();
+ int value = prop.CastValue();
+ node.InnerText = value.ToInvariantString();
}
}
}
diff --git a/XmlFormat/PropertyTokens/Tokens/Int64.cs b/XmlFormat/Tokens/Int64.cs
similarity index 82%
rename from XmlFormat/PropertyTokens/Tokens/Int64.cs
rename to XmlFormat/Tokens/Int64.cs
index bdbbbf3..48b6f8b 100644
--- a/XmlFormat/PropertyTokens/Tokens/Int64.cs
+++ b/XmlFormat/Tokens/Int64.cs
@@ -13,7 +13,8 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
{
- node.InnerText = prop.Value.ToString();
+ long value = prop.CastValue();
+ node.InnerText = value.ToString();
}
}
}
\ No newline at end of file
diff --git a/XmlFormat/PropertyTokens/Tokens/NumberRange.cs b/XmlFormat/Tokens/NumberRange.cs
similarity index 90%
rename from XmlFormat/PropertyTokens/Tokens/NumberRange.cs
rename to XmlFormat/Tokens/NumberRange.cs
index cf5c919..8198a57 100644
--- a/XmlFormat/PropertyTokens/Tokens/NumberRange.cs
+++ b/XmlFormat/Tokens/NumberRange.cs
@@ -35,7 +35,8 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
{
- node.InnerText = prop.Value.ToString() + ' ';
+ NumberRange value = prop.CastValue();
+ node.InnerText = value.ToString() + ' ';
}
}
}
diff --git a/XmlFormat/PropertyTokens/Tokens/NumberSequence.cs b/XmlFormat/Tokens/NumberSequence.cs
similarity index 92%
rename from XmlFormat/PropertyTokens/Tokens/NumberSequence.cs
rename to XmlFormat/Tokens/NumberSequence.cs
index db0183b..af7a1d5 100644
--- a/XmlFormat/PropertyTokens/Tokens/NumberSequence.cs
+++ b/XmlFormat/Tokens/NumberSequence.cs
@@ -44,7 +44,8 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
{
- node.InnerText = prop.Value.ToString() + ' ';
+ NumberSequence value = prop.CastValue();
+ node.InnerText = value.ToString() + ' ';
}
}
}
diff --git a/XmlFormat/PropertyTokens/Tokens/PhysicalProperties.cs b/XmlFormat/Tokens/PhysicalProperties.cs
similarity index 97%
rename from XmlFormat/PropertyTokens/Tokens/PhysicalProperties.cs
rename to XmlFormat/Tokens/PhysicalProperties.cs
index 2950156..3b6ef1e 100644
--- a/XmlFormat/PropertyTokens/Tokens/PhysicalProperties.cs
+++ b/XmlFormat/Tokens/PhysicalProperties.cs
@@ -61,7 +61,7 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
if (hasCustomPhysics)
{
- var customProps = prop.Value as PhysicalProperties;
+ var customProps = prop.CastValue();
var data = new Dictionary()
{
diff --git a/XmlFormat/Tokens/ProtectedString.cs b/XmlFormat/Tokens/ProtectedString.cs
new file mode 100644
index 0000000..833df8b
--- /dev/null
+++ b/XmlFormat/Tokens/ProtectedString.cs
@@ -0,0 +1,34 @@
+using System.Xml;
+using RobloxFiles.DataTypes;
+
+namespace RobloxFiles.XmlFormat.PropertyTokens
+{
+ public class ProtectedStringToken : IXmlPropertyToken
+ {
+ public string Token => "ProtectedString";
+
+ public bool ReadProperty(Property prop, XmlNode token)
+ {
+ ProtectedString contents = token.InnerText;
+ prop.Type = PropertyType.String;
+ prop.Value = contents;
+
+ return true;
+ }
+
+ public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
+ {
+ string value = prop.CastValue();
+
+ if (value.Contains("\r") || value.Contains("\n"))
+ {
+ XmlCDataSection cdata = doc.CreateCDataSection(value);
+ node.AppendChild(cdata);
+ }
+ else
+ {
+ node.InnerText = value;
+ }
+ }
+ }
+}
diff --git a/XmlFormat/PropertyTokens/Tokens/Ray.cs b/XmlFormat/Tokens/Ray.cs
similarity index 97%
rename from XmlFormat/PropertyTokens/Tokens/Ray.cs
rename to XmlFormat/Tokens/Ray.cs
index 7c3c98d..b861b0a 100644
--- a/XmlFormat/PropertyTokens/Tokens/Ray.cs
+++ b/XmlFormat/Tokens/Ray.cs
@@ -39,7 +39,7 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
{
- Ray ray = prop.Value as Ray;
+ Ray ray = prop.CastValue();
XmlElement origin = doc.CreateElement("origin");
XmlElement direction = doc.CreateElement("direction");
diff --git a/XmlFormat/PropertyTokens/Tokens/Rect.cs b/XmlFormat/Tokens/Rect.cs
similarity index 96%
rename from XmlFormat/PropertyTokens/Tokens/Rect.cs
rename to XmlFormat/Tokens/Rect.cs
index 3b31f55..6b9b6f9 100644
--- a/XmlFormat/PropertyTokens/Tokens/Rect.cs
+++ b/XmlFormat/Tokens/Rect.cs
@@ -39,7 +39,7 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
{
- Rect rect = prop.Value as Rect;
+ Rect rect = prop.CastValue();
XmlElement min = doc.CreateElement("min");
Vector2Token.WriteVector2(doc, min, rect.Min);
diff --git a/XmlFormat/PropertyTokens/Tokens/Ref.cs b/XmlFormat/Tokens/Ref.cs
similarity index 79%
rename from XmlFormat/PropertyTokens/Tokens/Ref.cs
rename to XmlFormat/Tokens/Ref.cs
index 05c46dd..d15a68f 100644
--- a/XmlFormat/PropertyTokens/Tokens/Ref.cs
+++ b/XmlFormat/Tokens/Ref.cs
@@ -10,7 +10,7 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
{
string refId = token.InnerText;
prop.Type = PropertyType.Ref;
- prop.Value = refId;
+ prop.XmlToken = refId;
return true;
}
@@ -19,9 +19,9 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
{
string result = "null";
- if (prop.Value != null && prop.Value.ToString() != "null")
+ if (prop.Value != null)
{
- Instance inst = prop.Value as Instance;
+ Instance inst = prop.CastValue();
result = inst.Referent;
}
diff --git a/XmlFormat/PropertyTokens/Tokens/SharedString.cs b/XmlFormat/Tokens/SharedString.cs
similarity index 60%
rename from XmlFormat/PropertyTokens/Tokens/SharedString.cs
rename to XmlFormat/Tokens/SharedString.cs
index 8ef7b85..942cc0a 100644
--- a/XmlFormat/PropertyTokens/Tokens/SharedString.cs
+++ b/XmlFormat/Tokens/SharedString.cs
@@ -1,6 +1,5 @@
-using System;
-using System.Text;
-using System.Xml;
+using System.Xml;
+using RobloxFiles.DataTypes;
namespace RobloxFiles.XmlFormat.PropertyTokens
{
@@ -10,17 +9,17 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
public bool ReadProperty(Property prop, XmlNode token)
{
- string contents = token.InnerText;
+ string md5 = token.InnerText;
prop.Type = PropertyType.SharedString;
- prop.Value = contents;
+ prop.Value = new SharedString(md5);
return true;
}
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
{
- var BinaryStringToken = XmlPropertyTokens.GetHandler();
- BinaryStringToken.WriteProperty(prop, doc, node);
+ SharedString value = prop.CastValue();
+ node.InnerText = value.MD5_Key;
}
}
}
diff --git a/XmlFormat/PropertyTokens/Tokens/String.cs b/XmlFormat/Tokens/String.cs
similarity index 87%
rename from XmlFormat/PropertyTokens/Tokens/String.cs
rename to XmlFormat/Tokens/String.cs
index 86bfdf3..12ea8f2 100644
--- a/XmlFormat/PropertyTokens/Tokens/String.cs
+++ b/XmlFormat/Tokens/String.cs
@@ -1,12 +1,10 @@
-using System;
-using System.Text;
-using System.Xml;
+using System.Xml;
namespace RobloxFiles.XmlFormat.PropertyTokens
{
public class StringToken : IXmlPropertyToken
{
- public string Token => "string; ProtectedString";
+ public string Token => "string";
public bool ReadProperty(Property prop, XmlNode token)
{
diff --git a/XmlFormat/PropertyTokens/Tokens/UDim.cs b/XmlFormat/Tokens/UDim.cs
similarity index 97%
rename from XmlFormat/PropertyTokens/Tokens/UDim.cs
rename to XmlFormat/Tokens/UDim.cs
index 1b3d97b..2316ad4 100644
--- a/XmlFormat/PropertyTokens/Tokens/UDim.cs
+++ b/XmlFormat/Tokens/UDim.cs
@@ -53,7 +53,7 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
{
- UDim value = prop.Value as UDim;
+ UDim value = prop.CastValue();
WriteUDim(doc, node, value);
}
}
diff --git a/XmlFormat/PropertyTokens/Tokens/UDim2.cs b/XmlFormat/Tokens/UDim2.cs
similarity index 94%
rename from XmlFormat/PropertyTokens/Tokens/UDim2.cs
rename to XmlFormat/Tokens/UDim2.cs
index 3affceb..2fc10cc 100644
--- a/XmlFormat/PropertyTokens/Tokens/UDim2.cs
+++ b/XmlFormat/Tokens/UDim2.cs
@@ -26,7 +26,7 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
{
- UDim2 value = prop.Value as UDim2;
+ UDim2 value = prop.CastValue();
UDim xUDim = value.X;
UDimToken.WriteUDim(doc, node, xUDim, "X");
diff --git a/XmlFormat/PropertyTokens/Tokens/Vector2.cs b/XmlFormat/Tokens/Vector2.cs
similarity index 96%
rename from XmlFormat/PropertyTokens/Tokens/Vector2.cs
rename to XmlFormat/Tokens/Vector2.cs
index a5b13b7..26ed07b 100644
--- a/XmlFormat/PropertyTokens/Tokens/Vector2.cs
+++ b/XmlFormat/Tokens/Vector2.cs
@@ -58,7 +58,7 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
{
- Vector2 value = prop.Value as Vector2;
+ Vector2 value = prop.CastValue();
WriteVector2(doc, node, value);
}
}
diff --git a/XmlFormat/PropertyTokens/Tokens/Vector3.cs b/XmlFormat/Tokens/Vector3.cs
similarity index 97%
rename from XmlFormat/PropertyTokens/Tokens/Vector3.cs
rename to XmlFormat/Tokens/Vector3.cs
index f45ee9f..fcd4b52 100644
--- a/XmlFormat/PropertyTokens/Tokens/Vector3.cs
+++ b/XmlFormat/Tokens/Vector3.cs
@@ -62,7 +62,7 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
{
- Vector3 value = prop.Value as Vector3;
+ Vector3 value = prop.CastValue();
WriteVector3(doc, node, value);
}
}
diff --git a/XmlFormat/PropertyTokens/Tokens/Vector3int16.cs b/XmlFormat/Tokens/Vector3int16.cs
similarity index 95%
rename from XmlFormat/PropertyTokens/Tokens/Vector3int16.cs
rename to XmlFormat/Tokens/Vector3int16.cs
index 2893b06..1bf7f8a 100644
--- a/XmlFormat/PropertyTokens/Tokens/Vector3int16.cs
+++ b/XmlFormat/Tokens/Vector3int16.cs
@@ -39,7 +39,7 @@ namespace RobloxFiles.XmlFormat.PropertyTokens
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
{
- Vector3int16 value = prop.Value as Vector3int16;
+ Vector3int16 value = prop.CastValue();
XmlElement x = doc.CreateElement("X");
x.InnerText = value.X.ToString();
diff --git a/XmlFormat/PropertyTokens/XmlPropertyTokens.cs b/XmlFormat/XmlPropertyTokens.cs
similarity index 75%
rename from XmlFormat/PropertyTokens/XmlPropertyTokens.cs
rename to XmlFormat/XmlPropertyTokens.cs
index 1bc1f6e..d15d4e3 100644
--- a/XmlFormat/PropertyTokens/XmlPropertyTokens.cs
+++ b/XmlFormat/XmlPropertyTokens.cs
@@ -35,38 +35,54 @@ namespace RobloxFiles.XmlFormat
Handlers = tokenHandlers;
}
- public static bool ReadPropertyGeneric(Property prop, PropertyType propType, XmlNode token) where T : struct
+ public static bool ReadPropertyGeneric(XmlNode token, out T outValue) where T : struct
{
try
{
string value = token.InnerText;
Type type = typeof(T);
- if (type == typeof(int))
- prop.Value = Formatting.ParseInt(value);
- else if (type == typeof(float))
- prop.Value = Formatting.ParseFloat(value);
- else if (type == typeof(double))
- prop.Value = Formatting.ParseDouble(value);
+ object result = null;
- if (prop.Value == null)
+ if (type == typeof(int))
+ result = Formatting.ParseInt(value);
+ else if (type == typeof(float))
+ result = Formatting.ParseFloat(value);
+ else if (type == typeof(double))
+ result = Formatting.ParseDouble(value);
+
+ if (result == null)
{
Type resultType = typeof(T);
- TypeConverter converter = TypeDescriptor.GetConverter(resultType);
-
- object result = converter.ConvertFromString(token.InnerText);
- prop.Value = result;
+ var converter = TypeDescriptor.GetConverter(resultType);
+ result = converter.ConvertFromString(token.InnerText);
}
- prop.Type = propType;
+ outValue = (T)result;
return true;
}
catch
{
+ outValue = default(T);
return false;
}
}
+ public static bool ReadPropertyGeneric(Property prop, PropertyType propType, XmlNode token) where T : struct
+ {
+ T result;
+
+ if (ReadPropertyGeneric(token, out result))
+ {
+ prop.Type = propType;
+ prop.Value = result;
+
+ return true;
+ }
+
+ return false;
+ }
+
public static IXmlPropertyToken GetHandler(string tokenName)
{
IXmlPropertyToken result = null;
diff --git a/XmlFormat/XmlRobloxFile.cs b/XmlFormat/XmlRobloxFile.cs
index 33763a7..917250f 100644
--- a/XmlFormat/XmlRobloxFile.cs
+++ b/XmlFormat/XmlRobloxFile.cs
@@ -6,20 +6,26 @@ using System.Linq;
using System.Text;
using System.Xml;
-namespace RobloxFiles.XmlFormat
+using RobloxFiles.DataTypes;
+using RobloxFiles.XmlFormat;
+
+namespace RobloxFiles
{
public class XmlRobloxFile : RobloxFile
{
- // Runtime Specific
- public readonly XmlDocument Root = new XmlDocument();
+ public readonly XmlDocument XmlDocument = new XmlDocument();
internal Dictionary Instances = new Dictionary();
- internal Dictionary SharedStrings = new Dictionary();
+ internal HashSet SharedStrings = new HashSet();
- internal XmlRobloxFile()
+ private Dictionary RawMetadata = new Dictionary();
+ public Dictionary Metadata => RawMetadata;
+
+ public XmlRobloxFile()
{
Name = "XmlRobloxFile";
ParentLocked = true;
+ Referent = "null";
}
protected override void ReadFile(byte[] buffer)
@@ -27,14 +33,14 @@ namespace RobloxFiles.XmlFormat
try
{
string xml = Encoding.UTF8.GetString(buffer);
- Root.LoadXml(xml);
+ XmlDocument.LoadXml(xml);
}
catch
{
throw new Exception("XmlRobloxFile: Could not read provided buffer as XML!");
}
- XmlNode roblox = Root.FirstChild;
+ XmlNode roblox = XmlDocument.FirstChild;
if (roblox != null && roblox.Name == "roblox")
{
@@ -59,6 +65,10 @@ namespace RobloxFiles.XmlFormat
{
XmlRobloxFileReader.ReadSharedStrings(child, this);
}
+ else if (child.Name == "Meta")
+ {
+ XmlRobloxFileReader.ReadMetadata(child, this);
+ }
}
// Query the properties.
@@ -71,7 +81,8 @@ namespace RobloxFiles.XmlFormat
foreach (Property refProp in refProps)
{
- string refId = refProp.Value as string;
+ string refId = refProp.XmlToken;
+ refProp.XmlToken = "Ref";
if (Instances.ContainsKey(refId))
{
@@ -86,26 +97,13 @@ namespace RobloxFiles.XmlFormat
}
}
- // Resolve shared strings.
+ // Record shared strings.
var sharedProps = allProps.Where(prop => prop.Type == PropertyType.SharedString);
foreach (Property sharedProp in sharedProps)
{
- string md5 = sharedProp.Value as string;
-
- if (SharedStrings.ContainsKey(md5))
- {
- string value = SharedStrings[md5];
- sharedProp.Value = value;
-
- byte[] data = Convert.FromBase64String(value);
- sharedProp.RawBuffer = data;
- }
- else
- {
- string name = sharedProp.GetFullName();
- Console.WriteLine("XmlRobloxFile: Could not resolve shared string for {0}", name);
- }
+ SharedString shared = sharedProp.CastValue();
+ SharedStrings.Add(shared.MD5_Key);
}
}
else
@@ -125,17 +123,32 @@ namespace RobloxFiles.XmlFormat
Instances.Clear();
SharedStrings.Clear();
+ // First, append the metadata
+ foreach (string key in Metadata.Keys)
+ {
+ string value = Metadata[key];
+
+ XmlElement meta = doc.CreateElement("Meta");
+ meta.SetAttribute("name", key);
+ meta.InnerText = value;
+
+ roblox.AppendChild(meta);
+ }
+
Instance[] children = GetChildren();
- // First, record all of the instances.
+ // Record all of the instances.
foreach (Instance inst in children)
XmlRobloxFileWriter.RecordInstances(this, inst);
// Now append them into the document.
foreach (Instance inst in children)
{
- XmlNode instNode = XmlRobloxFileWriter.WriteInstance(inst, doc, this);
- roblox.AppendChild(instNode);
+ if (inst.Archivable)
+ {
+ XmlNode instNode = XmlRobloxFileWriter.WriteInstance(inst, doc, this);
+ roblox.AppendChild(instNode);
+ }
}
// Append the shared strings.