Skip to content

Instantly share code, notes, and snippets.

@gdkchan
Last active September 12, 2016 03:34
Show Gist options
  • Save gdkchan/e852991a64f01dc0e1dc6caaa46e2e5b to your computer and use it in GitHub Desktop.
Save gdkchan/e852991a64f01dc0e1dc6caaa46e2e5b to your computer and use it in GitHub Desktop.
using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace KHPacker
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("KH ARD Packer/Unpacker v0.3 by gdkchan");
Console.WriteLine(string.Empty);
if (args.Length == 0)
PrintUsage();
else
{
string Operation = args[0];
string FileName = args[1];
switch (Operation)
{
case "-u":
if (FileName == "-all")
{
string[] Files = Directory.GetFiles(Environment.CurrentDirectory);
foreach (string File in Files) if (Path.GetExtension(File).ToLower() == ".ard") Dump(File);
}
else
Dump(FileName);
Console.WriteLine("Done!");
break;
case "-p":
if (FileName == "-all")
{
string[] Folders = Directory.GetDirectories(Environment.CurrentDirectory);
foreach (string Folder in Folders) Create(Folder);
}
else
Create(FileName);
Console.WriteLine("Done!");
break;
}
}
}
static void PrintUsage()
{
Console.WriteLine("-u [file.ard] Unpack ARD to folder");
Console.WriteLine("-p [folder] Pack folder to ARD");
Console.WriteLine("-u -all Unpack all ARD files on current folder");
Console.WriteLine("-p -all Pack all folders on current folder");
}
static void Dump(string FileName)
{
string Folder = Path.GetFileNameWithoutExtension(FileName);
Directory.CreateDirectory(Folder);
using (FileStream Input = new FileStream(FileName, FileMode.Open))
{
BinaryReader Reader = new BinaryReader(Input);
uint FilesCount = Reader.ReadUInt32();
List<uint> PointerTable = new List<uint>();
List<uint> AllPointers = new List<uint>();
for (int i = 0; i < FilesCount + 1; i++)
{
uint Address = Reader.ReadUInt32();
AllPointers.Add(Address);
if (!PointerTable.Contains(Address)) PointerTable.Add(Address);
}
PointerTable.Sort();
for (int i = 0; i < FilesCount; i++)
{
Input.Seek(4 + i * 4, SeekOrigin.Begin);
uint Address = Reader.ReadUInt32();
int Index = PointerTable.IndexOf(Address);
int Length = (int)(PointerTable[Index + 1] - PointerTable[Index]);
Input.Seek(Address, SeekOrigin.Begin);
byte[] Data = Reader.ReadBytes(Length);
bool Repeated = AllPointers.FindAll(a => a == Address).Count > 1;
string Name = string.Format("File_{0:D5}{1}.bin", i, Repeated ? "_R" : string.Empty);
File.WriteAllBytes(Path.Combine(Folder, Name), Data);
}
}
}
static void Create(string Folder)
{
string[] Files = Directory.GetFiles(Folder);
using (FileStream Output = new FileStream(Folder + ".ard", FileMode.Create))
{
BinaryWriter Writer = new BinaryWriter(Output);
Writer.Write(Files.Length);
int HeaderLength = 4 + Files.Length * 4 + 4;
while ((HeaderLength & 0x7f) != 0) HeaderLength += 4;
Dictionary<string, uint> PointerTable = new Dictionary<string, uint>();
using (MemoryStream DataStream = new MemoryStream())
{
foreach (string FName in Files)
{
byte[] Data = File.ReadAllBytes(FName);
string Hash = GetMD5(Data);
bool IsRepeated = PointerTable.ContainsKey(Hash);
if (!Path.GetFileName(FName).Contains("_R") || !IsRepeated)
{
uint Address = (uint)(DataStream.Position + HeaderLength);
if (!IsRepeated) PointerTable.Add(Hash, Address);
Writer.Write(Address);
DataStream.Write(Data, 0, Data.Length);
while ((DataStream.Position & 0x7f) != 0) DataStream.WriteByte(0);
}
else
Writer.Write((uint)PointerTable[Hash]);
}
Writer.Write((uint)(HeaderLength + DataStream.Length));
Output.Seek(HeaderLength, SeekOrigin.Begin);
Writer.Write(DataStream.ToArray());
}
}
}
static string GetMD5(byte[] Data)
{
using (MD5 Hash = MD5.Create())
{
byte[] Hashed = Hash.ComputeHash(Data);
StringBuilder Output = new StringBuilder();
foreach (byte HB in Hashed) Output.Append(HB.ToString("X2"));
return Output.ToString();
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment