Skip to content

Instantly share code, notes, and snippets.

@Anaminus
Created November 10, 2022 02:43
Show Gist options
  • Save Anaminus/efddd8ae175e58f202fe49b0430e839b to your computer and use it in GitHub Desktop.
Save Anaminus/efddd8ae175e58f202fe49b0430e839b to your computer and use it in GitHub Desktop.
ImHex pattern for the RBXL format.
// ImHex pattern for the RBXL format.
//
// Not currently supported:
// - Chunk decompression (use rbxfile-dcomp to decompress chunks)
// - Deinterleaving
// - Property value decoding
// - Zigzag decoding
// - Circular shift decoding
#pragma endian little
#pragma bitfield_order left_to_right
#include <std/io.pat>
////////
// Utility functions
fn printf(str format, auto ... args) {
return std::print(std::format("rbxl: " + format, args));
};
fn warnf(str format, auto ... args) {
return std::warning(std::format("rbxl: " + format, args));
};
fn errorf(str format, auto ... args) {
return std::error(std::format(format, args));
};
////////
// Primitive types
struct string {
u32 Length;
char Content[Length];
} [[sealed, format("format_string")]];
struct bstring {
u32 Length;
u8 Content[Length];
} [[sealed, format("format_string")]];
fn format_string(auto s) {
return s.Content;
};
union References<auto L> {
be s32 _[L] [[inline, comment("zigzag, interleave 4")]];
};
////////
// Property types
#define TypeInvalid 0x00
#define TypeString 0x01
#define TypeBool 0x02
#define TypeInt 0x03
#define TypeFloat 0x04
#define TypeDouble 0x05
#define TypeUDim 0x06
#define TypeUDim2 0x07
#define TypeRay 0x08
#define TypeFaces 0x09
#define TypeAxes 0x0A
#define TypeBrickColor 0x0B
#define TypeColor3 0x0C
#define TypeVector2 0x0D
#define TypeVector3 0x0E
#define TypeVector2int16 0x0F
#define TypeCFrame 0x10
#define TypeCFrameQuat 0x11
#define TypeToken 0x12
#define TypeReference 0x13
#define TypeVector3int16 0x14
#define TypeNumberSequence 0x15
#define TypeColorSequence 0x16
#define TypeNumberRange 0x17
#define TypeRect 0x18
#define TypePhysicalProperties 0x19
#define TypeColor3uint8 0x1A
#define TypeInt64 0x1B
#define TypeSharedString 0x1C
#define TypeBytecode 0x1D
#define TypeOptional 0x1E
#define TypeUniqueId 0x1F
#define TypeFont 0x20
enum Type : u8 {
Invalid = TypeInvalid,
String = TypeString,
Bool = TypeBool,
Int = TypeInt,
Float = TypeFloat,
Double = TypeDouble,
UDim = TypeUDim,
UDim2 = TypeUDim2,
Ray = TypeRay,
Faces = TypeFaces,
Axes = TypeAxes,
BrickColor = TypeBrickColor,
Color3 = TypeColor3,
Vector2 = TypeVector2,
Vector3 = TypeVector3,
Vector2int16 = TypeVector2int16,
CFrame = TypeCFrame,
CFrameQuat = TypeCFrameQuat,
Token = TypeToken,
Reference = TypeReference,
Vector3int16 = TypeVector3int16,
NumberSequence = TypeNumberSequence,
ColorSequence = TypeColorSequence,
NumberRange = TypeNumberRange,
Rect = TypeRect,
PhysicalProperties = TypePhysicalProperties,
Color3uint8 = TypeColor3uint8,
Int64 = TypeInt64,
SharedString = TypeSharedString,
Bytecode = TypeBytecode,
Optional = TypeOptional,
UniqueId = TypeUniqueId,
Font = TypeFont,
Reserved = TypeFont+1 ... 0xFF
};
struct TypeTODO {
// Calculates bytes remaining in chunk payload.
u8 TypeTODO[parent.parent.parent.parent.UncompressedLength-($-addressof(parent.parent.parent))];
} [[inline]];
struct Invalid { TypeTODO TODO; } [[inline]];
struct String { TypeTODO TODO; } [[inline]];
struct Bool { TypeTODO TODO; } [[inline]];
struct Int { TypeTODO TODO; } [[inline]];
struct Float { TypeTODO TODO; } [[inline]];
struct Double { TypeTODO TODO; } [[inline]];
struct UDim { TypeTODO TODO; } [[inline]];
struct UDim2 { TypeTODO TODO; } [[inline]];
struct Ray { TypeTODO TODO; } [[inline]];
struct Faces { TypeTODO TODO; } [[inline]];
struct Axes { TypeTODO TODO; } [[inline]];
struct BrickColor { TypeTODO TODO; } [[inline]];
struct Color3 { TypeTODO TODO; } [[inline]];
struct Vector2 { TypeTODO TODO; } [[inline]];
struct Vector3 { TypeTODO TODO; } [[inline]];
struct Vector2int16 { TypeTODO TODO; } [[inline]];
struct CFrame { TypeTODO TODO; } [[inline]];
struct CFrameQuat { TypeTODO TODO; } [[inline]];
struct Token { TypeTODO TODO; } [[inline]];
struct Reference { TypeTODO TODO; } [[inline]];
struct Vector3int16 { TypeTODO TODO; } [[inline]];
struct NumberSequence { TypeTODO TODO; } [[inline]];
struct ColorSequence { TypeTODO TODO; } [[inline]];
struct NumberRange { TypeTODO TODO; } [[inline]];
struct Rect { TypeTODO TODO; } [[inline]];
struct PhysicalProperties { TypeTODO TODO; } [[inline]];
struct Color3uint8 { TypeTODO TODO; } [[inline]];
struct Int64 { TypeTODO TODO; } [[inline]];
struct SharedString { TypeTODO TODO; } [[inline]];
struct Bytecode { TypeTODO TODO; } [[inline]];
struct Optional { TypeTODO TODO; } [[inline]];
struct UniqueId { TypeTODO TODO; } [[inline]];
struct Font { TypeTODO TODO; } [[inline]];
struct Values {
Type TypeID;
if (TypeID == TypeInvalid) {
warnf("invalid property type 0");
Invalid Values;
} else if (TypeID == TypeString) {
String Values;
} else if (TypeID == TypeBool) {
Bool Values;
} else if (TypeID == TypeInt) {
Int Values;
} else if (TypeID == TypeFloat) {
Float Values;
} else if (TypeID == TypeDouble) {
Double Values;
} else if (TypeID == TypeUDim) {
UDim Values;
} else if (TypeID == TypeUDim2) {
UDim2 Values;
} else if (TypeID == TypeRay) {
Ray Values;
} else if (TypeID == TypeFaces) {
Faces Values;
} else if (TypeID == TypeAxes) {
Axes Values;
} else if (TypeID == TypeBrickColor) {
BrickColor Values;
} else if (TypeID == TypeColor3) {
Color3 Values;
} else if (TypeID == TypeVector2) {
Vector2 Values;
} else if (TypeID == TypeVector3) {
Vector3 Values;
} else if (TypeID == TypeVector2int16) {
Vector2int16 Values;
} else if (TypeID == TypeCFrame) {
CFrame Values;
} else if (TypeID == TypeCFrameQuat) {
CFrameQuat Values;
} else if (TypeID == TypeToken) {
Token Values;
} else if (TypeID == TypeReference) {
Reference Values;
} else if (TypeID == TypeVector3int16) {
Vector3int16 Values;
} else if (TypeID == TypeNumberSequence) {
NumberSequence Values;
} else if (TypeID == TypeColorSequence) {
ColorSequence Values;
} else if (TypeID == TypeNumberRange) {
NumberRange Values;
} else if (TypeID == TypeRect) {
Rect Values;
} else if (TypeID == TypePhysicalProperties) {
PhysicalProperties Values;
} else if (TypeID == TypeColor3uint8) {
Color3uint8 Values;
} else if (TypeID == TypeInt64) {
Int64 Values;
} else if (TypeID == TypeSharedString) {
SharedString Values;
} else if (TypeID == TypeBytecode) {
Bytecode Values;
} else if (TypeID == TypeOptional) {
Optional Values;
} else if (TypeID == TypeUniqueId) {
UniqueId Values;
} else if (TypeID == TypeFont) {
Font Values;
} else {
warnf("unknown property type {}", TypeID);
Invalid Values;
}
};
////////
// Chunks
struct Entry {
string Key;
string Value;
};
struct ChunkMETA {
u32 Length;
Entry Entries[Length];
};
struct SharedStringValue {
u8 Hash[16];
bstring Value;
};
struct ChunkSSTR {
u32 Reserved;
u32 Length;
SharedStringValue SharedStrings[Length];
};
struct ChunkINST {
s32 ClassID;
string ClassName;
bool HasService;
u32 Length;
References<Length> IDs;
if (HasService) {
bool IsService[Length];
}
};
struct ChunkPROP {
s32 ClassID;
string Name;
Values Values;
};
struct ChunkPRNT {
u8 Reserved;
u32 Length;
References<Length> Children;
References<Length> Parents;
};
struct ChunkSIGN {
u8 Payload[parent.UncompressedLength];
};
struct ChunkEND {
u8 Payload[parent.UncompressedLength];
};
////////
// Main structure
struct Signature {
char Magic[7];
if (Magic != "<roblox") {
errorf("signature: expected '<roblox' for magic, got {0}", Magic);
}
char BinaryMarker;
if (BinaryMarker != '!') {
warnf("XML format detected");
return;
}
char HighBits[2];
if (HighBits != "\x89\xFF") {
errorf("signature: expected 0x89, 0xFF for high bits, got {}", HighBits);
}
char DOSLineEnding[2];
if (DOSLineEnding != "\r\n") {
errorf("signature: expectected \\r\\n for DOS line ending, got {}", DOSLineEnding);
}
char DOSEndOfFile;
if (DOSEndOfFile != 0x1A) {
errorf("signature: expected 0x1A for DOS EOF, got {}", DOSEndOfFile);
}
char UnixLineEnding;
if (UnixLineEnding != 0x0A) {
errorf("signature: expected 0x0A for Unix line ending, got {}", UnixLineEnding);
}
} [[single_color]];
struct Header {
u32 ClassCount;
u32 InstanceCount;
u8 Reserved[8];
};
struct Chunk {
char Signature[4];
u32 CompressedLength;
u32 UncompressedLength;
u8 Reserved[4];
if (CompressedLength == 0) {
if (Signature == "META") {
ChunkMETA Payload;
} else if (Signature == "SSTR") {
ChunkSSTR Payload;
} else if (Signature == "INST") {
ChunkINST Payload;
} else if (Signature == "PROP") {
ChunkPROP Payload;
} else if (Signature == "PRNT") {
ChunkPRNT Payload;
} else if (Signature == "SIGN") {
ChunkSIGN Payload;
} else if (Signature == "END\x00") {
ChunkEND Payload;
} else {
u8 Payload[UncompressedLength];
}
} else {
warnf("cannot decode LZ4-compressed chunk {}", Signature);
u8 Payload[CompressedLength];
}
};
struct main {
Signature Signature;
u16 Version;
if (Version != 0) {
errorf("version 0 expected, got {0}", Version);
}
Header Header;
Chunk Chunks[while(builtin::std::mem::read_string($, 4) != "END\x00" && $ < builtin::std::mem::size())];
Chunk End;
};
main rbxl @ 0x0;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment