From 2370bc6cf4de50cddeab693a48626cd864f1600c Mon Sep 17 00:00:00 2001 From: StudentBlake <6874208+StudentBlake@users.noreply.github.com> Date: Sun, 13 Nov 2022 11:50:57 -0500 Subject: [PATCH] Minor cleanup, update keys, add portable publish profile --- XCI Explorer.csproj | 6 + XCI_Explorer.Helpers/BetterBinaryReader.cs | 154 - XCI_Explorer.Helpers/BetterTreeNode.cs | 25 +- XCI_Explorer/CNMT.cs | 135 +- XCI_Explorer/CenterWinDialog.cs | 126 +- XCI_Explorer/CertForm.cs | 23 +- XCI_Explorer/HFS0.cs | 83 +- XCI_Explorer/MainForm.cs | 3454 ++++++++++---------- XCI_Explorer/NACP.cs | 65 +- XCI_Explorer/NCA.cs | 49 +- XCI_Explorer/PFS0.cs | 75 +- XCI_Explorer/Program.cs | 42 +- XCI_Explorer/TreeViewFileSystem.cs | 56 +- XCI_Explorer/Util.cs | 187 +- XCI_Explorer/XCI.cs | 41 +- XTSSharp/RandomAccessSectorStream.cs | 315 +- XTSSharp/SectorStream.cs | 206 +- XTSSharp/Xts.cs | 101 +- XTSSharp/XtsAes128.cs | 47 +- XTSSharp/XtsAes256.cs | 47 +- XTSSharp/XtsCryptoTransform.cs | 233 +- XTSSharp/XtsSectorStream.cs | 125 +- XTSSharp/XtsStream.cs | 27 +- 23 files changed, 2721 insertions(+), 2901 deletions(-) delete mode 100644 XCI_Explorer.Helpers/BetterBinaryReader.cs diff --git a/XCI Explorer.csproj b/XCI Explorer.csproj index 71c9199..663284c 100644 --- a/XCI Explorer.csproj +++ b/XCI Explorer.csproj @@ -15,4 +15,10 @@ + + + Always + true + + \ No newline at end of file diff --git a/XCI_Explorer.Helpers/BetterBinaryReader.cs b/XCI_Explorer.Helpers/BetterBinaryReader.cs deleted file mode 100644 index 21368b6..0000000 --- a/XCI_Explorer.Helpers/BetterBinaryReader.cs +++ /dev/null @@ -1,154 +0,0 @@ -using System; -using System.IO; - -namespace XCI_Explorer.Helpers -{ - public class BetterBinaryReader : IDisposable - { - public string FileName; - public bool Initiated; - public Stream Stream; - private BinaryReader br; - - public BetterBinaryReader() - { - Initiated = false; - } - - public BetterBinaryReader(string file) - { - Initiated = false; - Load(file); - } - - public BetterBinaryReader(Stream s) - { - Initiated = false; - FileName = ""; - Stream = s; - br = new BinaryReader(Stream); - } - - public void Dispose() - { - Initiated = false; - br.Close(); - br = null; - Stream.Close(); - Stream = null; - } - - public void Load(string file) - { - FileName = file; - Stream = new FileStream(file, FileMode.Open); - br = new BinaryReader(Stream); - Initiated = true; - } - - public void Seek(long o) - { - if (o > -1) - { - Stream.Seek(o, SeekOrigin.Begin); - } - } - - public void Skip(long o) - { - Stream.Seek(o, SeekOrigin.Current); - } - - public long Position() - { - return Stream.Position; - } - - public int Read() - { - return br.ReadBytes(1)[0]; - } - - public int Read(byte[] buffer, int index, int count) - { - return br.Read(buffer, index, count); - } - - public int Read(char[] buffer, int index, int count) - { - return br.Read(buffer, index, count); - } - - public byte[] ReadBytes(int l) - { - if (l >= 0 && l <= 2147483647) - { - return br.ReadBytes(l); - } - return new byte[0]; - } - - public Stream ReadBytesButLonger(long l) - { - MemoryStream memoryStream = new MemoryStream(); - for (long num = 0L; num < l; num++) - { - } - Console.WriteLine(memoryStream.Length); - return memoryStream; - } - - public string ReadCharsAsString(int l) - { - return new string(br.ReadChars(l)); - } - - public short ReadShort() - { - return br.ReadInt16(); - } - - public short ReadInt16() - { - return br.ReadInt16(); - } - - public int ReadInt() - { - return br.ReadInt32(); - } - - public int ReadInt32() - { - return br.ReadInt32(); - } - - public long ReadLong() - { - return br.ReadInt64(); - } - - public long ReadInt64() - { - return br.ReadInt64(); - } - - public string ReadString() - { - return br.ReadString(); - } - - private long GreatestDivisor(long n) - { - long result = 0L; - for (long num = 1L; num < n / 64; num++) - { - if (n % num == 0L && num != n) - { - result = num; - } - } - return result; - } - } -} diff --git a/XCI_Explorer.Helpers/BetterTreeNode.cs b/XCI_Explorer.Helpers/BetterTreeNode.cs index 89589a5..129fb7b 100644 --- a/XCI_Explorer.Helpers/BetterTreeNode.cs +++ b/XCI_Explorer.Helpers/BetterTreeNode.cs @@ -1,18 +1,17 @@ using System.Windows.Forms; -namespace XCI_Explorer.Helpers -{ - public class BetterTreeNode : TreeNode - { - public long Offset; - public long Size; - public string ExpectedHash; - public string ActualHash; - public long HashedRegionSize; +namespace XCI_Explorer.Helpers; - public BetterTreeNode(string t) - { - base.Text = t; - } +public class BetterTreeNode : TreeNode +{ + public long Offset; + public long Size; + public string ExpectedHash; + public string ActualHash; + public long HashedRegionSize; + + public BetterTreeNode(string t) + { + Text = t; } } diff --git a/XCI_Explorer/CNMT.cs b/XCI_Explorer/CNMT.cs index c7ab6d5..33d2975 100644 --- a/XCI_Explorer/CNMT.cs +++ b/XCI_Explorer/CNMT.cs @@ -1,80 +1,79 @@ using System; using System.Linq; -namespace XCI_Explorer +namespace XCI_Explorer; + +internal static class CNMT { - internal static class CNMT + public class CNMT_Header { - public class CNMT_Header + public byte[] Data; + public long TitleID; + public int TitleVersion; + public byte Type; + public byte Reserved1; + public short Offset; + public short ContentCount; + public short MetaCount; + public byte[] Reserved2; + + public enum TitleType { - public byte[] Data; - public long TitleID; - public int TitleVersion; - public byte Type; - public byte Reserved1; - public short Offset; - public short ContentCount; - public short MetaCount; - public byte[] Reserved2; - - public enum TitleType - { - SYSTEM_PROGRAMS = 0x01, - SYSTEM_DATA_ARCHIVES, - SYSTEM_UPDATE, - FIRMWARE_PACKAGE_A, - FIRMWARE_PACKAGE_B, - REGULAR_APPLICATION = 0x80, - UPDATE_TITLE, - ADD_ON_CONTENT, - DELTA_TITLE - } - - public CNMT_Header(byte[] data) - { - Data = data; - TitleID = BitConverter.ToInt64(data, 0); - TitleVersion = BitConverter.ToInt32(data, 8); - Type = Data[12]; - Reserved1 = Data[13]; - Offset = BitConverter.ToInt16(data, 14); - ContentCount = BitConverter.ToInt16(data, 16); - MetaCount = BitConverter.ToInt16(data, 16); - Reserved2 = Data.Skip(20).Take(12).ToArray(); - } + SYSTEM_PROGRAMS = 0x01, + SYSTEM_DATA_ARCHIVES, + SYSTEM_UPDATE, + FIRMWARE_PACKAGE_A, + FIRMWARE_PACKAGE_B, + REGULAR_APPLICATION = 0x80, + UPDATE_TITLE, + ADD_ON_CONTENT, + DELTA_TITLE } - public class CNMT_Entry + public CNMT_Header(byte[] data) { - public byte[] Data; - public byte[] Hash; - public byte[] NcaId; - public long Size; - public byte Type; - public byte Reserved; - - public enum ContentType - { - META, - PROGRAM, - DATA, - CONTROL, - OFFLINE_MANUAL, - LEGAL, - GAME_UPDATE - } - - public CNMT_Entry(byte[] data) - { - Data = data; - Hash = Data.Skip(0).Take(32).ToArray(); - NcaId = Data.Skip(32).Take(16).ToArray(); - Size = BitConverter.ToInt32(data, 48) + BitConverter.ToInt16(data, 52) * 65536; - Type = Data[54]; - Reserved = Data[55]; - } + Data = data; + TitleID = BitConverter.ToInt64(data, 0); + TitleVersion = BitConverter.ToInt32(data, 8); + Type = Data[12]; + Reserved1 = Data[13]; + Offset = BitConverter.ToInt16(data, 14); + ContentCount = BitConverter.ToInt16(data, 16); + MetaCount = BitConverter.ToInt16(data, 16); + Reserved2 = Data.Skip(20).Take(12).ToArray(); } - - public static CNMT_Header[] CNMT_Headers = new CNMT_Header[1]; } + + public class CNMT_Entry + { + public byte[] Data; + public byte[] Hash; + public byte[] NcaId; + public long Size; + public byte Type; + public byte Reserved; + + public enum ContentType + { + META, + PROGRAM, + DATA, + CONTROL, + OFFLINE_MANUAL, + LEGAL, + GAME_UPDATE + } + + public CNMT_Entry(byte[] data) + { + Data = data; + Hash = Data.Skip(0).Take(32).ToArray(); + NcaId = Data.Skip(32).Take(16).ToArray(); + Size = BitConverter.ToInt32(data, 48) + BitConverter.ToInt16(data, 52) * 65536; + Type = Data[54]; + Reserved = Data[55]; + } + } + + public static CNMT_Header[] CNMT_Headers = new CNMT_Header[1]; } diff --git a/XCI_Explorer/CenterWinDialog.cs b/XCI_Explorer/CenterWinDialog.cs index de53275..0119610 100644 --- a/XCI_Explorer/CenterWinDialog.cs +++ b/XCI_Explorer/CenterWinDialog.cs @@ -1,69 +1,77 @@ using System; -using System.Windows.Forms; -using System.Text; using System.Drawing; using System.Runtime.InteropServices; +using System.Text; +using System.Windows.Forms; -namespace XCI_Explorer +namespace XCI_Explorer; + +public class CenterWinDialog : IDisposable { - public class CenterWinDialog : IDisposable + private int mTries = 0; + private Form mOwner; + + public CenterWinDialog(Form owner) { - private int mTries = 0; - private Form mOwner; - - public CenterWinDialog(Form owner) + mOwner = owner; + if (owner.WindowState != FormWindowState.Minimized) { - mOwner = owner; - if (owner.WindowState != FormWindowState.Minimized) - { - owner.BeginInvoke(new MethodInvoker(findDialog)); - } + owner.BeginInvoke(new MethodInvoker(findDialog)); } - - private void findDialog() - { - // Enumerate windows to find the message box - if (mTries < 0) return; - EnumThreadWndProc callback = new EnumThreadWndProc(checkWindow); - if (EnumThreadWindows(GetCurrentThreadId(), callback, IntPtr.Zero)) - { - if (++mTries < 10) mOwner.BeginInvoke(new MethodInvoker(findDialog)); - } - } - private bool checkWindow(IntPtr hWnd, IntPtr lp) - { - // Checks if is a dialog - StringBuilder sb = new StringBuilder(260); - GetClassName(hWnd, sb, sb.Capacity); - if (sb.ToString() != "#32770") return true; - // Got it - Rectangle frmRect = new Rectangle(mOwner.Location, mOwner.Size); - RECT dlgRect; - GetWindowRect(hWnd, out dlgRect); - MoveWindow(hWnd, - frmRect.Left + (frmRect.Width - dlgRect.Right + dlgRect.Left) / 2, - frmRect.Top + (frmRect.Height - dlgRect.Bottom + dlgRect.Top) / 2, - dlgRect.Right - dlgRect.Left, - dlgRect.Bottom - dlgRect.Top, true); - return false; - } - public void Dispose() - { - mTries = -1; - } - - // P/Invoke declarations - private delegate bool EnumThreadWndProc(IntPtr hWnd, IntPtr lp); - [DllImport("user32.dll")] - private static extern bool EnumThreadWindows(int tid, EnumThreadWndProc callback, IntPtr lp); - [DllImport("kernel32.dll")] - private static extern int GetCurrentThreadId(); - [DllImport("user32.dll")] - private static extern int GetClassName(IntPtr hWnd, StringBuilder buffer, int buflen); - [DllImport("user32.dll")] - private static extern bool GetWindowRect(IntPtr hWnd, out RECT rc); - [DllImport("user32.dll")] - private static extern bool MoveWindow(IntPtr hWnd, int x, int y, int w, int h, bool repaint); - private struct RECT { public int Left; public int Top; public int Right; public int Bottom; } } + + private void findDialog() + { + // Enumerate windows to find the message box + if (mTries < 0) + { + return; + } + EnumThreadWndProc callback = new(checkWindow); + if (EnumThreadWindows(GetCurrentThreadId(), callback, IntPtr.Zero)) + { + if (++mTries < 10) + { + mOwner.BeginInvoke(new MethodInvoker(findDialog)); + } + } + } + private bool checkWindow(IntPtr hWnd, IntPtr lp) + { + // Checks if is a dialog + StringBuilder sb = new(260); + GetClassName(hWnd, sb, sb.Capacity); + if (sb.ToString() != "#32770") + { + return true; + } + // Got it + Rectangle frmRect = new Rectangle(mOwner.Location, mOwner.Size); + RECT dlgRect; + GetWindowRect(hWnd, out dlgRect); + MoveWindow(hWnd, + frmRect.Left + (frmRect.Width - dlgRect.Right + dlgRect.Left) / 2, + frmRect.Top + (frmRect.Height - dlgRect.Bottom + dlgRect.Top) / 2, + dlgRect.Right - dlgRect.Left, + dlgRect.Bottom - dlgRect.Top, true); + return false; + } + public void Dispose() + { + mTries = -1; + } + + // P/Invoke declarations + private delegate bool EnumThreadWndProc(IntPtr hWnd, IntPtr lp); + [DllImport("user32.dll")] + private static extern bool EnumThreadWindows(int tid, EnumThreadWndProc callback, IntPtr lp); + [DllImport("kernel32.dll")] + private static extern int GetCurrentThreadId(); + [DllImport("user32.dll")] + private static extern int GetClassName(IntPtr hWnd, StringBuilder buffer, int buflen); + [DllImport("user32.dll")] + private static extern bool GetWindowRect(IntPtr hWnd, out RECT rc); + [DllImport("user32.dll")] + private static extern bool MoveWindow(IntPtr hWnd, int x, int y, int w, int h, bool repaint); + private struct RECT { public int Left; public int Top; public int Right; public int Bottom; } } \ No newline at end of file diff --git a/XCI_Explorer/CertForm.cs b/XCI_Explorer/CertForm.cs index 24e353a..40b55a4 100644 --- a/XCI_Explorer/CertForm.cs +++ b/XCI_Explorer/CertForm.cs @@ -2,19 +2,18 @@ using System.IO; using System.Windows.Forms; -namespace XCI_Explorer +namespace XCI_Explorer; + +public partial class CertForm : Form { - public partial class CertForm : Form + public CertForm(MainForm mainForm) { - public CertForm(MainForm mainForm) - { - InitializeComponent(); - FileStream fileStream = new FileStream(mainForm.TB_File.Text, FileMode.Open, FileAccess.Read); - byte[] array = new byte[512]; - fileStream.Position = 28672L; - fileStream.Read(array, 0, 512); - hbxHexView.ByteProvider = new DynamicByteProvider(array); - fileStream.Close(); - } + InitializeComponent(); + FileStream fileStream = new(mainForm.TB_File.Text, FileMode.Open, FileAccess.Read); + byte[] array = new byte[512]; + fileStream.Position = 28672L; + fileStream.Read(array, 0, 512); + hbxHexView.ByteProvider = new DynamicByteProvider(array); + fileStream.Close(); } } diff --git a/XCI_Explorer/HFS0.cs b/XCI_Explorer/HFS0.cs index 761e350..a98e5e6 100644 --- a/XCI_Explorer/HFS0.cs +++ b/XCI_Explorer/HFS0.cs @@ -2,51 +2,50 @@ using System; using System.Linq; using System.Text; -namespace XCI_Explorer +namespace XCI_Explorer; + +internal static class HFS0 { - internal static class HFS0 + public class HFS0_Header { - public class HFS0_Header + public byte[] Data; + public string Magic; + public int FileCount; + public int StringTableSize; + public int Reserved; + + public HFS0_Header(byte[] data) { - public byte[] Data; - public string Magic; - public int FileCount; - public int StringTableSize; - public int Reserved; - - public HFS0_Header(byte[] data) - { - Data = data; - Magic = Encoding.UTF8.GetString(Data.Take(4).ToArray()); - FileCount = BitConverter.ToInt32(data, 4); - StringTableSize = BitConverter.ToInt32(data, 8); - Reserved = BitConverter.ToInt32(data, 12); - } + Data = data; + Magic = Encoding.UTF8.GetString(Data.Take(4).ToArray()); + FileCount = BitConverter.ToInt32(data, 4); + StringTableSize = BitConverter.ToInt32(data, 8); + Reserved = BitConverter.ToInt32(data, 12); } - - public class HSF0_Entry - { - public byte[] Data; - public long Offset; - public long Size; - public int Name_ptr; - public int HashedRegionSize; - public long Padding; - public byte[] Hash; - public string Name; - - public HSF0_Entry(byte[] data) - { - Data = data; - Offset = BitConverter.ToInt64(data, 0); - Size = BitConverter.ToInt64(data, 8); - Name_ptr = BitConverter.ToInt32(data, 16); - HashedRegionSize = BitConverter.ToInt32(data, 20); - Padding = BitConverter.ToInt64(data, 24); - Hash = Data.Skip(32).Take(32).ToArray(); - } - } - - public static HFS0_Header[] HFS0_Headers = new HFS0_Header[1]; } + + public class HSF0_Entry + { + public byte[] Data; + public long Offset; + public long Size; + public int Name_ptr; + public int HashedRegionSize; + public long Padding; + public byte[] Hash; + public string Name; + + public HSF0_Entry(byte[] data) + { + Data = data; + Offset = BitConverter.ToInt64(data, 0); + Size = BitConverter.ToInt64(data, 8); + Name_ptr = BitConverter.ToInt32(data, 16); + HashedRegionSize = BitConverter.ToInt32(data, 20); + Padding = BitConverter.ToInt64(data, 24); + Hash = Data.Skip(32).Take(32).ToArray(); + } + } + + public static HFS0_Header[] HFS0_Headers = new HFS0_Header[1]; } diff --git a/XCI_Explorer/MainForm.cs b/XCI_Explorer/MainForm.cs index ee92e1d..de1a9e4 100644 --- a/XCI_Explorer/MainForm.cs +++ b/XCI_Explorer/MainForm.cs @@ -15,1373 +15,339 @@ using System.Xml.Linq; using XCI_Explorer.Helpers; using XTSSharp; -namespace XCI_Explorer +namespace XCI_Explorer; + +public partial class MainForm : Form { - public partial class MainForm : Form + public List chars = new(); + public byte[] NcaHeaderEncryptionKey1_Prod; + public byte[] NcaHeaderEncryptionKey2_Prod; + public string Mkey; + public double UsedSize; + private Image[] Icons = new Image[16]; + private readonly string[] Language = new string[16] { + "American English", + "British English", + "Japanese", + "French", + "German", + "Latin American Spanish", + "Spanish", + "Italian", + "Dutch", + "Canadian French", + "Portuguese", + "Russian", + "Korean", + "Traditional Chinese", + "Simplified Chinese", + "???" + }; + + public MainForm() { - public List chars = new List(); - public byte[] NcaHeaderEncryptionKey1_Prod; - public byte[] NcaHeaderEncryptionKey2_Prod; - public string Mkey; - public double UsedSize; - private Image[] Icons = new Image[16]; - private string[] Language = new string[16] { - "American English", - "British English", - "Japanese", - "French", - "German", - "Latin American Spanish", - "Spanish", - "Italian", - "Dutch", - "Canadian French", - "Portuguese", - "Russian", - "Korean", - "Traditional Chinese", - "Simplified Chinese", - "???" - }; + InitializeComponent(); - public MainForm() + Text = $"XCI Explorer v{getAssemblyVersion()}"; + + LB_SelectedData.Text = ""; + LB_DataOffset.Text = ""; + LB_DataSize.Text = ""; + LB_HashedRegionSize.Text = ""; + LB_ActualHash.Text = ""; + LB_ExpectedHash.Text = ""; + + Show(); + + //MAC - Set Current Directory to application directory so it can find the keys + String startupPath = Application.StartupPath; + Directory.SetCurrentDirectory(startupPath); + + if (!File.Exists("keys.txt")) { - InitializeComponent(); - - this.Text = $"XCI Explorer v{getAssemblyVersion()}"; - - LB_SelectedData.Text = ""; - LB_DataOffset.Text = ""; - LB_DataSize.Text = ""; - LB_HashedRegionSize.Text = ""; - LB_ActualHash.Text = ""; - LB_ExpectedHash.Text = ""; - - this.Show(); - - //MAC - Set Current Directory to application directory so it can find the keys - String startupPath = Application.StartupPath; - Directory.SetCurrentDirectory(startupPath); + new CenterWinDialog(this); + if (MessageBox.Show("keys.txt is missing.\nDo you want to automatically download it now?\n\nBy pressing 'Yes' you agree that you own these keys.\n", "XCI Explorer", MessageBoxButtons.YesNo) == DialogResult.Yes) + { + using HttpClient client = new(); + using HttpResponseMessage response = client.Send(new HttpRequestMessage(HttpMethod.Get, Util.Base64Decode("aHR0cHM6Ly9wYXN0ZWJpbi5jb20vcmF3LzVnN3cxN2tF"))); + using Stream stream = response.Content.ReadAsStream(); + using FileStream fs = new("keys.txt", FileMode.CreateNew); + stream.CopyTo(fs); + } if (!File.Exists("keys.txt")) { new CenterWinDialog(this); - if (MessageBox.Show("keys.txt is missing.\nDo you want to automatically download it now?\n\nBy pressing 'Yes' you agree that you own these keys.\n", "XCI Explorer", MessageBoxButtons.YesNo) == DialogResult.Yes) - { - using HttpClient client = new(); - using HttpResponseMessage response = client.Send(new HttpRequestMessage(HttpMethod.Get, Util.Base64Decode("aHR0cHM6Ly9wYXN0ZWJpbi5jb20vcmF3L1d1TXZBaTN2"))); - using Stream stream = response.Content.ReadAsStream(); - using FileStream fs = new("keys.txt", FileMode.CreateNew); - stream.CopyTo(fs); - } - - if (!File.Exists("keys.txt")) - { - new CenterWinDialog(this); - MessageBox.Show("keys.txt failed to load.\nPlease include keys.txt in the root folder."); - Environment.Exit(0); - } - } - - if (!File.Exists(Path.Join("tools", "hactool.exe"))) - { - Directory.CreateDirectory("tools"); - new CenterWinDialog(this); - MessageBox.Show("hactool.exe is missing.\nPlease include hactool.exe in the 'tools' folder."); + MessageBox.Show("keys.txt failed to load.\nPlease include keys.txt in the root folder."); Environment.Exit(0); } - - getKey(); - - //MAC - Set the double clicked file name into the UI and process file - String[] args = Environment.GetCommandLineArgs(); - if (args.Length > 1) - { - TB_File.Text = args[1]; - Application.DoEvents(); - ProcessFile(); - } - } - private string getAssemblyVersion() + if (!File.Exists(Path.Join("tools", "hactool.exe"))) { - string assemblyVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString(); - string[] versionArray = assemblyVersion.Split('.'); - - assemblyVersion = string.Join(".", versionArray.Take(3)); - - return assemblyVersion; + Directory.CreateDirectory("tools"); + new CenterWinDialog(this); + MessageBox.Show("hactool.exe is missing.\nPlease include hactool.exe in the 'tools' folder."); + Environment.Exit(0); } - private void getKey() + getKey(); + + //MAC - Set the double clicked file name into the UI and process file + String[] args = Environment.GetCommandLineArgs(); + if (args.Length > 1) { - string text = (from x in File.ReadAllLines("keys.txt") - select x.Split('=') into x - where x.Length > 1 - select x).ToDictionary((string[] x) => x[0].Trim(), (string[] x) => x[1])["header_key"].Replace(" ", ""); - NcaHeaderEncryptionKey1_Prod = Util.StringToByteArray(text.Remove(32, 32)); - NcaHeaderEncryptionKey2_Prod = Util.StringToByteArray(text.Remove(0, 32)); + TB_File.Text = args[1]; + Application.DoEvents(); + ProcessFile(); } - public bool getMKey() + } + + private string getAssemblyVersion() + { + string assemblyVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString(); + string[] versionArray = assemblyVersion.Split('.'); + + assemblyVersion = string.Join(".", versionArray.Take(3)); + + return assemblyVersion; + } + + private void getKey() + { + string text = (from x in File.ReadAllLines("keys.txt") + select x.Split('=') into x + where x.Length > 1 + select x).ToDictionary((string[] x) => x[0].Trim(), (string[] x) => x[1])["header_key"].Replace(" ", ""); + NcaHeaderEncryptionKey1_Prod = Util.StringToByteArray(text.Remove(32, 32)); + NcaHeaderEncryptionKey2_Prod = Util.StringToByteArray(text.Remove(0, 32)); + } + + public bool getMKey() + { + Dictionary dictionary = (from x in File.ReadAllLines("keys.txt") + select x.Split('=') into x + where x.Length > 1 + select x).ToDictionary((string[] x) => x[0].Trim(), (string[] x) => x[1]); + Mkey = "master_key_"; + string MkeyL = "master_key_"; + if (NCA.NCA_Headers[0].MasterKeyRev == 0 || NCA.NCA_Headers[0].MasterKeyRev == 1) + { + Mkey += "00"; + } + else if (NCA.NCA_Headers[0].MasterKeyRev < 17) + { + int num = NCA.NCA_Headers[0].MasterKeyRev - 1; + string capchar = num.ToString("X"); + string lowchar = capchar.ToLower(); + Mkey += $"0{capchar}"; + MkeyL += $"0{lowchar}"; + } + else if (NCA.NCA_Headers[0].MasterKeyRev >= 17) + { + int num2 = NCA.NCA_Headers[0].MasterKeyRev - 1; + string capchar = num2.ToString("X"); + string lowchar = capchar.ToLower(); + Mkey += num2.ToString(); + MkeyL += num2.ToString(); + } + try + { + Mkey = dictionary[Mkey].Replace(" ", ""); + return true; + } + catch { - Dictionary dictionary = (from x in File.ReadAllLines("keys.txt") - select x.Split('=') into x - where x.Length > 1 - select x).ToDictionary((string[] x) => x[0].Trim(), (string[] x) => x[1]); - Mkey = "master_key_"; - string MkeyL = "master_key_"; - if (NCA.NCA_Headers[0].MasterKeyRev == 0 || NCA.NCA_Headers[0].MasterKeyRev == 1) - { - Mkey += "00"; - } - else if (NCA.NCA_Headers[0].MasterKeyRev < 17) - { - int num = NCA.NCA_Headers[0].MasterKeyRev - 1; - string capchar = num.ToString("X"); - string lowchar = capchar.ToLower(); - Mkey += $"0{capchar}"; - MkeyL += $"0{lowchar}"; - } - else if (NCA.NCA_Headers[0].MasterKeyRev >= 17) - { - int num2 = NCA.NCA_Headers[0].MasterKeyRev - 1; - string capchar = num2.ToString("X"); - string lowchar = capchar.ToLower(); - Mkey += num2.ToString(); - MkeyL += num2.ToString(); - } try { - Mkey = dictionary[Mkey].Replace(" ", ""); + MkeyL = dictionary[MkeyL].Replace(" ", ""); return true; } catch { - try - { - MkeyL = dictionary[MkeyL].Replace(" ", ""); - return true; - } - catch - { - return false; - } + return false; } } + } - private void ProcessFile() + private void ProcessFile() + { + // Code needs refactoring + LB_SelectedData.Text = ""; + LB_DataOffset.Text = ""; + LB_DataSize.Text = ""; + LB_HashedRegionSize.Text = ""; + LB_ExpectedHash.Text = ""; + LB_ActualHash.Text = ""; + B_Extract.Enabled = false; + + try { - // Code needs refactoring - LB_SelectedData.Text = ""; - LB_DataOffset.Text = ""; - LB_DataSize.Text = ""; - LB_HashedRegionSize.Text = ""; - LB_ExpectedHash.Text = ""; - LB_ActualHash.Text = ""; - B_Extract.Enabled = false; - - try - { - if (CheckNSP()) - { - B_TrimXCI.Enabled = false; - B_ExportCert.Enabled = false; - B_ImportCert.Enabled = false; - B_ViewCert.Enabled = false; - B_ClearCert.Enabled = false; - - LoadNSP(); - } - else if (CheckXCI()) - { - B_TrimXCI.Enabled = true; - B_ExportCert.Enabled = true; - B_ImportCert.Enabled = true; - B_ViewCert.Enabled = true; - B_ClearCert.Enabled = true; - - LoadXCI(); - } - else - { - TB_File.Text = null; - new CenterWinDialog(this); - MessageBox.Show("File is corrupt or unsupported."); - } - } - catch (Exception e) - { - new CenterWinDialog(this); - MessageBox.Show($"Error: {e.ToString()}\nFile is corrupt or unsupported."); - } - - } - - private void B_LoadROM_Click(object sender, EventArgs e) - { - OpenFileDialog openFileDialog = new OpenFileDialog(); - openFileDialog.Filter = "Switch Game File (*.xci, *.nsp, *.nsz)|*.xci;*.nsp;*.nsz|All Files (*.*)|*.*"; - new CenterWinDialog(this); - if (openFileDialog.ShowDialog() == DialogResult.OK) - { - TB_File.Text = openFileDialog.FileName; - ProcessFile(); - } - } - - private void LoadXCI() - { - string[] array = new string[5] - { - "B", - "KB", - "MB", - "GB", - "TB" - }; - double num = (double)new FileInfo(TB_File.Text).Length; - TB_ROMExactSize.Text = $"({num} bytes)"; - int num2 = 0; - while (num >= 1024.0 && num2 < array.Length - 1) - { - num2++; - num /= 1024.0; - } - TB_ROMSize.Text = $"{num:0.##} {array[num2]}"; - double num3 = UsedSize = (double)(XCI.XCI_Headers[0].CardSize2 * 512 + 512); - TB_ExactUsedSpace.Text = $"({num3} bytes)"; - if (isTrimmed()) + if (CheckNSP()) { B_TrimXCI.Enabled = false; + B_ExportCert.Enabled = false; + B_ImportCert.Enabled = false; + B_ViewCert.Enabled = false; + B_ClearCert.Enabled = false; + + LoadNSP(); } - - num2 = 0; - while (num3 >= 1024.0 && num2 < array.Length - 1) + else if (CheckXCI()) { - num2++; - num3 /= 1024.0; - } - TB_UsedSpace.Text = $"{num3:0.##} {array[num2]}"; - TB_Capacity.Text = Util.GetCapacity(XCI.XCI_Headers[0].CardSize1); - LoadPartitions(); - LoadNCAData(); - LoadGameInfos(); - } + B_TrimXCI.Enabled = true; + B_ExportCert.Enabled = true; + B_ImportCert.Enabled = true; + B_ViewCert.Enabled = true; + B_ClearCert.Enabled = true; - // Giba's better implementation (more native) - public void LoadNSP() - { - CB_RegionName.Items.Clear(); - CB_RegionName.Enabled = true; - TB_TID.Text = ""; - TB_Capacity.Text = ""; - TB_MKeyRev.Text = ""; - TB_SDKVer.Text = ""; - TB_GameRev.Text = ""; - TB_ProdCode.Text = ""; - TB_Name.Text = ""; - TB_Dev.Text = ""; - - int basenum = 0; - int updnum = 0; - int dlcnum = 0; - string pversion = ""; - int patchflag = 0; - int patchnum = 0; - string patchver = ""; - int baseflag = 0; - string[] basetitle = new string[5]; - string[] updtitle = new string[10]; - string[] dlctitle = new string[300]; - - PB_GameIcon.BackgroundImage = null; - Array.Clear(Icons, 0, Icons.Length); - TV_Partitions.Nodes.Clear(); - FileInfo fi = new FileInfo(TB_File.Text); - string contentType = ""; - - // Maximum number of files in NSP to read in - const int MAXFILES = 250; - - //Get File Size - string[] array_fs = new string[5] { "B", "KB", "MB", "GB", "TB" }; - double num_fs = (double)fi.Length; - int num2_fs = 0; - TB_ROMExactSize.Text = $"({num_fs} bytes)"; - TB_ExactUsedSpace.Text = TB_ROMExactSize.Text; - - while (num_fs >= 1024.0 && num2_fs < array_fs.Length - 1) - { - num2_fs++; - num_fs /= 1024.0; - } - TB_ROMSize.Text = $"{num_fs:0.##} {array_fs[num2_fs]}"; - TB_UsedSpace.Text = TB_ROMSize.Text; - - LoadNSPPartitions(); - - Process process = new Process(); - try - { - FileStream fileStream = File.OpenRead(TB_File.Text); - string ncaTarget = ""; - string xmlVersion = ""; - - List chars = new List(); - byte[] array = new byte[16]; - byte[] array2 = new byte[24]; - fileStream.Read(array, 0, 16); - PFS0.PFS0_Headers[0] = new PFS0.PFS0_Header(array); - if (!PFS0.PFS0_Headers[0].Magic.Contains("PFS0")) - { - return; - } - PFS0.PFS0_Entry[] array3; - array3 = new PFS0.PFS0_Entry[Math.Max(PFS0.PFS0_Headers[0].FileCount, MAXFILES)]; //Dump of TitleID 01009AA000FAA000 reports more than 10000000 files here, so it breaks the program. Standard is to have only 20 files - - for (int m = 0; m < PFS0.PFS0_Headers[0].FileCount; m++) - { - fileStream.Position = 16 + 24 * m; - fileStream.Read(array2, 0, 24); - array3[m] = new PFS0.PFS0_Entry(array2); - - if (m == MAXFILES - 1) //Dump of TitleID 01009AA000FAA000 reports more than 10000000 files here, so it breaks the program. Standard is to have only 20 files - { - break; - } - } - for (int n = 0; n < PFS0.PFS0_Headers[0].FileCount; n++) - { - fileStream.Position = 16 + 24 * PFS0.PFS0_Headers[0].FileCount + array3[n].Name_ptr; - int num4; - while ((num4 = fileStream.ReadByte()) != 0 && num4 != 0) - { - chars.Add((char)num4); - } - array3[n].Name = new string(chars.ToArray()); - chars.Clear(); - - if (array3[n].Name.EndsWith(".cnmt.xml")) - { - byte[] array4 = new byte[array3[n].Size]; - fileStream.Position = 16 + 24 * PFS0.PFS0_Headers[0].FileCount + PFS0.PFS0_Headers[0].StringTableSize + array3[n].Offset; - fileStream.Read(array4, 0, (int)array3[n].Size); - - XDocument xml = XDocument.Parse(Encoding.UTF8.GetString(array4)); - TB_TID.Text = xml.Element("ContentMeta").Element("Id").Value.Remove(1, 2).ToUpper(); //id - pversion = xml.Element("ContentMeta").Element("Version").Value; //version - contentType = xml.Element("ContentMeta").Element("Type").Value; //content - - if (contentType == "Patch") - { - patchflag = 1; - if (System.Convert.ToInt32(pversion) > patchnum) - { - patchnum = System.Convert.ToInt32(pversion); - xmlVersion = $"v{xml.Element("ContentMeta").Element("Version").Value}"; - int number = System.Convert.ToInt32(pversion); - patchver = $"v{System.Convert.ToString((double)number / 65536)}"; - } - - updtitle[updnum] = $"[{TB_TID.Text}][v{pversion}]"; - updnum++; - } - else if (contentType == "Application") - { - baseflag = 1; - if (patchflag != 1) - { - xmlVersion = $"v{xml.Element("ContentMeta").Element("Version").Value}"; - } - - basetitle[basenum] = $"[{TB_TID.Text}][v{pversion}]"; - basenum++; - } - else - { - if (baseflag == 0 && patchflag == 0) - { - xmlVersion = $"v{xml.Element("ContentMeta").Element("Version").Value}"; - } - - dlctitle[dlcnum] = $"[{TB_TID.Text}][v{pversion}]"; - dlcnum++; - } - - if (contentType != "AddOnContent") - { - foreach (XElement xe in xml.Descendants("Content")) - { - if (xe.Element("Type").Value != "Control") - { - continue; - } - - ncaTarget = $"{xe.Element("Id").Value}.nca"; - break; - } - } - else //This is a DLC - { - foreach (XElement xe in xml.Descendants("Content")) - { - if (xe.Element("Type").Value != "Meta") - { - continue; - } - - ncaTarget = $"{xe.Element("Id").Value}.cnmt.nca"; - break; - } - } - } - - if (n == MAXFILES - 1) //Dump of TitleID 01009AA000FAA000 reports more than 10000000 files here, so it breaks the program. Standard is to have only 20 files - { - break; - } - } - - if (String.IsNullOrEmpty(ncaTarget)) - { - //Missing content metadata xml. Read from content metadata nca instead - for (int n = 0; n < PFS0.PFS0_Headers[0].FileCount; n++) - { - if (array3[n].Name.EndsWith(".cnmt.nca")) - { - try - { - File.Delete("meta"); - Directory.Delete("data", true); - } - catch { } - - using (FileStream fileStream2 = File.OpenWrite("meta")) - { - fileStream.Position = 16 + 24 * PFS0.PFS0_Headers[0].FileCount + PFS0.PFS0_Headers[0].StringTableSize + array3[n].Offset; - byte[] buffer = new byte[8192]; - long num = array3[n].Size; - int num4; - while ((num4 = fileStream.Read(buffer, 0, 8192)) > 0 && num > 0) - { - fileStream2.Write(buffer, 0, num4); - num -= num4; - } - fileStream2.Close(); - } - - process = new Process(); - process.StartInfo = new ProcessStartInfo - { - WindowStyle = ProcessWindowStyle.Hidden, - FileName = Path.Join("tools", "hactool.exe"), - Arguments = "-k keys.txt --section0dir=data meta", - UseShellExecute = false, - RedirectStandardOutput = true, - CreateNoWindow = true - }; - process.Start(); - - string masterkey = ""; - while (!process.StandardOutput.EndOfStream) - { - string output = process.StandardOutput.ReadLine(); - if (output.StartsWith("Master Key Revision")) - { - masterkey = Regex.Replace(output, @"\s+", " "); - } - } - process.WaitForExit(); - - if (!Directory.Exists("data")) - { - new CenterWinDialog(this); - MessageBox.Show($"{masterkey} is missing!"); - } - else - { - try - { - string[] cnmt = Directory.GetFiles("data", "*.cnmt"); - if (cnmt.Length != 0) - { - using (FileStream fileStream3 = File.OpenRead(cnmt[0])) - { - byte[] buffer = new byte[32]; - byte[] buffer2 = new byte[56]; - CNMT.CNMT_Header[] array7 = new CNMT.CNMT_Header[1]; - - fileStream3.Read(buffer, 0, 32); - array7[0] = new CNMT.CNMT_Header(buffer); - - byte[] TitleID = BitConverter.GetBytes(array7[0].TitleID); - Array.Reverse(TitleID); - TB_TID.Text = BitConverter.ToString(TitleID).Replace("-", ""); - - if (array7[0].Type == (byte)CNMT.CNMT_Header.TitleType.REGULAR_APPLICATION) - { - contentType = "Application"; - baseflag = 1; - if (patchflag != 1) - { - xmlVersion = $"v{array7[0].TitleVersion.ToString()}"; - } - - basetitle[basenum] = $"[{TB_TID.Text}][v{array7[0].TitleVersion.ToString()}]"; - basenum++; - } - else if (array7[0].Type == (byte)CNMT.CNMT_Header.TitleType.UPDATE_TITLE) - { - contentType = "Patch"; - - patchflag = 1; - if (array7[0].TitleVersion > patchnum) - { - patchnum = array7[0].TitleVersion; - xmlVersion = $"v{array7[0].TitleVersion.ToString()}"; - int number = System.Convert.ToInt32(array7[0].TitleVersion); - patchver = $"v{System.Convert.ToString((double)number / 65536)}"; - } - - updtitle[updnum] = $"[{TB_TID.Text}][v{array7[0].TitleVersion.ToString()}]"; - updnum++; - } - else if (array7[0].Type == (byte)CNMT.CNMT_Header.TitleType.ADD_ON_CONTENT) - { - if (baseflag == 0 && patchflag == 0) - { - xmlVersion = $"v{array7[0].TitleVersion.ToString()}"; - } - - contentType = "AddOnContent"; - dlctitle[dlcnum] = $"[{TB_TID.Text}][v{array7[0].TitleVersion.ToString()}]"; - dlcnum++; - } - - fileStream3.Position = array7[0].Offset + 32; - CNMT.CNMT_Entry[] array9 = new CNMT.CNMT_Entry[array7[0].ContentCount]; - for (int k = 0; k < array7[0].ContentCount; k++) - { - fileStream3.Read(buffer2, 0, 56); - array9[k] = new CNMT.CNMT_Entry(buffer2); - if (array9[k].Type == (byte)CNMT.CNMT_Entry.ContentType.CONTROL || array9[k].Type == (byte)CNMT.CNMT_Entry.ContentType.DATA) - { - ncaTarget = $"{BitConverter.ToString(array9[k].NcaId).ToLower().Replace("-", "")}.nca"; - break; - } - } - - fileStream3.Close(); - } - } - } - catch { } - } - } - } - } - - for (int n = 0; n < PFS0.PFS0_Headers[0].FileCount; n++) - { - if (array3[n].Name.Equals(ncaTarget)) - { - Directory.CreateDirectory("tmp"); - - byte[] array5 = new byte[64 * 1024]; - fileStream.Position = 16 + 24 * PFS0.PFS0_Headers[0].FileCount + PFS0.PFS0_Headers[0].StringTableSize + array3[n].Offset; - - using (Stream output = File.Create(Path.Join("tmp", ncaTarget))) - { - long Size = array3[n].Size; - int result = 0; - while ((result = fileStream.Read(array5, 0, (int)Math.Min(array5.Length, Size))) > 0) - { - output.Write(array5, 0, result); - Size -= result; - } - } - - break; - } - - if (n == MAXFILES - 1) //Dump of TitleID 01009AA000FAA000 reports more than 10000000 files here, so it breaks the program. Standard is to have only 20 files - { - break; - } - } - - fileStream.Close(); - - if (contentType != "AddOnContent") - { - process = new Process(); - process.StartInfo = new ProcessStartInfo - { - WindowStyle = ProcessWindowStyle.Hidden, - FileName = Path.Join("tools", "hactool.exe"), - Arguments = $"-k keys.txt --romfsdir=tmp tmp/{ncaTarget}", - UseShellExecute = false, - CreateNoWindow = true - }; - - process.Start(); - process.WaitForExit(); - process.Close(); - byte[] flux = new byte[200]; - - try - { - byte[] source = File.ReadAllBytes(Path.Join("tmp", "control.nacp")); - NACP.NACP_Datas[0] = new NACP.NACP_Data(source.Skip(0x3000).Take(0x1000).ToArray()); - - for (int i = 0; i < NACP.NACP_Strings.Length; i++) - { - NACP.NACP_Strings[i] = new NACP.NACP_String(source.Skip(i * 0x300).Take(0x300).ToArray()); - - if (NACP.NACP_Strings[i].Check != 0) - { - CB_RegionName.Items.Add(Language[i]); - string icon_filename = Path.Join("tmp", $"icon_{Language[i].Replace(" ", "")}.dat"); - if (File.Exists(icon_filename)) - { - using (Bitmap original = new Bitmap(icon_filename)) - { - Icons[i] = new Bitmap(original); - PB_GameIcon.BackgroundImage = Icons[i]; - } - } - } - } - - string gameVer = NACP.NACP_Datas[0].GameVer.Replace("\0", ""); - if (xmlVersion.Trim() == "") - { - TB_GameRev.Text = $"GENERAL:{Environment.NewLine}({gameVer}){Environment.NewLine}"; - } - else - { - string cache = $"GENERAL:{Environment.NewLine}{gameVer}{((patchflag == 1) ? $" ({patchver})" : "")}{Environment.NewLine}"; - - if (basenum != 0) - { - cache += $"BASE:{Environment.NewLine}"; - for (int i = 0; i < basenum; i++) - { - cache += basetitle[i] + Environment.NewLine; - } - } - else - { - cache += $"BASE:{System.Environment.NewLine}EMPTY{System.Environment.NewLine}"; - } - if (updnum != 0) - { - cache += $"UPD:{System.Environment.NewLine}"; - for (int i = 0; i < updnum; i++) - { - cache += updtitle[i] + System.Environment.NewLine; - } - } - else - { - cache += $"UPD:{System.Environment.NewLine}EMPTY{System.Environment.NewLine}"; - } - if (dlcnum != 0) - { - cache += $"DLC:{System.Environment.NewLine}"; - for (int i = 0; i < dlcnum; i++) - { - if (i < dlcnum - 1) - { - cache += dlctitle[i] + System.Environment.NewLine; - } - else - { - cache += dlctitle[i]; - } - } - } - else - { - cache += $"DLC:{System.Environment.NewLine}EMPTY"; - } - TB_GameRev.Text = cache; - label12.Text = $"{basenum.ToString()} BASE, {updnum.ToString()} UPD, {dlcnum.ToString()} DLC"; - } - - TB_ProdCode.Text = NACP.NACP_Datas[0].GameProd.Replace("\0", ""); - if (TB_ProdCode.Text == "") - { - TB_ProdCode.Text = "No Prod. ID"; - } - - for (int z = 0; z < NACP.NACP_Strings.Length; z++) - { - if (NACP.NACP_Strings[z].GameName.Replace("\0", "") != "") - { - TB_Name.Text = NACP.NACP_Strings[z].GameName.Replace("\0", ""); - break; - } - } - for (int z = 0; z < NACP.NACP_Strings.Length; z++) - { - if (NACP.NACP_Strings[z].GameAuthor.Replace("\0", "") != "") - { - TB_Dev.Text = NACP.NACP_Strings[z].GameAuthor.Replace("\0", ""); - break; - } - } - } - catch { } - - } - else - { - if (xmlVersion.Trim() == "") - { - TB_GameRev.Text = $"GENERAL:{System.Environment.NewLine}{System.Environment.NewLine}"; - } - else - { - string gameVer = NACP.NACP_Datas[0].GameVer.Replace("\0", ""); - string cache = $"GENERAL:{Environment.NewLine}{gameVer}{((patchflag == 1) ? $" ({patchver})" : "")}{Environment.NewLine}"; - - if (basenum != 0) - { - cache += $"BASE:{System.Environment.NewLine}"; - for (int i = 0; i < basenum; i++) - { - cache += basetitle[i] + System.Environment.NewLine; - } - } - else - { - cache += $"BASE:{System.Environment.NewLine}EMPTY{System.Environment.NewLine}"; - } - if (updnum != 0) - { - cache += $"UPD:{System.Environment.NewLine}"; - for (int i = 0; i < updnum; i++) - { - cache += updtitle[i] + System.Environment.NewLine; - } - } - else - { - cache += $"UPD:{System.Environment.NewLine}EMPTY{System.Environment.NewLine}"; - } - if (dlcnum != 0) - { - cache += $"DLC:{System.Environment.NewLine}"; - for (int i = 0; i < dlcnum; i++) - { - if (i < dlcnum - 1) - { - cache += dlctitle[i] + System.Environment.NewLine; - } - else - { - cache += dlctitle[i]; - } - } - } - else - { - cache += $"DLC:{System.Environment.NewLine}EMPTY"; - } - TB_GameRev.Text = cache; - label12.Text = $"{basenum.ToString()} BASE, {updnum.ToString()} UPD, {dlcnum.ToString()} DLC"; - } - TB_ProdCode.Text = "No Prod. ID"; - } - - // Lets get SDK Version, Distribution Type and Masterkey revision - // This is far from the best aproach, but it's what we have for now - process = new Process(); - process.StartInfo = new ProcessStartInfo - { - WindowStyle = ProcessWindowStyle.Hidden, - FileName = Path.Join("tools", "hactool.exe"), - Arguments = $"-k keys.txt tmp/{ncaTarget}", - RedirectStandardOutput = true, - UseShellExecute = false, - CreateNoWindow = true - }; - process.Start(); - StreamReader sr = process.StandardOutput; - - while (sr.Peek() >= 0) - { - string str; - string[] strArray; - str = sr.ReadLine(); - strArray = str.Split(':'); - if (strArray[0] == "SDK Version") - { - TB_SDKVer.Text = strArray[1].Trim(); - } - else if (strArray[0] == "Master Key Revision") - { - string MasterKey = strArray[1].Trim(); - int keyblob; - - MasterKey = MasterKey.Split(new char[2] { 'x', ' ' })[1]; - keyblob = Convert.ToInt32(MasterKey, 16); - MasterKey = Util.GetMkey((byte)(keyblob + 1)); - TB_MKeyRev.Text = MasterKey; - break; - } - } - process.WaitForExit(); - process.Close(); - } - catch { } - - try - { - File.Delete("meta"); - Directory.Delete("data", true); - } - catch { } - - try - { - Directory.Delete("tmp", true); - } - catch { } - - TB_Capacity.Text = "eShop"; - - if (TB_Name.Text.Trim() != "") - { - CB_RegionName.SelectedIndex = 0; - } - } - - private void LoadGameInfos() - { - CB_RegionName.Items.Clear(); - CB_RegionName.Enabled = true; - TB_Name.Text = ""; - TB_Dev.Text = ""; - PB_GameIcon.BackgroundImage = null; - - int basenum = 0; - int updnum = 0; - int dlcnum = 0; - int patchflag = 0; - int patchnum = 0; - string patchver = ""; - int baseflag = 0; - string[] basetitle = new string[5]; - string[] updtitle = new string[10]; - string[] dlctitle = new string[300]; - string xmlVersion = ""; - string saveTID = ""; - - Array.Clear(Icons, 0, Icons.Length); - if (getMKey()) - { - using (FileStream fileStream = File.OpenRead(TB_File.Text)) - { - List ncaTarget = new List(); - string GameRevision = ""; - - for (int si = 0; si < SecureSize.Length; si++) - { - if (SecureSize[si] > 0x4E20000) - { - continue; - } - - if (SecureName[si].EndsWith(".cnmt.nca")) - { - try - { - File.Delete("meta"); - Directory.Delete("data", true); - } - catch { } - - using (FileStream fileStream2 = File.OpenWrite("meta")) - { - fileStream.Position = SecureOffset[si]; - byte[] buffer = new byte[8192]; - long num = SecureSize[si]; - int num4; - while ((num4 = fileStream.Read(buffer, 0, 8192)) > 0 && num > 0) - { - fileStream2.Write(buffer, 0, num4); - num -= num4; - } - fileStream2.Close(); - } - - Process process1 = new Process(); - process1.StartInfo = new ProcessStartInfo - { - WindowStyle = ProcessWindowStyle.Hidden, - FileName = Path.Join("tools", "hactool.exe"), - Arguments = "-k keys.txt --section0dir=data meta", - UseShellExecute = false, - CreateNoWindow = true - }; - process1.Start(); - process1.WaitForExit(); - - string[] cnmt = Directory.GetFiles("data", "*.cnmt"); - - if (cnmt.Length != 0) - { - using (FileStream fileStream3 = File.OpenRead(cnmt[0])) - { - byte[] buffer = new byte[32]; - byte[] buffer2 = new byte[56]; - CNMT.CNMT_Header[] array7 = new CNMT.CNMT_Header[1]; - - fileStream3.Read(buffer, 0, 32); - array7[0] = new CNMT.CNMT_Header(buffer); - - byte[] TitleID = BitConverter.GetBytes(array7[0].TitleID); - Array.Reverse(TitleID); - saveTID = BitConverter.ToString(TitleID).Replace("-", ""); - - if (array7[0].Type == (byte)CNMT.CNMT_Header.TitleType.REGULAR_APPLICATION) - { - baseflag = 1; - if (patchflag != 1) - { - xmlVersion = $"v{array7[0].TitleVersion.ToString()}"; - } - - basetitle[basenum] = $"[{saveTID}][v{array7[0].TitleVersion.ToString()}]"; - basenum++; - } - else if (array7[0].Type == (byte)CNMT.CNMT_Header.TitleType.UPDATE_TITLE) - { - patchflag = 1; - if (array7[0].TitleVersion > patchnum) - { - patchnum = array7[0].TitleVersion; - xmlVersion = $"v{array7[0].TitleVersion.ToString()}"; - int number = System.Convert.ToInt32(array7[0].TitleVersion); - patchver = $"v{System.Convert.ToString((double)number / 65536)}"; - } - - updtitle[updnum] = $"[{saveTID}][v{array7[0].TitleVersion.ToString()}]"; - updnum++; - } - else if (array7[0].Type == (byte)CNMT.CNMT_Header.TitleType.ADD_ON_CONTENT) - { - if (patchflag == 0 && baseflag == 0) - { - xmlVersion = $"v{array7[0].TitleVersion.ToString()}"; - } - - dlctitle[dlcnum] = $"[{saveTID}][v{array7[0].TitleVersion.ToString()}]"; - dlcnum++; - } - - fileStream3.Position = array7[0].Offset + 32; - CNMT.CNMT_Entry[] array9 = new CNMT.CNMT_Entry[array7[0].ContentCount]; - for (int k = 0; k < array7[0].ContentCount; k++) - { - fileStream3.Read(buffer2, 0, 56); - array9[k] = new CNMT.CNMT_Entry(buffer2); - if (array9[k].Type == (byte)CNMT.CNMT_Entry.ContentType.CONTROL || array9[k].Type == (byte)CNMT.CNMT_Entry.ContentType.DATA) - { - ncaTarget.Add($"{BitConverter.ToString(array9[k].NcaId).ToLower().Replace("-", "")}.nca"); - break; - } - } - fileStream3.Close(); - } - } - } - } - - for (int si = 0; si < SecureSize.Length; si++) - { - if (SecureSize[si] > 0x4E20000) - { - continue; - } - - if (ncaTarget.Contains(SecureName[si])) - { - try - { - File.Delete("meta"); - Directory.Delete("data", true); - } - catch { } - - using (FileStream fileStream2 = File.OpenWrite("meta")) - { - fileStream.Position = SecureOffset[si]; - byte[] buffer = new byte[8192]; - long num = SecureSize[si]; - int num2; - while ((num2 = fileStream.Read(buffer, 0, 8192)) > 0 && num > 0) - { - fileStream2.Write(buffer, 0, num2); - num -= num2; - } - fileStream2.Close(); - } - - - Process process = new Process(); - process.StartInfo = new ProcessStartInfo - { - WindowStyle = ProcessWindowStyle.Hidden, - FileName = Path.Join("tools", "hactool.exe"), - Arguments = "-k keys.txt --romfsdir=data meta", - UseShellExecute = false, - CreateNoWindow = true - }; - process.Start(); - process.WaitForExit(); - - if (File.Exists(Path.Join("data", "control.nacp"))) - { - byte[] source = File.ReadAllBytes(Path.Join("data", "control.nacp")); - NACP.NACP_Datas[0] = new NACP.NACP_Data(source.Skip(0x3000).Take(0x1000).ToArray()); - - string GameVer = NACP.NACP_Datas[0].GameVer.Replace("\0", ""); - Version version1, version2; - if (!Version.TryParse(Regex.Replace(GameRevision, @"[^\d.].*$", ""), out version1)) - { - version1 = new Version(); - } - if (!Version.TryParse(Regex.Replace(GameVer, @"[^\d.].*$", ""), out version2)) - { - version2 = new Version(); - } - if (version2.CompareTo(version1) > 0) - { - GameRevision = GameVer; - - for (int i = 0; i < NACP.NACP_Strings.Length; i++) - { - NACP.NACP_Strings[i] = new NACP.NACP_String(source.Skip(i * 0x300).Take(0x300).ToArray()); - if (NACP.NACP_Strings[i].Check != 0 && !CB_RegionName.Items.Contains(Language[i])) - { - CB_RegionName.Items.Add(Language[i]); - string icon_filename = Path.Join("data", $"icon_{Language[i].Replace(" ", "")}.dat"); - if (File.Exists(icon_filename)) - { - using (Bitmap original = new Bitmap(icon_filename)) - { - Icons[i] = new Bitmap(original); - PB_GameIcon.BackgroundImage = Icons[i]; - } - } - } - } - TB_ProdCode.Text = NACP.NACP_Datas[0].GameProd; - if (TB_ProdCode.Text == "") - { - TB_ProdCode.Text = "No Prod. ID"; - } - try - { - File.Delete("meta"); - Directory.Delete("data", true); - } - catch { } - } - } - } - } - - string gameVer = NACP.NACP_Datas[0].GameVer.Replace("\0", ""); - string cache = $"GENERAL:{Environment.NewLine}{gameVer}{((patchflag == 1) ? $" ({patchver})" : "")}{Environment.NewLine}"; - - if (basenum != 0) - { - cache += $"BASE:{System.Environment.NewLine}"; - for (int i = 0; i < basenum; i++) - { - cache += basetitle[i] + System.Environment.NewLine; - } - } - else - { - cache += $"BASE:{System.Environment.NewLine}EMPTY{System.Environment.NewLine}"; - } - if (updnum != 0) - { - cache += $"UPD:{System.Environment.NewLine}"; - for (int i = 0; i < updnum; i++) - { - cache += updtitle[i] + System.Environment.NewLine; - } - } - else - { - cache += $"UPD:{System.Environment.NewLine}EMPTY{System.Environment.NewLine}"; - } - if (dlcnum != 0) - { - cache += $"DLC:{System.Environment.NewLine}"; - for (int i = 0; i < dlcnum; i++) - { - if (i < dlcnum - 1) - { - cache += dlctitle[i] + System.Environment.NewLine; - } - else - { - cache += dlctitle[i]; - } - } - } - else - { - cache += $"DLC:{System.Environment.NewLine}EMPTY"; - } - TB_GameRev.Text = cache; - label12.Text = $"{basenum.ToString()} BASE, {updnum.ToString()} UPD, {dlcnum.ToString()} DLC"; - - CB_RegionName.SelectedIndex = 0; - - fileStream.Close(); - } + LoadXCI(); } else { - TB_Dev.Text = $"{Mkey} not found"; - TB_Name.Text = $"{Mkey} not found"; + TB_File.Text = null; + new CenterWinDialog(this); + MessageBox.Show("File is corrupt or unsupported."); } } - - private void LoadNCAData() + catch (Exception e) { - NCA.NCA_Headers[0] = new NCA.NCA_Header(DecryptNCAHeader(gameNcaOffset)); - TB_TID.Text = $"0{NCA.NCA_Headers[0].TitleID.ToString("X")}"; - TB_SDKVer.Text = $"{NCA.NCA_Headers[0].SDKVersion4}.{NCA.NCA_Headers[0].SDKVersion3}.{NCA.NCA_Headers[0].SDKVersion2}.{NCA.NCA_Headers[0].SDKVersion1}"; - TB_MKeyRev.Text = Util.GetMkey(NCA.NCA_Headers[0].MasterKeyRev); + new CenterWinDialog(this); + MessageBox.Show($"File is corrupt or unsupported.\nException: {e.Message}"); } - //https://stackoverflow.com/questions/311165/how-do-you-convert-a-byte-array-to-a-hexadecimal-string-and-vice-versa - public static string ByteArrayToString(byte[] ba) - { - StringBuilder hex = new StringBuilder(ba.Length * 2 + 2); - hex.Append("0x"); - foreach (byte b in ba) - { - hex.AppendFormat("{0:x2}", b); - } + } - return hex.ToString(); + private void B_LoadROM_Click(object sender, EventArgs e) + { + OpenFileDialog openFileDialog = new() + { + Filter = "Switch Game File (*.xci, *.nsp, *.nsz)|*.xci;*.nsp;*.nsz|All Files (*.*)|*.*" + }; + new CenterWinDialog(this); + if (openFileDialog.ShowDialog() == DialogResult.OK) + { + TB_File.Text = openFileDialog.FileName; + ProcessFile(); + } + } + + private void LoadXCI() + { + string[] array = new string[5] + { + "B", + "KB", + "MB", + "GB", + "TB" + }; + double num = new FileInfo(TB_File.Text).Length; + TB_ROMExactSize.Text = $"({num} bytes)"; + int num2 = 0; + while (num >= 1024.0 && num2 < array.Length - 1) + { + num2++; + num /= 1024.0; + } + TB_ROMSize.Text = $"{num:0.##} {array[num2]}"; + double num3 = UsedSize = XCI.XCI_Headers[0].CardSize2 * 512 + 512; + TB_ExactUsedSpace.Text = $"({num3} bytes)"; + if (isTrimmed()) + { + B_TrimXCI.Enabled = false; } - public static string SHA256Bytes(byte[] ba) + num2 = 0; + while (num3 >= 1024.0 && num2 < array.Length - 1) { - SHA256 mySHA256 = SHA256.Create(); - byte[] hashValue; - hashValue = mySHA256.ComputeHash(ba); - return ByteArrayToString(hashValue); + num2++; + num3 /= 1024.0; } + TB_UsedSpace.Text = $"{num3:0.##} {array[num2]}"; + TB_Capacity.Text = Util.GetCapacity(XCI.XCI_Headers[0].CardSize1); + LoadPartitions(); + LoadNCAData(); + LoadGameInfos(); + } - public bool isTrimmed() + // Giba's better implementation (more native) + public void LoadNSP() + { + CB_RegionName.Items.Clear(); + CB_RegionName.Enabled = true; + TB_TID.Text = ""; + TB_Capacity.Text = ""; + TB_MKeyRev.Text = ""; + TB_SDKVer.Text = ""; + TB_GameRev.Text = ""; + TB_ProdCode.Text = ""; + TB_Name.Text = ""; + TB_Dev.Text = ""; + + int basenum = 0; + int updnum = 0; + int dlcnum = 0; + string pversion = ""; + int patchflag = 0; + int patchnum = 0; + string patchver = ""; + int baseflag = 0; + string[] basetitle = new string[5]; + string[] updtitle = new string[10]; + string[] dlctitle = new string[300]; + + PB_GameIcon.BackgroundImage = null; + Array.Clear(Icons, 0, Icons.Length); + TV_Partitions.Nodes.Clear(); + FileInfo fi = new(TB_File.Text); + string contentType = ""; + + // Maximum number of files in NSP to read in + const int MAXFILES = 250; + + //Get File Size + string[] array_fs = new string[5] { "B", "KB", "MB", "GB", "TB" }; + double num_fs = fi.Length; + int num2_fs = 0; + TB_ROMExactSize.Text = $"({num_fs} bytes)"; + TB_ExactUsedSpace.Text = TB_ROMExactSize.Text; + + while (num_fs >= 1024.0 && num2_fs < array_fs.Length - 1) { - return TB_ROMExactSize.Text == TB_ExactUsedSpace.Text; + num2_fs++; + num_fs /= 1024.0; } + TB_ROMSize.Text = $"{num_fs:0.##} {array_fs[num2_fs]}"; + TB_UsedSpace.Text = TB_ROMSize.Text; - private void LoadPartitions() + LoadNSPPartitions(); + + Process process = new(); + try { - string actualHash; - byte[] hashBuffer; - long offset; - - TV_Partitions.Nodes.Clear(); - TV_Parti = new TreeViewFileSystem(TV_Partitions); - rootNode = new BetterTreeNode("root"); - rootNode.Offset = -1L; - rootNode.Size = -1L; - TV_Partitions.Nodes.Add(rootNode); - bool LogoPartition = false; - FileStream fileStream = new FileStream(TB_File.Text, FileMode.Open, FileAccess.Read); - HFS0.HSF0_Entry[] array = new HFS0.HSF0_Entry[HFS0.HFS0_Headers[0].FileCount]; - fileStream.Position = XCI.XCI_Headers[0].HFS0OffsetPartition + 16 + 64 * HFS0.HFS0_Headers[0].FileCount; - long num = XCI.XCI_Headers[0].HFS0OffsetPartition + XCI.XCI_Headers[0].HFS0SizeParition; - byte[] array2 = new byte[64]; - byte[] array3 = new byte[16]; - byte[] array4 = new byte[24]; - for (int i = 0; i < HFS0.HFS0_Headers[0].FileCount; i++) - { - fileStream.Position = XCI.XCI_Headers[0].HFS0OffsetPartition + 16 + 64 * i; - fileStream.Read(array2, 0, 64); - array[i] = new HFS0.HSF0_Entry(array2); - fileStream.Position = XCI.XCI_Headers[0].HFS0OffsetPartition + 16 + 64 * HFS0.HFS0_Headers[0].FileCount + array[i].Name_ptr; - int num2; - while ((num2 = fileStream.ReadByte()) != 0 && num2 != 0) - { - chars.Add((char)num2); - } - array[i].Name = new string(chars.ToArray()); - chars.Clear(); - offset = num + array[i].Offset; - hashBuffer = new byte[array[i].HashedRegionSize]; - fileStream.Position = offset; - fileStream.Read(hashBuffer, 0, array[i].HashedRegionSize); - actualHash = SHA256Bytes(hashBuffer); - - TV_Parti.AddFile($"{array[i].Name}.hfs0", rootNode, offset, array[i].Size, array[i].HashedRegionSize, ByteArrayToString(array[i].Hash), actualHash); - BetterTreeNode betterTreeNode = TV_Parti.AddDir(array[i].Name, rootNode); - HFS0.HFS0_Header[] array5 = new HFS0.HFS0_Header[1]; - fileStream.Position = array[i].Offset + num; - fileStream.Read(array3, 0, 16); - array5[0] = new HFS0.HFS0_Header(array3); - if (array[i].Name == "secure") - { - SecureSize = new long[array5[0].FileCount]; - SecureOffset = new long[array5[0].FileCount]; - SecureName = new string[array5[0].FileCount]; - } - if (array[i].Name == "normal") - { - NormalSize = new long[array5[0].FileCount]; - NormalOffset = new long[array5[0].FileCount]; - } - if (array[i].Name == "logo") - { - if (array5[0].FileCount > 0) - { - LogoPartition = true; - } - } - HFS0.HSF0_Entry[] array6 = new HFS0.HSF0_Entry[array5[0].FileCount]; - for (int j = 0; j < array5[0].FileCount; j++) - { - fileStream.Position = array[i].Offset + num + 16 + 64 * j; - fileStream.Read(array2, 0, 64); - array6[j] = new HFS0.HSF0_Entry(array2); - fileStream.Position = array[i].Offset + num + 16 + 64 * array5[0].FileCount + array6[j].Name_ptr; - while ((num2 = fileStream.ReadByte()) != 0 && num2 != 0) - { - chars.Add((char)num2); - } - array6[j].Name = new string(chars.ToArray()); - chars.Clear(); - if (array[i].Name == "secure") - { - SecureSize[j] = array6[j].Size; - SecureOffset[j] = array[i].Offset + array6[j].Offset + num + 16 + array5[0].StringTableSize + array5[0].FileCount * 64; - SecureName[j] = array6[j].Name; - } - if (array[i].Name == "normal") - { - NormalSize[j] = array6[j].Size; - NormalOffset[j] = array[i].Offset + array6[j].Offset + num + 16 + array5[0].StringTableSize + array5[0].FileCount * 64; - } - offset = array[i].Offset + array6[j].Offset + num + 16 + array5[0].StringTableSize + array5[0].FileCount * 64; - hashBuffer = new byte[array6[j].HashedRegionSize]; - fileStream.Position = offset; - fileStream.Read(hashBuffer, 0, array6[j].HashedRegionSize); - actualHash = SHA256Bytes(hashBuffer); - - TV_Parti.AddFile(array6[j].Name, betterTreeNode, offset, array6[j].Size, array6[j].HashedRegionSize, ByteArrayToString(array6[j].Hash), actualHash); - TreeNode[] array7 = TV_Partitions.Nodes.Find(betterTreeNode.Text, true); - if (array7.Length != 0) - { - TV_Parti.AddFile(array6[j].Name, (BetterTreeNode)array7[0], 0L, 0L); - } - } - } - long num3 = -9223372036854775808L; - for (int k = 0; k < SecureSize.Length; k++) - { - if (SecureSize[k] > num3) - { - gameNcaSize = SecureSize[k]; - gameNcaOffset = SecureOffset[k]; - num3 = SecureSize[k]; - } - } - PFS0Offset = gameNcaOffset + 32768; - fileStream.Position = PFS0Offset; - fileStream.Read(array3, 0, 16); - PFS0.PFS0_Headers[0] = new PFS0.PFS0_Header(array3); - if (PFS0.PFS0_Headers[0].FileCount == 2 || !LogoPartition) - { - PFS0.PFS0_Entry[] array8; - try - { - array8 = new PFS0.PFS0_Entry[PFS0.PFS0_Headers[0].FileCount]; - } - catch (Exception ex) - { - array8 = new PFS0.PFS0_Entry[0]; - Debug.WriteLine($"Partitions Error: {ex.Message}"); - } - for (int m = 0; m < PFS0.PFS0_Headers[0].FileCount; m++) - { - fileStream.Position = PFS0Offset + 16 + 24 * m; - fileStream.Read(array4, 0, 24); - array8[m] = new PFS0.PFS0_Entry(array4); - PFS0Size += array8[m].Size; - } - TV_Parti.AddFile("boot.psf0", rootNode, PFS0Offset, 16 + 24 * PFS0.PFS0_Headers[0].FileCount + 64 + PFS0Size); - BetterTreeNode betterTreeNode2 = TV_Parti.AddDir("boot", rootNode); - for (int n = 0; n < PFS0.PFS0_Headers[0].FileCount; n++) - { - fileStream.Position = PFS0Offset + 16 + 24 * PFS0.PFS0_Headers[0].FileCount + array8[n].Name_ptr; - int num4; - while ((num4 = fileStream.ReadByte()) != 0 && num4 != 0) - { - chars.Add((char)num4); - } - array8[n].Name = new string(chars.ToArray()); - chars.Clear(); - TV_Parti.AddFile(array8[n].Name, betterTreeNode2, PFS0Offset + array8[n].Offset + 16 + PFS0.PFS0_Headers[0].StringTableSize + PFS0.PFS0_Headers[0].FileCount * 24, array8[n].Size); - TreeNode[] array9 = TV_Partitions.Nodes.Find(betterTreeNode2.Text, true); - if (array9.Length != 0) - { - TV_Parti.AddFile(array8[n].Name, (BetterTreeNode)array9[0], 0L, 0L); - } - } - } - fileStream.Close(); - } - - - private void LoadNSPPartitions() - { - long offset; - - TV_Partitions.Nodes.Clear(); - TV_Parti = new TreeViewFileSystem(TV_Partitions); - rootNode = new BetterTreeNode("root"); - rootNode.Offset = -1L; - rootNode.Size = -1L; - TV_Partitions.Nodes.Add(rootNode); - - - // Maximum number of files in NSP to read in - const int MAXFILES = 250; - FileStream fileStream = File.OpenRead(TB_File.Text); - List chars = new List(); + string ncaTarget = ""; + string xmlVersion = ""; + + List chars = new(); byte[] array = new byte[16]; byte[] array2 = new byte[24]; fileStream.Read(array, 0, 16); - PFS0.PFS0_Headers[0] = new PFS0.PFS0_Header(array); + PFS0.PFS0_Headers[0] = new(array); if (!PFS0.PFS0_Headers[0].Magic.Contains("PFS0")) { return; } PFS0.PFS0_Entry[] array3; array3 = new PFS0.PFS0_Entry[Math.Max(PFS0.PFS0_Headers[0].FileCount, MAXFILES)]; //Dump of TitleID 01009AA000FAA000 reports more than 10000000 files here, so it breaks the program. Standard is to have only 20 files + for (int m = 0; m < PFS0.PFS0_Headers[0].FileCount; m++) { fileStream.Position = 16 + 24 * m; fileStream.Read(array2, 0, 24); - array3[m] = new PFS0.PFS0_Entry(array2); + array3[m] = new(array2); if (m == MAXFILES - 1) //Dump of TitleID 01009AA000FAA000 reports more than 10000000 files here, so it breaks the program. Standard is to have only 20 files { @@ -1396,475 +362,1551 @@ namespace XCI_Explorer { chars.Add((char)num4); } - array3[n].Name = new string(chars.ToArray()); + array3[n].Name = new(chars.ToArray()); chars.Clear(); - offset = 16 + 24 * PFS0.PFS0_Headers[0].FileCount + PFS0.PFS0_Headers[0].StringTableSize + array3[n].Offset; - fileStream.Position = offset; - TV_Parti.AddFile(array3[n].Name, rootNode, offset, array3[n].Size); - } + if (array3[n].Name.EndsWith(".cnmt.xml")) + { + byte[] array4 = new byte[array3[n].Size]; + fileStream.Position = 16 + 24 * PFS0.PFS0_Headers[0].FileCount + PFS0.PFS0_Headers[0].StringTableSize + array3[n].Offset; + fileStream.Read(array4, 0, (int)array3[n].Size); - fileStream.Close(); - } + XDocument xml = XDocument.Parse(Encoding.UTF8.GetString(array4)); + TB_TID.Text = xml.Element("ContentMeta").Element("Id").Value.Remove(1, 2).ToUpper(); //id + pversion = xml.Element("ContentMeta").Element("Version").Value; //version + contentType = xml.Element("ContentMeta").Element("Type").Value; //content + var test = NACP.NACP_Datas[0].GameVer.Replace("\0", ""); - - private void TV_Partitions_AfterSelect(object sender, TreeViewEventArgs e) - { - BetterTreeNode betterTreeNode = (BetterTreeNode)TV_Partitions.SelectedNode; - if (betterTreeNode.Offset != -1) - { - selectedOffset = betterTreeNode.Offset; - selectedSize = betterTreeNode.Size; - string expectedHash = betterTreeNode.ExpectedHash; - string actualHash = betterTreeNode.ActualHash; - long HashedRegionSize = betterTreeNode.HashedRegionSize; - - LB_DataOffset.Text = $"Offset: 0x{selectedOffset.ToString("X")}"; - LB_SelectedData.Text = e.Node.Text; - if (backgroundWorker1.IsBusy != true) - { - B_Extract.Enabled = true; - } - string[] array = new string[5] - { - "B", - "KB", - "MB", - "GB", - "TB" - }; - double num = (double)selectedSize; - int num2 = 0; - while (num >= 1024.0 && num2 < array.Length - 1) - { - num2++; - num /= 1024.0; - } - LB_DataSize.Text = $"Size: 0x{selectedSize.ToString("X")} ({num.ToString()}{array[num2]})"; - - if (HashedRegionSize != 0) - { - LB_HashedRegionSize.Text = $"HashedRegionSize: 0x{HashedRegionSize.ToString("X")}"; - } - else - { - LB_HashedRegionSize.Text = ""; - } - - if (!string.IsNullOrEmpty(expectedHash)) - { - LB_ExpectedHash.Text = $"Header Hash: {expectedHash.Substring(0, 32)}"; - } - else - { - LB_ExpectedHash.Text = ""; - } - - if (!string.IsNullOrEmpty(actualHash)) - { - LB_ActualHash.Text = $"Actual Hash: {actualHash.Substring(0, 32)}"; - if (actualHash == expectedHash) + if (contentType == "Patch") { - LB_ActualHash.ForeColor = System.Drawing.Color.Green; + patchflag = 1; + if (Convert.ToInt32(pversion) > patchnum) + { + patchnum = Convert.ToInt32(pversion); + xmlVersion = $"v{xml.Element("ContentMeta").Element("Version").Value}"; + int number = Convert.ToInt32(pversion); + patchver = $"v{Convert.ToString((double)number / 65536)}"; + } + + updtitle[updnum] = $"[{TB_TID.Text}][v{pversion}]"; + updnum++; + } + else if (contentType == "Application") + { + baseflag = 1; + if (patchflag != 1) + { + xmlVersion = $"v{xml.Element("ContentMeta").Element("Version").Value}"; + } + + basetitle[basenum] = $"[{TB_TID.Text}][v{pversion}]"; + basenum++; } else { - LB_ActualHash.ForeColor = System.Drawing.Color.Red; + if (baseflag == 0 && patchflag == 0) + { + xmlVersion = $"v{xml.Element("ContentMeta").Element("Version").Value}"; + } + + dlctitle[dlcnum] = $"[{TB_TID.Text}][v{pversion}]"; + dlcnum++; } + + if (contentType != "AddOnContent") + { + foreach (XElement xe in xml.Descendants("Content")) + { + if (xe.Element("Type").Value != "Control") + { + continue; + } + + ncaTarget = $"{xe.Element("Id").Value}.nca"; + break; + } + } + else //This is a DLC + { + foreach (XElement xe in xml.Descendants("Content")) + { + if (xe.Element("Type").Value != "Meta") + { + continue; + } + + ncaTarget = $"{xe.Element("Id").Value}.cnmt.nca"; + break; + } + } + } + + if (n == MAXFILES - 1) //Dump of TitleID 01009AA000FAA000 reports more than 10000000 files here, so it breaks the program. Standard is to have only 20 files + { + break; + } + } + + if (string.IsNullOrEmpty(ncaTarget)) + { + //Missing content metadata xml. Read from content metadata nca instead + for (int n = 0; n < PFS0.PFS0_Headers[0].FileCount; n++) + { + if (array3[n].Name.EndsWith(".cnmt.nca")) + { + try + { + File.Delete("meta"); + Directory.Delete("data", true); + } + catch { } + + using (FileStream fileStream2 = File.OpenWrite("meta")) + { + fileStream.Position = 16 + 24 * PFS0.PFS0_Headers[0].FileCount + PFS0.PFS0_Headers[0].StringTableSize + array3[n].Offset; + byte[] buffer = new byte[8192]; + long num = array3[n].Size; + int num4; + while ((num4 = fileStream.Read(buffer, 0, 8192)) > 0 && num > 0) + { + fileStream2.Write(buffer, 0, num4); + num -= num4; + } + fileStream2.Close(); + } + + process = new() + { + StartInfo = new ProcessStartInfo + { + WindowStyle = ProcessWindowStyle.Hidden, + FileName = Path.Join("tools", "hactool.exe"), + Arguments = "-k keys.txt --section0dir=data meta", + UseShellExecute = false, + RedirectStandardOutput = true, + CreateNoWindow = true + } + }; + process.Start(); + + string masterkey = ""; + while (!process.StandardOutput.EndOfStream) + { + string output = process.StandardOutput.ReadLine(); + if (output.StartsWith("Master Key Revision")) + { + masterkey = Regex.Replace(output, @"\s+", " "); + } + } + process.WaitForExit(); + + if (!Directory.Exists("data")) + { + new CenterWinDialog(this); + MessageBox.Show($"{masterkey} is missing!"); + } + else + { + try + { + string[] cnmt = Directory.GetFiles("data", "*.cnmt"); + if (cnmt.Length == 0) + { + return; + } + using FileStream fileStream3 = File.OpenRead(cnmt[0]); + byte[] buffer = new byte[32]; + byte[] buffer2 = new byte[56]; + CNMT.CNMT_Header[] array7 = new CNMT.CNMT_Header[1]; + + fileStream3.Read(buffer, 0, 32); + array7[0] = new CNMT.CNMT_Header(buffer); + + byte[] TitleID = BitConverter.GetBytes(array7[0].TitleID); + Array.Reverse(TitleID); + TB_TID.Text = BitConverter.ToString(TitleID).Replace("-", ""); + + if (array7[0].Type == (byte)CNMT.CNMT_Header.TitleType.REGULAR_APPLICATION) + { + contentType = "Application"; + baseflag = 1; + if (patchflag != 1) + { + xmlVersion = $"v{array7[0].TitleVersion}"; + } + + basetitle[basenum] = $"[{TB_TID.Text}][v{array7[0].TitleVersion}]"; + basenum++; + } + else if (array7[0].Type == (byte)CNMT.CNMT_Header.TitleType.UPDATE_TITLE) + { + contentType = "Patch"; + + patchflag = 1; + if (array7[0].TitleVersion > patchnum) + { + patchnum = array7[0].TitleVersion; + xmlVersion = $"v{array7[0].TitleVersion}"; + int number = Convert.ToInt32(array7[0].TitleVersion); + patchver = $"v{Convert.ToString((double)number / 65536)}"; + } + + updtitle[updnum] = $"[{TB_TID.Text}][v{array7[0].TitleVersion}]"; + updnum++; + } + else if (array7[0].Type == (byte)CNMT.CNMT_Header.TitleType.ADD_ON_CONTENT) + { + if (baseflag == 0 && patchflag == 0) + { + xmlVersion = $"v{array7[0].TitleVersion}"; + } + + contentType = "AddOnContent"; + dlctitle[dlcnum] = $"[{TB_TID.Text}][v{array7[0].TitleVersion}]"; + dlcnum++; + } + + fileStream3.Position = array7[0].Offset + 32; + CNMT.CNMT_Entry[] array9 = new CNMT.CNMT_Entry[array7[0].ContentCount]; + for (int k = 0; k < array7[0].ContentCount; k++) + { + fileStream3.Read(buffer2, 0, 56); + array9[k] = new CNMT.CNMT_Entry(buffer2); + if (array9[k].Type == (byte)CNMT.CNMT_Entry.ContentType.CONTROL || array9[k].Type == (byte)CNMT.CNMT_Entry.ContentType.DATA) + { + ncaTarget = $"{BitConverter.ToString(array9[k].NcaId).ToLower().Replace("-", "")}.nca"; + break; + } + } + + fileStream3.Close(); + } + catch + { + } + } + } + } + } + + for (int n = 0; n < PFS0.PFS0_Headers[0].FileCount; n++) + { + if (array3[n].Name.Equals(ncaTarget)) + { + Directory.CreateDirectory("tmp"); + + byte[] array5 = new byte[64 * 1024]; + fileStream.Position = 16 + 24 * PFS0.PFS0_Headers[0].FileCount + PFS0.PFS0_Headers[0].StringTableSize + array3[n].Offset; + + using (Stream output = File.Create(Path.Join("tmp", ncaTarget))) + { + long Size = array3[n].Size; + int result = 0; + while ((result = fileStream.Read(array5, 0, (int)Math.Min(array5.Length, Size))) > 0) + { + output.Write(array5, 0, result); + Size -= result; + } + } + + break; + } + + if (n == MAXFILES - 1) //Dump of TitleID 01009AA000FAA000 reports more than 10000000 files here, so it breaks the program. Standard is to have only 20 files + { + break; + } + } + + fileStream.Close(); + + if (contentType != "AddOnContent") + { + process = new Process + { + StartInfo = new ProcessStartInfo + { + WindowStyle = ProcessWindowStyle.Hidden, + FileName = Path.Join("tools", "hactool.exe"), + Arguments = $"-k keys.txt --romfsdir=tmp tmp/{ncaTarget}", + UseShellExecute = false, + CreateNoWindow = true + } + }; + + process.Start(); + process.WaitForExit(); + process.Close(); + byte[] flux = new byte[200]; + + try + { + byte[] source = File.ReadAllBytes(Path.Join("tmp", "control.nacp")); + NACP.NACP_Datas[0] = new NACP.NACP_Data(source.Skip(0x3000).Take(0x1000).ToArray()); + + for (int i = 0; i < NACP.NACP_Strings.Length; i++) + { + NACP.NACP_Strings[i] = new NACP.NACP_String(source.Skip(i * 0x300).Take(0x300).ToArray()); + + if (NACP.NACP_Strings[i].Check != 0) + { + CB_RegionName.Items.Add(Language[i]); + string icon_filename = Path.Join("tmp", $"icon_{Language[i].Replace(" ", "")}.dat"); + if (File.Exists(icon_filename)) + { + using Bitmap original = new(icon_filename); + Icons[i] = new Bitmap(original); + PB_GameIcon.BackgroundImage = Icons[i]; + } + } + } + + string gameVer = NACP.NACP_Datas[0].GameVer.Replace("\0", ""); + if (xmlVersion.Trim() == "") + { + TB_GameRev.Text = $"GENERAL:{Environment.NewLine}({gameVer}){Environment.NewLine}"; + } + else + { + string cache = $"GENERAL:{Environment.NewLine}{gameVer}{((patchflag == 1) ? $" ({patchver})" : "")}{Environment.NewLine}"; + + if (basenum != 0) + { + cache += $"BASE:{Environment.NewLine}"; + for (int i = 0; i < basenum; i++) + { + cache += basetitle[i] + Environment.NewLine; + } + } + else + { + cache += $"BASE:{Environment.NewLine}EMPTY{Environment.NewLine}"; + } + if (updnum != 0) + { + cache += $"UPD:{Environment.NewLine}"; + for (int i = 0; i < updnum; i++) + { + cache += updtitle[i] + Environment.NewLine; + } + } + else + { + cache += $"UPD:{Environment.NewLine}EMPTY{Environment.NewLine}"; + } + if (dlcnum != 0) + { + cache += $"DLC:{Environment.NewLine}"; + for (int i = 0; i < dlcnum; i++) + { + if (i < dlcnum - 1) + { + cache += dlctitle[i] + Environment.NewLine; + } + else + { + cache += dlctitle[i]; + } + } + } + else + { + cache += $"DLC:{Environment.NewLine}EMPTY"; + } + TB_GameRev.Text = cache; + label12.Text = $"{basenum} BASE, {updnum} UPD, {dlcnum} DLC"; + } + + TB_ProdCode.Text = NACP.NACP_Datas[0].GameProd.Replace("\0", ""); + if (TB_ProdCode.Text == "") + { + TB_ProdCode.Text = "No Prod. ID"; + } + + for (int z = 0; z < NACP.NACP_Strings.Length; z++) + { + if (NACP.NACP_Strings[z].GameName.Replace("\0", "") != "") + { + TB_Name.Text = NACP.NACP_Strings[z].GameName.Replace("\0", ""); + break; + } + } + for (int z = 0; z < NACP.NACP_Strings.Length; z++) + { + if (NACP.NACP_Strings[z].GameAuthor.Replace("\0", "") != "") + { + TB_Dev.Text = NACP.NACP_Strings[z].GameAuthor.Replace("\0", ""); + break; + } + } + } + catch { } + + } + else + { + if (xmlVersion.Trim() == "") + { + TB_GameRev.Text = $"GENERAL:{Environment.NewLine}{Environment.NewLine}"; } else { - LB_ActualHash.Text = ""; - } + string gameVer = NACP.NACP_Datas[0].GameVer.Replace("\0", ""); + string cache = $"GENERAL:{Environment.NewLine}{gameVer}{((patchflag == 1) ? $" ({patchver})" : "")}{Environment.NewLine}"; - } - else - { - LB_SelectedData.Text = ""; - LB_DataOffset.Text = ""; - LB_DataSize.Text = ""; - LB_HashedRegionSize.Text = ""; - LB_ExpectedHash.Text = ""; - LB_ActualHash.Text = ""; - B_Extract.Enabled = false; - } - } - - public bool CheckXCI() - { - FileStream fileStream = new FileStream(TB_File.Text, FileMode.Open, FileAccess.Read); - byte[] array = new byte[61440]; - byte[] array2 = new byte[16]; - fileStream.Read(array, 0, 61440); - XCI.XCI_Headers[0] = new XCI.XCI_Header(array); - if (!XCI.XCI_Headers[0].Magic.Contains("HEAD")) - { - return false; - } - fileStream.Position = XCI.XCI_Headers[0].HFS0OffsetPartition; - fileStream.Read(array2, 0, 16); - HFS0.HFS0_Headers[0] = new HFS0.HFS0_Header(array2); - fileStream.Close(); - return true; - } - - public bool CheckNSP() - { - FileStream fileStream = File.OpenRead(TB_File.Text); - byte[] array = new byte[16]; - fileStream.Read(array, 0, 16); - PFS0.PFS0_Headers[0] = new PFS0.PFS0_Header(array); - fileStream.Close(); - if (!PFS0.PFS0_Headers[0].Magic.Contains("PFS0")) - { - return false; - } - return true; - } - - private void B_ExportCert_Click(object sender, EventArgs e) - { - new CenterWinDialog(this); - if (Util.checkFile(TB_File.Text)) - { - SaveFileDialog saveFileDialog = new SaveFileDialog(); - saveFileDialog.Filter = "gamecard_cert.dat (*.dat)|*.dat"; - saveFileDialog.FileName = Path.GetFileName("gamecard_cert.dat"); - if (saveFileDialog.ShowDialog() == DialogResult.OK) - { - FileStream fileStream = new FileStream(TB_File.Text, FileMode.Open, FileAccess.Read); - byte[] array = new byte[512]; - fileStream.Position = 28672L; - fileStream.Read(array, 0, 512); - File.WriteAllBytes(saveFileDialog.FileName, array); - fileStream.Close(); - MessageBox.Show($"Cert successfully exported to:\n\n{saveFileDialog.FileName}"); - } - } - else - { - MessageBox.Show("File not found"); - } - } - - private void B_ImportCert_Click(object sender, EventArgs e) - { - new CenterWinDialog(this); - if (Util.checkFile(TB_File.Text)) - { - OpenFileDialog openFileDialog = new OpenFileDialog(); - openFileDialog.Filter = "gamecard_cert (*.dat)|*.dat|All files (*.*)|*.*"; - if (openFileDialog.ShowDialog() == DialogResult.OK && new FileInfo(openFileDialog.FileName).Length == 512) - { - using (Stream stream = File.Open(TB_File.Text, FileMode.Open)) + if (basenum != 0) { - stream.Position = 28672L; - stream.Write(File.ReadAllBytes(openFileDialog.FileName), 0, 512); - } - MessageBox.Show($"Cert successfully imported from:\n\n{openFileDialog.FileName}"); - } - } - else - { - MessageBox.Show("File not found"); - } - } - - private void B_ViewCert_Click(object sender, EventArgs e) - { - new CenterWinDialog(this); - if (Util.checkFile(TB_File.Text)) - { - CertForm cert = new CertForm(this); - cert.Text = $"Cert Data - {TB_File.Text}"; - cert.Show(); - } - else - { - MessageBox.Show("File not found"); - } - } - - private void B_ClearCert_Click(object sender, EventArgs e) - { - new CenterWinDialog(this); - if (Util.checkFile(TB_File.Text)) - { - if (MessageBox.Show("The cert will be deleted permanently.\nContinue?", "XCI Explorer", MessageBoxButtons.YesNo) == DialogResult.Yes) - { - using (Stream stream = File.Open(TB_File.Text, FileMode.Open)) - { - byte[] array = new byte[512]; - for (int i = 0; i < array.Length; i++) + cache += $"BASE:{Environment.NewLine}"; + for (int i = 0; i < basenum; i++) { - array[i] = byte.MaxValue; + cache += basetitle[i] + Environment.NewLine; } - stream.Position = 28672L; - stream.Write(array, 0, array.Length); - new CenterWinDialog(this); - MessageBox.Show("Cert deleted."); } - } - } - else - { - MessageBox.Show("File not found"); - } - } - - private void B_Extract_Click(object sender, EventArgs e) - { - SaveFileDialog saveFileDialog = new SaveFileDialog(); - saveFileDialog.FileName = LB_SelectedData.Text; - if (saveFileDialog.ShowDialog() == DialogResult.OK) - { - if (backgroundWorker1.IsBusy != true) - { - B_Extract.Enabled = false; - B_LoadROM.Enabled = false; - B_TrimXCI.Enabled = false; - B_ImportCert.Enabled = false; - B_ClearCert.Enabled = false; - - // Start the asynchronous operation. - backgroundWorker1.RunWorkerAsync(saveFileDialog.FileName); - } - } - } - - public byte[] DecryptNCAHeader(long offset) - { - byte[] array = new byte[3072]; - if (File.Exists(TB_File.Text)) - { - FileStream fileStream = new FileStream(TB_File.Text, FileMode.Open, FileAccess.Read); - fileStream.Position = offset; - fileStream.Read(array, 0, 3072); - File.WriteAllBytes($"{TB_File.Text}.tmp", array); - Xts xts = XtsAes128.Create(NcaHeaderEncryptionKey1_Prod, NcaHeaderEncryptionKey2_Prod); - using (BinaryReader binaryReader = new BinaryReader(File.OpenRead($"{TB_File.Text}.tmp"))) - { - using (XtsStream xtsStream = new XtsStream(binaryReader.BaseStream, xts, 512)) + else { - xtsStream.Read(array, 0, 3072); + cache += $"BASE:{Environment.NewLine} EMPTY {Environment.NewLine}"; } + if (updnum != 0) + { + cache += $"UPD:{Environment.NewLine}"; + for (int i = 0; i < updnum; i++) + { + cache += updtitle[i] + Environment.NewLine; + } + } + else + { + cache += $"UPD:{Environment.NewLine} EMPTY {Environment.NewLine}"; + } + if (dlcnum != 0) + { + cache += $"DLC:{Environment.NewLine}"; + for (int i = 0; i < dlcnum; i++) + { + if (i < dlcnum - 1) + { + cache += dlctitle[i] + Environment.NewLine; + } + else + { + cache += dlctitle[i]; + } + } + } + else + { + cache += $"DLC:{Environment.NewLine}EMPTY"; + } + TB_GameRev.Text = cache; + label12.Text = $"{basenum} BASE, {updnum} UPD, {dlcnum} DLC"; } - File.Delete($"{TB_File.Text}.tmp"); - fileStream.Close(); + TB_ProdCode.Text = "No Prod. ID"; } - return array; + + // Lets get SDK Version, Distribution Type and Masterkey revision + // This is far from the best aproach, but it's what we have for now + process = new Process + { + StartInfo = new ProcessStartInfo + { + WindowStyle = ProcessWindowStyle.Hidden, + FileName = Path.Join("tools", "hactool.exe"), + Arguments = $"-k keys.txt tmp/{ncaTarget}", + RedirectStandardOutput = true, + UseShellExecute = false, + CreateNoWindow = true + } + }; + process.Start(); + StreamReader sr = process.StandardOutput; + + while (sr.Peek() >= 0) + { + string str; + string[] strArray; + str = sr.ReadLine(); + strArray = str.Split(':'); + if (strArray[0] == "SDK Version") + { + TB_SDKVer.Text = strArray[1].Trim(); + } + else if (strArray[0] == "Master Key Revision") + { + string MasterKey = strArray[1].Trim(); + int keyblob; + + MasterKey = MasterKey.Split(new char[2] { 'x', ' ' })[1]; + keyblob = Convert.ToInt32(MasterKey, 16); + MasterKey = Util.GetMkey((byte)(keyblob + 1)); + TB_MKeyRev.Text = MasterKey; + break; + } + } + process.WaitForExit(); + process.Close(); + } + catch { } + + try + { + File.Delete("meta"); + Directory.Delete("data", true); + } + catch + { } - private void CB_RegionName_SelectedIndexChanged(object sender, EventArgs e) + try { - int num = Array.FindIndex(Language, (string element) => element.StartsWith(CB_RegionName.Text, StringComparison.Ordinal)); - // Icons for 1-2 Switch in some languages are "missing" - // This just shows the first real icon instead of a blank - if (Icons[num] != null) + Directory.Delete("tmp", true); + } + catch + { + } + + TB_Capacity.Text = "eShop"; + + if (TB_Name.Text.Trim() != "") + { + CB_RegionName.SelectedIndex = 0; + } + } + + private void LoadGameInfos() + { + CB_RegionName.Items.Clear(); + CB_RegionName.Enabled = true; + TB_Name.Text = ""; + TB_Dev.Text = ""; + PB_GameIcon.BackgroundImage = null; + + int basenum = 0; + int updnum = 0; + int dlcnum = 0; + int patchflag = 0; + int patchnum = 0; + string patchver = ""; + int baseflag = 0; + string[] basetitle = new string[5]; + string[] updtitle = new string[10]; + string[] dlctitle = new string[300]; + string xmlVersion = ""; + string saveTID = ""; + + Array.Clear(Icons, 0, Icons.Length); + if (getMKey()) + { + using FileStream fileStream = File.OpenRead(TB_File.Text); + List ncaTarget = new(); + string GameRevision = ""; + + for (int si = 0; si < SecureSize.Length; si++) { - PB_GameIcon.BackgroundImage = Icons[num]; - } - else - { - for (int i = 0; i < CB_RegionName.Items.Count; i++) + if (SecureSize[si] > 0x4E20000) { - if (Icons[i] != null) + continue; + } + + if (!SecureName[si].EndsWith(".cnmt.nca")) + { + continue; + } + + try + { + File.Delete("meta"); + Directory.Delete("data", true); + } + catch + { + } + + using (FileStream fileStream2 = File.OpenWrite("meta")) + { + fileStream.Position = SecureOffset[si]; + byte[] fsBuffer = new byte[8192]; + long num = SecureSize[si]; + int num4; + while ((num4 = fileStream.Read(fsBuffer, 0, 8192)) > 0 && num > 0) { - PB_GameIcon.BackgroundImage = Icons[i]; + fileStream2.Write(fsBuffer, 0, num4); + num -= num4; + } + fileStream2.Close(); + } + + Process process1 = new Process + { + StartInfo = new ProcessStartInfo + { + WindowStyle = ProcessWindowStyle.Hidden, + FileName = Path.Join("tools", "hactool.exe"), + Arguments = "-k keys.txt --section0dir=data meta", + UseShellExecute = false, + CreateNoWindow = true + } + }; + process1.Start(); + process1.WaitForExit(); + + string[] cnmt = Directory.GetFiles("data", "*.cnmt"); + if (cnmt.Length == 0) + { + continue; + } + + using FileStream fileStream3 = File.OpenRead(cnmt[0]); + byte[] buffer = new byte[32]; + byte[] buffer2 = new byte[56]; + CNMT.CNMT_Header[] array7 = new CNMT.CNMT_Header[1]; + + fileStream3.Read(buffer, 0, 32); + array7[0] = new CNMT.CNMT_Header(buffer); + + byte[] TitleID = BitConverter.GetBytes(array7[0].TitleID); + Array.Reverse(TitleID); + saveTID = BitConverter.ToString(TitleID).Replace("-", ""); + + if (array7[0].Type == (byte)CNMT.CNMT_Header.TitleType.REGULAR_APPLICATION) + { + baseflag = 1; + if (patchflag != 1) + { + xmlVersion = $"v{array7[0].TitleVersion}"; + } + + basetitle[basenum] = $"[{saveTID}][v{array7[0].TitleVersion}]"; + basenum++; + } + else if (array7[0].Type == (byte)CNMT.CNMT_Header.TitleType.UPDATE_TITLE) + { + patchflag = 1; + if (array7[0].TitleVersion > patchnum) + { + patchnum = array7[0].TitleVersion; + xmlVersion = $"v{array7[0].TitleVersion}"; + int number = Convert.ToInt32(array7[0].TitleVersion); + patchver = $"v{Convert.ToString((double)number / 65536)}"; + } + + updtitle[updnum] = $"[{saveTID}][v{array7[0].TitleVersion}]"; + updnum++; + } + else if (array7[0].Type == (byte)CNMT.CNMT_Header.TitleType.ADD_ON_CONTENT) + { + if (patchflag == 0 && baseflag == 0) + { + xmlVersion = $"v{array7[0].TitleVersion}"; + } + + dlctitle[dlcnum] = $"[{saveTID}][v{array7[0].TitleVersion}]"; + dlcnum++; + } + + fileStream3.Position = array7[0].Offset + 32; + CNMT.CNMT_Entry[] array9 = new CNMT.CNMT_Entry[array7[0].ContentCount]; + for (int k = 0; k < array7[0].ContentCount; k++) + { + fileStream3.Read(buffer2, 0, 56); + array9[k] = new CNMT.CNMT_Entry(buffer2); + if (array9[k].Type == (byte)CNMT.CNMT_Entry.ContentType.CONTROL || array9[k].Type == (byte)CNMT.CNMT_Entry.ContentType.DATA) + { + ncaTarget.Add($"{BitConverter.ToString(array9[k].NcaId).ToLower().Replace("-", "")}.nca"); break; } } - } - TB_Name.Text = NACP.NACP_Strings[num].GameName; - TB_Dev.Text = NACP.NACP_Strings[num].GameAuthor; - } + fileStream3.Close(); - private void B_TrimXCI_Click(object sender, EventArgs e) - { - new CenterWinDialog(this); - if (Util.checkFile(TB_File.Text)) + } + + for (int si = 0; si < SecureSize.Length; si++) { - if (MessageBox.Show("Trim XCI?", "XCI Explorer", MessageBoxButtons.YesNo) == DialogResult.Yes) + if (SecureSize[si] > 0x4E20000) { - new CenterWinDialog(this); - if (!isTrimmed()) + continue; + } + + if (!ncaTarget.Contains(SecureName[si])) + { + continue; + } + + try + { + File.Delete("meta"); + Directory.Delete("data", true); + } + catch + { + } + + using (FileStream fileStream2 = File.OpenWrite("meta")) + { + fileStream.Position = SecureOffset[si]; + byte[] buffer = new byte[8192]; + long num = SecureSize[si]; + int num2; + while ((num2 = fileStream.Read(buffer, 0, 8192)) > 0 && num > 0) { - FileStream fileStream = new FileStream(TB_File.Text, FileMode.Open, FileAccess.Write); - fileStream.SetLength((long)UsedSize); - fileStream.Close(); - B_TrimXCI.Enabled = false; - MessageBox.Show("Done."); - string[] array = new string[5] - { - "B", - "KB", - "MB", - "GB", - "TB" - }; - double num = (double)new FileInfo(TB_File.Text).Length; - TB_ROMExactSize.Text = $"({num.ToString()} bytes)"; - int num2 = 0; - while (num >= 1024.0 && num2 < array.Length - 1) - { - num2++; - num /= 1024.0; - } - TB_ROMSize.Text = $"{num:0.##} {array[num2]}"; - double num3 = UsedSize = (double)(XCI.XCI_Headers[0].CardSize2 * 512 + 512); - TB_ExactUsedSpace.Text = $"({num3.ToString()} bytes)"; - num2 = 0; - while (num3 >= 1024.0 && num2 < array.Length - 1) - { - num2++; - num3 /= 1024.0; - } - TB_UsedSpace.Text = $"{num3:0.##} {array[num2]}"; + fileStream2.Write(buffer, 0, num2); + num -= num2; } + fileStream2.Close(); + } + + + Process process = new(); + process.StartInfo = new ProcessStartInfo + { + WindowStyle = ProcessWindowStyle.Hidden, + FileName = Path.Join("tools", "hactool.exe"), + Arguments = "-k keys.txt --romfsdir=data meta", + UseShellExecute = false, + CreateNoWindow = true + }; + process.Start(); + process.WaitForExit(); + + if (!File.Exists(Path.Join("data", "control.nacp"))) + { + continue; + } + + byte[] source = File.ReadAllBytes(Path.Join("data", "control.nacp")); + NACP.NACP_Datas[0] = new NACP.NACP_Data(source.Skip(0x3000).Take(0x1000).ToArray()); + + string GameVer = NACP.NACP_Datas[0].GameVer.Replace("\0", ""); + Version version1, version2; + if (!Version.TryParse(Regex.Replace(GameRevision, @"[^\d.].*$", ""), out version1)) + { + version1 = new Version(); + } + if (!Version.TryParse(Regex.Replace(GameVer, @"[^\d.].*$", ""), out version2)) + { + version2 = new Version(); + } + if (version2.CompareTo(version1) > 0) + { + GameRevision = GameVer; + + for (int i = 0; i < NACP.NACP_Strings.Length; i++) + { + NACP.NACP_Strings[i] = new NACP.NACP_String(source.Skip(i * 0x300).Take(0x300).ToArray()); + if (NACP.NACP_Strings[i].Check == 0 || CB_RegionName.Items.Contains(Language[i])) + { + continue; + } + + CB_RegionName.Items.Add(Language[i]); + string icon_filename = Path.Join("data", $"icon_{Language[i].Replace(" ", "")}.dat"); + if (File.Exists(icon_filename)) + { + using Bitmap original = new(icon_filename); + Icons[i] = new Bitmap(original); + PB_GameIcon.BackgroundImage = Icons[i]; + } + } + TB_ProdCode.Text = NACP.NACP_Datas[0].GameProd; + if (TB_ProdCode.Text == "") + { + TB_ProdCode.Text = "No Prod. ID"; + } + try + { + File.Delete("meta"); + Directory.Delete("data", true); + } + catch + { + } + } + } + + string gameVer = NACP.NACP_Datas[0].GameVer.Replace("\0", ""); + string cache = $"GENERAL:{Environment.NewLine}{gameVer}{((patchflag == 1) ? $" ({patchver})" : "")}{Environment.NewLine}"; + + if (basenum != 0) + { + cache += $"BASE:{Environment.NewLine}"; + for (int i = 0; i < basenum; i++) + { + cache += basetitle[i] + System.Environment.NewLine; } } else { - MessageBox.Show("File not found"); + cache += $"BASE:{Environment.NewLine}EMPTY{Environment.NewLine}"; } - } - - private void LB_ExpectedHash_DoubleClick(object sender, EventArgs e) - { - BetterTreeNode betterTreeNode = (BetterTreeNode)TV_Partitions.SelectedNode; - if (betterTreeNode.Offset != -1) + if (updnum != 0) { - Clipboard.SetText(betterTreeNode.ExpectedHash); - } - } - - private void LB_ActualHash_DoubleClick(object sender, EventArgs e) - { - BetterTreeNode betterTreeNode = (BetterTreeNode)TV_Partitions.SelectedNode; - if (betterTreeNode.Offset != -1) - { - Clipboard.SetText(betterTreeNode.ActualHash); - } - } - - private void TB_File_DragDrop(object sender, DragEventArgs e) - { - if (backgroundWorker1.IsBusy != true) - { - string[] files = (string[])e.Data.GetData(DataFormats.FileDrop); - TB_File.Text = files[0]; - ProcessFile(); - } - } - - private void TB_File_DragEnter(object sender, DragEventArgs e) - { - if (backgroundWorker1.IsBusy != true) - { - if (e.Data.GetDataPresent(DataFormats.FileDrop)) + cache += $"UPD:{Environment.NewLine}"; + for (int i = 0; i < updnum; i++) { - e.Effect = DragDropEffects.Copy; - } - else - { - e.Effect = DragDropEffects.None; + cache += updtitle[i] + Environment.NewLine; } } - } - - private void TABP_XCI_DragDrop(object sender, DragEventArgs e) - { - if (backgroundWorker1.IsBusy != true) + else { - string[] files = (string[])e.Data.GetData(DataFormats.FileDrop); - TB_File.Text = files[0]; - ProcessFile(); + cache += $"UPD:{Environment.NewLine}EMPTY{Environment.NewLine}"; } - } - - private void TABP_XCI_DragEnter(object sender, DragEventArgs e) - { - if (backgroundWorker1.IsBusy != true) + if (dlcnum != 0) { - if (e.Data.GetDataPresent(DataFormats.FileDrop)) + cache += $"DLC:{Environment.NewLine}"; + for (int i = 0; i < dlcnum; i++) { - e.Effect = DragDropEffects.Copy; - } - else - { - e.Effect = DragDropEffects.None; - } - } - } - - private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) - { - BackgroundWorker worker = sender as BackgroundWorker; - string fileName = (string)e.Argument; - - using (FileStream fileStream = File.OpenRead(TB_File.Text)) - { - using (FileStream fileStream2 = File.OpenWrite(fileName)) - { - new BinaryReader(fileStream); - new BinaryWriter(fileStream2); - fileStream.Position = selectedOffset; - long num = selectedSize; - - if (selectedSize < 10000) + if (i < dlcnum - 1) { - byte[] buffer = new byte[1]; - int num2; - while ((num2 = fileStream.Read(buffer, 0, 1)) > 0 && num > 0) - { - fileStream2.Write(buffer, 0, num2); - num -= num2; - } + cache += dlctitle[i] + Environment.NewLine; } else { - byte[] buffer = new byte[8192]; - int num2; - while ((num2 = fileStream.Read(buffer, 0, 8192)) > 0 && num > 0) - { - fileStream2.Write(buffer, 0, num2); - num -= num2; - } + cache += dlctitle[i]; } - - fileStream.Close(); } } - } - - private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) - { - new CenterWinDialog(this); - B_Extract.Enabled = true; - B_LoadROM.Enabled = true; - B_TrimXCI.Enabled = true; - B_ImportCert.Enabled = true; - B_ClearCert.Enabled = true; - - if (e.Error != null) - { - MessageBox.Show($"Error: {e.Error.Message}"); - } else { - MessageBox.Show("Done extracting NCA!"); + cache += $"DLC:{Environment.NewLine}EMPTY"; + } + TB_GameRev.Text = cache; + label12.Text = $"{basenum} BASE, {updnum} UPD, {dlcnum} DLC"; + + CB_RegionName.SelectedIndex = 0; + + fileStream.Close(); + } + else + { + TB_Dev.Text = $"{Mkey} not found"; + TB_Name.Text = $"{Mkey} not found"; + } + } + + private void LoadNCAData() + { + NCA.NCA_Headers[0] = new NCA.NCA_Header(DecryptNCAHeader(gameNcaOffset)); + TB_TID.Text = $"0{NCA.NCA_Headers[0].TitleID.ToString("X")}"; + TB_SDKVer.Text = $"{NCA.NCA_Headers[0].SDKVersion4}.{NCA.NCA_Headers[0].SDKVersion3}.{NCA.NCA_Headers[0].SDKVersion2}.{NCA.NCA_Headers[0].SDKVersion1}"; + TB_MKeyRev.Text = Util.GetMkey(NCA.NCA_Headers[0].MasterKeyRev); + } + + //https://stackoverflow.com/questions/311165/how-do-you-convert-a-byte-array-to-a-hexadecimal-string-and-vice-versa + public static string ByteArrayToString(byte[] ba) + { + StringBuilder hex = new(ba.Length * 2 + 2); + hex.Append("0x"); + foreach (byte b in ba) + { + hex.AppendFormat("{0:x2}", b); + } + + return hex.ToString(); + } + + public static string SHA256Bytes(byte[] ba) + { + SHA256 mySHA256 = SHA256.Create(); + byte[] hashValue; + hashValue = mySHA256.ComputeHash(ba); + return ByteArrayToString(hashValue); + } + + public bool isTrimmed() => TB_ROMExactSize.Text == TB_ExactUsedSpace.Text; + + private void LoadPartitions() + { + string actualHash; + byte[] hashBuffer; + long offset; + + TV_Partitions.Nodes.Clear(); + TV_Parti = new TreeViewFileSystem(TV_Partitions); + rootNode = new BetterTreeNode("root") + { + Offset = -1L, + Size = -1L + }; + TV_Partitions.Nodes.Add(rootNode); + bool LogoPartition = false; + FileStream fileStream = new(TB_File.Text, FileMode.Open, FileAccess.Read); + HFS0.HSF0_Entry[] array = new HFS0.HSF0_Entry[HFS0.HFS0_Headers[0].FileCount]; + fileStream.Position = XCI.XCI_Headers[0].HFS0OffsetPartition + 16 + 64 * HFS0.HFS0_Headers[0].FileCount; + long num = XCI.XCI_Headers[0].HFS0OffsetPartition + XCI.XCI_Headers[0].HFS0SizeParition; + byte[] array2 = new byte[64]; + byte[] array3 = new byte[16]; + byte[] array4 = new byte[24]; + for (int i = 0; i < HFS0.HFS0_Headers[0].FileCount; i++) + { + fileStream.Position = XCI.XCI_Headers[0].HFS0OffsetPartition + 16 + 64 * i; + fileStream.Read(array2, 0, 64); + array[i] = new HFS0.HSF0_Entry(array2); + fileStream.Position = XCI.XCI_Headers[0].HFS0OffsetPartition + 16 + 64 * HFS0.HFS0_Headers[0].FileCount + array[i].Name_ptr; + int num2; + while ((num2 = fileStream.ReadByte()) != 0 && num2 != 0) + { + chars.Add((char)num2); + } + array[i].Name = new string(chars.ToArray()); + chars.Clear(); + offset = num + array[i].Offset; + hashBuffer = new byte[array[i].HashedRegionSize]; + fileStream.Position = offset; + fileStream.Read(hashBuffer, 0, array[i].HashedRegionSize); + actualHash = SHA256Bytes(hashBuffer); + + TV_Parti.AddFile($"{array[i].Name}.hfs0", rootNode, offset, array[i].Size, array[i].HashedRegionSize, ByteArrayToString(array[i].Hash), actualHash); + BetterTreeNode betterTreeNode = TV_Parti.AddDir(array[i].Name, rootNode); + HFS0.HFS0_Header[] array5 = new HFS0.HFS0_Header[1]; + fileStream.Position = array[i].Offset + num; + fileStream.Read(array3, 0, 16); + array5[0] = new HFS0.HFS0_Header(array3); + if (array[i].Name == "secure") + { + SecureSize = new long[array5[0].FileCount]; + SecureOffset = new long[array5[0].FileCount]; + SecureName = new string[array5[0].FileCount]; + } + if (array[i].Name == "normal") + { + NormalSize = new long[array5[0].FileCount]; + NormalOffset = new long[array5[0].FileCount]; + } + if (array[i].Name == "logo") + { + if (array5[0].FileCount > 0) + { + LogoPartition = true; + } + } + HFS0.HSF0_Entry[] array6 = new HFS0.HSF0_Entry[array5[0].FileCount]; + for (int j = 0; j < array5[0].FileCount; j++) + { + fileStream.Position = array[i].Offset + num + 16 + 64 * j; + fileStream.Read(array2, 0, 64); + array6[j] = new HFS0.HSF0_Entry(array2); + fileStream.Position = array[i].Offset + num + 16 + 64 * array5[0].FileCount + array6[j].Name_ptr; + while ((num2 = fileStream.ReadByte()) != 0 && num2 != 0) + { + chars.Add((char)num2); + } + array6[j].Name = new string(chars.ToArray()); + chars.Clear(); + if (array[i].Name == "secure") + { + SecureSize[j] = array6[j].Size; + SecureOffset[j] = array[i].Offset + array6[j].Offset + num + 16 + array5[0].StringTableSize + array5[0].FileCount * 64; + SecureName[j] = array6[j].Name; + } + if (array[i].Name == "normal") + { + NormalSize[j] = array6[j].Size; + NormalOffset[j] = array[i].Offset + array6[j].Offset + num + 16 + array5[0].StringTableSize + array5[0].FileCount * 64; + } + offset = array[i].Offset + array6[j].Offset + num + 16 + array5[0].StringTableSize + array5[0].FileCount * 64; + hashBuffer = new byte[array6[j].HashedRegionSize]; + fileStream.Position = offset; + fileStream.Read(hashBuffer, 0, array6[j].HashedRegionSize); + actualHash = SHA256Bytes(hashBuffer); + + TV_Parti.AddFile(array6[j].Name, betterTreeNode, offset, array6[j].Size, array6[j].HashedRegionSize, ByteArrayToString(array6[j].Hash), actualHash); + TreeNode[] array7 = TV_Partitions.Nodes.Find(betterTreeNode.Text, true); + if (array7.Length != 0) + { + TV_Parti.AddFile(array6[j].Name, (BetterTreeNode)array7[0], 0L, 0L); + } + } + } + long num3 = -9223372036854775808L; + for (int k = 0; k < SecureSize.Length; k++) + { + if (SecureSize[k] > num3) + { + gameNcaSize = SecureSize[k]; + gameNcaOffset = SecureOffset[k]; + num3 = SecureSize[k]; + } + } + PFS0Offset = gameNcaOffset + 32768; + fileStream.Position = PFS0Offset; + fileStream.Read(array3, 0, 16); + PFS0.PFS0_Headers[0] = new(array3); + if (PFS0.PFS0_Headers[0].FileCount == 2 || !LogoPartition) + { + PFS0.PFS0_Entry[] array8; + try + { + array8 = new PFS0.PFS0_Entry[PFS0.PFS0_Headers[0].FileCount]; + } + catch (Exception ex) + { + array8 = new PFS0.PFS0_Entry[0]; + Debug.WriteLine($"Partitions Error: {ex.Message}"); + } + for (int m = 0; m < PFS0.PFS0_Headers[0].FileCount; m++) + { + fileStream.Position = PFS0Offset + 16 + 24 * m; + fileStream.Read(array4, 0, 24); + array8[m] = new(array4); + PFS0Size += array8[m].Size; + } + TV_Parti.AddFile("boot.psf0", rootNode, PFS0Offset, 16 + 24 * PFS0.PFS0_Headers[0].FileCount + 64 + PFS0Size); + BetterTreeNode betterTreeNode2 = TV_Parti.AddDir("boot", rootNode); + for (int n = 0; n < PFS0.PFS0_Headers[0].FileCount; n++) + { + fileStream.Position = PFS0Offset + 16 + 24 * PFS0.PFS0_Headers[0].FileCount + array8[n].Name_ptr; + int num4; + while ((num4 = fileStream.ReadByte()) != 0 && num4 != 0) + { + chars.Add((char)num4); + } + array8[n].Name = new string(chars.ToArray()); + chars.Clear(); + TV_Parti.AddFile(array8[n].Name, betterTreeNode2, PFS0Offset + array8[n].Offset + 16 + PFS0.PFS0_Headers[0].StringTableSize + PFS0.PFS0_Headers[0].FileCount * 24, array8[n].Size); + TreeNode[] array9 = TV_Partitions.Nodes.Find(betterTreeNode2.Text, true); + if (array9.Length != 0) + { + TV_Parti.AddFile(array8[n].Name, (BetterTreeNode)array9[0], 0L, 0L); + } + } + } + fileStream.Close(); + } + + + private void LoadNSPPartitions() + { + long offset; + + TV_Partitions.Nodes.Clear(); + TV_Parti = new TreeViewFileSystem(TV_Partitions); + rootNode = new BetterTreeNode("root") + { + Offset = -1L, + Size = -1L + }; + TV_Partitions.Nodes.Add(rootNode); + + + // Maximum number of files in NSP to read in + const int MAXFILES = 250; + + FileStream fileStream = File.OpenRead(TB_File.Text); + List chars = new(); + byte[] array = new byte[16]; + byte[] array2 = new byte[24]; + fileStream.Read(array, 0, 16); + PFS0.PFS0_Headers[0] = new(array); + if (!PFS0.PFS0_Headers[0].Magic.Contains("PFS0")) + { + return; + } + PFS0.PFS0_Entry[] array3; + array3 = new PFS0.PFS0_Entry[Math.Max(PFS0.PFS0_Headers[0].FileCount, MAXFILES)]; //Dump of TitleID 01009AA000FAA000 reports more than 10000000 files here, so it breaks the program. Standard is to have only 20 files + for (int m = 0; m < PFS0.PFS0_Headers[0].FileCount; m++) + { + fileStream.Position = 16 + 24 * m; + fileStream.Read(array2, 0, 24); + array3[m] = new(array2); + + if (m == MAXFILES - 1) //Dump of TitleID 01009AA000FAA000 reports more than 10000000 files here, so it breaks the program. Standard is to have only 20 files + { + break; + } + } + for (int n = 0; n < PFS0.PFS0_Headers[0].FileCount; n++) + { + fileStream.Position = 16 + 24 * PFS0.PFS0_Headers[0].FileCount + array3[n].Name_ptr; + int num4; + while ((num4 = fileStream.ReadByte()) != 0 && num4 != 0) + { + chars.Add((char)num4); + } + array3[n].Name = new(chars.ToArray()); + chars.Clear(); + offset = 16 + 24 * PFS0.PFS0_Headers[0].FileCount + PFS0.PFS0_Headers[0].StringTableSize + array3[n].Offset; + fileStream.Position = offset; + + TV_Parti.AddFile(array3[n].Name, rootNode, offset, array3[n].Size); + } + + fileStream.Close(); + } + + + private void TV_Partitions_AfterSelect(object sender, TreeViewEventArgs e) + { + BetterTreeNode betterTreeNode = (BetterTreeNode)TV_Partitions.SelectedNode; + if (betterTreeNode.Offset == -1) + { + LB_SelectedData.Text = ""; + LB_DataOffset.Text = ""; + LB_DataSize.Text = ""; + LB_HashedRegionSize.Text = ""; + LB_ExpectedHash.Text = ""; + LB_ActualHash.Text = ""; + B_Extract.Enabled = false; + return; + } + + selectedOffset = betterTreeNode.Offset; + selectedSize = betterTreeNode.Size; + string expectedHash = betterTreeNode.ExpectedHash; + string actualHash = betterTreeNode.ActualHash; + long HashedRegionSize = betterTreeNode.HashedRegionSize; + + LB_DataOffset.Text = $"Offset: 0x{selectedOffset:X}"; + LB_SelectedData.Text = e.Node.Text; + if (backgroundWorker1.IsBusy != true) + { + B_Extract.Enabled = true; + } + string[] array = new string[5] + { + "B", + "KB", + "MB", + "GB", + "TB" + }; + double num = selectedSize; + int num2 = 0; + while (num >= 1024.0 && num2 < array.Length - 1) + { + num2++; + num /= 1024.0; + } + LB_DataSize.Text = $"Size: 0x{selectedSize.ToString("X")} ({num}{array[num2]})"; + + if (HashedRegionSize != 0) + { + LB_HashedRegionSize.Text = $"HashedRegionSize: 0x{HashedRegionSize:X}"; + } + else + { + LB_HashedRegionSize.Text = ""; + } + + if (!string.IsNullOrEmpty(expectedHash)) + { + LB_ExpectedHash.Text = $"Header Hash: {expectedHash[..32]}"; + } + else + { + LB_ExpectedHash.Text = ""; + } + + if (!string.IsNullOrEmpty(actualHash)) + { + LB_ActualHash.Text = $"Actual Hash: {actualHash[..32]}"; + if (actualHash == expectedHash) + { + LB_ActualHash.ForeColor = Color.Green; + } + else + { + LB_ActualHash.ForeColor = Color.Red; + } + } + else + { + LB_ActualHash.Text = ""; + } + } + + public bool CheckXCI() + { + FileStream fileStream = new(TB_File.Text, FileMode.Open, FileAccess.Read); + byte[] array = new byte[61440]; + byte[] array2 = new byte[16]; + fileStream.Read(array, 0, 61440); + XCI.XCI_Headers[0] = new XCI.XCI_Header(array); + if (!XCI.XCI_Headers[0].Magic.Contains("HEAD")) + { + return false; + } + fileStream.Position = XCI.XCI_Headers[0].HFS0OffsetPartition; + fileStream.Read(array2, 0, 16); + HFS0.HFS0_Headers[0] = new HFS0.HFS0_Header(array2); + fileStream.Close(); + return true; + } + + public bool CheckNSP() + { + FileStream fileStream = File.OpenRead(TB_File.Text); + byte[] array = new byte[16]; + fileStream.Read(array, 0, 16); + PFS0.PFS0_Headers[0] = new(array); + fileStream.Close(); + if (!PFS0.PFS0_Headers[0].Magic.Contains("PFS0")) + { + return false; + } + return true; + } + + private void B_ExportCert_Click(object sender, EventArgs e) + { + new CenterWinDialog(this); + if (!File.Exists(TB_File.Text)) + { + MessageBox.Show("File not found"); + return; + } + + SaveFileDialog saveFileDialog = new SaveFileDialog(); + saveFileDialog.Filter = "gamecard_cert.dat (*.dat)|*.dat"; + saveFileDialog.FileName = Path.GetFileName("gamecard_cert.dat"); + if (saveFileDialog.ShowDialog() != DialogResult.OK) + { + return; + } + + FileStream fileStream = new(TB_File.Text, FileMode.Open, FileAccess.Read); + byte[] array = new byte[512]; + fileStream.Position = 28672L; + fileStream.Read(array, 0, 512); + File.WriteAllBytes(saveFileDialog.FileName, array); + fileStream.Close(); + MessageBox.Show($"Cert successfully exported to:\n\n{saveFileDialog.FileName}"); + } + + private void B_ImportCert_Click(object sender, EventArgs e) + { + new CenterWinDialog(this); + if (!File.Exists(TB_File.Text)) + { + MessageBox.Show("File not found"); + return; + } + OpenFileDialog openFileDialog = new OpenFileDialog(); + openFileDialog.Filter = "gamecard_cert (*.dat)|*.dat|All files (*.*)|*.*"; + if (openFileDialog.ShowDialog() == DialogResult.OK && new FileInfo(openFileDialog.FileName).Length == 512) + { + using (Stream stream = File.Open(TB_File.Text, FileMode.Open)) + { + stream.Position = 28672L; + stream.Write(File.ReadAllBytes(openFileDialog.FileName), 0, 512); + } + MessageBox.Show($"Cert successfully imported from:\n\n{openFileDialog.FileName}"); + } + } + + private void B_ViewCert_Click(object sender, EventArgs e) + { + new CenterWinDialog(this); + if (!File.Exists(TB_File.Text)) + { + MessageBox.Show("File not found"); + return; + } + CertForm cert = new(this) + { + Text = $"Cert Data - {TB_File.Text}" + }; + cert.Show(); + } + + private void B_ClearCert_Click(object sender, EventArgs e) + { + new CenterWinDialog(this); + + if (!File.Exists(TB_File.Text)) + { + MessageBox.Show("File not found"); + return; + } + + if (MessageBox.Show("The cert will be deleted permanently.\nContinue?", "XCI Explorer", MessageBoxButtons.YesNo) != DialogResult.Yes) + { + return; + } + + using Stream stream = File.Open(TB_File.Text, FileMode.Open); + byte[] array = new byte[512]; + for (int i = 0; i < array.Length; i++) + { + array[i] = byte.MaxValue; + } + stream.Position = 28672L; + stream.Write(array, 0, array.Length); + + new CenterWinDialog(this); + MessageBox.Show("Cert deleted."); + + } + + private void B_Extract_Click(object sender, EventArgs e) + { + SaveFileDialog saveFileDialog = new SaveFileDialog + { + FileName = LB_SelectedData.Text + }; + + if (saveFileDialog.ShowDialog() != DialogResult.OK) + { + return; + } + + if (backgroundWorker1.IsBusy) + { + return; + } + + B_Extract.Enabled = false; + B_LoadROM.Enabled = false; + B_TrimXCI.Enabled = false; + B_ImportCert.Enabled = false; + B_ClearCert.Enabled = false; + + // Start the asynchronous operation. + backgroundWorker1.RunWorkerAsync(saveFileDialog.FileName); + } + + public byte[] DecryptNCAHeader(long offset) + { + byte[] array = new byte[3072]; + + if (!File.Exists(TB_File.Text)) + { + return array; + } + + FileStream fileStream = new(TB_File.Text, FileMode.Open, FileAccess.Read) + { + Position = offset + }; + fileStream.Read(array, 0, 3072); + File.WriteAllBytes($"{TB_File.Text}.tmp", array); + Xts xts = XtsAes128.Create(NcaHeaderEncryptionKey1_Prod, NcaHeaderEncryptionKey2_Prod); + using (BinaryReader binaryReader = new(File.OpenRead($"{TB_File.Text}.tmp"))) + { + using XtsStream xtsStream = new(binaryReader.BaseStream, xts, 512); + xtsStream.Read(array, 0, 3072); + } + File.Delete($"{TB_File.Text}.tmp"); + fileStream.Close(); + return array; + } + + private void CB_RegionName_SelectedIndexChanged(object sender, EventArgs e) + { + int num = Array.FindIndex(Language, (string element) => element.StartsWith(CB_RegionName.Text, StringComparison.Ordinal)); + // Icons for 1-2 Switch in some languages are "missing" + // This just shows the first real icon instead of a blank + if (Icons[num] != null) + { + PB_GameIcon.BackgroundImage = Icons[num]; + } + else + { + for (int i = 0; i < CB_RegionName.Items.Count; i++) + { + if (Icons[i] == null) + { + continue; + } + + PB_GameIcon.BackgroundImage = Icons[i]; + break; + } + } + TB_Name.Text = NACP.NACP_Strings[num].GameName; + TB_Dev.Text = NACP.NACP_Strings[num].GameAuthor; + } + + private void B_TrimXCI_Click(object sender, EventArgs e) + { + new CenterWinDialog(this); + + if (!File.Exists(TB_File.Text)) + { + MessageBox.Show("File not found"); + return; + } + + if (MessageBox.Show("Trim XCI?", "XCI Explorer", MessageBoxButtons.YesNo) != DialogResult.Yes) + { + return; + } + + new CenterWinDialog(this); + + if (isTrimmed()) + { + return; + } + + FileStream fileStream = new(TB_File.Text, FileMode.Open, FileAccess.Write); + fileStream.SetLength((long)UsedSize); + fileStream.Close(); + B_TrimXCI.Enabled = false; + MessageBox.Show("Done."); + string[] array = new string[5] + { + "B", + "KB", + "MB", + "GB", + "TB" + }; + double num = new FileInfo(TB_File.Text).Length; + TB_ROMExactSize.Text = $"({num} bytes)"; + int num2 = 0; + while (num >= 1024.0 && num2 < array.Length - 1) + { + num2++; + num /= 1024.0; + } + TB_ROMSize.Text = $"{num:0.##} {array[num2]}"; + double num3 = UsedSize = XCI.XCI_Headers[0].CardSize2 * 512 + 512; + TB_ExactUsedSpace.Text = $"({num3} bytes)"; + num2 = 0; + while (num3 >= 1024.0 && num2 < array.Length - 1) + { + num2++; + num3 /= 1024.0; + } + TB_UsedSpace.Text = $"{num3:0.##} {array[num2]}"; + } + + private void LB_ExpectedHash_DoubleClick(object sender, EventArgs e) + { + BetterTreeNode betterTreeNode = (BetterTreeNode)TV_Partitions.SelectedNode; + if (betterTreeNode.Offset != -1) + { + Clipboard.SetText(betterTreeNode.ExpectedHash); + } + } + + private void LB_ActualHash_DoubleClick(object sender, EventArgs e) + { + BetterTreeNode betterTreeNode = (BetterTreeNode)TV_Partitions.SelectedNode; + if (betterTreeNode.Offset != -1) + { + Clipboard.SetText(betterTreeNode.ActualHash); + } + } + + private void TB_File_DragDrop(object sender, DragEventArgs e) + { + if (backgroundWorker1.IsBusy != true) + { + string[] files = e.Data.GetData(DataFormats.FileDrop) as string[]; + TB_File.Text = files[0]; + ProcessFile(); + } + } + + private void TB_File_DragEnter(object sender, DragEventArgs e) + { + if (backgroundWorker1.IsBusy) + { + return; + } + + if (e.Data.GetDataPresent(DataFormats.FileDrop)) + { + e.Effect = DragDropEffects.Copy; + } + else + { + e.Effect = DragDropEffects.None; + } + } + + private void TABP_XCI_DragDrop(object sender, DragEventArgs e) + { + if (backgroundWorker1.IsBusy) + { + return; + } + + string[] files = e.Data.GetData(DataFormats.FileDrop) as string[]; + TB_File.Text = files[0]; + ProcessFile(); + } + + private void TABP_XCI_DragEnter(object sender, DragEventArgs e) + { + if (backgroundWorker1.IsBusy) + { + return; + } + + if (e.Data.GetDataPresent(DataFormats.FileDrop)) + { + e.Effect = DragDropEffects.Copy; + } + else + { + e.Effect = DragDropEffects.None; + } + } + + private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) + { + BackgroundWorker worker = sender as BackgroundWorker; + string fileName = (string)e.Argument; + + using FileStream fileStream = File.OpenRead(TB_File.Text); + using FileStream fileStream2 = File.OpenWrite(fileName); + new BinaryReader(fileStream); + new BinaryWriter(fileStream2); + fileStream.Position = selectedOffset; + long num = selectedSize; + + if (selectedSize < 10000) + { + byte[] buffer = new byte[1]; + int num2; + while ((num2 = fileStream.Read(buffer, 0, 1)) > 0 && num > 0) + { + fileStream2.Write(buffer, 0, num2); + num -= num2; + } + } + else + { + byte[] buffer = new byte[8192]; + int num2; + while ((num2 = fileStream.Read(buffer, 0, 8192)) > 0 && num > 0) + { + fileStream2.Write(buffer, 0, num2); + num -= num2; } } - private void TABP_XCI_Click(object sender, EventArgs e) + fileStream.Close(); + } + + private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) + { + new CenterWinDialog(this); + B_Extract.Enabled = true; + B_LoadROM.Enabled = true; + B_TrimXCI.Enabled = true; + B_ImportCert.Enabled = true; + B_ClearCert.Enabled = true; + + if (e.Error != null) { - + MessageBox.Show($"Error: {e.Error.Message}"); } - - private void label11_Click(object sender, EventArgs e) + else { - + MessageBox.Show("Done extracting NCA!"); } + } + + private void TABP_XCI_Click(object sender, EventArgs e) + { } + + private void label11_Click(object sender, EventArgs e) + { + + } + } \ No newline at end of file diff --git a/XCI_Explorer/NACP.cs b/XCI_Explorer/NACP.cs index cb9c647..a46d9db 100644 --- a/XCI_Explorer/NACP.cs +++ b/XCI_Explorer/NACP.cs @@ -1,42 +1,41 @@ using System.Linq; using System.Text; -namespace XCI_Explorer +namespace XCI_Explorer; + +public static class NACP { - public static class NACP + public class NACP_String { - public class NACP_String + public byte[] Data; + public byte Check; + public string GameName; + public string GameAuthor; + + public NACP_String(byte[] data) { - public byte[] Data; - public byte Check; - public string GameName; - public string GameAuthor; - - public NACP_String(byte[] data) - { - Data = data; - Check = Data[0]; - GameName = Encoding.UTF8.GetString(Data.Take(512).ToArray()); - GameAuthor = Encoding.UTF8.GetString(Data.Skip(512).Take(256).ToArray()); - } + Data = data; + Check = Data[0]; + GameName = Encoding.UTF8.GetString(Data.Take(512).ToArray()); + GameAuthor = Encoding.UTF8.GetString(Data.Skip(512).Take(256).ToArray()); } - - public class NACP_Data - { - public byte[] Data; - public string GameVer; - public string GameProd; - - public NACP_Data(byte[] data) - { - Data = data; - GameVer = Encoding.UTF8.GetString(Data.Skip(0x60).Take(16).ToArray()); - GameProd = Encoding.UTF8.GetString(Data.Skip(0xA8).Take(8).ToArray()); - } - } - - public static NACP_String[] NACP_Strings = new NACP_String[16]; - - public static NACP_Data[] NACP_Datas = new NACP_Data[1]; } + + public class NACP_Data + { + public byte[] Data; + public string GameVer; + public string GameProd; + + public NACP_Data(byte[] data) + { + Data = data; + GameVer = Encoding.UTF8.GetString(Data.Skip(0x60).Take(16).ToArray()); + GameProd = Encoding.UTF8.GetString(Data.Skip(0xA8).Take(8).ToArray()); + } + } + + public static NACP_String[] NACP_Strings = new NACP_String[16]; + + public static NACP_Data[] NACP_Datas = new NACP_Data[1]; } diff --git a/XCI_Explorer/NCA.cs b/XCI_Explorer/NCA.cs index f23ba9b..a789be6 100644 --- a/XCI_Explorer/NCA.cs +++ b/XCI_Explorer/NCA.cs @@ -2,34 +2,33 @@ using System; using System.Linq; using System.Text; -namespace XCI_Explorer +namespace XCI_Explorer; + +internal static class NCA { - internal static class NCA + public class NCA_Header { - public class NCA_Header + public byte[] Data; + public string Magic; + public long TitleID; + public byte SDKVersion1; + public byte SDKVersion2; + public byte SDKVersion3; + public byte SDKVersion4; + public byte MasterKeyRev; + + public NCA_Header(byte[] data) { - public byte[] Data; - public string Magic; - public long TitleID; - public byte SDKVersion1; - public byte SDKVersion2; - public byte SDKVersion3; - public byte SDKVersion4; - public byte MasterKeyRev; - - public NCA_Header(byte[] data) - { - Data = data; - Magic = Encoding.UTF8.GetString(Data.Skip(512).Take(4).ToArray()); - TitleID = BitConverter.ToInt64(data, 528); - SDKVersion1 = Data[540]; - SDKVersion2 = Data[541]; - SDKVersion3 = Data[542]; - SDKVersion4 = Data[543]; - MasterKeyRev = Data[544]; - } + Data = data; + Magic = Encoding.UTF8.GetString(Data.Skip(512).Take(4).ToArray()); + TitleID = BitConverter.ToInt64(data, 528); + SDKVersion1 = Data[540]; + SDKVersion2 = Data[541]; + SDKVersion3 = Data[542]; + SDKVersion4 = Data[543]; + MasterKeyRev = Data[544]; } - - public static NCA_Header[] NCA_Headers = new NCA_Header[1]; } + + public static NCA_Header[] NCA_Headers = new NCA_Header[1]; } diff --git a/XCI_Explorer/PFS0.cs b/XCI_Explorer/PFS0.cs index b36158f..303e935 100644 --- a/XCI_Explorer/PFS0.cs +++ b/XCI_Explorer/PFS0.cs @@ -2,47 +2,46 @@ using System; using System.Linq; using System.Text; -namespace XCI_Explorer +namespace XCI_Explorer; + +internal static class PFS0 { - internal static class PFS0 + public class PFS0_Header { - public class PFS0_Header + public byte[] Data; + public string Magic; + public int FileCount; + public int StringTableSize; + public int Reserved; + + public PFS0_Header(byte[] data) { - public byte[] Data; - public string Magic; - public int FileCount; - public int StringTableSize; - public int Reserved; - - public PFS0_Header(byte[] data) - { - Data = data; - Magic = Encoding.UTF8.GetString(Data.Take(4).ToArray()); - FileCount = BitConverter.ToInt32(data, 4); - StringTableSize = BitConverter.ToInt32(data, 8); - Reserved = BitConverter.ToInt32(data, 12); - } + Data = data; + Magic = Encoding.UTF8.GetString(Data.Take(4).ToArray()); + FileCount = BitConverter.ToInt32(data, 4); + StringTableSize = BitConverter.ToInt32(data, 8); + Reserved = BitConverter.ToInt32(data, 12); } - - public class PFS0_Entry - { - public byte[] Data; - public long Offset; - public long Size; - public int Name_ptr; - public int Reserved; - public string Name; - - public PFS0_Entry(byte[] data) - { - Data = data; - Offset = BitConverter.ToInt64(data, 0); - Size = BitConverter.ToInt64(data, 8); - Name_ptr = BitConverter.ToInt32(data, 16); - Reserved = BitConverter.ToInt32(data, 20); - } - } - - public static PFS0_Header[] PFS0_Headers = new PFS0_Header[1]; } + + public class PFS0_Entry + { + public byte[] Data; + public long Offset; + public long Size; + public int Name_ptr; + public int Reserved; + public string Name; + + public PFS0_Entry(byte[] data) + { + Data = data; + Offset = BitConverter.ToInt64(data, 0); + Size = BitConverter.ToInt64(data, 8); + Name_ptr = BitConverter.ToInt32(data, 16); + Reserved = BitConverter.ToInt32(data, 20); + } + } + + public static PFS0_Header[] PFS0_Headers = new PFS0_Header[1]; } diff --git a/XCI_Explorer/Program.cs b/XCI_Explorer/Program.cs index 2c9f687..0094802 100644 --- a/XCI_Explorer/Program.cs +++ b/XCI_Explorer/Program.cs @@ -1,28 +1,26 @@ using System; +using System.IO; using System.Windows.Forms; -namespace XCI_Explorer -{ - internal static class Program - { - [STAThread] - private static void Main() - { - AppDomain.CurrentDomain.AssemblyResolve += (Object sender, ResolveEventArgs args) => - { - System.Reflection.AssemblyName embeddedAssembly = new System.Reflection.AssemblyName(args.Name); - String resourceName = "XCI_Explorer" + "." + embeddedAssembly.Name + ".dll"; +namespace XCI_Explorer; - using (var stream = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName)) - { - Byte[] assemblyData = new Byte[stream.Length]; - stream.Read(assemblyData, 0, assemblyData.Length); - return System.Reflection.Assembly.Load(assemblyData); - } - }; - Application.EnableVisualStyles(); - Application.SetCompatibleTextRenderingDefault(false); - Application.Run(new MainForm()); - } +internal static class Program +{ + [STAThread] + private static void Main() + { + AppDomain.CurrentDomain.AssemblyResolve += (object sender, ResolveEventArgs args) => + { + System.Reflection.AssemblyName embeddedAssembly = new System.Reflection.AssemblyName(args.Name); + string resourceName = "XCI_Explorer" + "." + embeddedAssembly.Name + ".dll"; + + using Stream stream = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName); + byte[] assemblyData = new byte[stream.Length]; + stream.Read(assemblyData, 0, assemblyData.Length); + return System.Reflection.Assembly.Load(assemblyData); + }; + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new MainForm()); } } diff --git a/XCI_Explorer/TreeViewFileSystem.cs b/XCI_Explorer/TreeViewFileSystem.cs index 1a54ce3..8cda4f9 100644 --- a/XCI_Explorer/TreeViewFileSystem.cs +++ b/XCI_Explorer/TreeViewFileSystem.cs @@ -1,40 +1,40 @@ using System.Windows.Forms; using XCI_Explorer.Helpers; -namespace XCI_Explorer +namespace XCI_Explorer; + +public class TreeViewFileSystem { - public class TreeViewFileSystem + public TreeView treeView; + + public TreeViewFileSystem(TreeView tv) { - public TreeView treeView; + } - public TreeViewFileSystem(TreeView tv) + public BetterTreeNode AddDir(string name, BetterTreeNode parent = null) + { + BetterTreeNode betterTreeNode = new(name) { - } + Offset = -1L, + Size = -1L + }; + parent.Nodes.Add(betterTreeNode); + return betterTreeNode; + } - public BetterTreeNode AddDir(string name, BetterTreeNode parent = null) - { - BetterTreeNode betterTreeNode = new BetterTreeNode(name); - betterTreeNode.Offset = -1L; - betterTreeNode.Size = -1L; - parent.Nodes.Add(betterTreeNode); - return betterTreeNode; - } + public BetterTreeNode AddFile(string name, BetterTreeNode parent, long offset, long size) => AddFile(name, parent, offset, size, 0, "", ""); - public BetterTreeNode AddFile(string name, BetterTreeNode parent, long offset, long size) + public BetterTreeNode AddFile(string name, BetterTreeNode parent, long offset, long size, long HashedRegionSize, string ExpectedHash, string ActualHash) + { + BetterTreeNode betterTreeNode = new(name) { - return AddFile(name, parent, offset, size, 0, "", ""); - } - - public BetterTreeNode AddFile(string name, BetterTreeNode parent, long offset, long size, long HashedRegionSize, string ExpectedHash, string ActualHash) - { - BetterTreeNode betterTreeNode = new BetterTreeNode(name); - betterTreeNode.Offset = offset; - betterTreeNode.Size = size; - betterTreeNode.ExpectedHash = ExpectedHash; - betterTreeNode.ActualHash = ActualHash; - betterTreeNode.HashedRegionSize = HashedRegionSize; - parent.Nodes.Add(betterTreeNode); - return betterTreeNode; - } + Offset = offset, + Size = size, + ExpectedHash = ExpectedHash, + ActualHash = ActualHash, + HashedRegionSize = HashedRegionSize + }; + parent.Nodes.Add(betterTreeNode); + return betterTreeNode; } } diff --git a/XCI_Explorer/Util.cs b/XCI_Explorer/Util.cs index a7ce235..0527d14 100644 --- a/XCI_Explorer/Util.cs +++ b/XCI_Explorer/Util.cs @@ -1,130 +1,73 @@ using System; -using System.IO; using System.Linq; +using System.Text; -namespace XCI_Explorer +namespace XCI_Explorer; + +internal static class Util { - internal static class Util + public static string GetCapacity(int id) { - public static string GetCapacity(int id) + return id switch { - switch (id) - { - case 250: - return "1GB"; - case 248: - return "2GB"; - case 240: - return "4GB"; - case 224: - return "8GB"; - case 225: - return "16GB"; - case 226: - return "32GB"; - default: - return "?"; - } - } - - public static string GetMkey(byte id) - { - switch (id) - { - case 0: - case 1: - return "MasterKey0 (1.0.0-2.3.0)"; - case 2: - return "MasterKey1 (3.0.0)"; - case 3: - return "MasterKey2 (3.0.1-3.0.2)"; - case 4: - return "MasterKey3 (4.0.0-4.1.0)"; - case 5: - return "MasterKey4 (5.0.0-5.1.0)"; - case 6: - return "MasterKey5 (6.0.0-6.1.0)"; - case 7: - return "MasterKey6 (6.2.0)"; - case 8: - return "MasterKey7 (7.0.0-8.0.1)"; - case 9: - return "MasterKey8 (8.1.0-8.1.1)"; - case 10: - return "MasterKey9 (9.0.0-9.0.1)"; - case 11: - return "MasterKey10 (9.1.0-12.0.3)"; - case 12: - return "MasterKey11 (12.1.0)"; - case 13: - return "MasterKey12 (13.0.0-?)"; - case 14: - return "MasterKey13"; - case 15: - return "MasterKey14"; - case 16: - return "MasterKey15"; - case 17: - return "MasterKey16"; - case 18: - return "MasterKey17"; - case 19: - return "MasterKey18"; - case 20: - return "MasterKey19"; - case 21: - return "MasterKey20"; - case 22: - return "MasterKey21"; - case 23: - return "MasterKey22"; - case 24: - return "MasterKey23"; - case 25: - return "MasterKey24"; - case 26: - return "MasterKey25"; - case 27: - return "MasterKey26"; - case 28: - return "MasterKey27"; - case 29: - return "MasterKey28"; - case 30: - return "MasterKey29"; - case 31: - return "MasterKey30"; - case 32: - return "MasterKey31"; - case 33: - return "MasterKey32"; - default: - return "?"; - } - } - - public static bool checkFile(string filepath) - { - return File.Exists(filepath); - } - - public static byte[] StringToByteArray(string hex) - { - return (from x in Enumerable.Range(0, hex.Length) - where x % 2 == 0 - select Convert.ToByte(hex.Substring(x, 2), 16)).ToArray(); - } - - public static string Base64Encode(string plainText) - { - var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(plainText); - return System.Convert.ToBase64String(plainTextBytes); - } - - public static string Base64Decode(string base64EncodedData) - { - var base64EncodedBytes = System.Convert.FromBase64String(base64EncodedData); - return System.Text.Encoding.UTF8.GetString(base64EncodedBytes); - } + 250 => "1GB", + 248 => "2GB", + 240 => "4GB", + 224 => "8GB", + 225 => "16GB", + 226 => "32GB", + _ => "?", + }; } + + public static string GetMkey(byte id) + { + return id switch + { + 0 or 1 => "MasterKey0 (1.0.0-2.3.0)", + 2 => "MasterKey1 (3.0.0)", + 3 => "MasterKey2 (3.0.1-3.0.2)", + 4 => "MasterKey3 (4.0.0-4.1.0)", + 5 => "MasterKey4 (5.0.0-5.1.0)", + 6 => "MasterKey5 (6.0.0-6.1.0)", + 7 => "MasterKey6 (6.2.0)", + 8 => "MasterKey7 (7.0.0-8.0.1)", + 9 => "MasterKey8 (8.1.0-8.1.1)", + 10 => "MasterKey9 (9.0.0-9.0.1)", + 11 => "MasterKey10 (9.1.0-12.0.3)", + 12 => "MasterKey11 (12.1.0)", + 13 => "MasterKey12 (13.0.0-?)", + 14 => "MasterKey13", + 15 => "MasterKey14", + 16 => "MasterKey15", + 17 => "MasterKey16", + 18 => "MasterKey17", + 19 => "MasterKey18", + 20 => "MasterKey19", + 21 => "MasterKey20", + 22 => "MasterKey21", + 23 => "MasterKey22", + 24 => "MasterKey23", + 25 => "MasterKey24", + 26 => "MasterKey25", + 27 => "MasterKey26", + 28 => "MasterKey27", + 29 => "MasterKey28", + 30 => "MasterKey29", + 31 => "MasterKey30", + 32 => "MasterKey31", + 33 => "MasterKey32", + _ => "?", + }; + } + + public static byte[] StringToByteArray(string hex) => (from x in Enumerable.Range(0, hex.Length) + where x % 2 == 0 + select Convert.ToByte(hex.Substring(x, 2), 16)).ToArray(); + + public static string Base64Encode(string plainText) + => Convert.ToBase64String(Encoding.UTF8.GetBytes(plainText)); + + public static string Base64Decode(string base64EncodedData) + => Encoding.UTF8.GetString(Convert.FromBase64String(base64EncodedData)); } diff --git a/XCI_Explorer/XCI.cs b/XCI_Explorer/XCI.cs index 6a22152..613597b 100644 --- a/XCI_Explorer/XCI.cs +++ b/XCI_Explorer/XCI.cs @@ -2,30 +2,29 @@ using System; using System.Linq; using System.Text; -namespace XCI_Explorer +namespace XCI_Explorer; + +public static class XCI { - public static class XCI + public class XCI_Header { - public class XCI_Header + public byte[] Data; + public string Magic; + public byte CardSize1; + public long CardSize2; + public long HFS0OffsetPartition; + public long HFS0SizeParition; + + public XCI_Header(byte[] data) { - public byte[] Data; - public string Magic; - public byte CardSize1; - public long CardSize2; - public long HFS0OffsetPartition; - public long HFS0SizeParition; - - public XCI_Header(byte[] data) - { - Data = data; - Magic = Encoding.UTF8.GetString(Data.Skip(256).Take(4).ToArray()); - CardSize1 = Data[269]; - CardSize2 = BitConverter.ToInt64(data, 280); - HFS0OffsetPartition = BitConverter.ToInt64(data, 304); - HFS0SizeParition = BitConverter.ToInt64(data, 312); - } + Data = data; + Magic = Encoding.UTF8.GetString(Data.Skip(256).Take(4).ToArray()); + CardSize1 = Data[269]; + CardSize2 = BitConverter.ToInt64(data, 280); + HFS0OffsetPartition = BitConverter.ToInt64(data, 304); + HFS0SizeParition = BitConverter.ToInt64(data, 312); } - - public static XCI_Header[] XCI_Headers = new XCI_Header[1]; } + + public static XCI_Header[] XCI_Headers = new XCI_Header[1]; } diff --git a/XTSSharp/RandomAccessSectorStream.cs b/XTSSharp/RandomAccessSectorStream.cs index 4ed6ed2..6361b40 100644 --- a/XTSSharp/RandomAccessSectorStream.cs +++ b/XTSSharp/RandomAccessSectorStream.cs @@ -1,195 +1,190 @@ using System; using System.IO; -namespace XTSSharp +namespace XTSSharp; + +public class RandomAccessSectorStream : Stream { - public class RandomAccessSectorStream : Stream + private readonly byte[] _buffer; + private readonly int _bufferSize; + private readonly SectorStream _s; + private readonly bool _isStreamOwned; + private bool _bufferDirty; + private bool _bufferLoaded; + private int _bufferPos; + public override bool CanRead => _s.CanRead; + public override bool CanSeek => _s.CanSeek; + public override bool CanWrite => _s.CanWrite; + public override long Length => _s.Length + _bufferPos; + + public override long Position { - private readonly byte[] _buffer; - private readonly int _bufferSize; - private readonly SectorStream _s; - private readonly bool _isStreamOwned; - private bool _bufferDirty; - private bool _bufferLoaded; - private int _bufferPos; - public override bool CanRead => _s.CanRead; - public override bool CanSeek => _s.CanSeek; - public override bool CanWrite => _s.CanWrite; - public override long Length => _s.Length + _bufferPos; - - public override long Position { - get { - if (!_bufferLoaded) - { - return _s.Position + _bufferPos; - } - return _s.Position - _bufferSize + _bufferPos; - } - set { - if (value < 0) - { - throw new ArgumentOutOfRangeException("value"); - } - long num = value % _bufferSize; - long position = value - num; - if (_bufferLoaded) - { - long num2 = _s.Position - _bufferSize; - if (value > num2 && value < num2 + _bufferSize) - { - _bufferPos = (int)num; - return; - } - } - if (_bufferDirty) - { - WriteSector(); - } - _s.Position = position; - ReadSector(); - _bufferPos = (int)num; - } - } - - public RandomAccessSectorStream(SectorStream s) - : this(s, false) + get { - } - - public RandomAccessSectorStream(SectorStream s, bool isStreamOwned) - { - _s = s; - _isStreamOwned = isStreamOwned; - _buffer = new byte[s.SectorSize]; - _bufferSize = s.SectorSize; - } - - protected override void Dispose(bool disposing) - { - Flush(); - base.Dispose(disposing); - if (_isStreamOwned) + if (!_bufferLoaded) { - _s.Dispose(); + return _s.Position + _bufferPos; } + return _s.Position - _bufferSize + _bufferPos; } - - public override void Flush() + set { + if (value < 0) + { + throw new ArgumentOutOfRangeException(nameof(value)); + } + long num = value % _bufferSize; + long position = value - num; + if (_bufferLoaded) + { + long num2 = _s.Position - _bufferSize; + if (value > num2 && value < num2 + _bufferSize) + { + _bufferPos = (int)num; + return; + } + } if (_bufferDirty) { WriteSector(); } + _s.Position = position; + ReadSector(); + _bufferPos = (int)num; } + } - public override long Seek(long offset, SeekOrigin origin) + public RandomAccessSectorStream(SectorStream s) + : this(s, false) + { + } + + public RandomAccessSectorStream(SectorStream s, bool isStreamOwned) + { + _s = s; + _isStreamOwned = isStreamOwned; + _buffer = new byte[s.SectorSize]; + _bufferSize = s.SectorSize; + } + + protected override void Dispose(bool disposing) + { + Flush(); + base.Dispose(disposing); + if (_isStreamOwned) { - long num; - switch (origin) - { - case SeekOrigin.Begin: - num = offset; - break; - case SeekOrigin.End: - num = Length - offset; - break; - default: - num = Position + offset; - break; - } - Position = num; - return num; + _s.Dispose(); } + } - public override void SetLength(long value) + public override void Flush() + { + if (_bufferDirty) { - long num = value % _s.SectorSize; - if (num > 0) - { - value = value - num + _bufferSize; - } - _s.SetLength(value); + WriteSector(); } + } - public override int Read(byte[] buffer, int offset, int count) + public override long Seek(long offset, SeekOrigin origin) + { + long num = origin switch { - long position = Position; - if (position + count > _s.Length) + SeekOrigin.Begin => offset, + SeekOrigin.End => Length - offset, + _ => Position + offset, + }; + Position = num; + return num; + } + + public override void SetLength(long value) + { + long num = value % _s.SectorSize; + if (num > 0) + { + value = value - num + _bufferSize; + } + _s.SetLength(value); + } + + public override int Read(byte[] buffer, int offset, int count) + { + long position = Position; + if (position + count > _s.Length) + { + count = (int)(_s.Length - position); + } + if (!_bufferLoaded) + { + ReadSector(); + } + int num = 0; + while (count > 0) + { + int num2 = Math.Min(count, _bufferSize - _bufferPos); + Buffer.BlockCopy(_buffer, _bufferPos, buffer, offset, num2); + offset += num2; + _bufferPos += num2; + count -= num2; + num += num2; + if (_bufferPos == _bufferSize) { - count = (int)(_s.Length - position); + ReadSector(); } + } + return num; + } + + public override void Write(byte[] buffer, int offset, int count) + { + while (count > 0) + { if (!_bufferLoaded) { ReadSector(); } - int num = 0; - while (count > 0) - { - int num2 = Math.Min(count, _bufferSize - _bufferPos); - Buffer.BlockCopy(_buffer, _bufferPos, buffer, offset, num2); - offset += num2; - _bufferPos += num2; - count -= num2; - num += num2; - if (_bufferPos == _bufferSize) - { - ReadSector(); - } - } - return num; - } - - public override void Write(byte[] buffer, int offset, int count) - { - while (count > 0) - { - if (!_bufferLoaded) - { - ReadSector(); - } - int num = Math.Min(count, _bufferSize - _bufferPos); - Buffer.BlockCopy(buffer, offset, _buffer, _bufferPos, num); - offset += num; - _bufferPos += num; - count -= num; - _bufferDirty = true; - if (_bufferPos == _bufferSize) - { - WriteSector(); - } - } - } - - private void ReadSector() - { - if (_bufferLoaded && _bufferDirty) + int num = Math.Min(count, _bufferSize - _bufferPos); + Buffer.BlockCopy(buffer, offset, _buffer, _bufferPos, num); + offset += num; + _bufferPos += num; + count -= num; + _bufferDirty = true; + if (_bufferPos == _bufferSize) { WriteSector(); } - if (_s.Position != _s.Length) - { - int num = _s.Read(_buffer, 0, _buffer.Length); - if (num != _bufferSize) - { - Array.Clear(_buffer, num, _buffer.Length - num); - } - _bufferLoaded = true; - _bufferPos = 0; - _bufferDirty = false; - } - } - - private void WriteSector() - { - if (_bufferLoaded) - { - _s.Seek(-_bufferSize, SeekOrigin.Current); - } - _s.Write(_buffer, 0, _bufferSize); - _bufferDirty = false; - _bufferLoaded = false; - _bufferPos = 0; - Array.Clear(_buffer, 0, _bufferSize); } } + + private void ReadSector() + { + if (_bufferLoaded && _bufferDirty) + { + WriteSector(); + } + if (_s.Position != _s.Length) + { + int num = _s.Read(_buffer, 0, _buffer.Length); + if (num != _bufferSize) + { + Array.Clear(_buffer, num, _buffer.Length - num); + } + _bufferLoaded = true; + _bufferPos = 0; + _bufferDirty = false; + } + } + + private void WriteSector() + { + if (_bufferLoaded) + { + _s.Seek(-_bufferSize, SeekOrigin.Current); + } + _s.Write(_buffer, 0, _bufferSize); + _bufferDirty = false; + _bufferLoaded = false; + _bufferPos = 0; + Array.Clear(_buffer, 0, _bufferSize); + } } diff --git a/XTSSharp/SectorStream.cs b/XTSSharp/SectorStream.cs index c6a5bb9..70506fc 100644 --- a/XTSSharp/SectorStream.cs +++ b/XTSSharp/SectorStream.cs @@ -1,119 +1,115 @@ using System; using System.IO; -namespace XTSSharp +namespace XTSSharp; + +public class SectorStream : Stream { - public class SectorStream : Stream + private readonly Stream _baseStream; + private readonly long _offset; + private ulong _currentSector; + + public int SectorSize { - private readonly Stream _baseStream; - private readonly long _offset; - private ulong _currentSector; + get; + private set; + } - public int SectorSize { - get; - private set; - } + public override bool CanRead => _baseStream.CanRead; + public override bool CanSeek => _baseStream.CanSeek; + public override bool CanWrite => _baseStream.CanWrite; + public override long Length => _baseStream.Length - _offset; - public override bool CanRead => _baseStream.CanRead; - public override bool CanSeek => _baseStream.CanSeek; - public override bool CanWrite => _baseStream.CanWrite; - public override long Length => _baseStream.Length - _offset; - - public override long Position { - get { - return _baseStream.Position - _offset; - } - set { - ValidateSizeMultiple(value); - _baseStream.Position = value + _offset; - _currentSector = (ulong)(value / SectorSize); - } - } - - protected ulong CurrentSector => _currentSector; - - public SectorStream(Stream baseStream, int sectorSize) - : this(baseStream, sectorSize, 0L) + public override long Position + { + get { + return _baseStream.Position - _offset; } - - public SectorStream(Stream baseStream, int sectorSize, long offset) - { - SectorSize = sectorSize; - _baseStream = baseStream; - _offset = offset; - } - - private void ValidateSizeMultiple(long value) - { - if (value % SectorSize == 0L) - { - return; - } - throw new ArgumentException($"Value needs to be a multiple of {SectorSize}"); - } - - protected void ValidateSize(long value) - { - if (value == SectorSize) - { - return; - } - throw new ArgumentException($"Value needs to be {SectorSize}"); - } - - protected void ValidateSize(int value) - { - if (value == SectorSize) - { - return; - } - throw new ArgumentException($"Value needs to be {SectorSize}"); - } - - public override void Flush() - { - _baseStream.Flush(); - } - - public override long Seek(long offset, SeekOrigin origin) - { - long num; - switch (origin) - { - case SeekOrigin.Begin: - num = offset; - break; - case SeekOrigin.End: - num = Length - offset; - break; - default: - num = Position + offset; - break; - } - Position = num; - return num; - } - - public override void SetLength(long value) + set { ValidateSizeMultiple(value); - _baseStream.SetLength(value); - } - - public override int Read(byte[] buffer, int offset, int count) - { - ValidateSize(count); - int result = _baseStream.Read(buffer, offset, count); - _currentSector++; - return result; - } - - public override void Write(byte[] buffer, int offset, int count) - { - ValidateSize(count); - _baseStream.Write(buffer, offset, count); - _currentSector++; + _baseStream.Position = value + _offset; + _currentSector = (ulong)(value / SectorSize); } } + + protected ulong CurrentSector => _currentSector; + + public SectorStream(Stream baseStream, int sectorSize) + : this(baseStream, sectorSize, 0L) + { + } + + public SectorStream(Stream baseStream, int sectorSize, long offset) + { + SectorSize = sectorSize; + _baseStream = baseStream; + _offset = offset; + } + + private void ValidateSizeMultiple(long value) + { + if (value % SectorSize == 0L) + { + return; + } + throw new ArgumentException($"Value needs to be a multiple of {SectorSize}"); + } + + protected void ValidateSize(long value) + { + if (value == SectorSize) + { + return; + } + throw new ArgumentException($"Value needs to be {SectorSize}"); + } + + protected void ValidateSize(int value) + { + if (value == SectorSize) + { + return; + } + throw new ArgumentException($"Value needs to be {SectorSize}"); + } + + public override void Flush() + { + _baseStream.Flush(); + } + + public override long Seek(long offset, SeekOrigin origin) + { + long num = origin switch + { + SeekOrigin.Begin => offset, + SeekOrigin.End => Length - offset, + _ => Position + offset, + }; + Position = num; + return num; + } + + public override void SetLength(long value) + { + ValidateSizeMultiple(value); + _baseStream.SetLength(value); + } + + public override int Read(byte[] buffer, int offset, int count) + { + ValidateSize(count); + int result = _baseStream.Read(buffer, offset, count); + _currentSector++; + return result; + } + + public override void Write(byte[] buffer, int offset, int count) + { + ValidateSize(count); + _baseStream.Write(buffer, offset, count); + _currentSector++; + } } diff --git a/XTSSharp/Xts.cs b/XTSSharp/Xts.cs index 7a1fa13..04391b8 100644 --- a/XTSSharp/Xts.cs +++ b/XTSSharp/Xts.cs @@ -1,66 +1,65 @@ using System; using System.Security.Cryptography; -namespace XTSSharp +namespace XTSSharp; + +public class Xts { - public class Xts + private readonly SymmetricAlgorithm _key1; + private readonly SymmetricAlgorithm _key2; + + protected Xts(Func create, byte[] key1, byte[] key2) { - private readonly SymmetricAlgorithm _key1; - private readonly SymmetricAlgorithm _key2; - - protected Xts(Func create, byte[] key1, byte[] key2) + if (create == null) { - if (create == null) - { - throw new ArgumentNullException("create"); - } - if (key1 == null) - { - throw new ArgumentNullException("key1"); - } - if (key2 == null) - { - throw new ArgumentNullException("key2"); - } - _key1 = create(); - _key2 = create(); - if (key1.Length != key2.Length) - { - throw new ArgumentException("Key lengths don't match"); - } - _key1.KeySize = key1.Length * 8; - _key2.KeySize = key2.Length * 8; - _key1.Key = key1; - _key2.Key = key2; - _key1.Mode = CipherMode.ECB; - _key2.Mode = CipherMode.ECB; - _key1.Padding = PaddingMode.None; - _key2.Padding = PaddingMode.None; - _key1.BlockSize = 128; - _key2.BlockSize = 128; + throw new ArgumentNullException(nameof(create)); } - - public XtsCryptoTransform CreateEncryptor() + if (key1 == null) { - return new XtsCryptoTransform(_key1.CreateEncryptor(), _key2.CreateEncryptor(), false); + throw new ArgumentNullException(nameof(key1)); } - - public XtsCryptoTransform CreateDecryptor() + if (key2 == null) { - return new XtsCryptoTransform(_key1.CreateDecryptor(), _key2.CreateEncryptor(), true); + throw new ArgumentNullException(nameof(key2)); } - - protected static byte[] VerifyKey(int expectedSize, byte[] key) + _key1 = create(); + _key2 = create(); + if (key1.Length != key2.Length) { - if (key == null) - { - throw new ArgumentNullException("key"); - } - if (key.Length * 8 != expectedSize) - { - throw new ArgumentException($"Expected key length of {expectedSize} bits, got {key.Length * 8}"); - } - return key; + throw new ArgumentException("Key lengths don't match"); } + _key1.KeySize = key1.Length * 8; + _key2.KeySize = key2.Length * 8; + _key1.Key = key1; + _key2.Key = key2; + _key1.Mode = CipherMode.ECB; + _key2.Mode = CipherMode.ECB; + _key1.Padding = PaddingMode.None; + _key2.Padding = PaddingMode.None; + _key1.BlockSize = 128; + _key2.BlockSize = 128; + } + + public XtsCryptoTransform CreateEncryptor() + { + return new XtsCryptoTransform(_key1.CreateEncryptor(), _key2.CreateEncryptor(), false); + } + + public XtsCryptoTransform CreateDecryptor() + { + return new XtsCryptoTransform(_key1.CreateDecryptor(), _key2.CreateEncryptor(), true); + } + + protected static byte[] VerifyKey(int expectedSize, byte[] key) + { + if (key == null) + { + throw new ArgumentNullException(nameof(key)); + } + if (key.Length * 8 != expectedSize) + { + throw new ArgumentException($"Expected key length of {expectedSize} bits, got {key.Length * 8}"); + } + return key; } } diff --git a/XTSSharp/XtsAes128.cs b/XTSSharp/XtsAes128.cs index 5b1df9d..a8b242b 100644 --- a/XTSSharp/XtsAes128.cs +++ b/XTSSharp/XtsAes128.cs @@ -1,33 +1,32 @@ using System; using System.Security.Cryptography; -namespace XTSSharp +namespace XTSSharp; + +public class XtsAes128 : Xts { - public class XtsAes128 : Xts + private const int KEY_LENGTH = 128; + private const int KEY_BYTE_LENGTH = 16; + + protected XtsAes128(Func create, byte[] key1, byte[] key2) + : base(create, VerifyKey(128, key1), VerifyKey(128, key2)) { - private const int KEY_LENGTH = 128; - private const int KEY_BYTE_LENGTH = 16; + } - protected XtsAes128(Func create, byte[] key1, byte[] key2) - : base(create, Xts.VerifyKey(128, key1), Xts.VerifyKey(128, key2)) - { - } + public static Xts Create(byte[] key1, byte[] key2) + { + VerifyKey(128, key1); + VerifyKey(128, key2); + return new XtsAes128(Aes.Create, key1, key2); + } - public static Xts Create(byte[] key1, byte[] key2) - { - Xts.VerifyKey(128, key1); - Xts.VerifyKey(128, key2); - return new XtsAes128(Aes.Create, key1, key2); - } - - public static Xts Create(byte[] key) - { - Xts.VerifyKey(256, key); - byte[] array = new byte[16]; - byte[] array2 = new byte[16]; - Buffer.BlockCopy(key, 0, array, 0, 16); - Buffer.BlockCopy(key, 16, array2, 0, 16); - return new XtsAes128(Aes.Create, array, array2); - } + public static Xts Create(byte[] key) + { + VerifyKey(256, key); + byte[] array = new byte[16]; + byte[] array2 = new byte[16]; + Buffer.BlockCopy(key, 0, array, 0, 16); + Buffer.BlockCopy(key, 16, array2, 0, 16); + return new XtsAes128(Aes.Create, array, array2); } } diff --git a/XTSSharp/XtsAes256.cs b/XTSSharp/XtsAes256.cs index fd789c0..98f3eae 100644 --- a/XTSSharp/XtsAes256.cs +++ b/XTSSharp/XtsAes256.cs @@ -1,33 +1,32 @@ using System; using System.Security.Cryptography; -namespace XTSSharp +namespace XTSSharp; + +public class XtsAes256 : Xts { - public class XtsAes256 : Xts + private const int KEY_LENGTH = 256; + private const int KEY_BYTE_LENGTH = 32; + + protected XtsAes256(Func create, byte[] key1, byte[] key2) + : base(create, VerifyKey(256, key1), VerifyKey(256, key2)) { - private const int KEY_LENGTH = 256; - private const int KEY_BYTE_LENGTH = 32; + } - protected XtsAes256(Func create, byte[] key1, byte[] key2) - : base(create, Xts.VerifyKey(256, key1), Xts.VerifyKey(256, key2)) - { - } + public static Xts Create(byte[] key1, byte[] key2) + { + VerifyKey(256, key1); + VerifyKey(256, key2); + return new XtsAes256(Aes.Create, key1, key2); + } - public static Xts Create(byte[] key1, byte[] key2) - { - Xts.VerifyKey(256, key1); - Xts.VerifyKey(256, key2); - return new XtsAes256(Aes.Create, key1, key2); - } - - public static Xts Create(byte[] key) - { - Xts.VerifyKey(512, key); - byte[] array = new byte[32]; - byte[] array2 = new byte[32]; - Buffer.BlockCopy(key, 0, array, 0, 32); - Buffer.BlockCopy(key, 32, array2, 0, 32); - return new XtsAes256(Aes.Create, array, array2); - } + public static Xts Create(byte[] key) + { + VerifyKey(512, key); + byte[] array = new byte[32]; + byte[] array2 = new byte[32]; + Buffer.BlockCopy(key, 0, array, 0, 32); + Buffer.BlockCopy(key, 32, array2, 0, 32); + return new XtsAes256(Aes.Create, array, array2); } } diff --git a/XTSSharp/XtsCryptoTransform.cs b/XTSSharp/XtsCryptoTransform.cs index 64a2daf..e9b76b5 100644 --- a/XTSSharp/XtsCryptoTransform.cs +++ b/XTSSharp/XtsCryptoTransform.cs @@ -1,142 +1,141 @@ using System; using System.Security.Cryptography; -namespace XTSSharp +namespace XTSSharp; + +public class XtsCryptoTransform : IDisposable { - public class XtsCryptoTransform : IDisposable + private readonly byte[] _cc = new byte[16]; + private readonly bool _decrypting; + private readonly ICryptoTransform _key1; + private readonly ICryptoTransform _key2; + private readonly byte[] _pp = new byte[16]; + private readonly byte[] _t = new byte[16]; + private readonly byte[] _tweak = new byte[16]; + + public XtsCryptoTransform(ICryptoTransform key1, ICryptoTransform key2, bool decrypting) { - private readonly byte[] _cc = new byte[16]; - private readonly bool _decrypting; - private readonly ICryptoTransform _key1; - private readonly ICryptoTransform _key2; - private readonly byte[] _pp = new byte[16]; - private readonly byte[] _t = new byte[16]; - private readonly byte[] _tweak = new byte[16]; - - public XtsCryptoTransform(ICryptoTransform key1, ICryptoTransform key2, bool decrypting) + if (key1 == null) { - if (key1 == null) - { - throw new ArgumentNullException("key1"); - } - if (key2 == null) - { - throw new ArgumentNullException("key2"); - } - _key1 = key1; - _key2 = key2; - _decrypting = decrypting; + throw new ArgumentNullException(nameof(key1)); } - - public void Dispose() + if (key2 == null) { - _key1.Dispose(); - _key2.Dispose(); + throw new ArgumentNullException(nameof(key2)); } + _key1 = key1; + _key2 = key2; + _decrypting = decrypting; + } - public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset, ulong sector) + public void Dispose() + { + _key1.Dispose(); + _key2.Dispose(); + } + + public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset, ulong sector) + { + FillArrayFromSectorLittleEndian(_tweak, sector); + int num = inputCount >> 4; + int num2 = inputCount & 0xF; + _key2.TransformBlock(_tweak, 0, _tweak.Length, _t, 0); + int num3 = (num2 != 0) ? (num - 1) : num; + for (int i = 0; i < num3; i++) { - FillArrayFromSectorLittleEndian(_tweak, sector); - int num = inputCount >> 4; - int num2 = inputCount & 0xF; - _key2.TransformBlock(_tweak, 0, _tweak.Length, _t, 0); - int num3 = (num2 != 0) ? (num - 1) : num; - for (int i = 0; i < num3; i++) + TweakCrypt(inputBuffer, inputOffset, outputBuffer, outputOffset, _t); + inputOffset += 16; + outputOffset += 16; + } + if (num2 > 0) + { + if (_decrypting) { - TweakCrypt(inputBuffer, inputOffset, outputBuffer, outputOffset, _t); - inputOffset += 16; - outputOffset += 16; - } - if (num2 > 0) - { - if (_decrypting) + Buffer.BlockCopy(_t, 0, _cc, 0, 16); + MultiplyByX(_cc); + TweakCrypt(inputBuffer, inputOffset, _pp, 0, _cc); + int j; + for (j = 0; j < num2; j++) { - Buffer.BlockCopy(_t, 0, _cc, 0, 16); - MultiplyByX(_cc); - TweakCrypt(inputBuffer, inputOffset, _pp, 0, _cc); - int j; - for (j = 0; j < num2; j++) - { - _cc[j] = inputBuffer[16 + j + inputOffset]; - outputBuffer[16 + j + outputOffset] = _pp[j]; - } - for (; j < 16; j++) - { - _cc[j] = _pp[j]; - } - TweakCrypt(_cc, 0, outputBuffer, outputOffset, _t); + _cc[j] = inputBuffer[16 + j + inputOffset]; + outputBuffer[16 + j + outputOffset] = _pp[j]; } - else + for (; j < 16; j++) { - TweakCrypt(inputBuffer, inputOffset, _cc, 0, _t); - int k; - for (k = 0; k < num2; k++) - { - _pp[k] = inputBuffer[16 + k + inputOffset]; - outputBuffer[16 + k + outputOffset] = _cc[k]; - } - for (; k < 16; k++) - { - _pp[k] = _cc[k]; - } - TweakCrypt(_pp, 0, outputBuffer, outputOffset, _t); + _cc[j] = _pp[j]; } + TweakCrypt(_cc, 0, outputBuffer, outputOffset, _t); } - return inputCount; - } - - private static void FillArrayFromSectorBigEndian(byte[] value, ulong sector) - { - value[7] = (byte)((sector >> 56) & 0xFF); - value[6] = (byte)((sector >> 48) & 0xFF); - value[5] = (byte)((sector >> 40) & 0xFF); - value[4] = (byte)((sector >> 32) & 0xFF); - value[3] = (byte)((sector >> 24) & 0xFF); - value[2] = (byte)((sector >> 16) & 0xFF); - value[1] = (byte)((sector >> 8) & 0xFF); - value[0] = (byte)(sector & 0xFF); - } - - private static void FillArrayFromSectorLittleEndian(byte[] value, ulong sector) - { - value[8] = (byte)((sector >> 56) & 0xFF); - value[9] = (byte)((sector >> 48) & 0xFF); - value[10] = (byte)((sector >> 40) & 0xFF); - value[11] = (byte)((sector >> 32) & 0xFF); - value[12] = (byte)((sector >> 24) & 0xFF); - value[13] = (byte)((sector >> 16) & 0xFF); - value[14] = (byte)((sector >> 8) & 0xFF); - value[15] = (byte)(sector & 0xFF); - } - - private void TweakCrypt(byte[] inputBuffer, int inputOffset, byte[] outputBuffer, int outputOffset, byte[] t) - { - for (int i = 0; i < 16; i++) + else { - outputBuffer[i + outputOffset] = (byte)(inputBuffer[i + inputOffset] ^ t[i]); + TweakCrypt(inputBuffer, inputOffset, _cc, 0, _t); + int k; + for (k = 0; k < num2; k++) + { + _pp[k] = inputBuffer[16 + k + inputOffset]; + outputBuffer[16 + k + outputOffset] = _cc[k]; + } + for (; k < 16; k++) + { + _pp[k] = _cc[k]; + } + TweakCrypt(_pp, 0, outputBuffer, outputOffset, _t); } - _key1.TransformBlock(outputBuffer, outputOffset, 16, outputBuffer, outputOffset); - for (int j = 0; j < 16; j++) - { - outputBuffer[j + outputOffset] = (byte)(outputBuffer[j + outputOffset] ^ t[j]); - } - MultiplyByX(t); } + return inputCount; + } - private static void MultiplyByX(byte[] i) + private static void FillArrayFromSectorBigEndian(byte[] value, ulong sector) + { + value[7] = (byte)((sector >> 56) & 0xFF); + value[6] = (byte)((sector >> 48) & 0xFF); + value[5] = (byte)((sector >> 40) & 0xFF); + value[4] = (byte)((sector >> 32) & 0xFF); + value[3] = (byte)((sector >> 24) & 0xFF); + value[2] = (byte)((sector >> 16) & 0xFF); + value[1] = (byte)((sector >> 8) & 0xFF); + value[0] = (byte)(sector & 0xFF); + } + + private static void FillArrayFromSectorLittleEndian(byte[] value, ulong sector) + { + value[8] = (byte)((sector >> 56) & 0xFF); + value[9] = (byte)((sector >> 48) & 0xFF); + value[10] = (byte)((sector >> 40) & 0xFF); + value[11] = (byte)((sector >> 32) & 0xFF); + value[12] = (byte)((sector >> 24) & 0xFF); + value[13] = (byte)((sector >> 16) & 0xFF); + value[14] = (byte)((sector >> 8) & 0xFF); + value[15] = (byte)(sector & 0xFF); + } + + private void TweakCrypt(byte[] inputBuffer, int inputOffset, byte[] outputBuffer, int outputOffset, byte[] t) + { + for (int i = 0; i < 16; i++) { - byte b = 0; - byte b2 = 0; - for (int j = 0; j < 16; j++) - { - b2 = (byte)(i[j] >> 7); - i[j] = (byte)(((i[j] << 1) | b) & 0xFF); - b = b2; - } - if (b2 > 0) - { - i[0] ^= 135; - } + outputBuffer[i + outputOffset] = (byte)(inputBuffer[i + inputOffset] ^ t[i]); + } + _key1.TransformBlock(outputBuffer, outputOffset, 16, outputBuffer, outputOffset); + for (int j = 0; j < 16; j++) + { + outputBuffer[j + outputOffset] = (byte)(outputBuffer[j + outputOffset] ^ t[j]); + } + MultiplyByX(t); + } + + private static void MultiplyByX(byte[] i) + { + byte b = 0; + byte b2 = 0; + for (int j = 0; j < 16; j++) + { + b2 = (byte)(i[j] >> 7); + i[j] = (byte)(((i[j] << 1) | b) & 0xFF); + b = b2; + } + if (b2 > 0) + { + i[0] ^= 135; } } } diff --git a/XTSSharp/XtsSectorStream.cs b/XTSSharp/XtsSectorStream.cs index 3ec039b..2e85eba 100644 --- a/XTSSharp/XtsSectorStream.cs +++ b/XTSSharp/XtsSectorStream.cs @@ -1,74 +1,73 @@ using System.IO; -namespace XTSSharp +namespace XTSSharp; + +public class XtsSectorStream : SectorStream { - public class XtsSectorStream : SectorStream + public const int DEFAULT_SECTOR_SIZE = 512; + private readonly byte[] _tempBuffer; + private readonly Xts _xts; + private XtsCryptoTransform _decryptor; + private XtsCryptoTransform _encryptor; + + public XtsSectorStream(Stream baseStream, Xts xts) + : this(baseStream, xts, 512) { - public const int DEFAULT_SECTOR_SIZE = 512; - private readonly byte[] _tempBuffer; - private readonly Xts _xts; - private XtsCryptoTransform _decryptor; - private XtsCryptoTransform _encryptor; + } - public XtsSectorStream(Stream baseStream, Xts xts) - : this(baseStream, xts, 512) + public XtsSectorStream(Stream baseStream, Xts xts, int sectorSize) + : this(baseStream, xts, sectorSize, 0L) + { + } + + public XtsSectorStream(Stream baseStream, Xts xts, int sectorSize, long offset) + : base(baseStream, sectorSize, offset) + { + _xts = xts; + _tempBuffer = new byte[sectorSize]; + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + if (_encryptor != null) { + _encryptor.Dispose(); } - - public XtsSectorStream(Stream baseStream, Xts xts, int sectorSize) - : this(baseStream, xts, sectorSize, 0L) + if (_decryptor != null) { - } - - public XtsSectorStream(Stream baseStream, Xts xts, int sectorSize, long offset) - : base(baseStream, sectorSize, offset) - { - _xts = xts; - _tempBuffer = new byte[sectorSize]; - } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (_encryptor != null) - { - _encryptor.Dispose(); - } - if (_decryptor != null) - { - _decryptor.Dispose(); - } - } - - public override void Write(byte[] buffer, int offset, int count) - { - ValidateSize(count); - if (count != 0) - { - ulong currentSector = base.CurrentSector; - if (_encryptor == null) - { - _encryptor = _xts.CreateEncryptor(); - } - int count2 = _encryptor.TransformBlock(buffer, offset, count, _tempBuffer, 0, currentSector); - base.Write(_tempBuffer, 0, count2); - } - } - - public override int Read(byte[] buffer, int offset, int count) - { - ValidateSize(count); - ulong currentSector = base.CurrentSector; - int num = base.Read(_tempBuffer, 0, count); - if (num == 0) - { - return 0; - } - if (_decryptor == null) - { - _decryptor = _xts.CreateDecryptor(); - } - return _decryptor.TransformBlock(_tempBuffer, 0, num, buffer, offset, currentSector); + _decryptor.Dispose(); } } + + public override void Write(byte[] buffer, int offset, int count) + { + ValidateSize(count); + if (count != 0) + { + ulong currentSector = base.CurrentSector; + if (_encryptor == null) + { + _encryptor = _xts.CreateEncryptor(); + } + int count2 = _encryptor.TransformBlock(buffer, offset, count, _tempBuffer, 0, currentSector); + base.Write(_tempBuffer, 0, count2); + } + } + + public override int Read(byte[] buffer, int offset, int count) + { + ValidateSize(count); + ulong currentSector = base.CurrentSector; + int num = base.Read(_tempBuffer, 0, count); + if (num == 0) + { + return 0; + } + if (_decryptor == null) + { + _decryptor = _xts.CreateDecryptor(); + } + return _decryptor.TransformBlock(_tempBuffer, 0, num, buffer, offset, currentSector); + } } diff --git a/XTSSharp/XtsStream.cs b/XTSSharp/XtsStream.cs index f17bd1a..58d248c 100644 --- a/XTSSharp/XtsStream.cs +++ b/XTSSharp/XtsStream.cs @@ -1,22 +1,21 @@ using System.IO; -namespace XTSSharp +namespace XTSSharp; + +public class XtsStream : RandomAccessSectorStream { - public class XtsStream : RandomAccessSectorStream + public XtsStream(Stream baseStream, Xts xts) + : this(baseStream, xts, 512) { - public XtsStream(Stream baseStream, Xts xts) - : this(baseStream, xts, 512) - { - } + } - public XtsStream(Stream baseStream, Xts xts, int sectorSize) - : base(new XtsSectorStream(baseStream, xts, sectorSize), true) - { - } + public XtsStream(Stream baseStream, Xts xts, int sectorSize) + : base(new XtsSectorStream(baseStream, xts, sectorSize), true) + { + } - public XtsStream(Stream baseStream, Xts xts, int sectorSize, long offset) - : base(new XtsSectorStream(baseStream, xts, sectorSize, offset), true) - { - } + public XtsStream(Stream baseStream, Xts xts, int sectorSize, long offset) + : base(new XtsSectorStream(baseStream, xts, sectorSize, offset), true) + { } }