Roblox-File-Format/Tree/Attributes.cs

270 lines
7.9 KiB
C#
Raw Normal View History

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
2021-02-25 20:09:54 +00:00
using System.Reflection;
using System.Text;
namespace RobloxFiles
{
2021-02-25 20:09:54 +00:00
// This enum defines existing attributes
// Commented out values are known types
// which are unsupported at this time.
public enum AttributeType
{
2021-05-01 22:40:09 +00:00
// Null = 1,
2021-02-25 20:09:54 +00:00
String = 2,
Bool = 3,
2021-05-01 22:40:09 +00:00
// Int = 4,
2021-02-25 20:09:54 +00:00
Float = 5,
Double = 6,
2021-05-01 22:40:09 +00:00
// Array = 7,
// Dictionary = 8,
2021-02-25 20:09:54 +00:00
UDim = 9,
UDim2 = 10,
2021-05-01 22:40:09 +00:00
// Ray = 11,
// Faces = 12,
// Axes = 13
2021-02-25 20:09:54 +00:00
BrickColor = 14,
Color3 = 15,
Vector2 = 16,
Vector3 = 17,
2021-05-01 22:40:09 +00:00
// Vector2int16 = 18,
// Vector3int16 = 19,
2022-12-27 12:36:50 +00:00
CFrame = 20,
2021-05-01 22:40:09 +00:00
// Enum = 21,
NumberSequence = 23,
2021-05-01 22:40:09 +00:00
// NumberSequenceKeypoint = 24,
2021-02-25 20:09:54 +00:00
ColorSequence = 25,
2021-05-01 22:40:09 +00:00
// ColorSequenceKeypoint = 26,
2021-02-25 20:09:54 +00:00
NumberRange = 27,
Rect = 28,
2021-05-01 22:40:09 +00:00
// PhysicalProperties = 29
// Region3 = 31,
// Region3int16 = 32,
FontFace = 33
}
2021-06-05 22:21:12 +00:00
public class RbxAttribute : IDisposable
{
2021-02-25 20:09:54 +00:00
private static readonly IReadOnlyDictionary<AttributeType, Tokenizer> AttributeSupport;
private static readonly IReadOnlyDictionary<Type, AttributeType> SupportedTypes;
public AttributeType DataType { get; private set; }
public object Value { get; private set; }
2021-02-25 20:09:54 +00:00
private struct Tokenizer
{
2021-02-25 20:09:54 +00:00
public readonly Type Support;
public readonly object Token;
2021-02-25 20:09:54 +00:00
public readonly MethodInfo Reader;
public readonly MethodInfo Writer;
2021-02-25 20:09:54 +00:00
public Tokenizer(Type tokenType, Type support)
{
Support = support;
Token = Activator.CreateInstance(tokenType);
2021-02-25 20:09:54 +00:00
Reader = support.GetMethod("ReadAttribute");
Writer = support.GetMethod("WriteAttribute");
}
2021-06-05 22:21:12 +00:00
public object ReadAttribute(RbxAttribute attr)
2021-02-25 20:09:54 +00:00
{
var args = new object[1] { attr };
return Reader.Invoke(Token, args);
}
2021-06-05 22:21:12 +00:00
public void WriteAttribute(RbxAttribute attr, object value)
2021-02-25 20:09:54 +00:00
{
var args = new object[2] { attr, value };
Writer.Invoke(Token, args);
}
}
2021-06-05 22:21:12 +00:00
static RbxAttribute()
{
2021-02-25 20:09:54 +00:00
var attributeSupport = new Dictionary<AttributeType, Tokenizer>();
var supportedTypes = new Dictionary<Type, AttributeType>();
var assembly = Assembly.GetExecutingAssembly();
var handlerTypes =
from type in assembly.GetTypes()
let typeInfo = type.GetTypeInfo()
let support = typeInfo.GetInterface("IAttributeToken`1")
where (support != null)
select new Tokenizer(typeInfo, support);
foreach (var tokenizer in handlerTypes)
{
2021-02-25 20:09:54 +00:00
var token = tokenizer.Token;
var support = tokenizer.Support;
var genericType = support.GenericTypeArguments.FirstOrDefault();
var getAttributeType = support.GetMethod("get_AttributeType");
var attributeType = (AttributeType)getAttributeType.Invoke(token, null);
2020-09-13 01:16:19 +00:00
2021-02-25 20:09:54 +00:00
attributeSupport.Add(attributeType, tokenizer);
supportedTypes.Add(genericType, attributeType);
}
2021-02-25 20:09:54 +00:00
AttributeSupport = attributeSupport;
SupportedTypes = supportedTypes;
}
/// <summary>
/// Returns true if the provided type is supported by attributes.
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static bool SupportsType(Type type)
{
return SupportedTypes.ContainsKey(type);
}
/// <summary>
/// Returns true if the provided type is supported by attributes.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static bool SupportsType<T>()
{
Type type = typeof(T);
return SupportsType(type);
}
2021-02-25 20:09:54 +00:00
public override string ToString()
{
2021-02-25 20:09:54 +00:00
string value = Value?.ToString() ?? "null";
return $"[{DataType}: {value}]";
}
internal BinaryReader Reader;
internal BinaryWriter Writer;
internal int ReadInt() => Reader.ReadInt32();
internal byte ReadByte() => Reader.ReadByte();
internal bool ReadBool() => Reader.ReadBoolean();
internal short ReadShort() => Reader.ReadInt16();
internal float ReadFloat() => Reader.ReadSingle();
internal double ReadDouble() => Reader.ReadDouble();
internal string ReadString() => Reader.ReadString(true);
internal void WriteInt(int value) => Writer.Write(value);
2022-12-27 12:36:50 +00:00
internal void WriteByte(byte value) => Writer.Write(value);
2021-02-25 20:09:54 +00:00
internal void WriteBool(bool value) => Writer.Write(value);
internal void WriteFloat(float value) => Writer.Write(value);
internal void WriteDouble(double value) => Writer.Write(value);
internal void WriteString(string value)
{
byte[] utf8 = Encoding.UTF8.GetBytes(value);
2021-05-05 00:45:00 +00:00
Writer.Write(utf8.Length);
2021-02-25 20:09:54 +00:00
Writer.Write(utf8);
}
internal void Read()
{
if (Reader == null)
return;
2021-02-25 20:09:54 +00:00
var dataType = Reader.ReadByte();
DataType = (AttributeType)dataType;
2021-02-25 20:09:54 +00:00
var tokenizer = AttributeSupport[DataType];
Value = tokenizer.ReadAttribute(this);
2021-02-25 20:09:54 +00:00
Reader = null;
}
2020-08-17 05:33:59 +00:00
public void Dispose()
{
2021-02-25 20:09:54 +00:00
Reader?.Dispose();
}
internal void Write(BinaryWriter writer)
{
var tokenizer = AttributeSupport[DataType];
Writer = writer;
writer.Write((byte)DataType);
tokenizer.WriteAttribute(this, Value);
Writer = null;
2020-08-17 05:33:59 +00:00
}
2021-06-05 22:21:12 +00:00
internal RbxAttribute(BinaryReader reader)
{
2021-02-25 20:09:54 +00:00
Reader = reader;
Read();
}
2021-06-05 22:21:12 +00:00
internal RbxAttribute(MemoryStream stream)
{
2021-02-25 20:09:54 +00:00
Reader = new BinaryReader(stream);
Read();
}
2021-06-05 22:21:12 +00:00
internal RbxAttribute(object value)
2021-02-25 20:09:54 +00:00
{
Type type = value.GetType();
if (SupportedTypes.TryGetValue(type, out AttributeType dataType))
{
DataType = dataType;
Value = value;
}
}
}
2021-06-05 22:21:12 +00:00
public class RbxAttributes : SortedDictionary<string, RbxAttribute>
{
2021-06-05 22:21:12 +00:00
internal void Load(byte[] buffer)
{
2021-06-05 22:21:12 +00:00
Clear();
2019-11-04 21:25:22 +00:00
2021-06-05 22:21:12 +00:00
if (buffer == null || buffer.Length < 4)
2019-11-04 21:25:22 +00:00
// Not enough room to read the entry count, possibly empty?
return;
2021-06-05 22:21:12 +00:00
using (var input = new MemoryStream(buffer))
using (var reader = new BinaryReader(input))
{
2021-06-05 22:21:12 +00:00
int numEntries = reader.ReadInt32();
2021-02-25 20:09:54 +00:00
2021-06-05 22:21:12 +00:00
for (int i = 0; i < numEntries; i++)
{
string key = reader.ReadString(true);
var attribute = new RbxAttribute(reader);
Add(key, attribute);
}
}
}
2021-06-05 22:21:12 +00:00
internal byte[] Save()
{
2021-02-25 20:09:54 +00:00
if (Count == 0)
return Array.Empty<byte>();
using (var output = new MemoryStream())
using (var writer = new BinaryWriter(output))
{
writer.Write(Count);
foreach (string key in Keys)
{
var attribute = this[key];
attribute.Writer = writer;
attribute.WriteString(key);
attribute.Write(writer);
}
return output.ToArray();
}
}
}
}