Last active
May 1, 2023 16:00
-
-
Save Justasic/3b5ad2c351851623e6e423b4a429c1f3 to your computer and use it in GitHub Desktop.
ImHex (https://imhex.werwolv.net/) Telegram TLO object decoding (TLO schema v2 should match what Telegram uses)
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
#include <std/mem.pat> | |
#include <std/io.pat> | |
#include <std/sys.pat> | |
enum ConstructorIDType : u32 { | |
CID_TLSTYPE = 0x12eb4386, | |
CID_NATCONST = 0x8ce940b1, | |
CID_NATVAR = 0x4e8a14f0, | |
CID_EXPRNAT = 0xdcb49bd8, | |
CID_EXPRTYPE = 0xecc9da78, | |
CID_TYPEVAR = 0x0142ceae, | |
CID_TYPEEXPR = 0xc1863d08, | |
CID_TLSARG = 0x29dfe61b, | |
CID_ARRAY = 0xd9fb20de, | |
CID_COMBINATORLEFT = 0x4c12c6d9, | |
CID_COMBINATORLEFTBUILTIN = 0xcd211f63, | |
CID_COMBINATORRIGHT = 0x2c064372, | |
CID_COMBINATOR = 0x5c0a1ed5, | |
CID_SCHEMAv2 = 0x3a2f9be2 | |
}; | |
// These flags are defined in types and expressions | |
bitfield TypeFlags | |
{ | |
tf_bare : 1; // Is the type expression bare | |
tf_nocons : 1; // unknown? | |
padding : 8; | |
tf_empty : 1; // unknown? | |
padding : 6; | |
tf_optvar : 1; // Is the argument optional? (eg. wrapped in '{}' or flagged) | |
tf_excl : 1; // Is the argument a forwarded function? (via !) | |
padding : 1; | |
tf_optfield : 1; // Is the argument hidden with a field mask | |
tf_novar : 1; // unknown? only used with "Maybe" and "Bool" types | |
padding : 3; | |
tf_defcon : 1; // Does the type have a default constructor (e.g. constructor that will be used if no magic is presented.) | |
padding : 1; | |
tf_forwarded: 1; | |
padding : 4; | |
}; | |
// These flags only make sense for functions, not types | |
bitfield CombinatorFlags | |
{ | |
cf_read : 1; // Does the function represent a read query | |
cf_write : 1; // Does the function represent a write query | |
cf_internal : 1; // Is the function used only for internal engine interconnection | |
cf_kphp : 1; // Is the function processed by kPHP RPC server | |
}; | |
struct TLString | |
{ | |
if (std::mem::read_signed($, sizeof(u8)) == 254) | |
{ | |
$ += 1; // Skip 254 | |
u24 length; | |
} | |
else | |
u8 length; | |
char value[this.length]; | |
padding[-(this.length + 1) & 3]; | |
}; | |
// tls.type#12eb4386 name:int id:string constructors_num:int flags:int arity:int params_type:long = tls.Type; | |
struct TLSType | |
{ | |
ConstructorIDType ConstructorID; | |
s32 name; | |
TLString id; | |
s32 constructors_num; | |
s32 flags; | |
s32 arity; | |
s64 params_type; | |
}; | |
// tls.natConst#8ce940b1 value:int = tls.NatExpr; | |
struct NatConst | |
{ | |
ConstructorIDType ConstructorID; | |
s32 value; | |
}; | |
// tls.natVar#4e8a14f0 dif:int var_num:int = tls.NatExpr; | |
struct NatVar | |
{ | |
ConstructorIDType ConstructorID; | |
s32 dif; | |
s32 var_num; | |
}; | |
struct NatExpr | |
{ | |
if (u32(std::mem::read_signed($, sizeof(u32))) == ConstructorIDType::CID_NATVAR) | |
NatVar; | |
else if (u32(std::mem::read_signed($, sizeof(u32))) == ConstructorIDType::CID_NATCONST) | |
NatConst; | |
else | |
std::assert(false, std::format("Invalid ExprNat value CID: {:#x} @ {:#x}", u32(std::mem::read_signed($, sizeof(u32))), $)); | |
}; | |
// tls.exprNat#dcb49bd8 _:tls.NatExpr = tls.Expr; | |
struct ExprNat | |
{ | |
ConstructorIDType ConstructorID; | |
NatExpr underscore; | |
}; | |
using TypeVar; | |
using typeExpr; | |
using Array; | |
struct TypeExpr | |
{ | |
if (u32(std::mem::read_signed($, sizeof(u32))) == ConstructorIDType::CID_TYPEVAR) | |
TypeVar; | |
else if (u32(std::mem::read_signed($, sizeof(u32))) == ConstructorIDType::CID_TYPEEXPR) | |
typeExpr; | |
else if (u32(std::mem::read_signed($, sizeof(u32))) == ConstructorIDType::CID_ARRAY) | |
Array; | |
else | |
std::assert(false, std::format("Invalid TypeExpr value CID: {:#x} @ {:#x}", u32(std::mem::read_signed($, sizeof(u32))), $)); | |
}; | |
// tls.exprType#ecc9da78 _:tls.TypeExpr = tls.Expr; | |
struct ExprType | |
{ | |
ConstructorIDType ConstructorID; | |
TypeExpr underscore; | |
}; | |
// tls.typeVar#0142ceae var_num:int flags:int = tls.TypeExpr; | |
struct TypeVar | |
{ | |
ConstructorIDType ConstructorID; | |
s32 var_num; | |
s32 flags; | |
}; | |
struct Expr | |
{ | |
if (u32(std::mem::read_signed($, sizeof(u32))) == ConstructorIDType::CID_EXPRTYPE) | |
ExprType; | |
else if (u32(std::mem::read_signed($, sizeof(u32))) == ConstructorIDType::CID_EXPRNAT) | |
ExprNat; | |
else | |
std::assert(false, std::format("Invalid typeExpr value CID: {:#x} @ {:#x}", u32(std::mem::read_signed($, sizeof(u32))), $)); | |
}; | |
// tls.typeExpr#c1863d08 name:int flags:int children_num:# children:children_num*[tls.Expr] = tls.TypeExpr; | |
struct typeExpr | |
{ | |
ConstructorIDType ConstructorID; | |
s32 name; | |
s32 flags; | |
u32 children_num; | |
Expr children[this.children_num]; | |
}; | |
bitfield ArgFlags | |
{ | |
padding : 1; | |
exist_bit : 1; | |
var_bit : 1; | |
padding : 29; | |
}; | |
// tls.arg#29dfe61b id:string flags:# var_num:flags.2?int exist_var_num:flags.1?int | |
// exist_var_bit:flags.1?int type:tls.TypeExpr = tls.Arg; | |
struct Arg | |
{ | |
ConstructorIDType ConstructorID; | |
TLString id; | |
ArgFlags flags; | |
if (flags.var_bit) | |
s32 var_num; | |
if (flags.exist_bit) | |
{ | |
s32 exist_var_num; | |
s32 exist_var_bit; | |
} | |
TypeExpr type; | |
}; | |
// tls.array#d9fb20de multiplicity:tls.NatExpr args_num:# args:args_num*[tls.Arg] = tls.TypeExpr; | |
struct Array | |
{ | |
ConstructorIDType ConstructorID; | |
NatExpr multiplicity; | |
u32 args_num; | |
Arg args[this.args_num]; | |
}; | |
// tls.combinatorLeft#4c12c6d9 args_num:# args:args_num*[tls.Arg] = tls.CombinatorLeft; | |
struct TLSCombinerLeft | |
{ | |
ConstructorIDType ConstructorID; | |
u32 args_num; | |
Arg args[this.args_num]; | |
}; | |
// tls.combinatorLeftBuiltin#cd211f63 = tls.CombinatorLeft; | |
struct combinatorLeftBuiltin | |
{ | |
ConstructorIDType ConstructorID; | |
}; | |
struct CombinatorLeft | |
{ | |
if (u32(std::mem::read_signed($, sizeof(u32))) == ConstructorIDType::CID_COMBINATORLEFTBUILTIN) | |
combinatorLeftBuiltin; | |
else if (u32(std::mem::read_signed($, sizeof(u32))) == ConstructorIDType::CID_COMBINATORLEFT) | |
TLSCombinerLeft; | |
else | |
std::assert(false, std::format("Invalid combinator left value CID: {:#x} @ {:#x}", u32(std::mem::read_signed($, sizeof(u32))), $)); | |
}; | |
// tls.combinatorRight#2c064372 value:tls.TypeExpr = tls.CombinatorRight; | |
struct CombinatorRight | |
{ | |
ConstructorIDType ConstructorID; | |
TypeExpr value; | |
}; | |
// tls.combinator#5c0a1ed5 name:int id:string type_name:int | |
// left:tls.CombinatorLeft right:tls.CombinatorRight = tls.Combinator; | |
struct Combinator | |
{ | |
ConstructorIDType ConstructorID; | |
s32 name; | |
TLString id; | |
s32 type_name; | |
CombinatorLeft left; | |
CombinatorRight right; | |
}; | |
// tls.schema_v2 version:int date:int types_num:# types:types_num*[tls.Type] | |
// constructor_num:# constructors:constructor_num*[tls.Combinator] | |
// functions_num:# functions:functions_num*[tls.Combinator] = tls.Schema; | |
struct schemav2 | |
{ | |
ConstructorIDType ConstructorID; | |
s32 version; | |
s32 date; | |
u32 types_num; | |
TLSType types[this.types_num]; | |
u32 constructor_num; | |
Combinator constructors[this.constructor_num]; | |
u32 functions_num; | |
Combinator functions[this.functions_num]; | |
}; | |
schemav2 schema @ 0x00; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment