diff --git a/pk3DS.Core/CTR/SARC.cs b/pk3DS.Core/CTR/SARC.cs new file mode 100644 index 0000000..80bfeb9 --- /dev/null +++ b/pk3DS.Core/CTR/SARC.cs @@ -0,0 +1,125 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace pk3DS.Core.CTR +{ + public class SARC + { + private const string Identifier = nameof(SARC); + + public string Magic; + public ushort HeaderSize; + public ushort Endianness; + public uint FileSize; + public uint DataOffset; + public uint Unknown; + + public SFAT SFAT; + public SFNT SFNT; + + // Assigned Properties + public string FileName; + public string FilePath; + public string Extension; + public readonly bool Valid; + + public bool SigMatches => Magic == Identifier; + + public SARC() + { + SFAT = new SFAT(); + SFNT = new SFNT(); + } + public SARC(string path) + { + FileName = Path.GetFileNameWithoutExtension(path); + FilePath = Path.GetDirectoryName(path); + Extension = Path.GetExtension(path); + + using (var br = new BinaryReader(File.OpenRead(path))) + { + ReadHeader(br); + SFAT = new SFAT(br); + SFNT = new SFNT(br); + } + Valid = true; + } + private void ReadHeader(BinaryReader br) + { + Magic = new string(br.ReadChars(4)); + if (!SigMatches) + return; + + HeaderSize = br.ReadUInt16(); + Endianness = br.ReadUInt16(); + FileSize = br.ReadUInt32(); + DataOffset = br.ReadUInt32(); + Unknown = br.ReadUInt32(); + } + } + public class SFAT + { + public const string Identifier = nameof(SFAT); + public bool SigMatches => Magic == Identifier; + + public string Magic; + public ushort HeaderSize; + public ushort EntryCount; + public uint HashMult; + public List Entries; + + public SFAT() { } + public SFAT(BinaryReader br) + { + Magic = new string(br.ReadChars(4)); + if (!SigMatches) + throw new FormatException(nameof(SFAT)); + + HeaderSize = br.ReadUInt16(); + EntryCount = br.ReadUInt16(); + HashMult = br.ReadUInt32(); + Entries = new List(); + + for (int i = 0; i < EntryCount; i++) + Entries.Add(new SFATEntry(br)); + } + } + public class SFNT + { + public const string Identifier = nameof(SFNT); + public bool SigMatches => Magic == Identifier; + + public string Magic; + public ushort HeaderSize; + public ushort Unknown; + public uint StringOffset; + + public SFNT() { } + public SFNT(BinaryReader br) + { + Magic = new string(br.ReadChars(4)); + if (!SigMatches) + throw new FormatException(nameof(SFNT)); + + HeaderSize = br.ReadUInt16(); + Unknown = br.ReadUInt16(); + StringOffset = (uint)br.BaseStream.Position; + } + } + public class SFATEntry + { + public uint FileNameHash; + public uint FileNameOffset; + public uint FileDataStart; + public uint FileDataEnd; + + public SFATEntry(BinaryReader br) + { + FileNameHash = br.ReadUInt32(); + FileNameOffset = br.ReadUInt32(); + FileDataStart = br.ReadUInt32(); + FileDataEnd = br.ReadUInt32(); + } + } +} \ No newline at end of file diff --git a/pk3DS/ARCUtil.cs b/pk3DS/ARCUtil.cs index 045cab3..d33ea4d 100644 --- a/pk3DS/ARCUtil.cs +++ b/pk3DS/ARCUtil.cs @@ -1,67 +1,19 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using System.Text; +using pk3DS.Core.CTR; namespace pk3DS.ARCUtil { public static class ARC { - // Multi Type Archive Handling - internal static bool onefile = true; - internal static SARC analyzeSARC(string path) + private static SARC analyzeSARC(string path) { - SARC sarc = new SARC - { - FileName = Path.GetFileNameWithoutExtension(path), - FilePath = Path.GetDirectoryName(path), - Extension = Path.GetExtension(path) - }; - BinaryReader br = new BinaryReader(File.OpenRead(path)); - sarc.valid = true; - sarc.Signature = new string(br.ReadChars(4)); - if (sarc.Signature != "SARC") - { - sarc.valid = false; - return sarc; - } - sarc.HeaderSize = br.ReadUInt16(); - sarc.Endianness = br.ReadUInt16(); - sarc.FileSize = br.ReadUInt32(); - sarc.DataOffset = br.ReadUInt32(); - sarc.Unknown = br.ReadUInt32(); - sarc.SFat = new SFAT { Signature = new string(br.ReadChars(4)) }; - if (sarc.SFat.Signature != "SFAT") - { - sarc.valid = false; - return sarc; - } - sarc.SFat.HeaderSize = br.ReadUInt16(); - sarc.SFat.EntryCount = br.ReadUInt16(); - sarc.SFat.HashMult = br.ReadUInt32(); - sarc.SFat.Entries = new List(); - for (int i = 0; i < sarc.SFat.EntryCount; i++) - { - SFATEntry s = new SFATEntry - { - FileNameHash = br.ReadUInt32(), - FileNameOffset = br.ReadUInt32(), - FileDataStart = br.ReadUInt32(), - FileDataEnd = br.ReadUInt32() - }; - sarc.SFat.Entries.Add(s); - } - sarc.SFnt = new SFNT { Signature = new string(br.ReadChars(4)) }; - if (sarc.SFnt.Signature != "SFNT") - { - sarc.valid = false; - return sarc; - } - sarc.SFnt.HeaderSize = br.ReadUInt16(); - sarc.SFnt.Unknown = br.ReadUInt16(); - sarc.SFnt.StringOffset = (uint)br.BaseStream.Position; - return sarc; + try { return new SARC(path); } + catch { return new SARC(); } } internal static ShuffleARC AnalyzeShuffle(string path) { @@ -430,73 +382,86 @@ internal static string Interpret(string path) } else if (sharc.valid) { - ret += "New Shuffle Archive with " + sharc.FileCount + " files." + Environment.NewLine; - string dir = Path.GetDirectoryName(path) + Path.DirectorySeparatorChar + sharc.FileName + "_" + Path.DirectorySeparatorChar; - if (!Directory.Exists(dir)) - { - Console.WriteLine("Making dir: " + dir); - Directory.CreateDirectory(dir); - } - - string diglen = "".PadLeft((int)(Math.Log10(sharc.FileCount) + 1), '0'); - for (int i = 0; i < sharc.FileCount; i++) - { - var fs = File.OpenRead(path); - fs.Seek(sharc.Files[i].Offset, SeekOrigin.Begin); - byte[] fileBuffer = new byte[sharc.Files[i].Length]; - fs.Read(fileBuffer, 0, fileBuffer.Length); - fs.Close(); - uint check = 0; - for (int j = 0; j < fileBuffer.Length; j += 4) - check += BitConverter.ToUInt32(fileBuffer, j); - Console.WriteLine(i.ToString(diglen) + ": " + check.ToString("X8")); - File.WriteAllBytes(dir + i.ToString(diglen) + ".zip", fileBuffer); - ret += "Extracted " + i.ToString(diglen) + " (Offset: " + sharc.Files[i].Offset.ToString("X8") + ", Len: " + sharc.Files[i].Length.ToString("X8") + ")." + Environment.NewLine; - } - ret += Environment.NewLine; - } - else if (sarc.valid) - { - ret = "New SARC with " + sarc.SFat.EntryCount + " files." + Environment.NewLine; - string dir = Path.GetDirectoryName(path) + Path.DirectorySeparatorChar + sarc.FileName + "_" + Path.DirectorySeparatorChar; - if (!Directory.Exists(dir)) - { - Console.WriteLine("Making dir: " + dir); - Directory.CreateDirectory(dir); - } - - foreach (SFATEntry t in sarc.SFat.Entries) - { - var fs = File.OpenRead(path); - uint FileLen = t.FileDataEnd - t.FileDataStart; - fs.Seek(t.FileDataStart + sarc.DataOffset, SeekOrigin.Begin); - byte[] fileBuffer = new byte[FileLen]; - fs.Read(fileBuffer, 0, (int)FileLen); - fs.Seek(sarc.SFnt.StringOffset, SeekOrigin.Begin); - fs.Seek((t.FileNameOffset & 0x00FFFFFF) * 4, SeekOrigin.Current); - StringBuilder sb = new StringBuilder(); - for (char c = (char)fs.ReadByte(); c != 0; c = (char)fs.ReadByte()) - { - sb.Append(c); - } - string FileName = sb.ToString().Replace('/', Path.DirectorySeparatorChar); - fs.Close(); - string FileDir = Path.GetDirectoryName(dir + FileName) + Path.DirectorySeparatorChar; - if (!Directory.Exists(FileDir)) - { - Console.WriteLine("Making dir: " + FileDir); - Directory.CreateDirectory(FileDir); - } - File.WriteAllBytes(dir + FileName, fileBuffer); - } + UnpackShuffleARC(path, sharc, ret); } + else if (sarc.Valid) + UnpackSARC(path, sarc); else { ret = "Not a valid .DARC/.FARC/.SARC/.GAR/Shuffle Archive file"; } return ret; } - + + private static bool UnpackShuffleARC(string path, ShuffleARC sharc, string ret) + { + Debug.WriteLine($"New Shuffle Archive with {sharc.FileCount} files."); + string dir = Path.GetDirectoryName(path) + Path.DirectorySeparatorChar + sharc.FileName + "_" + Path.DirectorySeparatorChar; + if (!Directory.Exists(dir)) + { + Console.WriteLine("Making dir: " + dir); + Directory.CreateDirectory(dir); + } + + string diglen = "".PadLeft((int)(Math.Log10(sharc.FileCount) + 1), '0'); + for (int i = 0; i < sharc.FileCount; i++) + { + var fs = File.OpenRead(path); + fs.Seek(sharc.Files[i].Offset, SeekOrigin.Begin); + byte[] fileBuffer = new byte[sharc.Files[i].Length]; + fs.Read(fileBuffer, 0, fileBuffer.Length); + fs.Close(); + uint check = 0; + for (int j = 0; j < fileBuffer.Length; j += 4) + check += BitConverter.ToUInt32(fileBuffer, j); + Console.WriteLine(i.ToString(diglen) + ": " + check.ToString("X8")); + File.WriteAllBytes(dir + i.ToString(diglen) + ".zip", fileBuffer); + Debug.WriteLine($"Extracted {i.ToString(diglen)} (Offset: {sharc.Files[i].Offset:X8}, Len: {sharc.Files[i].Length:X8})."); + } + return true; + } + + private static bool UnpackSARC(string path, SARC sarc) + { + Debug.WriteLine($"New SARC with {sarc.SFAT.EntryCount} files."); + string dir = Path.GetDirectoryName(path) + Path.DirectorySeparatorChar + sarc.FileName + "_" + Path.DirectorySeparatorChar; + if (!Directory.Exists(dir)) + { + Debug.WriteLine($"Making dir: {dir}"); + Directory.CreateDirectory(dir); + } + + var fs = File.OpenRead(path); + foreach (SFATEntry t in sarc.SFAT.Entries) + { + // Read File Data + uint FileLen = t.FileDataEnd - t.FileDataStart; + fs.Seek(t.FileDataStart + sarc.DataOffset, SeekOrigin.Begin); + byte[] fileBuffer = new byte[FileLen]; + fs.Read(fileBuffer, 0, (int)FileLen); + + // Read File Name + fs.Seek(sarc.SFNT.StringOffset, SeekOrigin.Begin); + fs.Seek((t.FileNameOffset & 0x00FFFFFF) * 4, SeekOrigin.Current); + StringBuilder sb = new StringBuilder(); + for (char c = (char)fs.ReadByte(); c != 0; c = (char)fs.ReadByte()) + sb.Append(c); + + // Write File + string FileName = sb.ToString().Replace('/', Path.DirectorySeparatorChar); + string FileDir = Path.GetDirectoryName(dir + FileName) + Path.DirectorySeparatorChar; + if (!Directory.Exists(FileDir)) + { + Console.WriteLine($"Making dir: {FileDir}"); + Directory.CreateDirectory(FileDir); + } + File.WriteAllBytes(dir + FileName, fileBuffer); + } + fs.Close(); + + return true; + } + // Unpacking internal static string unpackDARC(string path, string outFolder = null, bool delete = true) { @@ -668,45 +633,6 @@ public struct FARCFile public string Name; } - public class SARC - { - public string Signature; - public ushort HeaderSize = 0x14; - public ushort Endianness; - public uint FileSize; - public uint DataOffset; - public uint Unknown; - public SFAT SFat; - public SFNT SFnt; - - public string FileName; - public string FilePath; - public string Extension; - public bool valid; - } - public class SFAT - { - public string Signature; - public ushort HeaderSize; - public ushort EntryCount; - public uint HashMult; - public List Entries; - } - public class SFATEntry - { - public uint FileNameHash; - public uint FileNameOffset; - public uint FileDataStart; - public uint FileDataEnd; - } - public class SFNT - { - public string Signature; - public ushort HeaderSize; - public ushort Unknown; - public uint StringOffset; - } - public class ShuffleARC { public uint magic; //0xB