Correct UniqueId/FontFace implementations.

This commit is contained in:
Max 2022-10-12 21:19:43 -05:00
parent 583d69713d
commit 619b89d2a9
17 changed files with 3045 additions and 2687 deletions

View File

@ -1,7 +1,7 @@
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Collections.Generic;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
@ -17,43 +17,46 @@ namespace RobloxFiles.BinaryFormat
File = file; File = file;
} }
// Reads 'count * sizeof(T)' interleaved bytes and converts // Reads 'count * sizeof(T)' interleaved bytes
// them into an array of T[count] where each value in the public T[] ReadInterleaved<T>(int count, Func<byte[], int, T> transform) where T : struct
// array has been decoded by the provided 'decode' function.
public T[] ReadInterleaved<T>(int count, Func<byte[], int, T> decode) where T : struct
{ {
int bufferSize = Marshal.SizeOf<T>(); int sizeof_T = Marshal.SizeOf<T>();
byte[] interleaved = ReadBytes(count * bufferSize); int blobSize = count * sizeof_T;
T[] values = new T[count]; var blob = ReadBytes(blobSize);
var work = new byte[sizeof_T];
var values = new T[count];
for (int i = 0; i < count; i++) for (int offset = 0; offset < count; offset++)
{ {
long buffer = 0; for (int i = 0; i < sizeof_T; i++)
for (int column = 0; column < bufferSize; column++)
{ {
long block = interleaved[(column * count) + i]; int index = (i * count) + offset;
int shift = (bufferSize - column - 1) * 8; work[sizeof_T - i - 1] = blob[index];
buffer |= (block << shift);
} }
byte[] sequence = BitConverter.GetBytes(buffer); values[offset] = transform(work, 0);
values[i] = decode(sequence, 0);
} }
return values; return values;
} }
// Decodes an int from an interleaved buffer. // Rotates the sign bit of an int32 buffer.
private int DecodeInt(byte[] buffer, int startIndex) public int RotateInt32(byte[] buffer, int startIndex)
{ {
int value = BitConverter.ToInt32(buffer, startIndex); int value = BitConverter.ToInt32(buffer, startIndex);
return (value >> 1) ^ (-(value & 1)); return (int)((uint)value >> 1) ^ (-(value & 1));
} }
// Decodes a float from an interleaved buffer. // Rotates the sign bit of an int64 buffer.
private float DecodeFloat(byte[] buffer, int startIndex) public long RotateInt64(byte[] buffer, int startIndex)
{
long value = BitConverter.ToInt64(buffer, startIndex);
return (long)((ulong)value >> 1) ^ (-(value & 1));
}
// Rotates the sign bit of a float buffer.
public float RotateFloat(byte[] buffer, int startIndex)
{ {
uint u = BitConverter.ToUInt32(buffer, startIndex); uint u = BitConverter.ToUInt32(buffer, startIndex);
uint i = (u >> 1) | (u << 31); uint i = (u >> 1) | (u << 31);
@ -62,28 +65,10 @@ namespace RobloxFiles.BinaryFormat
return BitConverter.ToSingle(b, 0); return BitConverter.ToSingle(b, 0);
} }
// Reads an interleaved buffer of integers. // Reads and accumulates an interleaved int32 buffer.
public int[] ReadInts(int count)
{
return ReadInterleaved(count, DecodeInt);
}
// Reads an interleaved buffer of floats.
public float[] ReadFloats(int count)
{
return ReadInterleaved(count, DecodeFloat);
}
// Reads an interleaved buffer of unsigned integers.
public uint[] ReadUInts(int count)
{
return ReadInterleaved(count, BitConverter.ToUInt32);
}
// Reads and accumulates an interleaved buffer of integers.
public List<int> ReadInstanceIds(int count) public List<int> ReadInstanceIds(int count)
{ {
int[] values = ReadInts(count); int[] values = ReadInterleaved(count, RotateInt32);
for (int i = 1; i < count; ++i) for (int i = 1; i < count; ++i)
values[i] += values[i - 1]; values[i] += values[i - 1];

View File

@ -104,14 +104,20 @@ namespace RobloxFiles.BinaryFormat
Marshal.FreeHGlobal(converter); Marshal.FreeHGlobal(converter);
} }
// Encodes an int for an interleaved buffer. // Rotates the sign bit of the provided int.
private static int EncodeInt(int value) public int RotateInt(int value)
{ {
return (value << 1) ^ (value >> 31); return (value << 1) ^ (value >> 31);
} }
// Encodes a float for an interleaved buffer. // Rotates the sign bit of the provided long.
private static float EncodeFloat(float value) public long RotateLong(long value)
{
return (value << 1) ^ (value >> 63);
}
// Rotates the sign bit of the provided float.
public float RotateFloat(float value)
{ {
byte[] buffer = BitConverter.GetBytes(value); byte[] buffer = BitConverter.GetBytes(value);
uint bits = BitConverter.ToUInt32(buffer, 0); uint bits = BitConverter.ToUInt32(buffer, 0);
@ -125,13 +131,19 @@ namespace RobloxFiles.BinaryFormat
// Writes an interleaved list of integers. // Writes an interleaved list of integers.
public void WriteInts(List<int> values) public void WriteInts(List<int> values)
{ {
WriteInterleaved(values, EncodeInt); WriteInterleaved(values, RotateInt);
}
// Writes an interleaved list of longs
public void WriteLongs(List<long> values)
{
WriteInterleaved(values, RotateLong);
} }
// Writes an interleaved list of floats // Writes an interleaved list of floats
public void WriteFloats(List<float> values) public void WriteFloats(List<float> values)
{ {
WriteInterleaved(values, EncodeFloat); WriteInterleaved(values, RotateFloat);
} }
// Accumulatively writes an interleaved array of integers. // Accumulatively writes an interleaved array of integers.

View File

@ -78,8 +78,8 @@ namespace RobloxFiles.BinaryFormat.Chunks
} }
// Setup some short-hand functions for actions used during the read procedure. // Setup some short-hand functions for actions used during the read procedure.
var readInts = new Func<int[]>(() => reader.ReadInts(instCount)); var readInts = new Func<int[]>(() => reader.ReadInterleaved(instCount, reader.RotateInt32));
var readFloats = new Func<float[]>(() => reader.ReadFloats(instCount)); var readFloats = new Func<float[]>(() => reader.ReadInterleaved(instCount, reader.RotateFloat));
var readProperties = new Action<Func<int, object>>(read => var readProperties = new Action<Func<int, object>>(read =>
{ {
@ -434,7 +434,7 @@ namespace RobloxFiles.BinaryFormat.Chunks
} }
case PropertyType.Enum: case PropertyType.Enum:
{ {
uint[] enums = reader.ReadUInts(instCount); uint[] enums = reader.ReadInterleaved(instCount, BitConverter.ToUInt32);
readProperties(i => readProperties(i =>
{ {
@ -619,22 +619,17 @@ namespace RobloxFiles.BinaryFormat.Chunks
} }
case PropertyType.Int64: case PropertyType.Int64:
{ {
long[] longs = reader.ReadInterleaved(instCount, (buffer, start) => var values = reader.ReadInterleaved(instCount, reader.RotateInt64);
{ readProperties(i => values[i]);
long result = BitConverter.ToInt64(buffer, start);
return (long)((ulong)result >> 1) ^ (-(result & 1));
});
readProperties(i => longs[i]);
break; break;
} }
case PropertyType.SharedString: case PropertyType.SharedString:
{ {
uint[] SharedKeys = reader.ReadUInts(instCount); var keys = reader.ReadInterleaved(instCount, BitConverter.ToUInt32);
readProperties(i => readProperties(i =>
{ {
uint key = SharedKeys[i]; uint key = keys[i];
return File.SharedStrings[key]; return File.SharedStrings[key];
}); });
@ -654,14 +649,16 @@ namespace RobloxFiles.BinaryFormat.Chunks
} }
case PropertyType.UniqueId: case PropertyType.UniqueId:
{ {
readProperties(i => var uniqueIds = reader.ReadInterleaved(instCount, (buffer, offset) =>
{ {
var index = reader.ReadUInt32(); var random = reader.RotateInt64(buffer, 0);
var time = reader.ReadUInt32(); var time = BitConverter.ToUInt32(buffer, 8);
var random = reader.ReadUInt64(); var index = BitConverter.ToUInt32(buffer, 12);
return new UniqueId(index, time, random);
return new UniqueId(random, time, index);
}); });
readProperties(i => uniqueIds[i]);
break; break;
} }
case PropertyType.FontFace: case PropertyType.FontFace:
@ -670,13 +667,11 @@ namespace RobloxFiles.BinaryFormat.Chunks
{ {
string family = reader.ReadString(); string family = reader.ReadString();
if (family.EndsWith(".otf") || family.EndsWith(".ttf"))
return new FontFace(family);
var weight = (FontWeight)reader.ReadUInt16(); var weight = (FontWeight)reader.ReadUInt16();
var style = (FontStyle)reader.ReadByte(); var style = (FontStyle)reader.ReadByte();
var cachedFaceId = reader.ReadString();
return new FontFace(family, weight, style); return new FontFace(family, weight, style, cachedFaceId);
}); });
break; break;
@ -1233,12 +1228,7 @@ namespace RobloxFiles.BinaryFormat.Chunks
longs.Add(value); longs.Add(value);
}); });
writer.WriteInterleaved(longs, value => writer.WriteLongs(longs);
{
// Move the sign bit to the front.
return (value << 1) ^ (value >> 63);
});
break; break;
} }
case PropertyType.SharedString: case PropertyType.SharedString:
@ -1293,14 +1283,16 @@ namespace RobloxFiles.BinaryFormat.Chunks
} }
case PropertyType.UniqueId: case PropertyType.UniqueId:
{ {
var uniqueIds = new List<UniqueId>();
props.ForEach(prop => props.ForEach(prop =>
{ {
var uniqueId = prop.CastValue<UniqueId>(); var uniqueId = prop.CastValue<UniqueId>();
writer.Write(uniqueId.Index); var rotated = writer.RotateLong(uniqueId.Random);
writer.Write(uniqueId.Time); uniqueIds.Add(new UniqueId(rotated, uniqueId.Time, uniqueId.Index));
writer.Write(uniqueId.Random);
}); });
writer.WriteInterleaved(uniqueIds);
break; break;
} }
case PropertyType.FontFace: case PropertyType.FontFace:
@ -1310,16 +1302,16 @@ namespace RobloxFiles.BinaryFormat.Chunks
var font = prop.CastValue<FontFace>(); var font = prop.CastValue<FontFace>();
string family = font.Family; string family = font.Family;
writer.WriteString(font.Family); writer.WriteString(family);
if (family.EndsWith(".otf") || family.EndsWith(".ttf"))
return;
var weight = (ushort)font.Weight; var weight = (ushort)font.Weight;
writer.Write(weight); writer.Write(weight);
var style = (byte)font.Style; var style = (byte)font.Style;
writer.Write(style); writer.Write(style);
var cachedFaceId = font.CachedFaceId;
writer.WriteString(cachedFaceId);
}); });
break; break;

View File

@ -91,20 +91,14 @@ namespace RobloxFiles.DataTypes
public CFrame(Vector3 eye, Vector3 look) public CFrame(Vector3 eye, Vector3 look)
{ {
Vector3 zAxis = (eye - look).Unit, Vector3 zAxis = (eye - look).Unit,
xAxis = Vector3.Up.Cross(zAxis), xAxis = Vector3.yAxis.Cross(zAxis),
yAxis = zAxis.Cross(xAxis); yAxis = zAxis.Cross(xAxis);
if (xAxis.Magnitude == 0) if (xAxis.Magnitude == 0)
{ {
xAxis = Vector3.z; xAxis = Vector3.zAxis;
yAxis = Vector3.x; yAxis = Vector3.xAxis;
zAxis = Vector3.y; zAxis = Vector3.yAxis;
if (zAxis.Y < 0)
{
xAxis = -xAxis;
zAxis = -zAxis;
}
} }
m11 = xAxis.X; m12 = yAxis.X; m13 = zAxis.X; m14 = eye.X; m11 = xAxis.X; m12 = yAxis.X; m13 = zAxis.X; m14 = eye.X;
@ -299,18 +293,18 @@ namespace RobloxFiles.DataTypes
public static CFrame FromAxisAngle(Vector3 axis, float theta) public static CFrame FromAxisAngle(Vector3 axis, float theta)
{ {
Vector3 r = VectorAxisAngle(axis, Vector3.x, theta), Vector3 r = VectorAxisAngle(axis, Vector3.xAxis, theta),
u = VectorAxisAngle(axis, Vector3.y, theta), u = VectorAxisAngle(axis, Vector3.yAxis, theta),
b = VectorAxisAngle(axis, Vector3.z, theta); b = VectorAxisAngle(axis, Vector3.zAxis, theta);
return new CFrame(0, 0, 0, r.X, u.X, b.X, r.Y, u.Y, b.Y, r.Z, u.Z, b.Z); return new CFrame(0, 0, 0, r.X, u.X, b.X, r.Y, u.Y, b.Y, r.Z, u.Z, b.Z);
} }
public static CFrame FromEulerAnglesXYZ(float x, float y, float z) public static CFrame FromEulerAnglesXYZ(float x, float y, float z)
{ {
CFrame cfx = FromAxisAngle(Vector3.x, x), CFrame cfx = FromAxisAngle(Vector3.xAxis, x),
cfy = FromAxisAngle(Vector3.y, y), cfy = FromAxisAngle(Vector3.yAxis, y),
cfz = FromAxisAngle(Vector3.z, z); cfz = FromAxisAngle(Vector3.zAxis, z);
return cfx * cfy * cfz; return cfx * cfy * cfz;
} }
@ -410,9 +404,9 @@ namespace RobloxFiles.DataTypes
{ {
var tests = new float[3] var tests = new float[3]
{ {
XVector.Dot(Vector3.x), XVector.Dot(Vector3.xAxis),
YVector.Dot(Vector3.y), YVector.Dot(Vector3.yAxis),
ZVector.Dot(Vector3.z) ZVector.Dot(Vector3.zAxis)
}; };
foreach (var test in tests) foreach (var test in tests)

View File

@ -2,8 +2,9 @@
namespace RobloxFiles.DataTypes namespace RobloxFiles.DataTypes
{ {
// Implementation of Roblox's Font datatype. // Implementation of Roblox's FontFace datatype.
// Renamed to FontFace to avoid disambiguation with System.Font and the Font enum. // In Luau this type is named Font, but we avoid that name
// to avoid ambiguity with System.Font and Roblox's Font enum.
public class FontFace public class FontFace
{ {
@ -11,8 +12,16 @@ namespace RobloxFiles.DataTypes
public readonly FontWeight Weight = FontWeight.Regular; public readonly FontWeight Weight = FontWeight.Regular;
public readonly FontStyle Style = FontStyle.Normal; public readonly FontStyle Style = FontStyle.Normal;
public FontFace(Content family, FontWeight weight = FontWeight.Regular, FontStyle style = FontStyle.Normal) // Roblox caches the asset of the font's face to make it
// load faster. At runtime both the Family and the CachedFaceId
// are loaded in parallel. If the CachedFaceId doesn't match with
// the family file's face asset, then the correct one will be loaded late.
// Setting this is not required, it's just a throughput optimization.
public Content CachedFaceId { get; set; } = "";
public FontFace(Content family, FontWeight weight = FontWeight.Regular, FontStyle style = FontStyle.Normal, string cachedFaceId = "")
{ {
CachedFaceId = cachedFaceId;
Family = family; Family = family;
Weight = weight; Weight = weight;
Style = style; Style = style;

View File

@ -1,16 +1,27 @@
namespace RobloxFiles.DataTypes using System;
namespace RobloxFiles.DataTypes
{ {
public struct UniqueId public struct UniqueId
{ {
public readonly uint Time; public readonly uint Time;
public readonly uint Index; public readonly uint Index;
public readonly ulong Random; public readonly long Random;
public UniqueId(uint time, uint index, ulong random) public UniqueId(long random, uint time, uint index)
{ {
Time = time; Time = time;
Index = index; Index = index;
Random = random; Random = random;
} }
public override string ToString()
{
string random = Random.ToString("x2").PadLeft(16, '0');
string index = Index.ToString("x2").PadLeft(8, '0');
string time = Time.ToString("x2").PadLeft(8, '0');
return $"{random}{time}{index}";
}
} }
} }

View File

@ -98,13 +98,13 @@ namespace RobloxFiles.DataTypes
public static readonly Vector3 zero = new Vector3(0, 0, 0); public static readonly Vector3 zero = new Vector3(0, 0, 0);
public static readonly Vector3 one = new Vector3(1, 1, 1); public static readonly Vector3 one = new Vector3(1, 1, 1);
public static readonly Vector3 x = new Vector3(1, 0, 0); public static readonly Vector3 xAxis = new Vector3(1, 0, 0);
public static readonly Vector3 y = new Vector3(0, 1, 0); public static readonly Vector3 yAxis = new Vector3(0, 1, 0);
public static readonly Vector3 z = new Vector3(0, 0, 1); public static readonly Vector3 zAxis = new Vector3(0, 0, 1);
public static Vector3 Right => x; public static Vector3 Right => xAxis;
public static Vector3 Up => y; public static Vector3 Up => yAxis;
public static Vector3 Back => z; public static Vector3 Back => zAxis;
public float Dot(Vector3 other) public float Dot(Vector3 other)
{ {

View File

@ -5,10 +5,10 @@ export type FormatFunc = (any) -> string
export type Format = { [string]: FormatFunc } export type Format = { [string]: FormatFunc }
export type IEnum = { GetEnumItems: (IEnum) -> {EnumItem} } export type IEnum = { GetEnumItems: (IEnum) -> {EnumItem} }
local function flags<T>(flags: T, enum: IEnum): string local function flags(flagType: any, enum: Enum): string
local value = 0 local value = 0
for i, item in enum:GetEnumItems() do for i, item: EnumItem in enum:GetEnumItems() do
if (flags :: any)[item.Name] then if (flags :: any)[item.Name] then
value += (2 ^ item.Value) value += (2 ^ item.Value)
end end

View File

@ -8,49 +8,45 @@ local classes = {}
local outStream = "" local outStream = ""
local stackLevel = 0 local stackLevel = 0
local singletons = local singletons = {
{ Speaker = Instance.new("Sound"), -- close enough
Speaker = Instance.new("Sound"); -- close enough Terrain = workspace:WaitForChild("Terrain", 1000),
Terrain = workspace:WaitForChild("Terrain", 1000); ParabolaAdornment = Instance.new("BoxHandleAdornment"), -- close enough
ParabolaAdornment = Instance.new("BoxHandleAdornment"); -- close enough StarterPlayerScripts = StarterPlayer:WaitForChild("StarterPlayerScripts"),
StarterPlayerScripts = StarterPlayer:WaitForChild("StarterPlayerScripts"); StarterCharacterScripts = StarterPlayer:WaitForChild("StarterCharacterScripts"),
StarterCharacterScripts = StarterPlayer:WaitForChild("StarterCharacterScripts"); ChatWindowConfiguration = TextChatService:WaitForChild("ChatWindowConfiguration", 10),
ChatWindowConfiguration = TextChatService:WaitForChild("ChatWindowConfiguration", 10); ChatInputBarConfiguration = TextChatService:WaitForChild("ChatInputBarConfiguration", 10),
ChatInputBarConfiguration = TextChatService:WaitForChild("ChatInputBarConfiguration", 10);
} }
local exceptionClasses = local exceptionClasses = {
{ PackageLink = true,
PackageLink = true; ScriptDebugger = true,
ScriptDebugger = true; ChatWindowConfiguration = true,
ChatWindowConfiguration = true; ChatInputBarConfiguration = true,
ChatInputBarConfiguration = true;
} }
local numberTypes = local numberTypes = {
{ int = true,
int = true; long = true,
long = true; int64 = true,
int64 = true; float = true,
float = true; double = true,
double = true;
} }
local stringTypes = local stringTypes = {
{ string = true,
string = true; Content = true,
Content = true; BinaryString = true,
BinaryString = true; ProtectedString = true,
ProtectedString = true;
} }
local isCoreScript = pcall(function () local isCoreScript = pcall(function()
local restricted = game:GetService("RobloxPluginGuiService") local restricted = game:GetService("RobloxPluginGuiService")
return tostring(restricted) return tostring(restricted)
end) end)
local function write(formatString, ...) local function write(formatString, ...)
local tabs = string.rep(' ', stackLevel * 4) local tabs = string.rep(" ", stackLevel * 4)
local fmt = formatString or "" local fmt = formatString or ""
local value = tabs .. fmt:format(...) local value = tabs .. fmt:format(...)
@ -59,21 +55,21 @@ end
local function writeLine(formatString, ...) local function writeLine(formatString, ...)
if not formatString then if not formatString then
outStream = outStream .. '\n' outStream = outStream .. "\n"
return return
end end
write(formatString .. '\n', ...) write(formatString .. "\n", ...)
end end
local function openStack() local function openStack()
writeLine('{') writeLine("{")
stackLevel += 1 stackLevel += 1
end end
local function closeStack() local function closeStack()
stackLevel -= 1 stackLevel -= 1
writeLine('}') writeLine("}")
end end
local function clearStream() local function clearStream()
@ -92,7 +88,7 @@ local function exportStream(label)
export.Name = label export.Name = label
export.Parent = workspace export.Parent = workspace
Selection:Add{export} Selection:Add({ export })
end end
if isCoreScript then if isCoreScript then
@ -110,7 +106,7 @@ local function getTags(object)
local tags = {} local tags = {}
if object.Tags ~= nil then if object.Tags ~= nil then
for _,tag in pairs(object.Tags) do for _, tag in pairs(object.Tags) do
tags[tag] = true tags[tag] = true
end end
end end
@ -166,7 +162,7 @@ end
local function collectProperties(class) local function collectProperties(class)
local propMap = {} local propMap = {}
for _,member in ipairs(class.Members) do for _, member in ipairs(class.Members) do
if member.MemberType == "Property" then if member.MemberType == "Property" then
local propName = member.Name local propName = member.Name
propMap[propName] = member propMap[propName] = member
@ -177,32 +173,30 @@ local function collectProperties(class)
end end
local function createProperty(propName, propType) local function createProperty(propName, propType)
local category = "DataType"; local category = "DataType"
local name = propType local name = propType
if propType:find(':') then if propType:find(":") then
local data = string.split(propType, ':') local data = string.split(propType, ":")
category = data[1] category = data[1]
name = data[2] name = data[2]
end end
return return
{ {
Name = propName; Name = propName,
Serialization = Serialization = {
{ CanSave = true,
CanSave = true; CanLoad = true,
CanLoad = true; },
};
ValueType = ValueType = {
{ Category = category,
Category = category; Name = name,
Name = name; },
};
Security = "None"; Security = "None",
} }
end end
@ -214,21 +208,20 @@ local formatting: Format = require(script.Formatting)
type FormatFunc = formatting.FormatFunc type FormatFunc = formatting.FormatFunc
type Format = formatting.Format type Format = formatting.Format
local formatLinks = local formatLinks = {
{ ["int"] = "Int",
["int"] = "Int"; ["long"] = "Int",
["long"] = "Int";
["float"] = "Float"; ["float"] = "Float",
["byte[]"] = "Bytes"; ["byte[]"] = "Bytes",
["double"] = "Double"; ["double"] = "Double",
["boolean"] = "Bool"; ["boolean"] = "Bool",
["string"] = "String"; ["string"] = "String",
["Content"] = "String"; ["Content"] = "String",
["Color3uint8"] = "Color3"; ["Color3uint8"] = "Color3",
["ProtectedString"] = "String"; ["ProtectedString"] = "String",
} }
local function getFormatFunction(valueType: string): FormatFunc local function getFormatFunction(valueType: string): FormatFunc
@ -307,15 +300,14 @@ local function generateClasses()
local classNames = {} local classNames = {}
classes = {} classes = {}
local enumMap = local enumMap = {
{ Axis = true,
Axis = true; FontSize = true,
FontSize = true; FontStyle = true,
FontStyle = true; FontWeight = true,
FontWeight = true;
} }
for _,class in ipairs(apiDump.Classes) do for _, class in ipairs(apiDump.Classes) do
local className = class.Name local className = class.Name
local superClass = classes[class.Superclass] local superClass = classes[class.Superclass]
@ -328,13 +320,13 @@ local function generateClasses()
local classTags = getTags(class) local classTags = getTags(class)
if classTags.Service then if classTags.Service then
pcall(function () pcall(function()
if not className:find("Network") then if not className:find("Network") then
class.Object = game:GetService(className) class.Object = game:GetService(className)
end end
end) end)
elseif not classTags.NotCreatable then elseif not classTags.NotCreatable then
pcall(function () pcall(function()
local dumpFolder = game:FindFirstChild("DumpFolder") local dumpFolder = game:FindFirstChild("DumpFolder")
class.Object = Instance.new(className) class.Object = Instance.new(className)
@ -393,7 +385,7 @@ local function generateClasses()
registerClass = false registerClass = false
end end
local noSecurityCheck = pcall(function () local noSecurityCheck = pcall(function()
if not classTags.Service then if not classTags.Service then
return tostring(object) return tostring(object)
end end
@ -435,7 +427,7 @@ local function generateClasses()
local propMap = collectProperties(class) local propMap = collectProperties(class)
local propNames = {} local propNames = {}
for _,propName in pairs(classPatches.Remove) do for _, propName in pairs(classPatches.Remove) do
propMap[propName] = nil propMap[propName] = nil
end end
@ -472,9 +464,7 @@ local function generateClasses()
local inheritProps = ancestor.PropertyMap local inheritProps = ancestor.PropertyMap
local inherited = ancestor.Inherited local inherited = ancestor.Inherited
local baseObject = if inherited local baseObject = if inherited then inherited.Object else nil
then inherited.Object
else nil
if inheritProps and baseObject then if inheritProps and baseObject then
for name, prop in pairs(inheritProps) do for name, prop in pairs(inheritProps) do
@ -484,11 +474,11 @@ local function generateClasses()
continue continue
end end
local gotPropValue, propValue = pcall(function () local gotPropValue, propValue = pcall(function()
return object[name] return object[name]
end) end)
local gotBaseValue, baseValue = pcall(function () local gotBaseValue, baseValue = pcall(function()
return baseObject[name] return baseObject[name]
end) end)
@ -572,7 +562,7 @@ local function generateClasses()
local default = "" local default = ""
if propName == className then if propName == className then
name = name .. '_' name = name .. "_"
end end
if valueType == "int64" then if valueType == "int64" then
@ -626,7 +616,7 @@ local function generateClasses()
openStack() openStack()
writeLine("get => %s;", get) writeLine("get => %s;", get)
if set:find('\n') then if set:find("\n") then
writeLine() writeLine()
writeLine("set") writeLine("set")
@ -654,7 +644,7 @@ local function generateClasses()
local gotValue = (value ~= nil) local gotValue = (value ~= nil)
if not gotValue then if not gotValue then
gotValue, value = pcall(function () gotValue, value = pcall(function()
return object[propName] return object[propName]
end) end)
end end
@ -675,7 +665,7 @@ local function generateClasses()
local DataType = env[valueType] local DataType = env[valueType]
if DataType and typeof(DataType) == "table" and not rawget(env, valueType) then if DataType and typeof(DataType) == "table" and not rawget(env, valueType) then
pcall(function () pcall(function()
value = DataType.new() value = DataType.new()
gotValue = true gotValue = true
end) end)
@ -685,7 +675,7 @@ local function generateClasses()
local lowestId = math.huge local lowestId = math.huge
local lowest local lowest
for _,item in pairs(enum:GetEnumItems()) do for _, item in pairs(enum:GetEnumItems()) do
local itemValue = item.Value local itemValue = item.Value
if itemValue < lowestId then if itemValue < lowestId then
@ -706,7 +696,13 @@ local function generateClasses()
if gotValue then if gotValue then
warn(src, "Fell back to implicit value for property:", id) warn(src, "Fell back to implicit value for property:", id)
else else
warn(src, "!! Could not figure out default value for property:", id, "value error was:", value) warn(
src,
"!! Could not figure out default value for property:",
id,
"value error was:",
value
)
end end
end end
@ -769,7 +765,7 @@ local function generateClasses()
closeStack() closeStack()
if (i ~= #classNames) then if i ~= #classNames then
writeLine() writeLine()
end end
end end
@ -808,20 +804,20 @@ local function generateEnums(whiteList)
local lastValue = -1 local lastValue = -1
local mapped = {} local mapped = {}
table.sort(enumItems, function (a, b) table.sort(enumItems, function(a, b)
return a.Value < b.Value return a.Value < b.Value
end) end)
for j, enumItem in ipairs(enumItems) do for j, enumItem in ipairs(enumItems) do
local text = "" local text = ""
local comma = ',' local comma = ","
local name = enumItem.Name local name = enumItem.Name
local value = enumItem.Value local value = enumItem.Value
if not mapped[value] then if not mapped[value] then
if (value - lastValue) ~= 1 then if (value - lastValue) ~= 1 then
text = " = " .. value; text = " = " .. value
end end
if j == #enumItems then if j == #enumItems then

1
Plugins/sourcemap.json Normal file
View File

@ -0,0 +1 @@
{"name":"GenerateApiDump","className":"Script","filePaths":["GenerateApiDump\\init.server.lua","default.project.json"],"children":[{"name":"Formatting","className":"ModuleScript","filePaths":["GenerateApiDump\\Formatting.lua"]},{"name":"PropertyPatches","className":"ModuleScript","filePaths":["GenerateApiDump\\PropertyPatches.lua"]}]}

Binary file not shown.

View File

@ -66,9 +66,9 @@ namespace RobloxFiles.Tokens
var style = (FontWeight)attribute.ReadByte(); var style = (FontWeight)attribute.ReadByte();
var family = attribute.ReadString(); var family = attribute.ReadString();
_ = attribute.ReadInt(); // Reserved var cachedFaceId = attribute.ReadString();
return new FontFace(family, style, weight); return new FontFace(family, style, weight, cachedFaceId);
} }
public void WriteAttribute(RbxAttribute attribute, FontFace value) public void WriteAttribute(RbxAttribute attribute, FontFace value)

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Linq;
using System.Xml; using System.Xml;
using RobloxFiles.DataTypes; using RobloxFiles.DataTypes;
@ -14,13 +15,21 @@ namespace RobloxFiles.Tokens
if (Guid.TryParse(hex, out var guid)) if (Guid.TryParse(hex, out var guid))
{ {
var bytes = guid.ToByteArray(); var bytes = new byte[16];
var random = BitConverter.ToUInt64(bytes, 0);
var time = BitConverter.ToUInt32(bytes, 8); for (int i = 0; i < 16; i++)
var index = BitConverter.ToUInt32(bytes, 12); {
var hexChar = hex.Substring(i * 2, 2);
bytes[15 - i] = Convert.ToByte(hexChar, 16);
}
var rand = BitConverter.ToInt64(bytes, 8);
var time = BitConverter.ToUInt32(bytes, 4);
var index = BitConverter.ToUInt32(bytes, 0);
var uniqueId = new UniqueId(rand, time, index);
prop.Value = uniqueId;
prop.Value = new UniqueId(time, index, random);
return true; return true;
} }
@ -30,8 +39,8 @@ namespace RobloxFiles.Tokens
public void WriteProperty(Property prop, XmlDocument doc, XmlNode node) public void WriteProperty(Property prop, XmlDocument doc, XmlNode node)
{ {
var uniqueId = prop.CastValue<UniqueId>(); var uniqueId = prop.CastValue<UniqueId>();
var random = BitConverter.GetBytes(uniqueId.Random);
var random = BitConverter.GetBytes(uniqueId.Random);
var time = BitConverter.GetBytes(uniqueId.Time); var time = BitConverter.GetBytes(uniqueId.Time);
var index = BitConverter.GetBytes(uniqueId.Index); var index = BitConverter.GetBytes(uniqueId.Index);

View File

@ -59,7 +59,7 @@ namespace RobloxFiles
internal static BindingFlags BindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy | BindingFlags.IgnoreCase; internal static BindingFlags BindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy | BindingFlags.IgnoreCase;
// TODO: Map typeof(ProtectedString) to PropertyType.ProtectedString // TODO: Map typeof(ProtectedString) to PropertyType.ProtectedString
// if binary files are ever publically allowed to read it. // if binary files are ever publicly allowed to read it.
public static readonly IReadOnlyDictionary<Type, PropertyType> Types = new Dictionary<Type, PropertyType>() public static readonly IReadOnlyDictionary<Type, PropertyType> Types = new Dictionary<Type, PropertyType>()
{ {

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,8 @@
using System.Collections.Generic; using System.Linq;
using System.Linq; using System.Collections.Generic;
using RobloxFiles.Enums; using RobloxFiles.Enums;
using RobloxFiles.DataTypes;
namespace RobloxFiles.Utility namespace RobloxFiles.Utility
{ {
@ -26,6 +27,56 @@ namespace RobloxFiles.Utility
{ 96, FontSize.Size96 }, { 96, FontSize.Size96 },
}; };
public static readonly IReadOnlyDictionary<Font, FontFace> FontFaces = new Dictionary<Font, FontFace>()
{
{ Font.Legacy, new FontFace("rbxasset://fonts/families/LegacyArial.json") },
{ Font.Arial, new FontFace("rbxasset://fonts/families/Arial.json") },
{ Font.ArialBold, new FontFace("rbxasset://fonts/families/Arial.json", FontWeight.Bold) },
{ Font.SourceSans, new FontFace("rbxasset://fonts/families/SourceSansPro.json") },
{ Font.SourceSansBold, new FontFace("rbxasset://fonts/families/SourceSansPro.json", FontWeight.Bold) },
{ Font.SourceSansSemibold, new FontFace("rbxasset://fonts/families/SourceSansPro.json", FontWeight.SemiBold) },
{ Font.SourceSansLight, new FontFace("rbxasset://fonts/families/SourceSansPro.json", FontWeight.Light) },
{ Font.SourceSansItalic, new FontFace("rbxasset://fonts/families/SourceSansPro.json", FontWeight.Regular, FontStyle.Italic) },
{ Font.Bodoni, new FontFace("rbxasset://fonts/families/AccanthisADFStd.json") },
{ Font.Garamond, new FontFace("rbxasset://fonts/families/Guru.json") },
{ Font.Cartoon, new FontFace("rbxasset://fonts/families/ComicNeueAngular.json") },
{ Font.Code, new FontFace("rbxasset://fonts/families/Inconsolata.json") },
{ Font.Highway, new FontFace("rbxasset://fonts/families/HighwayGothic.json") },
{ Font.SciFi, new FontFace("rbxasset://fonts/families/Zekton.json") },
{ Font.Arcade, new FontFace("rbxasset://fonts/families/PressStart2P.json") },
{ Font.Fantasy, new FontFace("rbxasset://fonts/families/Balthazar.json") },
{ Font.Antique, new FontFace("rbxasset://fonts/families/RomanAntique.json") },
{ Font.Gotham, new FontFace("rbxasset://fonts/families/GothamSSm.json") },
{ Font.GothamMedium, new FontFace("rbxasset://fonts/families/GothamSSm.json", FontWeight.Medium) },
{ Font.GothamBold, new FontFace("rbxasset://fonts/families/GothamSSm.json", FontWeight.Bold) },
{ Font.GothamBlack, new FontFace("rbxasset://fonts/families/GothamSSm.json", FontWeight.Heavy) },
{ Font.AmaticSC, new FontFace("rbxasset://fonts/families/AmaticSC.json") },
{ Font.Bangers, new FontFace("rbxasset://fonts/families/Bangers.json") },
{ Font.Creepster, new FontFace("rbxasset://fonts/families/Creepster.json") },
{ Font.DenkOne, new FontFace("rbxasset://fonts/families/DenkOne.json") },
{ Font.Fondamento, new FontFace("rbxasset://fonts/families/Fondamento.json") },
{ Font.FredokaOne, new FontFace("rbxasset://fonts/families/FredokaOne.json") },
{ Font.GrenzeGotisch, new FontFace("rbxasset://fonts/families/GrenzeGotisch.json") },
{ Font.IndieFlower, new FontFace("rbxasset://fonts/families/IndieFlower.json") },
{ Font.JosefinSans, new FontFace("rbxasset://fonts/families/JosefinSans.json") },
{ Font.Jura, new FontFace("rbxasset://fonts/families/Jura.json") },
{ Font.Kalam, new FontFace("rbxasset://fonts/families/Kalam.json") },
{ Font.LuckiestGuy, new FontFace("rbxasset://fonts/families/LuckiestGuy.json") },
{ Font.Merriweather, new FontFace("rbxasset://fonts/families/Merriweather.json") },
{ Font.Michroma, new FontFace("rbxasset://fonts/families/Michroma.json") },
{ Font.Nunito, new FontFace("rbxasset://fonts/families/Nunito.json") },
{ Font.Oswald, new FontFace("rbxasset://fonts/families/Oswald.json") },
{ Font.PatrickHand, new FontFace("rbxasset://fonts/families/PatrickHand.json") },
{ Font.PermanentMarker, new FontFace("rbxasset://fonts/families/PermanentMarker.json") },
{ Font.Roboto, new FontFace("rbxasset://fonts/families/Roboto.json") },
{ Font.RobotoCondensed, new FontFace("rbxasset://fonts/families/RobotoCondensed.json") },
{ Font.RobotoMono, new FontFace("rbxasset://fonts/families/RobotoMono.json") },
{ Font.Sarpanch, new FontFace("rbxasset://fonts/families/Sarpanch.json") },
{ Font.SpecialElite, new FontFace("rbxasset://fonts/families/SpecialElite.json") },
{ Font.TitilliumWeb, new FontFace("rbxasset://fonts/families/TitilliumWeb.json") },
{ Font.Ubuntu, new FontFace("rbxasset://fonts/families/Ubuntu.json") },
};
public static FontSize GetFontSize(int fontSize) public static FontSize GetFontSize(int fontSize)
{ {
if (fontSize > 60) if (fontSize > 60)
@ -57,5 +108,19 @@ namespace RobloxFiles.Utility
return value; return value;
} }
public static Font GetFont(FontFace face)
{
var result = Font.Unknown;
var faceQuery = FontFaces
.Where(pair => face.Equals(pair.Value))
.Select(pair => pair.Key);
if (faceQuery.Any())
result = faceQuery.First();
return result;
}
} }
} }