-
Notifications
You must be signed in to change notification settings - Fork 2
Getting Started
Spiff needs for you to have two things - a binary file to parse, and a description of the file format. The event dispatching mechanism in Spiff means that you can actually do whatever you want with the data (see TreeBuildingEventListener), but in most cases you'll probably also want some classes that will ultimately represent the contents of the file.
File formats are described in an .adf file (adf = Arbitrary Data Format). The formal grammar of the file is described in AdfGrammar. The .adf extension is a convention rather than a necessity.
##Primitives The most basic things you can do are to specify a list of datatypes, in the order in which they occur in the file, followed by a name to give to that field. The primitive datatypes map directly to Java primitives:
byte (1 byte)
short (2 bytes)
int (4 bytes)
long (8 bytes)
double (4 bytes)
float (8 bytes)
There are also unsigned equivalents for integers:
ubyte
ushort
uint
ulong
Names for the fields will be used to find corresponding fields in bound classes, so identifiers use the java convention (alphanumeric starting with an alpha). For instance, the file header of a bitmap file looks like this:
short bfType
int bfSize
short bfReserved1
short bfReserved2
int bfOffBits
##Comments
Comments can be included in the file with by preceding them with #. Multiline comments are not currently supported
##Strings Strings can be of three types, either fixed length, null terminated, or literal. A fixed length string will read as many bytes as you specify (either a literal number or an expression that can be evaluated), a null terminated string is read until the byte 0x00 is found, and a literal expects to read the literal string you specify, and will throw an exception if it finds anything else.
The syntax for these is
string aNullTerminatedString
string(10) aFixedLengthString
string('expected') literalString
Each of these can optionally take a second parameter (or single parameter for null terminated strings) giving an expected encoding for the string. Encodings can currently be one of UTF-8, UTF-16, UTF-16LE or US-ASCII. See also SettingDefaultOptions
##Flow of execution Most binary file formats also require some sort of logic. Spiff allows you to specify conditions and other instructions in amongst the datatypes to control the flow of execution. Instructions in adf files always start with a period ('.'). Blocks (such as conditionals or loops) are surrounded with curly braces ('{}'). Remember though that the "flow of execution" describes simply the order in which instructions are executed - the byte position of the "pointer" in the file only moves forward, unless you use a .jump or .skip to move it backwards (although why you'd want to parse the same data twice is up to you...)
Conditionals can be achieved with .if and .else . These work just like you'd expect them to:
.if(bfType == 1) {
short theDataIfBfTypeIs1
} .else {
int theDataisBfTypeIsNot1
}
Expressions passed as arguments can reference values from earlier instructions simply by using the identifier, and can also reference the byte position in the file of an earlier instruction by prefixing the name with an ampersand. For more detail on what you can specify in expressions, see ExpressionLanguage
Loops can be done using the .repeat instruction. Repeat takes an argument specifying how many times to repeat:
.repeat(bitmapWidth*bitmapHeight) {
int pixelData
}
Many file formats include pieces of data that are reserved for future use, or are simply bits of the file that you don't care about. You can change position in the file with the .skip and .jump instructions (no .hop though). The .jump instruction moves to an absolute position:
int nextDataPosition
.jump nextDataPosition
whereas the .skip instruction moves the "pointer" relative to the current position:
short numberOfUnusedBytes
.skip numberOfUnusedBytes
##Setting Default Options There are two instructions that can be specified in the .adf file that determine how subsequent instructions work. The instructions can appear anywhere in the file, and will change the default option whenever the flow of execution passes through them.
The .setorder instruction determines the endianness of the data. Valid values are LITTLE-ENDIAN and BIG-ENDIAN
.setorder BIG-ENDIAN
The .setencoding instruction sets a default encoding for the file. This encoding will be used for all string datatypes, unless specified otherwise. Valid values are UTF-8, UTF-16, UTF-16LE and US-ASCII
.setencoding UTF-16
string aStringThatWillBeDecodedUsingUTF-16
string(US-ASCII) aStringThatWillBeDecodedUsingUSASCII
string anotherStringThatWillBeDecodedUsingUTF-16
.setencoding UTF-8
string thisStringWillNowBeDecodedUsingUTF-8