pk3DS/pk3DS.WinForms/Tools/Shuffler.cs
2024-06-02 18:30:56 -05:00

138 lines
4.6 KiB
C#

using pk3DS.Core;
using pk3DS.Core.CTR;
using System;
using System.IO;
using System.Linq;
using System.Windows.Forms;
namespace pk3DS.WinForms;
/* GARC File Shuffler
* Shuffles the FATB table references around (Start/End/Length)
* Only shuffles nonfoldered files around.
* Backs up the original file incase the user shuffles a file with ill-effect.
*/
public partial class Shuffler : Form
{
public Shuffler()
{
InitializeComponent();
CB_a.SelectedIndex = CB_b.SelectedIndex = CB_c.SelectedIndex = 0;
// Ban Models, Encounters, TitleScreen etc
banlist = Main.Config.ORAS
?
[
"a005", "a008", "a013", "a039", "a040", "a071", "a072", "a073", "a074", "a075", "a076", "a078", "a079", "a080", "a081", "a082", "a083", "a084", "a085", "a086",
"a100", "a152",
"a195",
]
:
[
"a005", "a007", "a012", "a041", "a042", "a072", "a073", "a074", "a075", "a076", "a078", "a079", "a080", "a081", "a082", "a083", "a084", "a085", "a086", "a087",
"a101", "a165",
"a218",
];
}
private string garc;
private readonly string[] banlist;
private void UpdateLabel(object sender, EventArgs e)
{
garc = Path.Combine(Main.RomFSPath, "a",
CB_a.SelectedIndex.ToString(), CB_b.SelectedIndex.ToString(), CB_c.SelectedIndex.ToString());
if (File.Exists(garc))
{
L_File.Text = $"File: a\\{CB_a.SelectedIndex}\\{CB_b.SelectedIndex}\\{CB_c.SelectedIndex}";
B_Shuffle.Enabled = true;
}
else
{
L_File.Text = "File does not exist!";
B_Shuffle.Enabled = false;
garc = null;
}
}
private void B_Shuffle_Click(object sender, EventArgs e)
{
if (garc == null)
return;
string garcID = L_File.Text.Split(':')[1].Replace("\\", "").Replace(" ", "");
if (banlist.Contains(garcID))
{ WinFormsUtil.Alert("GARC is prevented from being shuffled."); return; }
var g = GARC.UnpackGARC(garc);
// Build a list of all the files we can relocate.
int[] randFiles = new int[g.fatb.FileCount];
int ctr = 0;
for (int i = 0; i < randFiles.Length; i++)
{
if (!g.fatb.Entries[i].IsFolder)
randFiles[ctr++] = i;
}
Array.Resize(ref randFiles, ctr);
if (ctr == 0)
{ WinFormsUtil.Alert("No files to shuffle...?"); return; }
// Create backup
string dest = "backup" + Path.DirectorySeparatorChar + $"PreShuffle {garcID}";
if (!File.Exists(dest))
File.Copy(garc, dest);
var g2 = GARC.UnpackGARC(garc);
int[] newFileOffset = (int[])randFiles.Clone();
Util.Shuffle(newFileOffset);
for (int i = 0; i < randFiles.Length; i++)
g.fatb.Entries[randFiles[i]] = g2.fatb.Entries[newFileOffset[i]];
#region Re-write GARC Header information!
using (var newGARC = File.OpenWrite(garc))
using (var gw = new BinaryWriter(newGARC))
{
gw.Seek(7 * 4, SeekOrigin.Begin); // Skip GARC Header
// Write GARC
// gw.Write((uint)0x47415243); // GARC
// gw.Write((uint)0x0000001C); // Header Length
// gw.Write((ushort)0xFEFF); // Endianness BOM
// gw.Write((ushort)0x0400); // Const (4)
// gw.Write((uint)0x00000004); // Section Count (4)
// gw.Write((uint)0x00000000); // Data Offset (temp)
// gw.Write((uint)0x00000000); // File Length (temp)
// gw.Write((uint)0x00000000); // Largest File Size (temp)
// Write FATO
gw.Write((uint)0x4641544F); // FATO
gw.Write(g.fato.HeaderSize); // Header Size
gw.Write(g.fato.EntryCount); // Entry Count
gw.Write(g.fato.Padding); // Padding
foreach (var t in g.fato.Entries)
gw.Write((uint)t.Offset);
// Write FATB
gw.Write((uint)0x46415442); // FATB
gw.Write(g.fatb.HeaderSize); // Header Size
gw.Write(g.fatb.FileCount); // File Count
foreach (var fe in g.fatb.Entries)
{
gw.Write(fe.Vector);
foreach (var s in fe.SubEntries.Where(s => s.Exists))
{
gw.Write((uint)s.Start);
gw.Write((uint)s.End);
gw.Write((uint)s.Length);
}
}
}
#endregion
WinFormsUtil.Alert("GARC Shuffled!");
}
}