Skip to content
Radfordhound edited this page Apr 26, 2017 · 6 revisions

Home » File Formats » Common » BINA Format

  • Specification Version: 1.0
  • Author(s): Radfordhound
  • Rules:
    • All string/character data is encoded in ASCII, unless otherwise specified.

What is the BINA Format?

The BINA format is a generic container format, designed to hold data in many different types, ranging from set data (such as the SOBJ Format) to simple lists (such as the GISM Format), even to full-fledged archives (such as Lost World's PAC Format)!

This specification will attempt to explain how the BINA format works in detail, while still being as simplistic as possible.

The BINA Header

The BINA header comes in multiple forms depending on the game.

Sonic Colors

Colors-specific Rules:

  • All data is in big-endian, unless otherwise specified.
  • All offsets (not sizes/lengths) in the following specification are relative to a 0x20 position, meaning you must add 0x20 to them to get the absolute position, unless otherwise specified.
class HEADER
{
	uint fileSize;
	uint offsetTableOffset; // The non-absolute offset to the BINA offset table explained below
	uint offsetTableLength;
	uint unknown1 = 0; // Appears to just be padding.
	
	ushort unknownFlag1;
	
	// Whether or not the Colors BINA "Footer Magic" is present.
	// Appears to be a boolean padded out to be 2 bytes for some reason.
	ushort isFooterMagicPresent;
	char[4] headerMagic = "\0\01B"; // The "1B" at the end could possibly be the version number and big/little endian flag?
	char[4] BINASignature = "BINA";
	uint unknown2 = 0; // Appears to just be padding.
}

Colors Header Example

Sonic Lost World

Lost-World specific rules:

  • All offsets (not sizes/lengths) in the following specification are relative to a 0x40 position, meaning you must add 0x40 to them to get the absolute position, unless otherwise specified.
class HEADER
{
	char[4] BINASignature = "BINA";
	char[3] versionString = "200";
	char bigEndianFlag; // If this value is 'B' the file is big-endian, otherwise it's in little-endian.
	uint fileSize;
	ushort unknownFlag1 = 1;
	ushort unknownFlag2 = 0;
	
	char[4] DATASignature = "DATA";
	uint dataLength; // How long the file is from the beginning of "DATA" until the end.
	uint stringTableOffset; // The non-absolute offset to the BINA string table explained below.
	uint stringTableLength;
	
	uint offsetTableLength; // The length of the BINA offset table explained below.
	ushort headerPaddingAmount = 0x18; // How much padding there is at the end of the header
	ushort padding1 = 0; // Just two nulls here to pad headerPaddingAmount out to 4 bytes.
	byte[] padding2 = new byte[headerPaddingAmount];
}

Lost World PC Header Example Lost World Wii U Header Example

BINA String Tables

BINA String Tables are the easiest part of the BINA format - they're literally just arrays of null-terminated strings, and that's it!

The only other thing to them is that the final string value is padded to a 0x4 offset. So, for example, if the last string ends at 0x3DB (not divisible by 0x4) you add nulls until it is divisible by 0x4, making the table end at 0x3DC.

BINA String Table Example

BINA Offset Tables

BINA Offset Tables are, by far, the most complicated part of the BINA Format. In concept, these are simple. Literally just a table (or array) of offsets that point to all the other offsets in the file, similar to the offset tables in Unleashed/Generations Formats. However, in execution, they're a bit trickier than that.

Essentially, to avoid taking up a large amount of space at the end of each file, the offsets are stored using a clever trick involving bit-shifting. Brace yourselves, here we go.

BINA Offset Table Example

This is an example of a BINA offset table. Essentially, it is an array of 2 bits which define the "type" of offset, followed by a set number of bits (depending on the type) which is the actual offset's data. This pattern is simply repeated all the way until the end of the file, so once you get it down, understanding these is actually fairly simple.

In the above example, you can see that the first value is 0x43 ("C" in ASCII). This, in binary, is 0100 0011. The first two bits from that (01) represent how many bits are going to come next as part of the value.

These two bits can be:

  • 00 (You've reached the end of the offset table, stop reading)
  • 01 (The next 6 bits in the file are the value)
  • 10 (The next 14 bits in the file are the value)
  • 11 (The next 30 bits in the file are the value)

In this case, the first two bits are "01", so the next 6 bits (000011, or "3") are the value of the offset. The only other thing to it is that these offsets must be bit-shifted two to the left, meaning in this example, 000011 (or "3") would become 001100 (or "12").

So how are we using this data? Well we're basically starting from the end of the header (0x20 for Colors files, 0x40 for LW files) and just adding from there. So our first offset is at 0x4C, since we started at 0x40, got this value of 12 (0xC in hex) and added that. See? It's just like the SU/Gens offset tables, except much smaller!

Alright then, cool! Now we just have to keep doing that. 😃

The next value in the above example is 0x42 ("B" in ASCII). In binary, this is 0100 0010. So, we get the first two bits (01), which tell us we need to read the next 6 bits (000010, or "2"). Now we just have to bit-shift two to the left like we did before (so it becomes 001000, or "8") and add it to the current position (we were at 0x4C after reading the last offset - so just add 8 to that).

Done! This next offset is at 0x54. Just keep doing that until the end of the file (or until you read 0 as described above), and you're good!

The BINA Footer

Hardly anything is known about the BINA Footer, though it seems to only be present in select files from Sonic Colors (such as it's set data), and seems to always go as such:

class FOOTER
{
	uint unknown1 = 0x10;
	uint unknown2 = 0; // Probably just padding.
	char[4] footerMagic = "bvh\0";
}