From ab6575995faab77a824b28b00389f18e00cb12af Mon Sep 17 00:00:00 2001 From: givememystuffplease Date: Fri, 17 Jul 2009 16:22:58 +0000 Subject: [PATCH] Some improvements to content editing. Has not been tested, but is implemented. --- NUS Downloader/Form1.Designer.cs | 119 ++++-- NUS Downloader/Form1.cs | 400 ++++++++++++++++-- NUS Downloader/Form1.resx | 206 +++++++++ NUS Downloader/NUS Downloader.csproj | 1 + .../Properties/Resources.Designer.cs | 7 + NUS Downloader/Properties/Resources.resx | 3 + NUS Downloader/Resources/link.png | Bin 0 -> 343 bytes 7 files changed, 652 insertions(+), 84 deletions(-) create mode 100644 NUS Downloader/Resources/link.png diff --git a/NUS Downloader/Form1.Designer.cs b/NUS Downloader/Form1.Designer.cs index b386bfd..787afe0 100644 --- a/NUS Downloader/Form1.Designer.cs +++ b/NUS Downloader/Form1.Designer.cs @@ -95,7 +95,8 @@ this.shamelessvariablelabel = new System.Windows.Forms.Label(); this.button3 = new System.Windows.Forms.Button(); this.contentsEdit = new System.Windows.Forms.ListBox(); - this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.contentModBox = new System.Windows.Forms.GroupBox(); + this.button16 = new System.Windows.Forms.Button(); this.button13 = new System.Windows.Forms.Button(); this.button15 = new System.Windows.Forms.Button(); this.button14 = new System.Windows.Forms.Button(); @@ -106,10 +107,11 @@ this.button10 = new System.Windows.Forms.Button(); this.radioButton1 = new System.Windows.Forms.RadioButton(); this.radioButton2 = new System.Windows.Forms.RadioButton(); + this.button17 = new System.Windows.Forms.Button(); this.databaseStrip.SuspendLayout(); this.tmdgpbox.SuspendLayout(); this.ticketgpbox.SuspendLayout(); - this.groupBox1.SuspendLayout(); + this.contentModBox.SuspendLayout(); this.SuspendLayout(); // // TMDButton @@ -685,7 +687,7 @@ // this.button7.FlatStyle = System.Windows.Forms.FlatStyle.Popup; this.button7.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.button7.Location = new System.Drawing.Point(278, 459); + this.button7.Location = new System.Drawing.Point(278, 322); this.button7.Name = "button7"; this.button7.Size = new System.Drawing.Size(249, 27); this.button7.TabIndex = 28; @@ -728,43 +730,57 @@ this.contentsEdit.FormattingEnabled = true; this.contentsEdit.Location = new System.Drawing.Point(9, 19); this.contentsEdit.Name = "contentsEdit"; - this.contentsEdit.Size = new System.Drawing.Size(138, 134); + this.contentsEdit.ScrollAlwaysVisible = true; + this.contentsEdit.Size = new System.Drawing.Size(198, 173); this.contentsEdit.TabIndex = 32; // - // groupBox1 + // contentModBox // - this.groupBox1.Controls.Add(this.button13); - this.groupBox1.Controls.Add(this.button15); - this.groupBox1.Controls.Add(this.button14); - this.groupBox1.Controls.Add(this.button12); - this.groupBox1.Controls.Add(this.contentsEdit); - this.groupBox1.Controls.Add(this.button8); - this.groupBox1.Controls.Add(this.button11); - this.groupBox1.Controls.Add(this.button9); - this.groupBox1.Controls.Add(this.button10); - this.groupBox1.Location = new System.Drawing.Point(278, 290); - this.groupBox1.Name = "groupBox1"; - this.groupBox1.Size = new System.Drawing.Size(249, 160); - this.groupBox1.TabIndex = 41; - this.groupBox1.TabStop = false; - this.groupBox1.Text = "Edit Title Contents"; + this.contentModBox.Controls.Add(this.button16); + this.contentModBox.Controls.Add(this.button13); + this.contentModBox.Controls.Add(this.button15); + this.contentModBox.Controls.Add(this.button14); + this.contentModBox.Controls.Add(this.button12); + this.contentModBox.Controls.Add(this.contentsEdit); + this.contentModBox.Controls.Add(this.button8); + this.contentModBox.Controls.Add(this.button11); + this.contentModBox.Controls.Add(this.button9); + this.contentModBox.Controls.Add(this.button10); + this.contentModBox.Location = new System.Drawing.Point(268, 208); + this.contentModBox.Name = "contentModBox"; + this.contentModBox.Size = new System.Drawing.Size(249, 272); + this.contentModBox.TabIndex = 41; + this.contentModBox.TabStop = false; + this.contentModBox.Text = "Edit Title Contents"; + this.contentModBox.Visible = false; + // + // button16 + // + this.button16.FlatStyle = System.Windows.Forms.FlatStyle.Popup; + this.button16.Image = global::NUS_Downloader.Properties.Resources.bug_add; + this.button16.Location = new System.Drawing.Point(136, 198); + this.button16.Name = "button16"; + this.button16.Size = new System.Drawing.Size(26, 26); + this.button16.TabIndex = 44; + this.button16.UseVisualStyleBackColor = true; // // button13 // this.button13.FlatStyle = System.Windows.Forms.FlatStyle.Popup; - this.button13.Image = global::NUS_Downloader.Properties.Resources.bug_add; - this.button13.Location = new System.Drawing.Point(217, 53); + this.button13.Image = global::NUS_Downloader.Properties.Resources.link; + this.button13.Location = new System.Drawing.Point(104, 198); this.button13.Name = "button13"; this.button13.Size = new System.Drawing.Size(26, 26); this.button13.TabIndex = 43; this.button13.UseVisualStyleBackColor = true; + this.button13.Click += new System.EventHandler(this.button13_Click); // // button15 // this.button15.FlatStyle = System.Windows.Forms.FlatStyle.Popup; - this.button15.Location = new System.Drawing.Point(153, 95); + this.button15.Location = new System.Drawing.Point(9, 235); this.button15.Name = "button15"; - this.button15.Size = new System.Drawing.Size(90, 26); + this.button15.Size = new System.Drawing.Size(70, 26); this.button15.TabIndex = 42; this.button15.Text = "Revert"; this.button15.UseVisualStyleBackColor = true; @@ -773,18 +789,19 @@ // button14 // this.button14.FlatStyle = System.Windows.Forms.FlatStyle.Popup; - this.button14.Location = new System.Drawing.Point(153, 127); + this.button14.Location = new System.Drawing.Point(85, 235); this.button14.Name = "button14"; - this.button14.Size = new System.Drawing.Size(90, 26); + this.button14.Size = new System.Drawing.Size(158, 26); this.button14.TabIndex = 41; - this.button14.Text = "Overwrite"; + this.button14.Text = "Overwrite Title Contents..."; this.button14.UseVisualStyleBackColor = true; + this.button14.Click += new System.EventHandler(this.button14_Click); // // button12 // this.button12.FlatStyle = System.Windows.Forms.FlatStyle.Popup; - this.button12.Image = global::NUS_Downloader.Properties.Resources.connect; - this.button12.Location = new System.Drawing.Point(217, 19); + this.button12.Image = ((System.Drawing.Image)(resources.GetObject("button12.Image"))); + this.button12.Location = new System.Drawing.Point(72, 198); this.button12.Name = "button12"; this.button12.Size = new System.Drawing.Size(26, 26); this.button12.TabIndex = 40; @@ -794,8 +811,8 @@ // button8 // this.button8.FlatStyle = System.Windows.Forms.FlatStyle.Popup; - this.button8.Image = global::NUS_Downloader.Properties.Resources.arrow_up; - this.button8.Location = new System.Drawing.Point(153, 19); + this.button8.Image = ((System.Drawing.Image)(resources.GetObject("button8.Image"))); + this.button8.Location = new System.Drawing.Point(213, 19); this.button8.Name = "button8"; this.button8.Size = new System.Drawing.Size(26, 26); this.button8.TabIndex = 33; @@ -805,8 +822,8 @@ // button11 // this.button11.FlatStyle = System.Windows.Forms.FlatStyle.Popup; - this.button11.Image = global::NUS_Downloader.Properties.Resources.package_add; - this.button11.Location = new System.Drawing.Point(185, 19); + this.button11.Image = ((System.Drawing.Image)(resources.GetObject("button11.Image"))); + this.button11.Location = new System.Drawing.Point(9, 198); this.button11.Name = "button11"; this.button11.Size = new System.Drawing.Size(26, 26); this.button11.TabIndex = 36; @@ -816,8 +833,8 @@ // button9 // this.button9.FlatStyle = System.Windows.Forms.FlatStyle.Popup; - this.button9.Image = global::NUS_Downloader.Properties.Resources.arrow_down; - this.button9.Location = new System.Drawing.Point(153, 53); + this.button9.Image = ((System.Drawing.Image)(resources.GetObject("button9.Image"))); + this.button9.Location = new System.Drawing.Point(213, 53); this.button9.Name = "button9"; this.button9.Size = new System.Drawing.Size(26, 26); this.button9.TabIndex = 34; @@ -827,8 +844,8 @@ // button10 // this.button10.FlatStyle = System.Windows.Forms.FlatStyle.Popup; - this.button10.Image = global::NUS_Downloader.Properties.Resources.package_delete; - this.button10.Location = new System.Drawing.Point(185, 53); + this.button10.Image = ((System.Drawing.Image)(resources.GetObject("button10.Image"))); + this.button10.Location = new System.Drawing.Point(41, 198); this.button10.Name = "button10"; this.button10.Size = new System.Drawing.Size(26, 26); this.button10.TabIndex = 35; @@ -839,7 +856,7 @@ // this.radioButton1.BackColor = System.Drawing.Color.Transparent; this.radioButton1.Checked = true; - this.radioButton1.Image = global::NUS_Downloader.Properties.Resources.wilolgoi; + this.radioButton1.Image = ((System.Drawing.Image)(resources.GetObject("radioButton1.Image"))); this.radioButton1.Location = new System.Drawing.Point(12, 385); this.radioButton1.Name = "radioButton1"; this.radioButton1.Size = new System.Drawing.Size(60, 31); @@ -850,7 +867,7 @@ // // radioButton2 // - this.radioButton2.Image = global::NUS_Downloader.Properties.Resources.dsi; + this.radioButton2.Image = ((System.Drawing.Image)(resources.GetObject("radioButton2.Image"))); this.radioButton2.ImageAlign = System.Drawing.ContentAlignment.TopCenter; this.radioButton2.Location = new System.Drawing.Point(78, 385); this.radioButton2.Name = "radioButton2"; @@ -859,16 +876,27 @@ this.radioButton2.UseVisualStyleBackColor = true; this.radioButton2.CheckedChanged += new System.EventHandler(this.radioButton2_CheckedChanged); // + // button17 + // + this.button17.FlatStyle = System.Windows.Forms.FlatStyle.Popup; + this.button17.Location = new System.Drawing.Point(278, 290); + this.button17.Name = "button17"; + this.button17.Size = new System.Drawing.Size(249, 26); + this.button17.TabIndex = 42; + this.button17.Text = "Modify Individual Titles..."; + this.button17.UseVisualStyleBackColor = true; + this.button17.Click += new System.EventHandler(this.button17_Click); + // // Form1 // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(539, 492); - this.Controls.Add(this.groupBox1); + this.Controls.Add(this.contentModBox); + this.Controls.Add(this.button17); this.Controls.Add(this.button3); this.Controls.Add(this.shamelessvariablelabel); this.Controls.Add(this.label12); - this.Controls.Add(this.button6); this.Controls.Add(this.button1); this.Controls.Add(this.ticketgpbox); this.Controls.Add(this.button5); @@ -876,8 +904,8 @@ this.Controls.Add(this.tmdgpbox); this.Controls.Add(this.truchabox); this.Controls.Add(this.databaseButton); - this.Controls.Add(this.button7); this.Controls.Add(this.decryptbox); + this.Controls.Add(this.button7); this.Controls.Add(this.ignoreticket); this.Controls.Add(this.wadnamebox); this.Controls.Add(this.getcerts); @@ -894,6 +922,7 @@ this.Controls.Add(this.downloadstartbtn); this.Controls.Add(this.titleidbox); this.Controls.Add(this.TMDButton); + this.Controls.Add(this.button6); this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.MaximizeBox = false; @@ -907,7 +936,7 @@ this.tmdgpbox.PerformLayout(); this.ticketgpbox.ResumeLayout(false); this.ticketgpbox.PerformLayout(); - this.groupBox1.ResumeLayout(false); + this.contentModBox.ResumeLayout(false); this.ResumeLayout(false); this.PerformLayout(); @@ -987,10 +1016,12 @@ private System.Windows.Forms.Button button10; private System.Windows.Forms.Button button11; private System.Windows.Forms.Button button12; - private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.GroupBox contentModBox; private System.Windows.Forms.Button button14; private System.Windows.Forms.Button button15; private System.Windows.Forms.Button button13; + private System.Windows.Forms.Button button16; + private System.Windows.Forms.Button button17; } } diff --git a/NUS Downloader/Form1.cs b/NUS Downloader/Form1.cs index e31188e..d4b0808 100644 --- a/NUS Downloader/Form1.cs +++ b/NUS Downloader/Form1.cs @@ -6,6 +6,7 @@ using System.Security.Cryptography; using System.Xml; using System.Drawing; + namespace NUS_Downloader { public partial class Form1 : Form @@ -19,8 +20,6 @@ namespace NUS_Downloader static bool dsidecrypt = false; const string certs_MD5 = "7677AD47BAA5D6E3E313E72661FBDC16"; - - // Images do not compare unless globalized... Image green = Properties.Resources.bullet_green; Image orange = Properties.Resources.bullet_orange; @@ -40,6 +39,19 @@ namespace NUS_Downloader public int FooterSize; }; + public struct TitleContent + { + public byte[] ContentID; + public byte[] Index; + public byte[] Type; + public byte[] Size; + public byte[] SHAHash; + }; + + public enum ContentTypes : int { + Shared = 0x8001, Normal = 0x0001 + } + // This is the standard entry to the GUI public Form1() { @@ -141,6 +153,8 @@ namespace NUS_Downloader { this.Text = "NUSD - " + version + " - WB3000"; this.Size = this.MinimumSize; + + } private bool BootChecks() @@ -289,6 +303,7 @@ namespace NUS_Downloader string[] tmdsizes = GetContentSizes(tmd, nbr_cont); byte[] tmdhashes = GetContentHashes(tmd, nbr_cont); byte[] tmdindices = GetContentIndices(tmd, nbr_cont); + int[] tmdtypes = GetContentTypes(tmd, nbr_cont); // Loop through each content and display name, hash, index for (int i = 0; i < nbr_cont; i++) @@ -299,9 +314,9 @@ namespace NUS_Downloader { hash[x] = tmdhashes[(i*20)+x]; } - WriteStatus(" - Hash: " + DisplayBytes(hash, "")); + WriteStatus(" - Hash: " + DisplayBytes(hash, "").Substring(0, 8) + "..."); WriteStatus(" - Index: " + tmdindices[i]); - + WriteStatus(" - Shared: " + (tmdtypes[i] == 0x8001)); } } } @@ -546,6 +561,26 @@ namespace NUS_Downloader return contenthashes; } + // Returns array of shared/normal values for a tmd... + private int[] GetContentTypes(byte[] tmdfile, int length) + { + int[] contenttypes = new int[length]; + int startoffset = 0x1EA; + + for (int i = 0; i < length; i++) + { + if (tmdfile[startoffset] == 0x80) + contenttypes[i] = (int)ContentTypes.Shared; + else + contenttypes[i] = (int)ContentTypes.Normal; + startoffset += 36; + // DEBUG: + //WriteStatus(contenttypes[i].ToString()); + } + + return contenttypes; + } + private byte[] GetContentIndices(byte[] tmdfile, int length) { byte[] contentindices = new byte[length]; @@ -805,24 +840,25 @@ namespace NUS_Downloader // IV (00+IDX+more000) byte[] iv = new byte[16]; - for (int x = 0; x < 8; x++) + for (int x = 0; x < 16; x++) { iv[x] = 0x00; } - for (int x = 0; x < 7; x++) - { - iv[x + 8] = 0x00; - } iv[1] = tmdindices[i]; initCrypt(iv, titlekey); - // Create decrypted file + /* Create decrypted file string zeros = "000000"; - FileStream decfs = new FileStream(RemoveIllegalCharacters(titledirectory + @"\" + zeros + i.ToString("X2") + ".app"), FileMode.Create); + FileStream decfs = new FileStream(titledirectory + @"\" + zeros + i.ToString("X2") + ".app", FileMode.Create); decfs.Write(Decrypt(contbuf), 0, int.Parse(tmdsizes[i], System.Globalization.NumberStyles.HexNumber)); decfs.Close(); - WriteStatus(" - Decrypted: " + zeros + i.ToString("X2") + ".app"); + WriteStatus(" - Decrypted: " + zeros + i.ToString("X2") + ".app"); */ + + FileStream decfs = new FileStream(titledirectory + @"\" + tmdcontents[i] + ".app", FileMode.Create); + decfs.Write(Decrypt(contbuf), 0, int.Parse(tmdsizes[i], System.Globalization.NumberStyles.HexNumber)); + decfs.Close(); + WriteStatus(" - Decrypted: " + tmdcontents[i] + ".app"); // Hash Check... byte[] hash = new byte[20]; @@ -1060,7 +1096,7 @@ namespace NUS_Downloader wadnamebox.Text = wadnamebox.Text.Replace("[v]", "v" + titleversion.Text); // Create wad file - FileStream wadfs = new FileStream(RemoveIllegalCharacters(totaldirectory + @"\" + wadnamebox.Text), FileMode.Create); + FileStream wadfs = new FileStream(totaldirectory + @"\" + RemoveIllegalCharacters(wadnamebox.Text), FileMode.Create); // Add wad stuffs WADHeader wad = new WADHeader(); @@ -1232,40 +1268,27 @@ namespace NUS_Downloader if (currentdir.EndsWith(Convert.ToString(Path.DirectorySeparatorChar)) == false) currentdir += Path.DirectorySeparatorChar; if (File.Exists(currentdir + "key.bin") == false) - { WriteStatus("Wii Decryption: Need (key.bin)"); - } else - { WriteStatus("Wii Decryption: OK"); - } if (File.Exists(currentdir + "kkey.bin") == false) - { WriteStatus("Wii Korea Decryption: Need (kkey.bin)"); - } else - { WriteStatus("Wii Korea Decryption: OK"); - } if (File.Exists(currentdir + "dsikey.bin") == false) - { WriteStatus("DSi Decryption: Need (dsikey.bin)"); - } else - { WriteStatus("DSi Decryption: OK"); - } if (File.Exists(currentdir + "database.xml") == false) - { WriteStatus("Database: Need (database.xml)"); - } else - { WriteStatus("Database: OK"); - } + + if (IsWin7()) + WriteStatus("Windows 7 Features: Enabled"); WriteStatus(""); WriteStatus("Special thanks to:"); @@ -1276,7 +1299,7 @@ namespace NUS_Downloader WriteStatus(" * Anyone who helped beta test on GBATemp!"); WriteStatus(" * Famfamfam for the Silk Icon Set."); } - + private void getcerts_Click(object sender, EventArgs e) { // Get a certs.sys from NUS... @@ -1287,7 +1310,7 @@ namespace NUS_Downloader currentdir += Path.DirectorySeparatorChar; // Create certs file - FileStream certsfs = new FileStream(RemoveIllegalCharacters(currentdir + @"\cert.sys"), FileMode.Create); + FileStream certsfs = new FileStream(currentdir + @"\cert.sys", FileMode.Create); // Getting it from SystemMenu 3.2 DownloadNUSFile("0000000100000002", "tmd.289", currentdir + @"\", 0, true); @@ -1699,12 +1722,13 @@ namespace NUS_Downloader if (e.ClickedItem.Text.Contains("v")) { if (e.ClickedItem.Text.Contains(" ")) - titleversion.Text = e.ClickedItem.Text.Substring(1, e.ClickedItem.Text.IndexOf(' ')); + titleversion.Text = e.ClickedItem.Text.Substring(1, e.ClickedItem.Text.IndexOf(' ') - 1); else titleversion.Text = e.ClickedItem.Text.Substring(1, e.ClickedItem.Text.Length - 1); } else { + // Apparently it's a region code.. titleidbox.Text = titleidbox.Text.Replace("XX", e.ClickedItem.Text.Substring(0, 2)); titleversion.Text = ""; } @@ -2010,7 +2034,7 @@ namespace NUS_Downloader tmd = ZeroSignature(tmd); tmd = TruchaSign(tmd); - FileStream testtmd = new FileStream(RemoveIllegalCharacters(fileinfo[0] + fileinfo[1]), FileMode.Open); + FileStream testtmd = new FileStream(fileinfo[0] + fileinfo[1], FileMode.Open); testtmd.Write(tmd, 0, tmd.Length); testtmd.Close(); } @@ -2068,7 +2092,7 @@ namespace NUS_Downloader cetkbuff = TruchaSign(cetkbuff); // Write changes to cetk. - FileStream testtik = new FileStream(RemoveIllegalCharacters(fileinfo[0] + "cetk"), FileMode.Open); + FileStream testtik = new FileStream(fileinfo[0] + "cetk", FileMode.Open); testtik.Write(cetkbuff, 0, cetkbuff.Length); testtik.Close(); } @@ -2243,13 +2267,18 @@ namespace NUS_Downloader string[] tmdcontents = GetContentNames(tmd, nbr_cont); byte[] tmdindices = GetContentIndices(tmd, nbr_cont); + int[] tmdtypes = GetContentTypes(tmd, nbr_cont); // Loop and add contents to listbox... for (int a = 0; a < nbr_cont; a++) { contentsEdit.Items.Add(String.Format("[{0}] [{1}]", tmdindices[a], tmdcontents[a])); + + if (tmdtypes[a] == 0x8001) + contentsEdit.Items[a] += " [S]"; } + // Identify Boot Content... contentsEdit.Items[boot_idx] += " [BOOT]"; @@ -2258,7 +2287,7 @@ namespace NUS_Downloader private void button8_Click(object sender, EventArgs e) { // Move selected content upwards (down an index)... - if (contentsEdit.SelectedIndex <= 0) + if (contentsEdit.SelectedIndex < 0) return; int sel_idx = contentsEdit.SelectedIndex; @@ -2290,7 +2319,7 @@ namespace NUS_Downloader private void button12_Click(object sender, EventArgs e) { // Set a new boot index... - if (contentsEdit.SelectedIndex <= 0) + if (contentsEdit.SelectedIndex < 0) return; for (int a = 0; a < contentsEdit.Items.Count; a++) @@ -2306,14 +2335,14 @@ namespace NUS_Downloader { // Add a file to the contents... OpenFileDialog opencont = new OpenFileDialog(); - opencont.Filter = "All Files|*"; + opencont.Filter = "Decrypted Contents|*.app|All Files|*"; opencont.Multiselect = false; opencont.Title = "Locate a Content"; if (opencont.ShowDialog() != DialogResult.Cancel) { - if ((opencont.SafeFileName.Length != 8) && (OnlyHexInString(opencont.SafeFileName) == false)) + if ((OnlyHexInString(opencont.SafeFileName.Substring(0,8)) == false)) { - MessageBox.Show("Please locate/rename a file to be (8 HEX CHARACTERS) long!", "Bad!", MessageBoxButtons.OK); + MessageBox.Show("Please locate/rename a file to be (8 HEX CHARACTERS) long + (.app) extention!", "Bad!", MessageBoxButtons.OK); return; } @@ -2329,6 +2358,12 @@ namespace NUS_Downloader // D: TODO? string[] fileinfo = shamelessvariablelabel.Text.Split(','); + if (File.Exists(fileinfo[0] + opencont.SafeFileName) ) + { + MessageBox.Show("Rename the file you are adding, it already exists in the title directory!", "Bad!", MessageBoxButtons.OK); + return; + } + if (fileinfo[0] + opencont.SafeFileName != opencont.FileName) { // Move the file into the directory... @@ -2346,7 +2381,7 @@ namespace NUS_Downloader private void button10_Click(object sender, EventArgs e) { // Remove a content from the list... - if ((contentsEdit.SelectedIndex <= 0) || (contentsEdit.Items.Count <= 1)) + if ((contentsEdit.SelectedIndex < 0) || (contentsEdit.Items.Count <= 1)) return; string[] fileinfo = shamelessvariablelabel.Text.Split(','); @@ -2359,6 +2394,291 @@ namespace NUS_Downloader contentsEdit.Items.RemoveAt(contentsEdit.SelectedIndex); } + private void button14_Click(object sender, EventArgs e) + { + // Write changes to TMD of contents... + WriteStatus("Updating TMD with content information..."); + string[] fileinfo = shamelessvariablelabel.Text.Split(','); + byte[] ticket = FileLocationToByteArray(fileinfo[0] + "cetk"); + byte[] etitlekey = new byte[16]; + for (int a = 0; a < 16; a++) + { + etitlekey[a] = ticket[0x1BF + a]; + } + // TODO: Add more key support + byte[] commonkey = LoadCommonKey(@"\key.bin"); + // IV (TITLEID00000000) + byte[] iv = new byte[16]; + for (int b = 0; b < 8; b++) + { + iv[b] = ticket[0x1DC + b]; + } + for (int c = 0; c < 8; c++) + { + iv[c+8] = 0x00; + } + + initCrypt(iv, commonkey); + byte[] dtitlekey = Decrypt(etitlekey); + + // Holds all the content data... + TitleContent[] contents = new TitleContent[contentsEdit.Items.Count]; + + // Previous TMD for analysis + byte[] tmd = FileLocationToByteArray(fileinfo[0] + fileinfo[1]); + + for (int c = 0; c < contentsEdit.Items.Count; c++) + { + string itemstr = contentsEdit.Items[c].ToString(); + contents[c] = new TitleContent(); + if (itemstr.Contains(".app")) + { + // This is already decrypted, we're going to add it to the TMD... + string filename = itemstr.Substring(5, 12); + byte[] contentbytes = FileLocationToByteArray(fileinfo[0] + filename); + WriteStatus(" - Encrypting " + filename + "..."); + + // Gather the contentID (crappy way to do it)... + contents[c].ContentID = new byte[4]; + contents[c].ContentID[0] = byte.Parse(filename.Substring(0, 2), System.Globalization.NumberStyles.HexNumber); + contents[c].ContentID[1] = byte.Parse(filename.Substring(2, 2), System.Globalization.NumberStyles.HexNumber); + contents[c].ContentID[2] = byte.Parse(filename.Substring(4, 2), System.Globalization.NumberStyles.HexNumber); + contents[c].ContentID[3] = byte.Parse(filename.Substring(6, 2), System.Globalization.NumberStyles.HexNumber); + + // Grab SHA1/size of file + contents[c].SHAHash = new byte[20]; + contents[c].SHAHash = ComputeSHA(contentbytes); + + contents[c].Size = new byte[8]; + // TODOCHECK THIS OVER + contents[c].Size = InttoByteArray(contentbytes.Length, 8); + + contents[c].Index = new byte[2]; + contents[c].Index = InttoByteArray(c, 2); + + contents[c].Type = new byte[2]; + contents[c].Type[1] = 0x01; + if (contentsEdit.Items[c].ToString().Contains(" [S]")) + contents[c].Type[0] = 0x80; + else + contents[c].Type[0] = 0x00; + + // Pad to be 16 byte aligned + contentbytes = AlignByteArray(contentbytes, 16); + + // Encrypt with correct index IV/titlekey + byte[] ivindex = new byte[16]; + for (int d = 0; d < ivindex.Length; d++) + { + ivindex[d] = 0x00; + } + ivindex[0] = contents[c].Index[0]; + ivindex[1] = contents[c].Index[1]; + + initCrypt(ivindex, dtitlekey); + + FileStream encryptwrite = new FileStream(fileinfo[0] + filename.Substring(0, 8), FileMode.Create); + encryptwrite.Write(Encrypt(contentbytes), 0, contentbytes.Length); + encryptwrite.Close(); + + WriteStatus(" - " + filename.Substring(0, 8) + " written!"); + } + else + { + // An encrypted content...it was from the original TMD, leave alone? (hmmm) + string filename = itemstr.Substring(5, 8); + byte[] contentbytes = FileLocationToByteArray(fileinfo[0] + filename); + WriteStatus("Gathering " + filename + " information..."); + + // Grab previous values from TMD... + int nbr_cont = ContentCount(tmd); + string[] tmdoldcontents = GetContentNames(tmd, nbr_cont); + string[] tmdsizes = GetContentSizes(tmd, nbr_cont); + byte[] tmdhashes = GetContentHashes(tmd, nbr_cont); + + int thiscontentidx = 0; + + for (int f = 0; f < nbr_cont; f++) + { + if (tmdoldcontents[f] == filename) + thiscontentidx = f; + } + + if (thiscontentidx != c) + { + // We have to decrypt the content, and then encrypt to keep IV in line... + byte[] ivindex = new byte[16]; + for (int d = 0; d < ivindex.Length; d++) + { + ivindex[d] = 0x00; + } + // TODO: Complete this... + ivindex[0] = 0x00; + ivindex[1] = (byte)thiscontentidx; + + initCrypt(ivindex, dtitlekey); + + byte[] hash = new byte[20]; + for (int x = 0; x < 20; x++) + { + hash[x] = tmdhashes[(thiscontentidx * 20) + x]; + } + + byte[] decContent = Decrypt(contentbytes); + Array.Resize(ref decContent, int.Parse(tmdsizes[c], System.Globalization.NumberStyles.HexNumber)); + if ((Convert.ToBase64String(ComputeSHA(decContent))) == Convert.ToBase64String(hash)) + { + WriteStatus(" - Hash Check: Content is Unchanged..."); + contents[c].SHAHash = hash; + } + else + { + WriteStatus(" - Hash Check: Content changed (probably REALLY BAD)..."); + contents[c].SHAHash = ComputeSHA(decContent); + } + + // Re-encrypt + byte[] newiv = new byte[16]; + for (int g = 0; g < newiv.Length; g++) + { + newiv[g] = 0x00; + } + // TODO: Complete this... + ivindex[0] = 0x00; + ivindex[1] = (byte)c; + + // Pad back to 0x16 alignment + AlignByteArray(decContent, 0x16); + + initCrypt(newiv, dtitlekey); + + byte[] encContent = Encrypt(decContent); + + File.Delete(fileinfo[0] + filename.Substring(0, 8)); + + FileStream encryptwrite = new FileStream(fileinfo[0] + filename.Substring(0, 8), FileMode.OpenOrCreate); + encryptwrite.Write(encContent, 0, encContent.Length); + encryptwrite.Close(); + + WriteStatus(" - Encrypted Content Again!"); + } + else + { + // Hopefully this content has not been touched... + // TODO: um, i'm 12 and what is this? + } + + contents[c].ContentID = new byte[4]; + contents[c].ContentID[0] = byte.Parse(filename.Substring(0, 2), System.Globalization.NumberStyles.HexNumber); + contents[c].ContentID[1] = byte.Parse(filename.Substring(2, 2), System.Globalization.NumberStyles.HexNumber); + contents[c].ContentID[2] = byte.Parse(filename.Substring(4, 2), System.Globalization.NumberStyles.HexNumber); + contents[c].ContentID[3] = byte.Parse(filename.Substring(6, 2), System.Globalization.NumberStyles.HexNumber); + + contents[c].Index = new byte[2]; + contents[c].Index = InttoByteArray(c, 2); // TODOTODOTODO + + contents[c].Type = new byte[2]; + contents[c].Type[0] = 0x01; + if (contentsEdit.Items[c].ToString().Contains(" [S]")) + contents[c].Type[0] = 0x80; + else + contents[c].Type[0] = 0x00; + + contents[c].Size = new byte[8]; + // TODOCHECK THIS OVER + contents[c].Size = InttoByteArray(contentbytes.Length, 8); + } + + // Write all this stuff to the TMD... + byte[] contentSection = new byte[contents.Length * 36]; + + for (int h = 0; h < contents.Length; h++) + { + for (int i = 0; i < contents[h].ContentID.Length; i++) + { + contentSection[(h * 36) + i] = contents[h].ContentID[i]; + } + + for (int j = 0; j < contents[h].Index.Length; j++) + { + contentSection[(h * 36) + (contents[h].ContentID.Length + j)] = contents[h].Index[j]; + } + + for (int k = 0; k < contents[h].Type.Length; k++) + { + contentSection[(h * 36) + (contents[h].ContentID.Length + contents[h].Index.Length + k)] = contents[h].Type[k]; + } + + for (int l = 0; l < contents[h].Size.Length; l++) + { + contentSection[(h * 36) + (contents[h].ContentID.Length + contents[h].Index.Length + contents[h].Type.Length + l)] = contents[h].Type[l]; + } + + for (int m = 0; m < contents[h].SHAHash.Length; m++) + { + contentSection[(h * 36) + (contents[h].ContentID.Length + contents[h].Index.Length + contents[h].Type.Length + contents[h].Size.Length + m)] = contents[h].Type[m]; + } + } + + for (int n = 0; n < contentSection.Length; n++) + { + tmd[0x1E4 + n] = contentSection[n]; + } + + // Fakesign the TMD again... + tmd = ZeroSignature(tmd); + tmd = TruchaSign(tmd); + + FileStream testtmd = new FileStream(fileinfo[0] + fileinfo[1], FileMode.Open); + testtmd.Write(tmd, 0, tmd.Length); + testtmd.Close(); + } + } + + // Pad Byte[] to specific alignment... + private byte[] AlignByteArray(byte[] content, int alignto) + { + long thelength = content.Length - 1; + long remainder = 1; + + while (remainder != 0) + { + thelength += 1; + remainder = thelength % alignto; + } + + Array.Resize(ref content, (int)thelength); + return content; + } + + private void button17_Click(object sender, EventArgs e) + { + // Move groupbox to display title modder... + contentModBox.Location = new Point(278, 12); + contentModBox.Visible = true; + } + + private void button13_Click(object sender, EventArgs e) + { + // Share/Unshare Contents in the list... + if (contentsEdit.SelectedIndex < 0) + return; + + if (contentsEdit.Items[contentsEdit.SelectedIndex].ToString().Contains(" [S]")) + contentsEdit.Items[contentsEdit.SelectedIndex] = contentsEdit.SelectedItem.ToString().Replace(" [S]", ""); + else + { + if (contentsEdit.Items[contentsEdit.SelectedIndex].ToString().Contains(" [BOOT]")) + contentsEdit.Items[contentsEdit.SelectedIndex] = contentsEdit.SelectedItem.ToString().Replace(" [BOOT]", "") + " [S] [BOOT]"; + else + contentsEdit.Items[contentsEdit.SelectedIndex] = contentsEdit.SelectedItem.ToString() + " [S]"; + } + } + + private bool IsWin7() + { + return (Environment.OSVersion.VersionString.Contains("6.1") == true); + } } } diff --git a/NUS Downloader/Form1.resx b/NUS Downloader/Form1.resx index c296b72..d63ad55 100644 --- a/NUS Downloader/Form1.resx +++ b/NUS Downloader/Form1.resx @@ -132,6 +132,212 @@ * Always have a brick restoration method in place (BootMii boot2). + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29m + dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAKgSURBVDhPlZPdT5JhGMY58MS/oDMO+zDXsuWSrVSm + K3WLrIOYbs5lztTFEDN0ipEmJMJ4FSHfXhUUSJyggMo08QPzIz8IdX6jZZnYah27JbarF1oumrZ58Jw9 + 92/X/XuuhwGAcZIj6diMFbUu+Quo+b2cupmk/w63ur6HNTm/MNUOH0vetcWpMK2rK01rkFtWUWVaxF3J + 6N6xAN3wt3Cyf3eyvvczlPYtyMxeKDq9IB2bqLWuoqjRjcSSvv1jARqHT9g8sAPz+C6Mw9toev0B9d3r + EBsWIGlbwG2xE7H5XXFHAkjnxmlpz8RPAz1I9W1B2r6M8tZ5PKJm8VTvQZZ8BFdzzZqAuxBAIDbVv2tV + dQ/8qJwoRoXNAZVtDUTXKvgN08ghxiBsnEKKwI4r6XpOCEA79DWsoc/n1Tl3EDiStpYD8agQ6YTtgP/i + Lcq0s0iTDOKhagxx99sQlUqxQgCqXh+TsH/8vfPINpSdaxCo2/e5NebipBLjQUaVE3mEC/dqBhF9pwmR + iXXMEEB153uWzLwB/dAnPO9YgbpnHaU6DzJlGr9okIfr/HrcemwDO8uI8wmE9yxbHhYCEOlXOE8MS8Fn + Cggrotx4ZpyDkJpCtqIcpf35iE5V+CPYSuuZ+OrwP+U7lMgjPXZp+yKUlhUIyGnk1o4js2YED4g3yKat + 38gV+y9xpOf+bW0QkKd2FwqbPVBYlmnLsxC1uIO2+ZqJ4N5JPAuibpLCoyrPoPssELx0o8IwH4xcpp1B + Bm07WWBFfLYJMVwtLqY0TEYmEIex/wYx0qrHUK6fowvyDjkKF5ILbYjJeKW5zNVxolJJ1oVkNTMigQgK + OzJBStkAbdqFLBm9J88aKAj7JL+TwS6wn7qWZwYr07QXzdXFnWQ4cPcXyBm6QmRn5bAAAAAASUVORK5C + YII= + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29m + dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAFHSURBVDhPY/j//z8DJRiv5uJj6ScLDqUewWcBTgOK + jqZd7DhV97/1RPX/jB2x53EZgmFAyfEMtqIjabfbT9X8X35t3v+lV+b8rz1Q/D9uTejNyGUBbOgGYRhQ + eDh1d97+pO/Nxyr/L74y6//CyzP+tx2u/R+xxP978DyvHQQNgClI3RL9f/6laf/nXJjyP3VN7P+gOZ4W + 2LyBMwzi14b9n3V+8v8Z5yb8D5jlAdSLPbZwGgD07/+pZ/r+Tz7d/d9rkhPpBgD9+3/iqa7/fSfb/zt3 + 2ZBugM9U1/89x1v/dx5r+m/bZEa6AW699v/bjzb+bz1c99+sypB0A+xbLf83Har933Cw6r9BkTbpBljW + Gv+vO1D5v2Z/2X/tbDXSDTAu1f1ftbfkf8Xeov9qqUqkG6Cbp/FfM1P1v2qK4n+lBDnSDSA2iwMA5nrY + LX6N1KIAAAAASUVORK5CYII= + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29m + dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAMxSURBVDhPdZPrT5tlGIf7RRP/gSXzwz4ZQ2RTZ4wn + sgxxg85x0JhsiZ/YIprpEozRsMTNRcNGkbKSShejI1vMioSJo5WDFApjFmgZDFZoSylMOiinUvrCLFAK + 7+Xd6jRx803uPHmf3L/rPj4aQPMoG2vKSx+zHr7ua3yzw/uzdu//+T0k9rfk7xBxqc96OLroucTcgA7P + Twfvj9Yf0I/8mPXkf0H/AMZbCx4TcdFYU24gNGRka2MWdW2a1cnrrM9YCd74hDtX908N/7Dv5NCVjCce + gFIAf2tBtr85/+ZU7xesL9+RqzVQFRKRW4w3vMW6r4J4sJ5Vr4mA9Ti3a151Dn73Ul5SmxRf9jfnqRG/ + mVjYTeRuJ3dd4vhbOd5fP2ay7QQxXxWxobNsBGrYnK5nqb8UT+3b3DK9UKuRehPqRpB4eJCI20h4oJRQ + VzEByxGc1RlsLt2GmB8W21B/N4GnBGbMJCZKcRmfS2ikWcr2xj24P8rmTCMrQ+eJDZ8VO8WMJTd1x0IL + BL9H9Z4iZs9koXkfK54qnIY9isbbeEjZXhPAH+MC8UDYzrqnnJjrBAtNuSTGK2FCT9yhZanlFea7tMy3 + F6AM6+mrTFc0noYcZSsF8MGqpBt1CsRGImBkpfsI8ZtaFHsGSz05LNgPMtucxawlh+V+Hb1fpykame9f + ACkBxQWRTqn3F5hrQPWfJtqnZdHxBg31+Xx68SjvXyig8HwmNVfepafsaUUzUpcVVxNzMrlJid4t0Vth + /po06ipq8CLhXi11tYf48toxmkZNDM/aMLR/yFHDHj4r2bWlcZsz2yfsJayFugQSgGWBhMwwfVkA1Sx2 + Z3NMdwDLSBUWr1FGD/qOIgwdH/DayZ3bGtmsx2WzimQ5AlM3zrC13Ce9GJAs6lCnqpmzvU7u53tpGb2U + Ej/4rG4TL3+089+HJJu1Y+DbF8/JckRDjq+kqQKJOlhxV7C/eBcVtuOU2QpT+rK2wlQGAlh/6DH1f/N8 + uqvqWbPzwm41PGjgXmsxZ07v5h19GpW290hGTp7JfwHoH/mUkzveW/FMdk95Wqd02uE491SeOOvEVpNp + /33qkn5/AiEv99RPttotAAAAAElFTkSuQmCC + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29m + dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAFESURBVDhPY/j//z8DJRin5pLjGf+Lj6X/LziU+j93 + XxLQDuwW4TSg6Gja/9U3Fv1feX3B/4wdsaQbkH8g+f/ya/P+L70y53/ypkjSDcjaHf9/8ZVZ/xdenvE/ + dlUw6Qakbon+P//StP9zLkz5H77Ij3QD4teG/Z91fvL/Gecm/A+Y5UG6AZHLAv5PPdP3f/Lp7v9ek5xI + NyB4ntf/iae6/vedbP/v3GVDugE+U13/9xxv/d95rOm/bZMZaQZ4T3G2CJ3l97/9aOP/1sN1/82qDIk3 + AOjfHa49tt9z1qb+bzpU+7/hYNX/gEne/3Vy1b9rZKjsRk+RGCkR6F82u2aLm+EzA//XHaj8X7O/7L93 + r9t/9TTl28pJCmwEDYApMC7VPe/b7/Hfq8f1v1qq0kWS8wJIg2am6hHVFMWT+HIrRVkZZDAAFl/VUw/P + Z/oAAAAASUVORK5CYII= + + + + + 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 diff --git a/NUS Downloader/NUS Downloader.csproj b/NUS Downloader/NUS Downloader.csproj index 9302500..da16609 100644 --- a/NUS Downloader/NUS Downloader.csproj +++ b/NUS Downloader/NUS Downloader.csproj @@ -102,6 +102,7 @@ + diff --git a/NUS Downloader/Properties/Resources.Designer.cs b/NUS Downloader/Properties/Resources.Designer.cs index 0dca0dd..9c1bdc8 100644 --- a/NUS Downloader/Properties/Resources.Designer.cs +++ b/NUS Downloader/Properties/Resources.Designer.cs @@ -130,6 +130,13 @@ namespace NUS_Downloader.Properties { } } + internal static System.Drawing.Bitmap link { + get { + object obj = ResourceManager.GetObject("link", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + internal static System.Drawing.Bitmap package_add { get { object obj = ResourceManager.GetObject("package_add", resourceCulture); diff --git a/NUS Downloader/Properties/Resources.resx b/NUS Downloader/Properties/Resources.resx index 52cff2a..05f969e 100644 --- a/NUS Downloader/Properties/Resources.resx +++ b/NUS Downloader/Properties/Resources.resx @@ -157,4 +157,7 @@ ..\Resources\bug_add.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + ..\Resources\link.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + \ No newline at end of file diff --git a/NUS Downloader/Resources/link.png b/NUS Downloader/Resources/link.png new file mode 100644 index 0000000000000000000000000000000000000000..25eacb7c2524142262d68bf729c5e2b61adfd6d4 GIT binary patch literal 343 zcmV-d0jU0oP)$`dXYaZs9=SbAto%g@>T~?_bH&lTUn@`uo|1bXE{eSR(AO)ESb=V4`uk}mK|39Px&03WLbv~pzk+s7D@lK^ zn+aB+sp)&Y_x-B3>;6ywU--WQNUr<8>TU0P-|L#1U&;A)67w(+> pDf@fM7q9#F25QXo3rUI;002ro52U44e~JJA002ovPDHLkV1l;_q@Mr) literal 0 HcmV?d00001