Skip to content
This repository has been archived by the owner on Nov 29, 2023. It is now read-only.

Commit

Permalink
Add support for binding CPK folders for Y5R, JE, and LJ
Browse files Browse the repository at this point in the history
  • Loading branch information
mosamadeeb committed Sep 19, 2022
1 parent f3d7b29 commit fd00937
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 10 deletions.
15 changes: 13 additions & 2 deletions ModLoadOrder/Generator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ public static async Task GenerateModLoadOrder(List<string> mods, bool looseFiles
string modPath;
string subPathName;
List<string> foldersNotFound;
Dictionary<string, List<int>> cpkDictionary = new Dictionary<string, List<int>>();
Console.WriteLine("Reading mods...\n");

// TODO: Make mod reading async
Expand All @@ -68,10 +69,20 @@ public static async Task GenerateModLoadOrder(List<string> mods, bool looseFiles

mod.PrintInfo();

if (mod.Files.Count > 0 || mod.ParFolders.Count > 0)
if (mod.Files.Count > 0 || mod.ParFolders.Count > 0 || mod.CpkFolders.Count > 0)
{
files.UnionWith(mod.Files);
modIndices.Add(files.Count);

foreach (string folder in mod.CpkFolders)
{
if (!cpkDictionary.ContainsKey(folder))
{
cpkDictionary[folder] = new List<int>();
}

cpkDictionary[folder].Add(mods.Count - 1 - i);
}
}
else
{
Expand Down Expand Up @@ -119,7 +130,7 @@ public static async Task GenerateModLoadOrder(List<string> mods, bool looseFiles
Console.Write($"Generating {Constants.MLO} file...");

// Generate MLO
ModLoadOrder mlo = new ModLoadOrder(modIndices, mods, files, loose.ParlessFolders);
ModLoadOrder mlo = new ModLoadOrder(modIndices, mods, files, loose.ParlessFolders, cpkDictionary);
mlo.WriteMLO(Path.Combine(GamePath.GetGamePath(), Constants.MLO));

Console.WriteLine(" DONE!\n");
Expand Down
39 changes: 33 additions & 6 deletions ModLoadOrder/ModLoadOrder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@ public class ModLoadOrder
{
public const string MAGIC = "_OLM"; // MLO_ but in little endian cause that's how the yakuza works
public const uint ENDIANNESS = 0x21; // Little endian
public const uint VERSION = 0x010002; // 1.2
public const uint VERSION = 0x020000; // 2.0
public const uint FILESIZE = 0x0; // Remaining faithful to RGG by adding a filesize that is not used

public List<string> Mods;
public List<(string, int)> Files;
public List<(string, int)> ParlessFolders;
public List<(string, List<ushort>)> CpkFolders;

public ModLoadOrder(List<int> modIndices, List<string> mods, OrderedSet<string> fileSet, List<(string, int)> parlessFolders)
public ModLoadOrder(List<int> modIndices, List<string> mods, OrderedSet<string> fileSet, List<(string, int)> parlessFolders, Dictionary<string, List<int>> cpkFolders)
{
List<string> files = fileSet.ToList();

Expand All @@ -33,6 +34,7 @@ public ModLoadOrder(List<int> modIndices, List<string> mods, OrderedSet<string>
}

this.ParlessFolders = parlessFolders.Select(f => (f.Item1.ToLowerInvariant().Replace('\\', '/'), f.Item2)).ToList();
this.CpkFolders = cpkFolders.Select(pair => (pair.Key.ToLowerInvariant().Replace('\\', '/'), pair.Value.Select(v => (ushort)v).ToList())).ToList();
}

public void WriteMLO(string path)
Expand All @@ -45,7 +47,7 @@ public void WriteMLO(string path)
writer.Write(VERSION);
writer.Write(FILESIZE);

writer.Write(0x30); // Mods start (size of header)
writer.Write(0x40); // Mods start (size of header)
writer.WriteOfType(typeof(uint), this.Mods.Count);

writer.Write(0); // Files start (to be written later)
Expand All @@ -54,8 +56,11 @@ public void WriteMLO(string path)
writer.Write(0); // Parless folders start (to be written later)
writer.WriteOfType(typeof(uint), this.ParlessFolders.Count);

// Align
writer.WriteTimes(0, 8);
writer.Write(0); // Cpk folders start (to be written later)
writer.WriteOfType(typeof(uint), this.CpkFolders.Count);

// Pad
writer.WriteTimes(0, 0x10);

// 0x0: length
// 0x2: string
Expand Down Expand Up @@ -89,6 +94,24 @@ public void WriteMLO(string path)
writer.Write(folder);
}

long cpkFoldersStartPos = writer.Stream.Position;

// 0x0: mod count
// 0x2: length
// 0x4: string
// 0x?: mod indices
foreach ((string folder, List<ushort> indices) in this.CpkFolders)
{
writer.WriteOfType(typeof(ushort), indices.Count);
writer.WriteOfType(typeof(ushort), folder.Length + 1);
writer.Write(folder);

foreach (ushort index in indices)
{
writer.WriteOfType(typeof(ushort), index);
}
}

// Write file size
writer.Stream.Seek(0xC, SeekOrigin.Begin);
writer.WriteOfType(typeof(uint), writer.Stream.Length);
Expand All @@ -97,10 +120,14 @@ public void WriteMLO(string path)
writer.Stream.Seek(0x18, SeekOrigin.Begin);
writer.WriteOfType(typeof(uint), fileStartPos);

// Write mods start position
// Write parless folders start position
writer.Stream.Seek(0x20, SeekOrigin.Begin);
writer.WriteOfType(typeof(uint), parlessStartPos);

// Write cpk folders start position
writer.Stream.Seek(0x28, SeekOrigin.Begin);
writer.WriteOfType(typeof(uint), cpkFoldersStartPos);

// Close the file stream
writer.Stream.Dispose();
}
Expand Down
52 changes: 50 additions & 2 deletions ModLoadOrder/Mods/Mod.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,27 @@ public class Mod

public string Name { get; set; }

// Files that can be directly loaded from the mod path
/// <summary>
/// Files that can be directly loaded from the mod path.
/// </summary>
public List<string> Files { get; }

// Folders that have to be repacked into pars before running the game
/// <summary>
/// Folders that have to be repacked into pars before running the game.
/// </summary>
public List<string> ParFolders { get; }

/// <summary>
/// Folders that need to be bound as a directory to a CPK binder.
/// </summary>
public List<string> CpkFolders { get; }

public Mod(string name, int indent = 2)
{
this.Name = name;
this.Files = new List<string>();
this.ParFolders = new List<string>();
this.CpkFolders = new List<string>();

this.console = new ConsoleOutput(indent);
this.console.WriteLine($"Reading directory: {name} ...");
Expand All @@ -43,6 +53,11 @@ public void PrintInfo()
{
this.console.WriteLine($"Added {this.ParFolders.Count} folder(s) to be repacked");
}

if (this.CpkFolders.Count > 0)
{
this.console.WriteLine($"Added {this.CpkFolders.Count} CPK folder(s) to be bound");
}
}
else
{
Expand Down Expand Up @@ -101,6 +116,39 @@ public void AddFiles(string path, string check)
break;
}

// Check for CPK directories
string cpkDataPath;
switch (basename)
{
case "se":
case "speech":
cpkDataPath = GamePath.RemoveModPath(path);
if (GamePath.GetGame() == Game.Yakuza5)
{
this.CpkFolders.Add(cpkDataPath + ".cpk");
this.console.WriteLineIfVerbose($"Adding CPK folder: {cpkDataPath}");
}

break;
case "stream":
case "stream_en":
case "stmdlc":
case "stmdlc_en":
case "movie":
case "moviesd":
case "moviesd_dlc":
cpkDataPath = GamePath.RemoveModPath(path);
if (GamePath.GetGame() == Game.Judgment || GamePath.GetGame() == Game.LostJudgment)
{
this.CpkFolders.Add(cpkDataPath + ".par");
this.console.WriteLineIfVerbose($"Adding CPK folder: {cpkDataPath}");
}

break;
default:
break;
}

if (needsRepack)
{
string dataPath = GamePath.GetDataPathFrom(path);
Expand Down

0 comments on commit fd00937

Please sign in to comment.