Decode binary data on the fly quicker.
[user@host project]$ livedecode example/png.spec example/example.png
The tool is meant to be run live while typing on a spec, so one can do stuff like this:
Just run the program periodically in the background:
[user@host project]$ while true; do
clear
date
livedecode docs/wmb6.spec data/wmb/wmb6/block.wmb > /tmp/dump.txt
sleep 1
done
The format is a very crude line based syntax. Empty lines are ignored, everything past a #
is a comment.
Lines are split into tokens separated by either space or tab characters.
The first token in a line determines the command or type of the line.
A line starting with .
is a macro and is always executed. All other lines can be conditionally be executed.
If a line starts with a type, this type is decoded. If a name is given after the type, a variable is created with that name.
In a lot of places where not a name is expected, either immediate numbers can be written as decimal, hexadecimal (0x1A
) or a variable reference can be used (*variable
).
The parser can also accept tuple types, which are started by (
and terminated by )
. Tuples are inhomogenous arrays that are passed as a single argument. They are used for grouping arguments together.
Lines can be continued with a \
, so the following code is considered a single line by the parser:
lut *key \
(key value) \
(key value) \
(key value) \
(key value) \
(key value)
prints all arguments. if an argument is a semicolon, no space is printed after the argument. If a semicolon is last, no line feed is printed.
creates a variable called with the value . Useful for constants or aliases
sums up all offsets and moves the read cursor to the absolute position
sums up all offsets and moves the read cursor relatively. Accepts negative numbers
prints the file cursor
stores the file cursor in
dumps bytes
appends everything past the ! to the last created program
invokes a program named . all arguments past that are passed as variables arg[0] to arg[n]
Creates an array of items called . In , the first occurance of ?
will be replaced with the array index. determines the type of the array items.
Creates an array of items called where is a sized type (str, blob, ...). In , the first occurance of ?
will be replaced with the array index. determines the type of the array items.
Builds a variable name from and all provided s. For each key, the next ?
in the is replaced with the value of .
After all ?
are resolved, a global lookup is performed and the variable with the computed name is then copied into .
changes integer endianess to little endian
changes integer endianess to little endian
changes integer endianess to big endian
changes integer endianess to big endian
switches to bit-reading mode
switches out of bit-reading mode, discarding any unread bits in the current byte
consumes a bitmap of size * and rgb565, rgb888, bgr888, rgbx8888 or rgba8888
same as previous, but saves result to a PPM file called
Will perform a lookup on value . If matches , the following is printed. Any number of pairs can be passed.
prints all possible integer divisors of
Reads bytes and writes them into a file called . Useful to extract portions of a file.
All arguments together form a pattern. This pattern is then searched in the file from the cursor position on and each occurrence is printed with offset.
Pattern components can either be a *
for any kind of byte, or a list of |
-separated integers that list the possibilities for this option.
To make this more clear, let's consider this example:
We're searching for a list of u32 that can only consist of the integer values 1, 2 or 3, but there's a unknown length marker at the start that is a 16 bit value less than 256:
# Search for at least 3 items:
# len item 0 item 1 item 2
findpattern * 0 1|2|3 0 0 0 1|2|3 0 0 0 1|2|3 0 0 0
.if <value> # the code following this will be executed if <value> is not 0
.if <value> <equals> # the code following this will be executed if <value> is equals to <equals>
.else # swaps the current execution condition
.endif # ends a if block
.loop <count> # Repeats the following code for <count> times.
.loop <count> <var> # Repeats the following code for <count> times. Writes the current index into <var>.
.endloop # Terminates the current loop
.pgm <name> # creates a new program called <name>
.endpgm # ends the current program
u8 # 8 bit unsigned integer
u16 # 16 bit unsigned integer
u32 # 32 bit unsigned integer
u64 # 64 bit unsigned integer
i8 # 8 bit signed integer, two's complement
i16 # 16 bit signed integer, two's complement
i32 # 32 bit signed integer, two's complement
i64 # 64 bit signed integer, two's complement
f32 # 32 bit floating point
f64 # 64 bit floating point
str <len> # ascii string of <len> bytes, displayed as string+escapes
blob <len> # binary blob of <len> bytes, displayed as array
bitblob <len> # binary blob of <len> bits, displayed as array of bits (only valid in bit-reading mode)
bits <len> # an unsigned integer of <len> bits up to 64 (only valid in bit-reading mode)
str <?> file.path # The full path of the current file
str <?> file.name # The file name of the current file
u64 file.size # Size of the current file in bytes
endian le
u32 magic
u32 type
u32 offset
u32 length
print section 1
seek *offset
dump *length
.if *type 10
seek 0x200
str 10 description
.endif
A more complete example can be found here, which will decode a good amount of a PNG file.
- Fetch the latest zig install (tested with
0.10.0-dev.4442+ce3ffa5e1
). - Invoke
zig build
. - Install
zig-out/bin/livedecode
into your system in a way you like it.