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 0000000..25eacb7
Binary files /dev/null and b/NUS Downloader/Resources/link.png differ