Actions

SCHG

Sonic Forces

From Sonic Retro

Revision as of 11:00, 3 January 2018 by BSonirachi (talk | contribs)
Sonic Community Hacking Guide
Sonic Forces
File Index
wars_0

wars_1
wars_patch

BINA Formats
GEdit

This is the Sonic Community Hacking Guide for the PC version of Sonic Forces.

Sonic Forces you to use this stuff to fix his game.

File/Format specifications

BINA

NOTE: This section is highly based on my page on Sonic Colors formats: SCHG:Sonic_Colors

The BINA format is a generic container format used by Sonic Team in titles such as Sonic the Hedgehog (2006 game), Sonic and the Secret Rings, Sonic and the Black Knight, Sonic Colors, Sonic Lost World, and now in Sonic Forces.

Generally speaking, any file that follows the BINA Format is structured as follows:

  • BINA Header
  • Data (Entirely based on the type of data the file contains)
  • BINA String Table
  • BINA Offset Table (aka BINA Final Table or BINA Footer)

Each of BINA-specific components are detailed below:

Header

struct Header
{
    char[4] BINASignature = "BINA";
    char[3] VersionNumber = "210";
    char EndianFlag; // "B" if the file is big-endian, "L" if the file is little-endian.
    uint FileSize;
    ushort Unknown1;
    ushort Unknown2;

    char[4] DATASignature = "DATA";
    uint DataLength = (FileSize - 16); // How long the file is from the beginning of DATASignature until the end.
    uint StringTableOffset; // The non-absolute offset to the BINA String Table explained below.
    uint StringTableLength; // The length of the BINA String Table explained below.

    uint OffsetTableLength; // The length of the BINA Offset Table explained below.
    ushort AdditionalDataLength = 0x18;
    ushort Padding = 0; // Just two nulls to pad-out AdditionalDataLength to 4 bytes.
    byte[AdditionalDataLength] AdditionalData; // Seems to always just contain nulls
}

String Table

Literally just an array of null-terminated strings (which are referenced via offsets by other values in the file). The only other thing noteworthy about them is that the last value in the array must be padded to a 0x4 offset.

Offset Table

This is the most complicated part of the BINA format by far, so brace yourselves!!
The BINA Offset Table, in concept, is actually very simple. It's literally just an array that lists the position of every offset in the file (not counting the offsets in the header, such as the offset to the string table).

However, in execution, BINA offset tables are quite complicated, as they use a few clever techniques to ensure the offset tables are as small as possible.

As an example, let's use some values (in hex) from an actual BINA offset table, namely the first 4 bytes in w5a01_obj_area01.gedit's offset table:

44 48 42 42


The first value in this table (44 in hex) is represented as the following in binary:

0100 0100


So basically, here's how this works. The first two bits represent how long this offset is, corresponding to one of the values in the table below:

00 = This offset is 0 bits long, you've reached the end of the offset table!
01 = This offset is 6 bits long.
10 = This offset is 14 bits long.
11 = This offset is 30 bits long.


In this case, since the first two bits are 01, the offset is 6 bits long, so we read the next 6 bits (00 0100) to get the value for the offset. Simple, right?

Now here's where things get a little tricky.

You see, these values aren't simply the positions of each offset. Rather, these values represent the distance between this offset and the last offset we've currently read (or the length of the BINA Header, aka 64 bytes, if we haven't actually read an offset yet).

Beyond that, due to how binary works, the last two bits in a value can only possibly be used to represent a value from 0-3, which wouldn't be very helpful here, as each offset, at a minimum, must be 4 bytes long (in Forces they're actually typically 8 bytes long but shh let's save that for later)!

So, to better take advantage of space, all of the values have actually been bit-shifted by two to the right (01 0100 is stored in the file like 00 0101, for example). Therefore, you also need to bit-shift these values back to the left by 2 to counter-act this.

Example

Phew, alright, we covered a lot just then! Still with us? Good!
Let's go over how you'd read each offset in the above example table.

The first value in our example offset table is 44 in hex and 0100 0100 in binary. Since the first two bits are 01, it means we need to read the next 6 bits (00 0100) to get our value. But remember, these values are actually all shifted by 2 to the right, so we need to shift it to the left by 2 to get the actual value: 0001 0000 (or 16).

Now we just add that to the position of the last offset we read, except in this case we haven't actually read an offset yet, so instead we use the length of the BINA header (again, 64 bytes). 64 + 16 = 80. So, the position of our first offset is 80! Now we just have to repeat this process for each offset in the table.

The next offset in this table is 0x48 (0100 1000). The first two bits are 01, so we use the next 6 bits (00 1000) as the value for the offset. Left-shift it by two as described above, and we get 0010 0000 (32). Add that to the position of the last offset we read (80), and presto, we get the position of our second offset (112)!

I think you get it now, so I'll stop there. Hopefully the BINA Offset Table now makes sense!

GEdit

The GEdit format is used for defining "set data" (stage object layouts). You can find each stage's gedit files in "wars_patch.cpk" (for some reason not in wars_0 or wars_1), inside the PAC file that has the stage's ID in its name under the "gedit" folder.

The GEdit format is a BINA format, therefore it contains a BINA Header, String Table, and Offset Table as described in the above section on the BINA format. The format-specific "Data" portion of the GEdit format works as follows:

struct Header
{
    ulong Padding1 = 0;
    ulong Padding2 = 0;
    
    ulong ObjectOffsetTableOffset;
    ulong ObjectCount;

    ulong ObjectCount2 = ObjectCount; // Not sure why the object count is in here twice? Lol
    ulong Padding3 = 0;

    ulong[ObjectCount] ObjectOffsetTable; // An array of offsets to each object entry
    ObjectEntry[ObjectCount] Objects;
}

class ObjectEntry
{
    ulong Padding1 = 0;
    ulong ObjectTypeOffset; // Points to the object's type in the BINA String Table.

    ulong ObjectNameOffset; // Points to the object's name in the BINA String Table.
    ushort ObjectID;
    ushort Unknown1;
    ushort ParentObjectID; // Set to 0 if the object has no parent
    ushort ParentUnknown1; // Set to 0 if the object has no parent

    float[3] Position;
    float[3] Rotation; // In radians, not degrees.

    // These seem to have no effect in-game, so they might just be
    // values used by the dev team's level editor.
    float[3] Position2;
    float[3] Rotation2; // Also in radians
    
    // See the following section for more information on tags
    ulong TagsOffsetTableOffset;
    ulong TagCount; // ?
    ulong UnknownCount1 = TagCount; // Seems to always be the same as TagCount?
    ulong Padding3 = 0;

    // Parameters are hard-coded for each object type. Since there are too
    // many object-specific parameters to list on one page, see our Forces
    // object templates on the HedgeLib repository for type-specific parameter
    // specificatons. (https://goo.gl/e1tYXT)
    ulong ParametersOffset;
    
    ulong[TagCount] TagsOffsetTable; // An array of offsets to each tag
    Tag[TagCount] Tags;
}

Tags

Tags are a special type of data that can be assigned to an object.

They act very similarly to the object's parameters, except tags actually have types that are included in the set data, as well as a pre-defined structure that can be used to read them even if their type is not known.

class Tag
{
    ulong Padding1 = 0;
    ulong TypeOffset; // Points to the tag's type in the BINA String Table
    ulong DataLength;
    ulong DataOffset;

    // The actual data in this array has to be parsed differently based on the tag's type.
    // See the following section for currently-known tags.
    byte[DataLength] Data;
}


While the data itself can be read due to this structure even if the type of tag being read is unknown, actually parsing it unfortunately still requires type-specific code or templates. Here are the specifications for every currently-known type:

RangeSpawning

class RangeSpawning
{
    float RangeIn; // How close the player needs to get to the object before the object will spawn
    float RangeOut; // How far the player needs to move away from the object once it has spawned before the object will de-spawn.
}

Filesystem

All files are located in their directories found in the steam game directory.

  • build\main\projects\exec - Data regarding the executables and modules.
  • image\x64\disk - Packed cpk archives which contains most of the game data are located here including DLC content downloaded from the Steam app's database.
  • savedata\ - Save data are stored in this folder everytime the game saves.

Tools

  • Mods and codes can be managed and run by Hedge Mod Manager by Radfordhound and thesupersonic16.
Sonic Community Hacking Guide
General
SonED2 Manual | Subroutine Equivalency List
Game-Specific
Sonic the Hedgehog (16-bit) | Sonic the Hedgehog (8-bit) | Sonic CD (prototype 510) | Sonic CD | Sonic CD (PC) | Sonic CD (2011) | Sonic 2 (Simon Wai prototype) | Sonic 2 (16-bit) | Sonic 2 (Master System) | Sonic 3 | Sonic 3 & Knuckles | Chaotix | Sonic Jam | Sonic Jam 6 | Sonic Adventure | Sonic Adventure DX: Director's Cut | Sonic Adventure DX: PC | Sonic Adventure (2010) | Sonic Adventure 2 | Sonic Adventure 2: Battle | Sonic Adventure 2 (PC) | Sonic Heroes | Sonic Riders | Sonic the Hedgehog (2006) | Sonic & Sega All-Stars Racing | Sonic Unleashed (Xbox 360/PS3) | Sonic Colours | Sonic Generations | Sonic Forces
Technical information
Sonic Eraser | Sonic 2 (Nick Arcade prototype) | Sonic CD (prototype; 1992-12-04) | Dr. Robotnik's Mean Bean Machine | Sonic Triple Trouble | Tails Adventures | Sonic Crackers | Sonic 3D: Flickies' Island | Sonic & Knuckles Collection | Sonic R | Sonic Shuffle | Sonic Advance | Sonic Advance 3 | Sonic Battle | Shadow the Hedgehog | Sonic Rush | Sonic Classic Collection | Sonic Free Riders | Sonic Lost World
Legacy Guides
The Nemesis Hacking Guides The Esrael Hacking Guides
ROM: Sonic 1 | Sonic 2 | Sonic 2 Beta | Sonic 3

Savestate: Sonic 1 | Sonic 2 Beta/Final | Sonic 3

Sonic 1 (English / Portuguese) | Sonic 2 Beta (English / Portuguese) | Sonic 2 and Knuckles (English / Portuguese)
Move to Sega Retro
Number Systems (or scrap) | Assembly Hacking Guide | 68000 Instruction Set | 68000 ASM-to-Hex Code Reference | SMPS Music Hacking Guide | Mega Drive technical information