e14b092aa7
This isn't 100% finished yet. I intend to add some better API for reading specific attributes, as well as write support (of course!)
253 lines
7.4 KiB
C#
253 lines
7.4 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
|
|
using RobloxFiles.DataTypes;
|
|
|
|
namespace RobloxFiles
|
|
{
|
|
public enum AttributeType
|
|
{
|
|
Null = 1,
|
|
String,
|
|
Bool,
|
|
Int,
|
|
Float,
|
|
Double,
|
|
Array,
|
|
Dictionary,
|
|
UDim,
|
|
UDim2,
|
|
Ray,
|
|
Faces,
|
|
Axes,
|
|
BrickColor,
|
|
Color3,
|
|
Vector2,
|
|
Vector3,
|
|
Vector2int16,
|
|
Vector3int16,
|
|
CFrame,
|
|
Enum,
|
|
NumberSequence = 23,
|
|
NumberSequenceKeypoint,
|
|
ColorSequence,
|
|
ColorSequenceKeypoint,
|
|
NumberRange,
|
|
Rect,
|
|
PhysicalProperties,
|
|
Region3 = 31,
|
|
Region3int16,
|
|
}
|
|
|
|
public class Attribute
|
|
{
|
|
public AttributeType DataType { get; private set; }
|
|
public object Value { get; private set; }
|
|
|
|
public override string ToString()
|
|
{
|
|
string type = Enum.GetName(typeof(AttributeType), DataType);
|
|
string value = Value?.ToString() ?? "null";
|
|
return $"[{type}: {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);
|
|
|
|
private Attribute[] readArray()
|
|
{
|
|
int count = readInt();
|
|
var result = new Attribute[count];
|
|
|
|
for (int i = 0; i < count; i++)
|
|
result[i] = new Attribute(reader);
|
|
|
|
return result;
|
|
}
|
|
|
|
private object readEnum()
|
|
{
|
|
string name = readString();
|
|
int value = readInt();
|
|
|
|
try
|
|
{
|
|
Type enumType = Type.GetType($"RobloxFiles.Enums.{name}");
|
|
return Enum.ToObject(enumType, value);
|
|
}
|
|
catch
|
|
{
|
|
Console.WriteLine($"RobloxFile - Got unknown Enum {name} in Attribute.");
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private void readData()
|
|
{
|
|
if (reader == null)
|
|
return;
|
|
|
|
DataType = (AttributeType)reader.ReadByte();
|
|
|
|
switch (DataType)
|
|
{
|
|
//////////////////////////
|
|
case AttributeType.Null:
|
|
break;
|
|
case AttributeType.String:
|
|
Value = readString();
|
|
break;
|
|
case AttributeType.Bool:
|
|
Value = readBool();
|
|
break;
|
|
case AttributeType.Int:
|
|
Value = readInt();
|
|
break;
|
|
case AttributeType.Float:
|
|
Value = readFloat();
|
|
break;
|
|
case AttributeType.Double:
|
|
Value = readDouble();
|
|
break;
|
|
case AttributeType.Array:
|
|
Value = readArray();
|
|
break;
|
|
case AttributeType.Dictionary:
|
|
Value = new Attributes(reader);
|
|
break;
|
|
case AttributeType.UDim:
|
|
Value = new UDim(this);
|
|
break;
|
|
case AttributeType.UDim2:
|
|
Value = new UDim2(this);
|
|
break;
|
|
case AttributeType.Ray:
|
|
Value = new Ray(this);
|
|
break;
|
|
case AttributeType.Faces:
|
|
Value = (Faces)readInt();
|
|
break;
|
|
case AttributeType.Axes:
|
|
Value = (Axes)readInt();
|
|
break;
|
|
case AttributeType.BrickColor:
|
|
Value = (BrickColor)readInt();
|
|
break;
|
|
case AttributeType.Color3:
|
|
Value = new Color3(this);
|
|
break;
|
|
case AttributeType.Vector2:
|
|
Value = new Vector2(this);
|
|
break;
|
|
case AttributeType.Vector3:
|
|
Value = new Vector3(this);
|
|
break;
|
|
case AttributeType.Vector2int16:
|
|
Value = new Vector2int16(this);
|
|
break;
|
|
case AttributeType.Vector3int16:
|
|
Value = new Vector3int16(this);
|
|
break;
|
|
case AttributeType.CFrame:
|
|
Value = new CFrame(this);
|
|
break;
|
|
case AttributeType.Enum:
|
|
Value = readEnum();
|
|
break;
|
|
case AttributeType.NumberSequence:
|
|
Value = new NumberSequence(this);
|
|
break;
|
|
case AttributeType.NumberSequenceKeypoint:
|
|
Value = new NumberSequenceKeypoint(this);
|
|
break;
|
|
case AttributeType.ColorSequence:
|
|
Value = new ColorSequence(this);
|
|
break;
|
|
case AttributeType.ColorSequenceKeypoint:
|
|
Value = new ColorSequenceKeypoint(this);
|
|
break;
|
|
case AttributeType.NumberRange:
|
|
Value = new NumberRange(this);
|
|
break;
|
|
case AttributeType.Rect:
|
|
Value = new Rect(this);
|
|
break;
|
|
case AttributeType.PhysicalProperties:
|
|
bool custom = readBool();
|
|
|
|
if (custom)
|
|
Value = new PhysicalProperties(this);
|
|
|
|
break;
|
|
case AttributeType.Region3:
|
|
Value = new Region3(this);
|
|
break;
|
|
case AttributeType.Region3int16:
|
|
Value = new Region3int16(this);
|
|
break;
|
|
default:
|
|
throw new InvalidDataException($"Cannot handle AttributeType {DataType}!");
|
|
//////////////////////////
|
|
}
|
|
|
|
reader = null;
|
|
}
|
|
|
|
internal Attribute(BinaryReader reader)
|
|
{
|
|
this.reader = reader;
|
|
readData();
|
|
}
|
|
|
|
internal Attribute(MemoryStream stream)
|
|
{
|
|
reader = new BinaryReader(stream);
|
|
readData();
|
|
}
|
|
}
|
|
|
|
public class Attributes : Dictionary<string, Attribute>
|
|
{
|
|
private void initialize(BinaryReader reader)
|
|
{
|
|
int numEntries = reader.ReadInt32();
|
|
|
|
for (int i = 0; i < numEntries; i++)
|
|
{
|
|
string key = reader.ReadString(true);
|
|
var attribute = new Attribute(reader);
|
|
Add(key, attribute);
|
|
}
|
|
}
|
|
|
|
internal Attributes(BinaryReader reader)
|
|
{
|
|
initialize(reader);
|
|
}
|
|
|
|
internal Attributes(MemoryStream stream)
|
|
{
|
|
using (BinaryReader reader = new BinaryReader(stream))
|
|
{
|
|
initialize(reader);
|
|
}
|
|
}
|
|
|
|
internal byte[] Serialize()
|
|
{
|
|
// TODO
|
|
return new byte[0];
|
|
}
|
|
}
|
|
}
|