Skip to content

Commit

Permalink
(Wayforward) Ducktales Remastered Collision Support
Browse files Browse the repository at this point in the history
Very close to Half-Genie Hero, just needed some stuff shuffling around.
  • Loading branch information
Knuxfan24 committed Nov 25, 2023
1 parent 27bef7e commit 64b61ec
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 23 deletions.
4 changes: 2 additions & 2 deletions Experimental_Formats.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ The following formats are only partially supported, either due to missing functi

## Wayforward Engine

- [Collision (.clb)](KnuxLib/Engines/Wayforward/Collision.cs). Reads and writes fine, but Seven Sirens has a massive chunk of Unknown Data added on to the end that is completely alien to me and needs to be properly reverse engineered. Also has a (potentially temporary) OBJ exporter and Assimp importer.
- [Collision (.clb)](KnuxLib/Engines/Wayforward/Collision.cs). Reads and writes fine, but Seven Sirens has a massive chunk of Unknown Data added on to the end that is completely alien to me and needs to be properly reverse engineered. Ducktales Remastered also handles some of the data slightly differently, which isn't properly handled right now. Also has a (potentially temporary?) OBJ exporter and Assimp importer.

- [Level Binary (.lvb)](KnuxLib/Engines/Wayforward/LevelBinary.cs). Really, REALLY unfinished reading. Honestly this format has pushed me to my limit I hate it. Left out of KnuxTools because it has next to no functionality in its current state.
- [Level Binary (.lvb)](KnuxLib/Engines/Wayforward/LevelBinary.cs). Really, REALLY unfinished reading that barely gets anywhere. Honestly this format has pushed me to my limit I hate it. Left out of KnuxTools because it has next to no functionality in its current state.

- [Mesh (.wf3d/.gpu)](KnuxLib/Engines/Wayforward/Mesh.cs) and the chunks that make them up. This code currently reads MOST of the data (although a lot of chunks have unknown bits that I am yet to successfully read), but a lot of the data is a mystery. This format also has some (slightly messy) unfinished functionality for Saving, Exporting to OBJ (which is intended to be temporary due to it not supporting things I've yet to reverse engineer) and Assimp Importing.
83 changes: 64 additions & 19 deletions KnuxLib/Engines/Wayforward/Collision.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@ public Collision(string filepath, FormatVersion version = FormatVersion.hero, bo
// Classes for this format.
public enum FormatVersion
{
hero = 0,
sevensirens = 1
duck = 0,
hero = 1,
sevensirens = 2
}

[Flags]
Expand Down Expand Up @@ -72,8 +73,21 @@ public class Model
{
/// <summary>
/// This model's Axis-Aligned Bounding Box.
/// Doesn't seem to exist in the Ducktales Remastered version of the format?
/// </summary>
public Vector3[] AABB { get; set; } = new Vector3[2];
public Vector3[]? AABB { get; set; }

/// <summary>
/// An unknown Vector3 value that is only present in Ducktales Remastered.
/// TODO: What is this?
/// </summary>
public Vector3? UnknownVector3_1 { get; set; }

/// <summary>
/// An unknown integer value that is only present in Ducktales Remastered.
/// TODO: What is this?
/// </summary>
public uint? UnknownUInt32_1 { get; set; }

/// <summary>
/// How this model behaves.
Expand Down Expand Up @@ -150,16 +164,33 @@ public void Load(string filepath, FormatVersion version = FormatVersion.hero)
// Define a new model entry.
Model model = new();

// Loop through and read the two values of this model's Axis-Aligned Bounding Box.
for (int aabb = 0; aabb < 2; aabb++)
model.AABB[aabb] = reader.ReadVector3();
if (version != FormatVersion.duck)
{
// Initialise the AABB values.
model.AABB = new Vector3[2];

// Read an unknown 64 bit integer.
model.Behaviour = (Behaviour)reader.ReadUInt64();
// Loop through and read the two values of this model's Axis-Aligned Bounding Box.
for (int aabb = 0; aabb < 2; aabb++)
model.AABB[aabb] = reader.ReadVector3();

// Read an unknown 64 bit integer that only exists in Seven Sirens.
if (version == FormatVersion.sevensirens)
model.UnknownULong_1 = reader.ReadUInt64();
// Read an this model's behaviour flags.
model.Behaviour = (Behaviour)reader.ReadUInt64();

// Read an unknown 64 bit integer that only exists in Seven Sirens.
if (version == FormatVersion.sevensirens)
model.UnknownULong_1 = reader.ReadUInt64();
}
else
{
// Read the unknown Vector3 that only exists in Ducktales Remastered.
model.UnknownVector3_1 = reader.ReadVector3();

// Read the unknown integer value that only exists in Ducktales Remastered.
model.UnknownUInt32_1 = reader.ReadUInt32();

// Read an this model's behaviour flags.
model.Behaviour = (Behaviour)reader.ReadUInt64();
}

// Read the offset to this model's vertex and face data.
long modelDataOffset = reader.ReadInt64();
Expand Down Expand Up @@ -287,16 +318,30 @@ public void Save(string filepath, FormatVersion version = FormatVersion.hero)
// Loop through and write the model table.
for (int modelIndex = 0; modelIndex < Data.Models.Count; modelIndex++)
{
// Write this model's AABB values.
writer.Write(Data.Models[modelIndex].AABB[0]);
writer.Write(Data.Models[modelIndex].AABB[1]);
if (version != FormatVersion.duck)
{
// Write this model's AABB values.
writer.Write(Data.Models[modelIndex].AABB[0]);
writer.Write(Data.Models[modelIndex].AABB[1]);

// Write this model's behaviour tag.
writer.Write((ulong)Data.Models[modelIndex].Behaviour);
// Write this model's behaviour tag.
writer.Write((ulong)Data.Models[modelIndex].Behaviour);

// Write this model's unknown integer value.
if (version == FormatVersion.sevensirens)
writer.Write((ulong)Data.Models[modelIndex].UnknownULong_1);
// Write this model's unknown integer value that only exists in Seven Sirens.
if (version == FormatVersion.sevensirens)
writer.Write((ulong)Data.Models[modelIndex].UnknownULong_1);
}
else
{
// Write the unknown Vector3 that only exists in Ducktales Remastered.
writer.Write((Vector3)Data.Models[modelIndex].UnknownVector3_1);

// Write the unknown integer value that only exists in Ducktales Remastered.
writer.Write((uint)Data.Models[modelIndex].UnknownUInt32_1);

// Write this model's behaviour tag.
writer.Write((ulong)Data.Models[modelIndex].Behaviour);
}

// Add an offset for this model's data.
writer.AddOffset($"Model{modelIndex}Data", 0x08);
Expand Down
1 change: 1 addition & 0 deletions KnuxLib/Engines/Wayforward/Environment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public Environment(string filepath, bool export = false)
JsonSerialise($@"{Path.GetDirectoryName(filepath)}\{Path.GetFileNameWithoutExtension(filepath)}.wayforward.environment.json", Data);
}

// Classes for this format.
public class Entity
{
/// <summary>
Expand Down
2 changes: 2 additions & 0 deletions KnuxLib/Engines/Wayforward/LevelBinary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public LevelBinary(string filepath, bool export = false)
JsonSerialise($@"{Path.GetDirectoryName(filepath)}\{Path.GetFileNameWithoutExtension(filepath)}.wayforward.levelbinary.json", Data);
}

// Classes for this format.
public class FormatData
{
public Entity[] Entities { get; set; } = Array.Empty<Entity>();
Expand Down Expand Up @@ -45,6 +46,7 @@ public class Spline
public override string ToString() => Name;
}

// Actual data presented to the end user.
public FormatData Data = new();

/// <summary>
Expand Down
14 changes: 14 additions & 0 deletions KnuxTools/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ namespace KnuxTools
// World Adventure Wii SET -> HSON
// Wayforward Model Importing
// TODO: Tidy up duplicated comments and parts where they are sorely lacking.
// TODO: Make the indents for version/extension flags consistent.
internal class Program
{
static void Main(string[] args)
Expand Down Expand Up @@ -176,6 +177,7 @@ static void Main(string[] args)

Console.WriteLine("Wayforward Engine:");
ColourConsole("Collision (.clb) - Converts to an OBJ format and imports from an Assimp compatible model.");
ColourConsole(" Version Flag (Ducktales Remastered) - wayforward_collision_duck", true, ConsoleColor.Yellow);
ColourConsole(" Version Flag (Half-Genie Hero) - wayforward_collision_hgh", true, ConsoleColor.Yellow);
ColourConsole(" Version Flag (Seven Sirens) - wayforward_collision_ss", true, ConsoleColor.Yellow);
Console.WriteLine("Environment (.env)");
Expand Down Expand Up @@ -382,6 +384,7 @@ private static void HandleFile(string arg, string? extension, string? version)
Console.WriteLine("This file is a generic model, please specifiy what format to import and save it as:");
Console.WriteLine(" carz\t\t\t(CarZ Engine SCO model & MAT material library)");
ColourConsole(" wayforward\t\t\t(Wayforward Engine WF3D Mesh)");
ColourConsole(" wayforward_collision_duck\t(Wayforward Engine Collision for Ducktales Remastered)");
ColourConsole(" wayforward_collision_hgh\t(Wayforward Engine Collision for Half-Genie Hero)");
ColourConsole(" wayforward_collision_ss\t(Wayforward Engine Collision for Seven Sirens)");

Expand Down Expand Up @@ -425,6 +428,15 @@ private static void HandleFile(string arg, string? extension, string? version)
// TODO: Figure this out later.
break;

case "wayforward_collision_duck":
Console.WriteLine("Converting model to a Wayforward Engine collision file.");
using (KnuxLib.Engines.Wayforward.Collision collision = new())
{
collision.ImportAssimp(arg);
collision.Save($@"{Path.GetDirectoryName(arg)}\{Path.GetFileNameWithoutExtension(arg)}.clb", KnuxLib.Engines.Wayforward.Collision.FormatVersion.duck);
}
break;

case "wayforward_collision_hgh":
Console.WriteLine("Converting model to a Wayforward Engine collision file.");
using (KnuxLib.Engines.Wayforward.Collision collision = new())
Expand Down Expand Up @@ -1662,6 +1674,7 @@ private static void HandleFile(string arg, string? extension, string? version)
if (string.IsNullOrEmpty(version))
{
Console.WriteLine("This file has multiple variants that can't be auto detected, please specifiy the variant:");
Console.WriteLine(" duck\t\t(Ducktales Remastered)");
Console.WriteLine(" hgh\t\t\t(Half-Genie Hero)");
Console.WriteLine(" ss\t\t\t(Seven Sirens)");

Expand All @@ -1686,6 +1699,7 @@ private static void HandleFile(string arg, string? extension, string? version)
Console.WriteLine("Converting Wayforward Engine collision to OBJ.");
switch (version.ToLower())
{
case "duck": using (KnuxLib.Engines.Wayforward.Collision clb = new(arg, KnuxLib.Engines.Wayforward.Collision.FormatVersion.duck, true)) break;
case "hgh": using (KnuxLib.Engines.Wayforward.Collision clb = new(arg, KnuxLib.Engines.Wayforward.Collision.FormatVersion.hero, true)) break;
case "ss": using (KnuxLib.Engines.Wayforward.Collision clb = new(arg, KnuxLib.Engines.Wayforward.Collision.FormatVersion.sevensirens, true)) break;

Expand Down
6 changes: 4 additions & 2 deletions Supported_Formats.md
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,8 @@ Known games:

- Shantae Risky's Revenge (DSi, iOS, PC, PS4, Wii U, NSW, XB1, PS5)

- Ducktales Remastered (PS3, Wii U, PC, X360, Android, iOS, WP)

- Shantae Half-Genie Hero (PS4, PSV, Wii U, PC, XB1, NSW, PS5)

- Shantae and the Seven Sirens (iOS, NSW, PS4, PC, XB1, PS5)
Expand All @@ -235,14 +237,14 @@ Supported formats:
- [Layer List (.lgb)](KnuxLib/Engines/Wayforward/Layers.cs) reading, writing, JSON serialisation and JSON deserialisation.

> **Note**
> Unsure on the length of the strings within this file, 40 characters seems to work, but I'm not confident that all 40 can be used. Needs testing.
> String lengths are definitely wrong if the garbage data presented by the Ducktales Remastered format is anything to go by.
- [List Table (.ltb)](KnuxLib/Engines/Wayforward/ListTable.cs) reading, writing, JSON serialisation and JSON deserialisation.

- [Package Archive (.pak)](KnuxLib/Engines/Wayforward/Package.cs) reading, writing, data extraction and data importing.

> **Note**
> The Package Archive code produces incorrect results for some files in Half-Genie Hero due to an incorrect assumption that needs revising. Risky's Revenge resaving has not been tested.
> The Package Archive code produces incorrect results for some files in Half-Genie Hero due to an incorrect assumption that needs revising. Risky's Revenge and Ducktales resaving has not been tested.
- Definitely used by other Wayforward games, but I have yet to obtain and look at them myself.

Expand Down

0 comments on commit 64b61ec

Please sign in to comment.