Actions

SCHG

Difference between revisions of "Sonic Forces"

From Sonic Retro

(Added a WIP SCHG page for Forces.)
 
m (Text replacement - " =([A-Za-z]*)= " to " ==$1== ")
 
(9 intermediate revisions by 4 users not shown)
Line 1: Line 1:
{{SCHG SC}}
+
{{SCHG SF}}
  
 
This is the '''[[Sonic Community Hacking Guide]]''' for the PC version of '''''[[Sonic Forces]]'''''.<br />
 
This is the '''[[Sonic Community Hacking Guide]]''' for the PC version of '''''[[Sonic Forces]]'''''.<br />
 
Sonic Forces you to use this stuff to fix his game.
 
Sonic Forces you to use this stuff to fix his game.
  
=File/Format specifications=
+
==Tools==
==BINA==
+
==Hedge Mod Manager==
'''NOTE:''' This section is highly based on my page on Sonic Colors formats: [[SCHG:Sonic_Colors]]
+
[[Hedge Mod Manager]] is a mod/code loader and manager for various Hedgehog Engine-based games, most noticeably ''[[Sonic Forces]]''. It was written in C# by Radfordhound and thesupersonic16, and is [https://github.com/thesupersonic16/HedgeModManager fully open-source]/[https://github.com/thesupersonic16/HedgeModManager/blob/master/LICENSE.md under the MIT License]. You can download the latest version [https://github.com/thesupersonic16/HedgeModManager/releases/latest here].
  
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]].
+
==SFPac==
 +
SFPac is an open-source Python script written by Skyth which can be used to extract/repack archives in the Forces variation of the .PAC format. You can download the latest version [https://gist.github.com/blueskythlikesclouds/887d227301dd3c0ea3c62ab6984388cc here].
  
Generally speaking, any file that follows the BINA Format is structured as follows:
+
Simply drop .PAC files onto it to extract them, or drop a folder onto it to repack.
* 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:
+
==ModelConverter==
 +
ModelConverter is a command-line tool written by Skyth in C++ that allows you to convert FBX/DAE files to .model and .material files in the Forces variation of the format (I.E. with the new header). You can download it [https://drive.google.com/file/d/122Q3iefdAcGp1vRNI0Ng_KQztFK_quSd/view?usp=drivesdk here].
  
===Header===
+
Simply drop an FBX or DAE file onto it to convert it to Forces .model and .material formats.
<pre>
 
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";
+
Additionally however, you can append "tags" to the end of your model/mesh/material names before exporting to FBX/DAE to set various options.
    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.
+
* '''@LYR(layer)'''    Specifies what layer the given material will operate on.
    ushort AdditionalDataLength = 0x18;
+
* '''@NAME(meshName)'''     Specifies the name of the given mesh. Used for mouth-swapping, for example.
    ushort Padding = 0; // Just two nulls to pad-out AdditionalDataLength to 4 bytes.
+
* '''@PRP(propertyName, value)'''    Specifies a property that will be added to the header of the given mesh.
    byte[AdditionalDataLength] AdditionalData; // Seems to always just contain nulls
 
}
 
</pre>
 
  
===String Table===
+
Properties are a component of the new header used in Lost World and Forces. Some examples of properties include:
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===
+
* '''ShadowCa'''    Specifies whether or not the given mesh should cast a shadow.
This is the most complicated part of the BINA format by far, so brace yourselves!! <br />
+
* '''ShadowRe'''   Specifies whether or not the given mesh should receive a shadow.
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.
+
For example, if you wish to make a mesh called "MyMesh" cast a shadow, you'd simply append "@PRP(ShadowCa, true)" to the end of its name, so that the mesh's name is "MyMesh@PRP(ShadowCa, true)" upon exporting the FBX/DAE file.
  
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:
+
==HedgeEdit==
<pre>
+
[[HedgeEdit]] is an [https://github.com/Radfordhound/HedgeLib/tree/master/HedgeEdit open-source] level editor similar to [[SonicGlvl]], but with support for various 3D Sonic games in the series, rather than being primarily focused on ''[[Sonic Generations]]''. It was written in C# by Radfordhound using [[HedgeLib]], and is [https://github.com/Radfordhound/HedgeLib/blob/master/HedgeEdit/License.txt under the MIT License]. You can download the latest version [https://ci.appveyor.com/project/Radfordhound/hedgelib/build/artifacts here].
44 48 42 42
 
</pre>
 
  
 +
==References==
 +
<references />
  
The first value in this table (44 in hex) is represented as the following in binary:
+
{{SCHGuides}}
<pre>
+
[[Category:Sonic Community Hacking Guide]]
0100 0100
 
</pre>
 
 
 
 
 
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:
 
<pre>
 
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.
 
</pre>
 
 
 
 
 
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! <br />
 
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:
 
 
 
<pre>
 
struct Header
 
{
 
    ulong Padding1 = 0;
 
    ulong Padding2 = 0;
 
   
 
    ulong ObjectOffsetTableOffset;
 
    ulong ObjectCount;
 
 
 
    ulong ObjectCount2; // 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;
 
    uint Padding2 = 0;
 
 
 
    float[3] Position;
 
    float[3] Rotation; // Not euler angles. I honestly don't know what it is yet..
 
    float[3] Position2; // No idea lol. Seems to be the position again most of the time?
 
    float[3] Rotation2; // Same as with Position2.
 
   
 
    ulong NamedParametersOffsetTableOffset;
 
    ulong NamedParametersCount; // ?
 
    ulong UnknownCount1; // Seems to always be the same as NamedParametersCount?
 
    ulong Padding3 = 0;
 
    ulong ParametersOffset; // Parameters are hard-coded for each object type.
 
 
 
    ulong[NamedParametersCount] NamedParametersOffsetTable; // An array of offsets to each named parameter
 
    NamedParameter[NamedParametersCount] NamedParameters;
 
}
 
 
 
class NamedParameter
 
{
 
    ulong Padding1 = 0;
 
    ulong NameOffset; // Points to the parameter's name in the BINA String Table
 
    ulong DataLength;
 
    ulong DataOffset;
 
 
 
    // The actual data in this array has to be parsed differently based on the parameter's name
 
    // RangeSpawning = Two floats which represent the spawn and despawn distance between the player and the object.
 
    byte[DataLength] Data;
 
}
 
</pre>
 

Latest revision as of 16:52, 24 March 2020

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.

Tools

Hedge Mod Manager

Hedge Mod Manager is a mod/code loader and manager for various Hedgehog Engine-based games, most noticeably Sonic Forces. It was written in C# by Radfordhound and thesupersonic16, and is fully open-source/under the MIT License. You can download the latest version here.

SFPac

SFPac is an open-source Python script written by Skyth which can be used to extract/repack archives in the Forces variation of the .PAC format. You can download the latest version here.

Simply drop .PAC files onto it to extract them, or drop a folder onto it to repack.

ModelConverter

ModelConverter is a command-line tool written by Skyth in C++ that allows you to convert FBX/DAE files to .model and .material files in the Forces variation of the format (I.E. with the new header). You can download it here.

Simply drop an FBX or DAE file onto it to convert it to Forces .model and .material formats.

Additionally however, you can append "tags" to the end of your model/mesh/material names before exporting to FBX/DAE to set various options.

  • @LYR(layer) Specifies what layer the given material will operate on.
  • @NAME(meshName) Specifies the name of the given mesh. Used for mouth-swapping, for example.
  • @PRP(propertyName, value) Specifies a property that will be added to the header of the given mesh.

Properties are a component of the new header used in Lost World and Forces. Some examples of properties include:

  • ShadowCa Specifies whether or not the given mesh should cast a shadow.
  • ShadowRe Specifies whether or not the given mesh should receive a shadow.

For example, if you wish to make a mesh called "MyMesh" cast a shadow, you'd simply append "@PRP(ShadowCa, true)" to the end of its name, so that the mesh's name is "MyMesh@PRP(ShadowCa, true)" upon exporting the FBX/DAE file.

HedgeEdit

HedgeEdit is an open-source level editor similar to SonicGlvl, but with support for various 3D Sonic games in the series, rather than being primarily focused on Sonic Generations. It was written in C# by Radfordhound using HedgeLib, and is under the MIT License. You can download the latest version here.

References


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