mirror of
https://github.com/kwsch/pk3DS.git
synced 2026-03-22 01:44:33 -05:00
393 lines
15 KiB
C#
393 lines
15 KiB
C#
using pk3DS.Core.CTR;
|
|
using System;
|
|
using System.Drawing;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Threading;
|
|
using System.Windows.Forms;
|
|
using pk3DS.Core;
|
|
|
|
namespace pk3DS.WinForms;
|
|
|
|
public sealed partial class ToolsUI : Form
|
|
{
|
|
public ToolsUI()
|
|
{
|
|
InitializeComponent();
|
|
AllowDrop = PB_Unpack.AllowDrop = PB_Repack.AllowDrop = PB_BCLIM.AllowDrop = true;
|
|
DragEnter += TabMain_DragEnter;
|
|
DragDrop += TabMain_DragDrop;
|
|
PB_Unpack.DragEnter += TabMain_DragEnter;
|
|
PB_Unpack.DragDrop += TabMain_DragDrop;
|
|
PB_Repack.DragEnter += TabMain_DragEnter;
|
|
PB_Repack.DragDrop += TabMain_DragDrop;
|
|
PB_BCLIM.DragEnter += TabMain_DragEnter;
|
|
PB_BCLIM.DragDrop += TabMain_DragDrop;
|
|
CLIMWindow = PB_BCLIM.Size;
|
|
CB_Repack.Items.Add("Autodetect");
|
|
CB_Repack.Items.Add("GARC Pack");
|
|
CB_Repack.Items.Add("DARC Pack (use filenames)");
|
|
CB_Repack.Items.Add("Mini Pack (from Name)");
|
|
CB_Repack.SelectedIndex = 0;
|
|
}
|
|
|
|
private void TabMain_DragEnter(object sender, DragEventArgs e)
|
|
{
|
|
if (e.Data.GetDataPresent(DataFormats.FileDrop))
|
|
e.Effect = DragDropEffects.Copy;
|
|
}
|
|
|
|
private void TabMain_DragDrop(object sender, DragEventArgs e)
|
|
{
|
|
string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
|
|
foreach (var path in files)
|
|
HandleDrop(sender, path);
|
|
System.Media.SystemSounds.Asterisk.Play();
|
|
}
|
|
|
|
private void HandleDrop(object sender, string path)
|
|
{
|
|
if (sender == PB_Unpack)
|
|
OpenARC(path, pBar1);
|
|
else if (sender == PB_BCLIM)
|
|
OpenIMG(path);
|
|
else if (sender == PB_Repack)
|
|
SaveARC(path);
|
|
else
|
|
DecompressLZSS_BLZ(path);
|
|
}
|
|
|
|
private void DecompressLZSS_BLZ(string path)
|
|
{
|
|
try
|
|
{
|
|
LZSS.Decompress(path, Path.Combine(Path.GetDirectoryName(path), "dec_" + Path.GetFileName(path)));
|
|
File.Delete(path);
|
|
}
|
|
catch
|
|
{
|
|
try
|
|
{
|
|
if (threads < 1)
|
|
new Thread(() => { Interlocked.Increment(ref threads); new BLZCoder(["-d", path], pBar1); Interlocked.Decrement(ref threads); WinFormsUtil.Alert("Decompressed!"); }).Start();
|
|
}
|
|
catch { WinFormsUtil.Error("Unable to process file."); threads = 0; }
|
|
}
|
|
}
|
|
|
|
private void DropHover(object sender, EventArgs e) => ((Panel)sender).BackColor = Color.Gray;
|
|
private void DropLeave(object sender, EventArgs e) => ((Panel)sender).BackColor = Color.Transparent;
|
|
|
|
private void OpenIMG(string path)
|
|
{
|
|
var img = BCLIM.MakeBMP(path, CHK_PNG.Checked);
|
|
if (img == null)
|
|
{
|
|
try
|
|
{
|
|
var flim = new BFLIM(path);
|
|
if (!flim.Footer.Valid)
|
|
return;
|
|
img = flim.GetBitmap();
|
|
}
|
|
catch (Exception ree)
|
|
{
|
|
Console.WriteLine(ree.Message);
|
|
return;
|
|
}
|
|
if (CHK_PNG.Checked)
|
|
{
|
|
var dir = Path.GetDirectoryName(path);
|
|
var fn = Path.GetFileNameWithoutExtension(path);
|
|
var outpath = Path.Combine(dir, $"{fn}.png");
|
|
img.Save(outpath);
|
|
}
|
|
}
|
|
PB_BCLIM.Size = new Size(img.Width + 2, img.Height + 2);
|
|
PB_BCLIM.BackgroundImage = img;
|
|
int leftpad = PB_BCLIM.Location.X;
|
|
int suggestedWidth = (leftpad * 2) + PB_BCLIM.Width + 10;
|
|
if (Width < suggestedWidth)
|
|
Width = suggestedWidth;
|
|
|
|
int suggestedHeight = PB_BCLIM.Location.Y + PB_BCLIM.Height + leftpad + 30;
|
|
if (Height < suggestedHeight)
|
|
Height = suggestedHeight;
|
|
}
|
|
|
|
internal static volatile int threads;
|
|
|
|
internal static void OpenARC(string path, ProgressBar pBar1, bool recursing = false)
|
|
{
|
|
string newFolder = "";
|
|
try
|
|
{
|
|
// Pre-check file length to see if it is at least valid.
|
|
var fi = new FileInfo(path);
|
|
if (fi.Length > (long)2 * (1 << 30))
|
|
{ WinFormsUtil.Error("File is too big!"); return; } // 2 GB
|
|
string folderPath = Path.Combine(Path.GetDirectoryName(path), Path.GetFileNameWithoutExtension(path));
|
|
|
|
byte[] first4 = new byte[4];
|
|
try
|
|
{
|
|
using var fs = new FileStream(path, FileMode.Open);
|
|
using var bw = new BinaryReader(fs);
|
|
first4 = bw.ReadBytes(4);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
WinFormsUtil.Error("Cannot open file!", e.ToString());
|
|
}
|
|
|
|
// Determine if it is a DARC or a Mini
|
|
// Check if Mini first
|
|
string fx = fi.Length > 10 * (1 << 20) ? null : Mini.GetIsMini(path); // no mini is above 10MB
|
|
if (fx != null) // Is Mini Packed File
|
|
{
|
|
newFolder = folderPath + "_" + fx;
|
|
// Fetch Mini File Contents
|
|
Mini.UnpackMini(path, fx, newFolder, false);
|
|
// Recurse throught the extracted contents if they extract successfully
|
|
if (Directory.Exists(newFolder))
|
|
{
|
|
foreach (string file in Directory.GetFiles(newFolder))
|
|
OpenARC(file, pBar1, true);
|
|
BatchRenameExtension(newFolder);
|
|
}
|
|
}
|
|
else if (first4.SequenceEqual(BitConverter.GetBytes(0x54594C41))) // ALYT
|
|
{
|
|
if (threads > 0) { WinFormsUtil.Alert("Please wait for all operations to finish first."); return; }
|
|
new Thread(() =>
|
|
{
|
|
Interlocked.Increment(ref threads);
|
|
var alyt = new ALYT(File.ReadAllBytes(path));
|
|
var sarc = new SARC(alyt.Data) // rip out sarc
|
|
{
|
|
FileName = Path.GetFileNameWithoutExtension(path) + "_sarc",
|
|
FilePath = Path.GetDirectoryName(path),
|
|
};
|
|
if (!sarc.Valid)
|
|
return;
|
|
var files = sarc.Dump();
|
|
foreach (var _ in files)
|
|
{
|
|
// openARC(file, pBar1, true);
|
|
}
|
|
Interlocked.Decrement(ref threads);
|
|
}).Start();
|
|
}
|
|
else if (first4.SequenceEqual(BitConverter.GetBytes(0x47415243))) // GARC
|
|
{
|
|
if (threads > 0) { WinFormsUtil.Alert("Please wait for all operations to finish first."); return; }
|
|
bool SkipDecompression = ModifierKeys == Keys.Control;
|
|
new Thread(() =>
|
|
{
|
|
Interlocked.Increment(ref threads);
|
|
bool r = GarcUtil.UnpackGARC(path, folderPath + "_g", SkipDecompression, pBar1);
|
|
Interlocked.Decrement(ref threads);
|
|
if (r)
|
|
{
|
|
BatchRenameExtension(newFolder);
|
|
}
|
|
else
|
|
{ WinFormsUtil.Alert("Unpacking failed."); return; }
|
|
System.Media.SystemSounds.Asterisk.Play();
|
|
}).Start();
|
|
}
|
|
else if (ARC.Analyze(path).valid) // DARC
|
|
{
|
|
var data = File.ReadAllBytes(path);
|
|
int pos = 0;
|
|
while (BitConverter.ToUInt32(data, pos) != 0x63726164)
|
|
{
|
|
pos += 4;
|
|
if (pos >= data.Length) return;
|
|
}
|
|
var darcData = data.Skip(pos).ToArray();
|
|
newFolder = folderPath + "_d";
|
|
bool r = Core.CTR.DARC.Darc2files(darcData, newFolder);
|
|
if (!r)
|
|
{ WinFormsUtil.Alert("Unpacking failed."); }
|
|
}
|
|
else if (ARC.AnalyzeSARC(path).Valid)
|
|
{
|
|
var sarc = ARC.AnalyzeSARC(path);
|
|
Console.WriteLine($"New SARC with {sarc.SFAT.EntryCount} files.");
|
|
foreach (var _ in sarc.Dump(path))
|
|
{
|
|
}
|
|
}
|
|
else if (!recursing)
|
|
{ WinFormsUtil.Alert("File is not a darc or a mini packed file:" + Environment.NewLine + path); }
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
if (!recursing)
|
|
WinFormsUtil.Error("File error:" + Environment.NewLine + path, e.ToString());
|
|
threads = 0;
|
|
}
|
|
}
|
|
|
|
private void SaveARC(string path)
|
|
{
|
|
if (!Directory.Exists(path)) { WinFormsUtil.Error("Input path is not a Folder", path); return; }
|
|
string folderName = Path.GetFileName(path);
|
|
string parentName = Directory.GetParent(path).FullName;
|
|
int type = CB_Repack.SelectedIndex;
|
|
switch (type)
|
|
{
|
|
case 0: // AutoDetect
|
|
{
|
|
if (!folderName.Contains('_'))
|
|
{ WinFormsUtil.Alert("Unable to autodetect pack type."); return; }
|
|
|
|
if (folderName.Contains("_g"))
|
|
goto case 1;
|
|
if (folderName.Contains("_d"))
|
|
goto case 2;
|
|
// else
|
|
goto case 3;
|
|
}
|
|
case 1: // GARC Pack
|
|
{
|
|
if (threads > 0) { WinFormsUtil.Alert("Please wait for all operations to finish first."); return; }
|
|
DialogResult dr = WinFormsUtil.Prompt(MessageBoxButtons.YesNoCancel, "Format Selection:",
|
|
"Yes: Sun/Moon (Version 6)\nNo: XY/ORAS (Version 4)");
|
|
if (dr == DialogResult.Cancel)
|
|
return;
|
|
|
|
var version = dr == DialogResult.Yes ? GARC.VER_6 : GARC.VER_4;
|
|
int padding = (int)NUD_Padding.Value;
|
|
if (version == GARC.VER_4)
|
|
padding = 4;
|
|
|
|
string outfolder = Directory.GetParent(path).FullName;
|
|
new Thread(() =>
|
|
{
|
|
bool r = GarcUtil.PackGARC(path, Path.Combine(outfolder, folderName + ".garc"), version, padding, pBar1);
|
|
if (!r) { WinFormsUtil.Alert("Packing failed."); return; }
|
|
// Delete path after repacking
|
|
if (CHK_Delete.Checked && Directory.Exists(path))
|
|
Directory.Delete(path, true);
|
|
|
|
System.Media.SystemSounds.Asterisk.Play();
|
|
}).Start();
|
|
return;
|
|
}
|
|
case 2: // DARC Pack (from existing if exists)
|
|
{
|
|
string oldFile = path.Replace("_d", "");
|
|
if (File.Exists(Path.Combine(parentName, oldFile)))
|
|
oldFile = Path.Combine(parentName, oldFile);
|
|
else if (File.Exists(Path.Combine(parentName, oldFile + ".bin")))
|
|
oldFile = Path.Combine(parentName, oldFile + ".bin");
|
|
else if (File.Exists(Path.Combine(parentName, oldFile + ".darc")))
|
|
oldFile = Path.Combine(parentName, oldFile + ".darc");
|
|
else oldFile = null;
|
|
|
|
bool r = Core.CTR.DARC.Files2darc(path, false, oldFile);
|
|
if (!r) WinFormsUtil.Alert("Packing failed.");
|
|
break;
|
|
}
|
|
case 3: // Mini Pack
|
|
{
|
|
// Get Folder Name
|
|
string fileName = Path.GetFileName(path);
|
|
if (fileName.Length < 3) { WinFormsUtil.Error("Mini Folder name not valid:", path); return; }
|
|
|
|
int index = fileName.LastIndexOf('_');
|
|
string fileNum = fileName[..index];
|
|
string fileExt = fileName[(index + 1)..];
|
|
|
|
// Find old file for reference...
|
|
string file;
|
|
if (File.Exists(Path.Combine(parentName, fileNum + ".bin")))
|
|
file = Path.Combine(parentName, fileNum + ".bin");
|
|
else if (File.Exists(Path.Combine(parentName, fileNum + "." + fileExt)))
|
|
file = Path.Combine(parentName, fileNum + "." + fileExt);
|
|
else
|
|
file = null;
|
|
|
|
byte[] oldData = file != null ? File.ReadAllBytes(file) : null;
|
|
bool r = Mini.PackMini2(path, fileExt, Path.Combine(parentName, fileNum + "." + fileExt));
|
|
if (!r)
|
|
{
|
|
WinFormsUtil.Alert("Packing failed.");
|
|
break;
|
|
}
|
|
|
|
// Check to see if the header size is different...
|
|
if (oldData == null) // No data to compare to.
|
|
break;
|
|
|
|
byte[] newData = File.ReadAllBytes(Path.Combine(parentName, fileNum + "." + fileExt));
|
|
if (newData[2] == oldData[2])
|
|
{
|
|
int newPtr = BitConverter.ToInt32(newData, 4);
|
|
int oldPtr = BitConverter.ToInt32(oldData, 4);
|
|
if (newPtr != oldPtr) // Header size is different. Prompt repointing.
|
|
{
|
|
if (DialogResult.Yes != WinFormsUtil.Prompt(MessageBoxButtons.YesNo, "Header size of existing file is nonstandard.", "Adjust newly packed file to have the same header size as old file? Data pointers will be updated accordingly."))
|
|
break;
|
|
|
|
// Fix pointers
|
|
byte[] update = Mini.AdjustMiniHeader(newData, oldPtr);
|
|
File.WriteAllBytes(Path.Combine(parentName, fileNum + "." + fileExt), update);
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
default:
|
|
WinFormsUtil.Alert("Repacking not implemented." + Environment.NewLine + path);
|
|
return;
|
|
}
|
|
// Delete path after repacking
|
|
if (CHK_Delete.Checked && Directory.Exists(path))
|
|
Directory.Delete(path, true);
|
|
System.Media.SystemSounds.Asterisk.Play();
|
|
}
|
|
|
|
private void PB_BCLIM_Click(object sender, EventArgs e)
|
|
{
|
|
if (ModifierKeys == Keys.Control && WinFormsUtil.Prompt(MessageBoxButtons.YesNo, "Copy image to clipboard?") == DialogResult.Yes)
|
|
Clipboard.SetImage(PB_BCLIM.BackgroundImage);
|
|
else if (PB_BCLIM.BackColor == Color.Transparent)
|
|
PB_BCLIM.BackColor = Color.GreenYellow;
|
|
else PB_BCLIM.BackColor = Color.Transparent;
|
|
}
|
|
|
|
// Utility
|
|
private readonly Size CLIMWindow;
|
|
|
|
private void B_Reset_Click(object sender, EventArgs e)
|
|
{
|
|
PB_BCLIM.Size = CLIMWindow;
|
|
}
|
|
|
|
private static void BatchRenameExtension(string Folder)
|
|
{
|
|
if (!Directory.Exists(Folder))
|
|
return;
|
|
|
|
foreach (string f in Directory.GetFiles(Folder, "*", SearchOption.AllDirectories))
|
|
{
|
|
try
|
|
{
|
|
string ext = Path.GetExtension(f);
|
|
string newExt = FileFormat.Guess(f);
|
|
if (ext != newExt)
|
|
File.Move(f, Path.Combine(Path.GetDirectoryName(f), Path.GetFileNameWithoutExtension(f)) + newExt);
|
|
}
|
|
catch { }
|
|
}
|
|
}
|
|
|
|
private void CloseForm(object sender, FormClosingEventArgs e)
|
|
{
|
|
if (threads > 0 && DialogResult.Yes != WinFormsUtil.Prompt(MessageBoxButtons.YesNo, "Currently processing files.", "Abort?"))
|
|
e.Cancel = true;
|
|
}
|
|
} |