Binary Format Specification¶
Comments start with //
, everything after for the rest of the line is ignored.
Spaces can be used to improve the readability, but are not required. Spaces are preferred over tabs.
Variable or data type names are case sensitive.
Endianness¶
Available values are little
and big
.
When defining the reserved data type name ENDIANNESS
, all other data types who do not define an own endianness are interpreted as this.
| ENDIANNESS : little // default value for all non defined
Data Types¶
Every data type name, can also be used as an variable name, they don’t interfere. Data types can be defined the following way | <name> : <bit size> - <endianness> // description
, whereas - <endianness>
is optional, if a default endianness is given.
The bit size can be set by a variable too.
| Bit : 1 - little // A bit, 0 or 1.
| uByte : 4 - little // An unsigned byte.
| uInt : 8 - little // unsigned integer
// or
| ENDIANNESS : little
| Bit : 1 // A bit, 0 or 1.
| uInt : 8 // unsigned integer
uInt : mySize = 0
| myType : mySize // My special type.
Struct Types¶
If you have compressed data, it can be easily described as a struct struct <name> {
.
Members can be access with a dot.
struct SpecSummary {
> uByte (1) : size // Size
> uByte (1) : spec_a // Specification A
> uByte (1) : spec_b // Specification B
}
> SpecSummary (1) : spec // specification summary
if (spec.size > 0) {
read_spec()
}
Bit Reading & Variables¶
Each bit interpretation starts with >
, whereas variable name and the reinterpreted type are optional. Variables are only valid in their declared scope.
<counter>
is a value, how often the data type is read, it will create a list with its content.
If a reinterpreted type is given, it will be reinterpreted as if the amount of single bits was read by the reinterpretation type (> Bit (3) : uByte
reinterprets 3 bits into one uByte).
If the reinterpreted type is bigger, it will be filled with 0 regarding to the endianness, to not change the value (e.g. little endianness will fill from the left side). Same with cutting it off.
> <data type> (<counter>) <reinterpreted type> : <variable name> // <description>
^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^ optional
// examples
> uByte (7) // string identifier: "myimage"
> uInt (1) : image_size // image size in bytes
> Bit (3) uByte : red_color // red color
> Bit (3) uByte // possible but does not make any sense, as the reinterpreted type will not be used.
uInt : other_size = image_size // declare new variable other_size with content of image_size
uInt : id = 0 // declare a new variable of type uInt and content 0
Conditions¶
if
, else if
, else
. Available comparisons are ==
, !=
, <
, >
, <=
, >=
. Which can be combined with &&
(and) and ||
(or).
Brackets are not required around the condition, but can improve the readability.
You cannot compare a list to a number. You have to reinterpret it beforehand or cast it.
> Bit (2) uByte : roll_over // roll over simulation type
uInt : roll_over_int = roll_over
if (roll_over_int == 0) { // roll over specification 0
> uInt (1) // roll over specifications
} else if (uInt(roll_over) == 1) { // roll over specification 1
> uInt (1) // roll over specifications
> uInt (1) // more roll over specifications
} else { // default roll over specification
> uByte (1) // weight in kg
> uInt (1) // roll over specifications
}
A short version for else if
chaining is switch
, case
. default
will be used if no other case matches.
There is no default fallthrough, to get this behaviour use fallthrough
. A break
inside a switch case will break out of it.
> Bit (2) uByte : roll_over // roll over simulation type
uInt : roll_over_int = roll_over
switch (roll_over_int) {
case 0: // roll over specification 0
> uInt (1) // roll over specifications
case 1: // roll over specification 1
> uInt (1) // roll over specifications
> uInt (1) // more roll over specifications
default: // default roll over specification
> uByte (1) // weight in kg
> uInt (1) // roll over specifications
}
Loops¶
There are three types of loops: for
, range-based for
and a while
loop.
Brackets are not required around the condition, but can improve the readability. You can break out of a loop with the break
keyword.
for (id; id <= 3; id++) { // 0..2
// ...
}
for (0..2 : id) { // including both borders 0 and 2
// ...
}
uInt : id = 0
while (id <= 3) {
// ...
id = id + 1
}
uInt : id = 0
while (true) {
if (id == 3) {
break
}
// ...
id = id + 1
}
Functions¶
Functions can be used to reuse a specific block again. def <name>(<parameter>) <return values> {}
def get_cube(uInt : param) uInt, uByte {
> uInt (1) : first
> uByte (1) : second
return first, second
} // get_cube
> uInt (1) : rec_first
> uByte (1) : rec_second
rec_first, rec_second = get_cube()