Created
November 10, 2022 02:43
-
-
Save Anaminus/efddd8ae175e58f202fe49b0430e839b to your computer and use it in GitHub Desktop.
ImHex pattern for the RBXL format.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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