nusdownloader-fork/NUS Downloader/Form1.cs

3229 lines
128 KiB
C#
Raw Normal View History

2009-06-11 03:16:49 +02:00
using System;
using System.Windows.Forms;
using System.IO;
using System.Net;
using System.Security.Cryptography;
using System.Xml;
using System.Drawing;
using System.Text.RegularExpressions;
using System.ComponentModel;
using System.Threading;
using System.Text;
using wyDay.Controls;
2009-06-11 03:16:49 +02:00
using System.Diagnostics;
2009-06-11 03:16:49 +02:00
namespace NUS_Downloader
{
public partial class Form1 : Form
{
const string NUSURL = "http://nus.cdn.shop.wii.com/ccs/download/";
const string DSiNUSURL = "http://nus.cdn.t.shop.nintendowifi.net/ccs/download/";
// TODO: Always remember to change version!
string version = "v1.5a (Beta)";
2009-06-11 03:16:49 +02:00
WebClient generalWC = new WebClient();
static RijndaelManaged rijndaelCipher;
static bool dsidecrypt = false;
// Cross-thread Windows Formsing
delegate void AddToolStripItemToStripCallback(int type, ToolStripMenuItem additionitem, XmlAttributeCollection attributes); //TODO
delegate void WriteStatusCallback(string Update);
// Images do not compare unless globalized...
Image green = Properties.Resources.bullet_green;
Image orange = Properties.Resources.bullet_orange;
Image redorb = Properties.Resources.bullet_red;
Image redgreen = Properties.Resources.bullet_redgreen;
Image redorange = Properties.Resources.bullet_redorange;
// Certs storage
byte[] cert_CA = new byte[0x400];
byte[] cert_CACP = new byte[0x300];
byte[] cert_CAXS = new byte[0x300];
byte[] cert_CA_sha1 = new byte[20] {0x5B, 0x7D, 0x3E, 0xE2, 0x87, 0x06, 0xAD, 0x8D, 0xA2, 0xCB, 0xD5, 0xA6, 0xB7, 0x5C, 0x15, 0xD0, 0xF9, 0xB6, 0xF3, 0x18};
byte[] cert_CACP_sha1 = new byte[20] {0x68, 0x24, 0xD6, 0xDA, 0x4C, 0x25, 0x18, 0x4F, 0x0D, 0x6D, 0xAF, 0x6E, 0xDB, 0x9C, 0x0F, 0xC5, 0x75, 0x22, 0xA4, 0x1C};
byte[] cert_CAXS_sha1 = new byte[20] {0x09, 0x78, 0x70, 0x45, 0x03, 0x71, 0x21, 0x47, 0x78, 0x24, 0xBC, 0x6A, 0x3E, 0x5E, 0x07, 0x61, 0x56, 0x57, 0x3F, 0x8A};
byte[] cert_total_sha1 = new byte[20] {0xAC, 0xE0, 0xF1, 0x5D, 0x2A, 0x85, 0x1C, 0x38, 0x3F, 0xE4, 0x65, 0x7A, 0xFC, 0x38, 0x40, 0xD6, 0xFF, 0xE3, 0x0A, 0xD0};
string WAD_Saveas_Filename;
2009-08-20 19:41:08 +02:00
// TODO: OOP scripting
string script_filename;
bool script_mode = false;
string[] nusentries;
2009-08-20 19:41:08 +02:00
// Proxy stuff...
string proxy_url;
string proxy_usr;
string proxy_pwd;
2010-06-29 18:06:15 +02:00
// Database thread
private BackgroundWorker fds;
// Common Key hash
byte[] wii_commonkey_sha1 = new byte[20] { 0xEB, 0xEA, 0xE6, 0xD2, 0x76, 0x2D, 0x4D, 0x3E, 0xA1, 0x60, 0xA6, 0xD8, 0x32, 0x7F, 0xAC, 0x9A, 0x25, 0xF8, 0x06, 0x2B };
byte[] wii_commonkey_sha1_asstring = new byte[20] { 0x56, 0xdd, 0x4e, 0xb3, 0x59, 0x75, 0xc2, 0xfd, 0x5a, 0xe8, 0xba, 0x8c, 0x7d, 0x89, 0x9a, 0xc5, 0xe6, 0x17, 0x54, 0x19 };
/*
2009-06-11 03:16:49 +02:00
public struct WADHeader
{
public int HeaderSize;
public int WadType;
public int CertChainSize;
public int Reserved;
public int TicketSize;
public int TMDSize;
public int DataSize;
public int FooterSize;
};*/
2009-06-11 03:16:49 +02:00
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
2009-06-11 03:16:49 +02:00
public Form1()
{
InitializeComponent();
KoreaMassUpdate.DropDownItemClicked += new ToolStripItemClickedEventHandler(upditem_itemclicked);
NTSCMassUpdate.DropDownItemClicked += new ToolStripItemClickedEventHandler(upditem_itemclicked);
PALMassUpdate.DropDownItemClicked += new ToolStripItemClickedEventHandler(upditem_itemclicked);
2010-06-29 18:06:15 +02:00
this.fds = new BackgroundWorker();
this.fds.DoWork += new DoWorkEventHandler(DoAllDatabaseyStuff);
this.fds.RunWorkerCompleted += new RunWorkerCompletedEventHandler(DoAllDatabaseyStuff_Completed);
this.fds.ProgressChanged += new ProgressChangedEventHandler(DoAllDatabaseyStuff_ProgressChanged);
this.fds.WorkerReportsProgress = true;
2009-06-11 03:16:49 +02:00
BootChecks();
}
// CLI Mode
2009-06-11 03:16:49 +02:00
public Form1(string[] args)
{
InitializeComponent();
Application.DoEvents();
2009-06-11 03:16:49 +02:00
2009-08-20 19:41:08 +02:00
BootChecks();
// Fix proxy entry.
if (!(String.IsNullOrEmpty(proxy_url)))
while (String.IsNullOrEmpty(proxy_pwd))
Thread.Sleep(1000);
if ((args.Length == 1) && (File.Exists(args[0])))
{
script_filename = args[0];
BackgroundWorker scripter = new BackgroundWorker();
scripter.DoWork += new DoWorkEventHandler(RunScript);
scripter.RunWorkerAsync();
}
2009-08-20 19:41:08 +02:00
/* CLI MODE DEPRECATED...
2009-06-11 03:16:49 +02:00
// Vars
bool startnow = false;
2009-07-04 20:32:52 +02:00
bool endafter = false;
2009-06-11 03:16:49 +02:00
// Fix'd
localuse.Checked = false;
// Switch through arguments
for (int i = 0; i < args.Length; i++)
{
switch (args[i])
{
case "-t":
if (args[i + 1].Length == 16)
titleidbox.Text = args[i + 1];
else
{
WriteStatus("Title ID: Your Doing It Wrong (c)");
WriteStatus("ex: -t 0000000100000002");
}
break;
case "-v":
titleversion.Text = args[i + 1];
break;
case "-s":
startnow = true;
break;
2009-07-04 20:32:52 +02:00
case "-close":
endafter = true;
break;
2009-06-11 03:16:49 +02:00
case "-d":
decryptbox.Checked = true;
break;
case "-ticket":
ignoreticket.Checked = true;
break;
case "-local":
localuse.Checked = true;
break;
case "-p":
packbox.Checked = true;
wadnamebox.Text = args[i + 1];
break;
case "-dsi":
radioButton2.Checked = true;
break;
default:
break;
}
}
// Start doing stuff...
if ((startnow) && (titleidbox.Text.Length != 0))
{
// Prevent mass deletion
if ((titleidbox.Text == "") && (titleversion.Text == ""))
{
WriteStatus("Please enter SOME info...");
return;
}
else
{
if (!statusbox.Lines[0].StartsWith(" ---"))
statusbox.Text = " --- " + titleidbox.Text + " ---";
}
2009-06-11 03:16:49 +02:00
// Running Downloads in background so no form freezing
NUSDownloader.RunWorkerAsync();
}
2009-07-04 20:32:52 +02:00
// Close if specified
while (NUSDownloader.IsBusy)
{
Thread.Sleep(1000);
2009-07-04 20:32:52 +02:00
}
if ((NUSDownloader.IsBusy == false) && (endafter == true))
{
Application.Exit();
} */
2009-06-11 03:16:49 +02:00
}
private void Form1_Load(object sender, EventArgs e)
{
this.Text = "NUSD - " + version + " - WB3000";
2009-06-19 03:57:52 +02:00
this.Size = this.MinimumSize;
2009-06-11 03:16:49 +02:00
}
/// <summary>
/// Checks certain file existances, etc.
/// </summary>
/// <returns></returns>
2009-08-20 19:41:08 +02:00
private void BootChecks()
2009-06-11 03:16:49 +02:00
{
// Directory stuff
string currentdir = Directory.GetCurrentDirectory();
if (currentdir.EndsWith(Convert.ToString(Path.DirectorySeparatorChar.ToString())) == false)
currentdir += Path.DirectorySeparatorChar.ToString();
2009-06-11 03:16:49 +02:00
// Check for Wii common key bin file...
if (File.Exists(currentdir + "key.bin") == false)
{
WriteStatus("Common Key (key.bin) missing! Decryption disabled!");
WriteStatus(" - To enable it, why not try choosing \"Retrieve Common Key\" from the Extras menu?");
2009-06-11 03:16:49 +02:00
decryptbox.Visible = false;
}
else
{
WriteStatus("Common Key detected.");
if ((Convert.ToBase64String(ComputeSHA(LoadCommonKey("key.bin")))) != (Convert.ToBase64String(wii_commonkey_sha1)))
{ // Hmm, seems to be a bad hash
// Let's check if it matches the hex string version...
if ((Convert.ToBase64String(ComputeSHA(LoadCommonKey("key.bin")))) != (Convert.ToBase64String(wii_commonkey_sha1_asstring)))
WriteStatus(" - (PS: Your common key isn't hashing right!)");
else
{
WriteStatus(" - Converting your key.bin file to the correct format...");
// Directory stuff
string keydir = Directory.GetCurrentDirectory();
if (!(keydir.EndsWith(Path.DirectorySeparatorChar.ToString())) || !(keydir.EndsWith(Path.AltDirectorySeparatorChar.ToString())))
keydir += Path.DirectorySeparatorChar.ToString();
TextReader ckreader = new StreamReader(currentdir + "key.bin");
String ckashex = ckreader.ReadLine();
ckreader.Close();
File.Delete(currentdir + "key.bin");
WriteCommonKey("key.bin", HexStringToByteArray(ckashex));
}
}
2009-06-11 03:16:49 +02:00
}
2009-06-13 18:04:54 +02:00
// Check for Wii KOR common key bin file...
2009-08-20 19:41:08 +02:00
if (File.Exists(currentdir + "kkey.bin") == true)
2009-06-13 18:04:54 +02:00
{
WriteStatus("Korean Common Key detected.");
}
2009-06-11 03:16:49 +02:00
// Check for DSi common key bin file...
2009-08-20 19:41:08 +02:00
if (File.Exists(currentdir + "dsikey.bin") == true)
2009-06-11 03:16:49 +02:00
{
WriteStatus("DSi Common Key detected.");
dsidecrypt = true;
}
// Check for database.xml
if (File.Exists(currentdir + "database.xml") == false)
{
WriteStatus("Database.xml not found. Title database not usable!");
databaseButton.Visible = false;
Extrasbtn.Size = new System.Drawing.Size(134, 20);
2010-06-29 18:06:15 +02:00
updateDatabaseToolStripMenuItem.Text = "Download Database";
2009-06-11 03:16:49 +02:00
}
else
{
string version = GetDatabaseVersion("database.xml");
2009-06-11 03:16:49 +02:00
WriteStatus("Database.xml detected.");
WriteStatus(" - Version: " + version);
2010-06-29 18:06:15 +02:00
databaseButton.Enabled = false;
databaseButton.Text = "DB Loading";
2009-06-11 03:16:49 +02:00
// Load it up...
2010-06-29 18:06:15 +02:00
this.fds.RunWorkerAsync();
2009-06-11 03:16:49 +02:00
}
2009-08-20 19:41:08 +02:00
// Check for Proxy Settings file...
if (File.Exists(currentdir + "proxy.txt") == true)
{
WriteStatus("Proxy settings detected.");
string[] proxy_file = File.ReadAllLines(currentdir + "proxy.txt");
proxy_url = proxy_file[0];
if (proxy_file.Length > 1)
{
proxy_usr = proxy_file[1];
SetAllEnabled(false);
ProxyVerifyBox.Visible = true; ProxyVerifyBox.Enabled = true;
ProxyPwdBox.Enabled = true; SaveProxyBtn.Enabled = true;
ProxyVerifyBox.Select();
}
}
}
2010-06-29 18:06:15 +02:00
private void DoAllDatabaseyStuff(object sender, System.ComponentModel.DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
ClearDatabaseStrip();
FillDatabaseStrip(worker);
LoadRegionCodes();
ShowInnerToolTips(false);
}
private void DoAllDatabaseyStuff_Completed(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
this.databaseButton.Enabled = true;
this.databaseButton.Text = "Database...";
}
private void DoAllDatabaseyStuff_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
{
this.databaseButton.Text = "DB: " + e.ProgressPercentage + "%";
}
2009-08-20 19:41:08 +02:00
private void SetAllEnabled(bool enabled)
{
for (int a = 0; a < this.Controls.Count; a++)
{
try
{
this.Controls[a].Enabled = enabled;
}
catch
{
// ...
}
}
2009-06-11 03:16:49 +02:00
}
/// <summary>
/// Gets the database version.
/// </summary>
/// <param name="file">The database file.</param>
/// <returns></returns>
private string GetDatabaseVersion(string file)
{
// Read version of Database.xml
XmlDocument xDoc = new XmlDocument();
if (file.Contains("<"))
xDoc.LoadXml(file);
else
2010-06-29 18:06:15 +02:00
{
if (File.Exists(file))
{
xDoc.Load(file);
}
else
{
return "None Found";
}
}
XmlNodeList DatabaseList = xDoc.GetElementsByTagName("database");
XmlAttributeCollection Attributes = DatabaseList[0].Attributes;
return Attributes[0].Value;
}
2009-06-11 03:16:49 +02:00
private void button1_Click(object sender, EventArgs e)
{
// Show extras menu
extrasStrip.Show(Extrasbtn, 2, 2);
}
/// <summary>
/// Loads the title info from TMD.
/// </summary>
private void LoadTitleFromTMD()
2009-06-11 03:16:49 +02:00
{
// Show dialog for opening TMD file...
OpenFileDialog opentmd = new OpenFileDialog();
opentmd.Filter = "TMD Files|tmd";
opentmd.Title = "Open TMD";
if (opentmd.ShowDialog() != DialogResult.Cancel)
{
// Read the tmd as a stream...
byte[] tmd = FileLocationToByteArray(opentmd.FileName);
2009-06-11 03:16:49 +02:00
WriteStatus("TMD Loaded (" + tmd.Length + " bytes)");
// Read ID...
for (int x = 396; x < 404; x++)
{
titleidbox.Text += MakeProperLength(ConvertToHex(Convert.ToString(tmd[x])));
}
WriteStatus("Title ID: " + titleidbox.Text);
// Show TitleID Type/likelyhood of NUS existance...
ReadIDType(titleidbox.Text);
// Read Title Version...
string tmdversion = "";
for (int x = 476; x < 478; x++)
{
tmdversion += MakeProperLength(ConvertToHex(Convert.ToString(tmd[x])));
}
titleversion.Text = Convert.ToString(int.Parse(tmdversion, System.Globalization.NumberStyles.HexNumber));
// Read System Version (Needed IOS)
string sysversion = IOSNeededFromTMD(tmd);
2009-06-11 03:16:49 +02:00
if (sysversion != "0")
WriteStatus("Requires: IOS" + sysversion);
// Read Content #...
int nbr_cont = ContentCount(tmd);
/*string contentstrnum = "";
2009-06-11 03:16:49 +02:00
for (int x = 478; x < 480; x++)
{
contentstrnum += TrimLeadingZeros(Convert.ToString(tmd[x]));
}*/
WriteStatus("Content Count: " + nbr_cont);
2009-06-11 03:16:49 +02:00
string[] tmdcontents = GetContentNames(tmd, nbr_cont);
string[] tmdsizes = GetContentSizes(tmd, nbr_cont);
byte[] tmdhashes = GetContentHashes(tmd, nbr_cont);
byte[] tmdindices = GetContentIndices(tmd, nbr_cont);
int[] tmdtypes = GetContentTypes(tmd, nbr_cont);
2009-06-11 03:16:49 +02:00
// Loop through each content and display name, hash, index
for (int i = 0; i < nbr_cont; i++)
2009-06-11 03:16:49 +02:00
{
WriteStatus(" Content " + (i + 1) + ": " + tmdcontents[i] + " (" + Convert.ToString(int.Parse(tmdsizes[i], System.Globalization.NumberStyles.HexNumber)) + " bytes)");
byte[] hash = new byte[20];
for (int x = 0; x < 20; x++)
{
hash[x] = tmdhashes[(i * 20) + x];
2009-06-11 03:16:49 +02:00
}
WriteStatus(" - Hash: " + DisplayBytes(hash, "").Substring(0, 8) + "...");
2009-06-11 03:16:49 +02:00
WriteStatus(" - Index: " + tmdindices[i]);
WriteStatus(" - Shared: " + (tmdtypes[i] == 0x8001));
2009-06-11 03:16:49 +02:00
}
}
}
/// <summary>
/// Returns needed IOS from TMD.
/// </summary>
/// <param name="tmd">The TMD.</param>
/// <returns></returns>
private string IOSNeededFromTMD(byte[] tmd)
{
string sysversion = "";
for (int i = 0; i < 8; i++)
sysversion += MakeProperLength(ConvertToHex(Convert.ToString(tmd[0x184 + i])));
sysversion = Convert.ToString(int.Parse(sysversion.Substring(14, 2), System.Globalization.NumberStyles.HexNumber));
return sysversion;
}
/// <summary>
/// Returns content count of TMD
/// </summary>
/// <param name="tmd">The TMD.</param>
/// <returns>int Count of Contents</returns>
private int ContentCount(byte[] tmd)
{
// nbr_cont (0xDE) len=0x02
int nbr_cont = 0;
nbr_cont = (tmd[0x1DE] * 256) + tmd[0x1DF];
return nbr_cont;
}
/// <summary>
/// Gets a TMD Boot Index
/// </summary>
/// <param name="tmd">The TMD.</param>
/// <returns>int BootIndex</returns>
2009-07-23 00:22:04 +02:00
private int GetBootIndex(byte[] tmd)
{
// nbr_cont (0xE0) len=0x02
int bootidx = 0;
bootidx = (tmd[0x1E0] * 256) + tmd[0x1E1];
return bootidx;
}
/// <summary>
/// Sets the Boot index of a TMD.
/// </summary>
/// <param name="tmd">The TMD.</param>
/// <param name="bootindex">Index to set it too</param>
/// <returns>Edited TMD</returns>
2009-07-23 00:22:04 +02:00
private byte[] SetBootIndex(byte[] tmd, int bootindex)
{
// nbr_cont (0xE0) len=0x02
byte[] bootbytes = NewIntegertoByteArray(bootindex, 2);
tmd[0x1E0] = bootbytes[0];
tmd[0x1E1] = bootbytes[1];
return tmd;
}
/// <summary>
/// Writes the status to the statusbox.
/// </summary>
/// <param name="Update">The update.</param>
public void WriteStatus(string Update)
2009-06-11 03:16:49 +02:00
{
// Check if thread-safe
if (this.InvokeRequired)
{
Debug.WriteLine("InvokeRequired...");
WriteStatusCallback wsc = new WriteStatusCallback(WriteStatus);
this.Invoke(wsc, new object[] { Update });
return;
}
// Small function for writing text to the statusbox...
2009-06-11 03:16:49 +02:00
if (statusbox.Text == "")
statusbox.Text = Update;
else
statusbox.Text += "\r\n" + Update;
// Scroll to end of text box.
statusbox.SelectionStart = statusbox.TextLength;
statusbox.ScrollToCaret();
}
/// <summary>
/// Reads data from a stream until the end is reached. The
/// data is returned as a byte array. An IOException is
/// thrown if any of the underlying IO calls fail.
/// </summary>
/// <param name="stream">The stream to read data from</param>
/// <param name="initialLength">The initial buffer length</param>
public static byte[] ReadFully(Stream stream, int initialLength)
{
// If we've been passed an unhelpful initial length, just use 32K.
2009-06-11 03:16:49 +02:00
if (initialLength < 1)
{
initialLength = 32768;
}
byte[] buffer = new byte[initialLength];
int read = 0;
int chunk;
while ((chunk = stream.Read(buffer, read, buffer.Length - read)) > 0)
{
read += chunk;
// If we've reached the end of our buffer, check to see if there's
// any more information
if (read == buffer.Length)
{
int nextByte = stream.ReadByte();
// End of stream? If so, we're done
if (nextByte == -1)
{
return buffer;
}
// Nope. Resize the buffer, put in the byte we've just
// read, and continue
byte[] newBuffer = new byte[buffer.Length * 2];
Array.Copy(buffer, newBuffer, buffer.Length);
newBuffer[read] = (byte)nextByte;
buffer = newBuffer;
read++;
}
}
// Buffer is now too big. Shrink it.
byte[] ret = new byte[read];
Array.Copy(buffer, ret, read);
return ret;
}
/// <summary>
/// Makes a hex string the correct length.
/// </summary>
/// <param name="hex">The hex.</param>
/// <returns></returns>
2009-06-11 03:16:49 +02:00
private string MakeProperLength(string hex)
{
// If hex is like, 'A', makes it '0A', etc.
if (hex.Length == 1)
hex = "0" + hex;
return hex;
}
/// <summary>
/// Converts to hex.
/// </summary>
/// <param name="decval">The string.</param>
/// <returns>hex string</returns>
2009-06-11 03:16:49 +02:00
private string ConvertToHex(string decval)
{
// Convert text string to unsigned integer
int uiDecimal = System.Convert.ToInt32(decval);
return String.Format("{0:x2}", uiDecimal);
2009-06-11 03:16:49 +02:00
}
/// <summary>
/// Reads the type of the Title ID.
/// </summary>
/// <param name="ttlid">The TitleID.</param>
2009-06-11 03:16:49 +02:00
private void ReadIDType(string ttlid)
{
/* Wiibrew TitleID Info...
# 3 00000001: Essential system titles
# 4 00010000 and 00010004 : Disc-based games
# 5 00010001: Downloaded channels
* 5.1 000010001-Cxxx : Commodore 64 Games
* 5.2 000010001-Exxx : NeoGeo Games
* 5.3 000010001-Fxxx : NES Games
* 5.4 000010001-Hxxx : Channels
* 5.5 000010001-Jxxx : SNES Games
* 5.6 000010001-Nxxx : Nintendo 64 Games
* 5.7 000010001-Wxxx : WiiWare
# 6 00010002: System channels
# 7 00010004: Game channels and games that use them
# 8 00010005: Downloaded Game Content
# 9 00010008: "Hidden" channels
*/
if (ttlid.Substring(0, 8) == "00000001")
WriteStatus("ID Type: System Title. BE CAREFUL!");
else if ((ttlid.Substring(0, 8) == "00010000") || (ttlid.Substring(0, 8) == "00010004"))
WriteStatus("ID Type: Disc-Based Game. Unlikely NUS Content!");
else if (ttlid.Substring(0, 8) == "00010001")
WriteStatus("ID Type: Downloaded Channel. Possible NUS Content.");
else if (ttlid.Substring(0, 8) == "00010002")
WriteStatus("ID Type: System Channel. BE CAREFUL!");
else if (ttlid.Substring(0, 8) == "00010004")
WriteStatus("ID Type: Game Channel. Unlikely NUS Content!");
else if (ttlid.Substring(0, 8) == "00010005")
WriteStatus("ID Type: Downloaded Game Content. Unlikely NUS Content!");
else if (ttlid.Substring(0, 8) == "00010008")
WriteStatus("ID Type: 'Hidden' Channel. Unlikely NUS Content!");
else
WriteStatus("ID Type: Unknown. Unlikely NUS Content!");
}
/// <summary>
/// Trims the leading zeros of a string.
/// </summary>
/// <param name="num">The string with leading zeros.</param>
/// <returns>no-0-string</returns>
2009-06-11 03:16:49 +02:00
private string TrimLeadingZeros(string num)
{
int startindex = 0;
for (int i = 0; i < num.Length; i++)
{
if ((num[i] == 0) || (num[i] == '0'))
startindex += 1;
else
break;
}
return num.Substring(startindex, (num.Length - startindex));
}
/// <summary>
/// Gets the content names in a TMD.
/// </summary>
/// <param name="tmdfile">The TMD.</param>
/// <param name="length">The TMD contentcount.</param>
/// <returns>Array of Content names</returns>
2009-06-11 03:16:49 +02:00
private string[] GetContentNames(byte[] tmdfile, int length)
{
string[] contentnames = new string[length];
int startoffset = 484;
for (int i = 0; i < length; i++)
{
contentnames[i] = MakeProperLength(ConvertToHex(Convert.ToString(tmdfile[startoffset]))) +
MakeProperLength(ConvertToHex(Convert.ToString(tmdfile[startoffset + 1]))) +
MakeProperLength(ConvertToHex(Convert.ToString(tmdfile[startoffset + 2]))) +
MakeProperLength(ConvertToHex(Convert.ToString(tmdfile[startoffset + 3])));
startoffset += 36;
}
return contentnames;
}
/// <summary>
/// Gets the content sizes in a TMD.
/// </summary>
/// <param name="tmdfile">The TMD.</param>
/// <param name="length">The TMD contentcount.</param>
/// <returns></returns>
2009-06-11 03:16:49 +02:00
private string[] GetContentSizes(byte[] tmdfile, int length)
{
string[] contentsizes = new string[length];
int startoffset = 492;
for (int i = 0; i < length; i++)
{
contentsizes[i] = MakeProperLength(ConvertToHex(Convert.ToString(tmdfile[startoffset]))) +
MakeProperLength(ConvertToHex(Convert.ToString(tmdfile[startoffset + 1]))) +
MakeProperLength(ConvertToHex(Convert.ToString(tmdfile[startoffset + 2]))) +
MakeProperLength(ConvertToHex(Convert.ToString(tmdfile[startoffset + 3]))) +
MakeProperLength(ConvertToHex(Convert.ToString(tmdfile[startoffset + 4]))) +
MakeProperLength(ConvertToHex(Convert.ToString(tmdfile[startoffset + 5]))) +
MakeProperLength(ConvertToHex(Convert.ToString(tmdfile[startoffset + 6]))) +
MakeProperLength(ConvertToHex(Convert.ToString(tmdfile[startoffset + 7])));
contentsizes[i] = TrimLeadingZeros(contentsizes[i]);
/*contentsizes[i] = Convert.ToString(tmdfile[startoffset]) +
Convert.ToString(tmdfile[startoffset + 1]) +
Convert.ToString(tmdfile[startoffset + 2]) +
Convert.ToString(tmdfile[startoffset + 3]) +
Convert.ToString(tmdfile[startoffset + 4]) +
Convert.ToString(tmdfile[startoffset + 5]) +
Convert.ToString(tmdfile[startoffset + 6]) +
Convert.ToString(tmdfile[startoffset + 7]);
contentsizes[i] = TrimLeadingZeros(contentsizes[i]); */
startoffset += 36;
}
return contentsizes;
}
/// <summary>
/// Gets the content hashes.
/// </summary>
/// <param name="tmdfile">The tmd.</param>
/// <param name="length">The content_count.</param>
/// <returns></returns>
2009-06-11 03:16:49 +02:00
private byte[] GetContentHashes(byte[] tmdfile, int length)
{
byte[] contenthashes = new byte[length*20];
int startoffset = 500;
for (int i = 0; i < length; i++)
{
for (int x = 0; x < 20; x++)
{
contenthashes[(i * 20) + x] = tmdfile[startoffset + x];
}
startoffset += 36;
}
return contenthashes;
}
/// <summary>
/// Gets the content types.
/// </summary>
/// <param name="tmdfile">The tmd.</param>
/// <param name="length">The content_count.</param>
/// <returns></returns>
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;
}
return contenttypes;
}
/// <summary>
/// Gets the content indices.
/// </summary>
/// <param name="tmdfile">The tmd.</param>
/// <param name="length">The contentcount.</param>
/// <returns></returns>
2009-06-11 03:16:49 +02:00
private byte[] GetContentIndices(byte[] tmdfile, int length)
{
byte[] contentindices = new byte[length];
int startoffset = 0x1E9;
for (int i = 0; i < length; i++)
{
contentindices[i] = tmdfile[startoffset];
startoffset += 36;
}
return contentindices;
}
private void button3_Click(object sender, EventArgs e)
{
// Prevent mass deletion
if ((titleidbox.Text == "") && (titleversion.Text == ""))
{
WriteStatus("Please enter SOME info...");
return;
}
else if (!(script_mode))
{
try
{
if (!statusbox.Lines[0].StartsWith(" ---"))
statusbox.Text = " --- " + titleidbox.Text + " ---";
}
catch // No lines present...
{
statusbox.Text = " --- " + titleidbox.Text + " ---";
}
}
else
statusbox.Text += "\r\n --- " + titleidbox.Text + " ---";
2009-06-11 03:16:49 +02:00
// Handle SaveAs here so it shows up properly...
if (saveaswadbox.Checked)
{
SaveFileDialog wad_saveas = new SaveFileDialog();
wad_saveas.Title = "Save WAD File...";
wad_saveas.Filter = "WAD Files|*.wad|All Files|*.*";
wad_saveas.AddExtension = true;
DialogResult dres = wad_saveas.ShowDialog();
if (dres != DialogResult.Cancel)
WAD_Saveas_Filename = wad_saveas.FileName;
}
else
WAD_Saveas_Filename = "";
2009-06-11 03:16:49 +02:00
// Running Downloads in background so no form freezing
NUSDownloader.RunWorkerAsync();
}
private void NUSDownloader_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
// Preparations for Downloading
Control.CheckForIllegalCrossThreadCalls = false;
if (!(script_mode))
WriteStatus("Starting NUS Download. Please be patient!");
SetEnableforDownload(false);
2009-06-19 03:57:52 +02:00
downloadstartbtn.Text = "Starting NUS Download!";
2009-06-11 03:16:49 +02:00
// Current directory...
string currentdir = Directory.GetCurrentDirectory();
if (!(currentdir.EndsWith(Path.DirectorySeparatorChar.ToString())) || !(currentdir.EndsWith(Path.AltDirectorySeparatorChar.ToString())))
currentdir += Path.DirectorySeparatorChar.ToString();
2009-06-11 03:16:49 +02:00
// Prevent crossthread issues
string titleid = titleidbox.Text;
// Creates the directory
CreateTitleDirectory();
// Wii / DSi
bool wiimode = radioButton1.Checked;
// Set UserAgent to Wii value
generalWC.Headers.Add("User-Agent", "wii libnup/1.0");
2009-06-11 03:16:49 +02:00
2009-08-19 18:40:43 +02:00
// Proxy
2009-08-20 19:41:08 +02:00
if (!(String.IsNullOrEmpty(proxy_url)))
{
WebProxy customproxy = new WebProxy();
customproxy.Address = new Uri(proxy_url);
if (String.IsNullOrEmpty(proxy_usr))
customproxy.UseDefaultCredentials = true;
else
{
NetworkCredential cred = new NetworkCredential();
cred.UserName = proxy_usr;
2009-08-25 18:41:49 +02:00
if (!(String.IsNullOrEmpty(proxy_pwd)))
2009-08-20 19:41:08 +02:00
cred.Password = proxy_pwd;
customproxy.Credentials = cred;
}
generalWC.Proxy = customproxy;
WriteStatus("Custom proxy settings applied!");
}
else
{
generalWC.Proxy = WebRequest.GetSystemWebProxy();
generalWC.UseDefaultCredentials = true;
}
2009-08-19 18:40:43 +02:00
2009-06-11 03:16:49 +02:00
// Get placement directory early...
string titledirectory;
if (titleversion.Text == "")
titledirectory = currentdir + titleid + Path.DirectorySeparatorChar.ToString();
2009-06-11 03:16:49 +02:00
else
titledirectory = currentdir + titleid + "v" + titleversion.Text + Path.DirectorySeparatorChar.ToString();
2009-06-11 03:16:49 +02:00
2009-06-19 03:57:52 +02:00
downloadstartbtn.Text = "Prerequisites: (0/2)";
2009-06-11 03:16:49 +02:00
// Windows 7?
if (IsWin7())
{
// Windows 7 Taskbar progress can be used.
dlprogress.ShowInTaskbar = true;
}
2009-06-11 03:16:49 +02:00
// Download TMD before the rest...
string tmdfull = "tmd";
if (titleversion.Text != "")
tmdfull += "." + titleversion.Text;
try
{
DownloadNUSFile(titleid, tmdfull, titledirectory, 0, wiimode);
}
catch (Exception ex)
{
WriteStatus("Download Failed: " + tmdfull);
WriteStatus(" - Reason: " + ex.Message.ToString().Replace("The remote server returned an error: ", ""));
SetEnableforDownload(true);
2009-06-19 03:57:52 +02:00
downloadstartbtn.Text = "Start NUS Download!";
2009-06-11 03:16:49 +02:00
dlprogress.Value = 0;
DeleteTitleDirectory();
return;
}
2009-06-19 03:57:52 +02:00
downloadstartbtn.Text = "Prerequisites: (1/2)";
2009-06-11 03:16:49 +02:00
dlprogress.Value = 50;
// Download CETK after tmd...
bool ticket_exists = true;
2009-06-11 03:16:49 +02:00
try
{
DownloadNUSFile(titleid, "cetk", titledirectory, 0, wiimode);
}
catch (Exception ex)
{
if (ignoreticket.Checked == false)
{
WriteStatus("Download Failed: cetk");
WriteStatus(" - Reason: " + ex.Message.ToString().Replace("The remote server returned an error: ", ""));
WriteStatus("You may be able to retrieve the contents by Ignoring the Ticket (Check below)");
SetEnableforDownload(true);
2009-06-19 03:57:52 +02:00
downloadstartbtn.Text = "Start NUS Download!";
2009-06-11 03:16:49 +02:00
dlprogress.Value = 0;
DeleteTitleDirectory();
return;
}
else
{
WriteStatus("Ticket not found! Continuing, however WAD packing and decryption are not possible!");
packbox.Checked = false;
decryptbox.Checked = false;
ticket_exists = false;
2009-06-11 03:16:49 +02:00
}
}
2009-06-19 03:57:52 +02:00
downloadstartbtn.Text = "Prerequisites: (2/2)";
2009-06-11 03:16:49 +02:00
dlprogress.Value = 100;
byte[] cetkbuf = new byte[0];
byte[] titlekey = new byte[0];
if (ticket_exists)
{
// Create ticket file holder
cetkbuf = FileLocationToByteArray(titledirectory + Path.DirectorySeparatorChar.ToString() + @"cetk");
// Obtain TitleKey
titlekey = new byte[16];
if (decryptbox.Checked == true)
{
// Load TitleKey into it's byte[]
// It is currently encrypted...
for (int i = 0; i < 16; i++)
{
titlekey[i] = cetkbuf[0x1BF + i];
}
2009-06-11 03:16:49 +02:00
// IV (TITLEID+0000s)
byte[] iv = new byte[16];
for (int i = 0; i < 8; i++)
{
iv[i] = cetkbuf[0x1DC + i];
}
for (int i = 0; i < 8; i++)
{
iv[i + 8] = 0x00;
}
2009-06-11 03:16:49 +02:00
// Standard/Korea Common Key
byte[] keyBytes;
if (cetkbuf[0x01F1] == 0x01)
{
WriteStatus("Key Type: Korean");
keyBytes = LoadCommonKey(Path.DirectorySeparatorChar.ToString() + @"kkey.bin");
}
else
{
WriteStatus("Key Type: Standard");
if (wiimode)
keyBytes = LoadCommonKey(Path.DirectorySeparatorChar.ToString() + @"key.bin");
else
keyBytes = LoadCommonKey(Path.DirectorySeparatorChar.ToString() + @"dsikey.bin");
}
2009-06-11 03:16:49 +02:00
initCrypt(iv, keyBytes);
WriteStatus("Title Key: " + DisplayBytes(Decrypt(titlekey), ""));
titlekey = Decrypt(titlekey);
}
2009-06-11 03:16:49 +02:00
}
// Read the tmd as a stream...
byte[] tmd = FileLocationToByteArray(titledirectory + tmdfull);
2009-06-11 03:16:49 +02:00
if (ticket_exists == true)
{
// Locate Certs **************************************
if (!(CertsValid()))
{
WriteStatus("Searching for certs...");
ScanForCerts(tmd);
ScanForCerts(cetkbuf);
}
else
WriteStatus("Using cached certs...");
// /Locate Cert **************************************
}
2009-06-11 03:16:49 +02:00
// Read Title Version...
string tmdversion = "";
for (int x = 476; x < 478; x++)
{
tmdversion += MakeProperLength(ConvertToHex(Convert.ToString(tmd[x])));
}
titleversion.Text = Convert.ToString(int.Parse(tmdversion, System.Globalization.NumberStyles.HexNumber));
//Read System Version (Needed IOS)
string sysversion = IOSNeededFromTMD(tmd);
if (sysversion != "0")
WriteStatus("Requires: IOS" + sysversion);
// Renaming would be ideal, but gives too many permission errors...
/*if ((currentdir + titleid + "v" + titleversion.Text + Path.DirectorySeparatorChar.ToString()) != titledirectory)
{
Directory.Move(titledirectory, currentdir + titleid + "v" + titleversion.Text + Path.DirectorySeparatorChar.ToString());
titledirectory = currentdir + titleid + "v" + titleversion.Text + Path.DirectorySeparatorChar.ToString();
2009-06-11 03:16:49 +02:00
} */
// Read Content #...
string contentstrnum = "";
for (int x = 478; x < 480; x++)
{
contentstrnum += TrimLeadingZeros(Convert.ToString(tmd[x]));
}
WriteStatus("Content #: " + contentstrnum);
2009-06-19 03:57:52 +02:00
downloadstartbtn.Text = "Content: (0/" + contentstrnum + ")";
2009-06-11 03:16:49 +02:00
dlprogress.Value = 0;
// Gather information...
string[] tmdcontents = GetContentNames(tmd, Convert.ToInt32(contentstrnum));
string[] tmdsizes = GetContentSizes(tmd, Convert.ToInt32(contentstrnum));
byte[] tmdhashes = GetContentHashes(tmd, Convert.ToInt32(contentstrnum));
byte[] tmdindices = GetContentIndices(tmd, Convert.ToInt32(contentstrnum));
// Progress bar total size tally info...
float totalcontentsize = 0;
float currentcontentlocation = 0;
for (int i = 0; i < tmdsizes.Length; i++)
{
totalcontentsize += int.Parse(tmdsizes[i], System.Globalization.NumberStyles.HexNumber);
}
WriteStatus("Total Size: " + (long)totalcontentsize + " bytes");
for (int i = 0; i < tmdcontents.Length; i++)
{
try
{
// If it exists we leave it...
if ((localuse.Checked) && (File.Exists(titledirectory + tmdcontents[i])))
{
WriteStatus("Leaving local " + tmdcontents[i] + ".");
}
else
{
DownloadNUSFile(titleid, tmdcontents[i], titledirectory, int.Parse(tmdsizes[i], System.Globalization.NumberStyles.HexNumber), wiimode);
}
}
catch (Exception ex)
{
WriteStatus("Download Failed: " + tmdcontents[i]);
WriteStatus(" - Reason: " + ex.Message.ToString().Replace("The remote server returned an error: ", ""));
SetEnableforDownload(true);
2009-06-19 03:57:52 +02:00
downloadstartbtn.Text = "Start NUS Download!";
2009-06-11 03:16:49 +02:00
dlprogress.Value = 0;
DeleteTitleDirectory();
return;
}
// Progress reporting advances...
downloadstartbtn.Text = "Content: (" + (i + 1) + Path.AltDirectorySeparatorChar.ToString() + contentstrnum + ")";
2009-06-11 03:16:49 +02:00
currentcontentlocation += int.Parse(tmdsizes[i], System.Globalization.NumberStyles.HexNumber);
// Decrypt stuff...
if (decryptbox.Checked == true)
{
// Create content file holder
byte[] contbuf = FileLocationToByteArray(titledirectory + Path.DirectorySeparatorChar.ToString() + tmdcontents[i]);
2009-06-11 03:16:49 +02:00
// IV (00+IDX+more000)
byte[] iv = new byte[16];
for (int x = 0; x < 16; x++)
2009-06-11 03:16:49 +02:00
{
iv[x] = 0x00;
}
iv[1] = tmdindices[i];
initCrypt(iv, titlekey);
2009-06-11 03:16:49 +02:00
/* Create decrypted file
2009-06-11 03:16:49 +02:00
string zeros = "000000";
FileStream decfs = new FileStream(titledirectory + Path.DirectorySeparatorChar.ToString() + 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"); */
FileStream decfs = new FileStream(titledirectory + Path.DirectorySeparatorChar.ToString() + tmdcontents[i] + ".app", FileMode.Create);
2009-06-11 03:16:49 +02:00
decfs.Write(Decrypt(contbuf), 0, int.Parse(tmdsizes[i], System.Globalization.NumberStyles.HexNumber));
decfs.Close();
WriteStatus(" - Decrypted: " + tmdcontents[i] + ".app");
2009-06-11 03:16:49 +02:00
// Hash Check...
byte[] hash = new byte[20];
for (int x = 0; x < 20; x++)
{
hash[x] = tmdhashes[(i * 20) + x];
}
byte[] deccont = Decrypt(contbuf);
Array.Resize(ref deccont, int.Parse(tmdsizes[i], System.Globalization.NumberStyles.HexNumber));
if ((Convert.ToBase64String(ComputeSHA(deccont))) == Convert.ToBase64String(hash))
{
WriteStatus(" - Hash Check: Pass");
}
else
{
WriteStatus(" - Hash Check: Fail");
WriteStatus(" - True Hash: " + DisplayBytes(hash, ""));
WriteStatus(" - You Have: " + DisplayBytes(ComputeSHA(Decrypt(contbuf)), ""));
2009-06-11 03:16:49 +02:00
}
}
dlprogress.Value = Convert.ToInt32(((currentcontentlocation / totalcontentsize) * 100));
}
WriteStatus("NUS Download Finished.");
if ((packbox.Checked == true) && (wiimode == true))
{
PackWAD(titleid, tmdfull, titledirectory);
2009-06-11 03:16:49 +02:00
}
SetEnableforDownload(true);
2009-06-19 03:57:52 +02:00
downloadstartbtn.Text = "Start NUS Download!";
2009-06-11 03:16:49 +02:00
dlprogress.Value = 0;
if (IsWin7())
dlprogress.ShowInTaskbar = false;
2009-06-11 03:16:49 +02:00
if (script_mode)
statusbox.Text = "";
2009-06-11 03:16:49 +02:00
}
/// <summary>
/// Creates the title directory.
/// </summary>
2009-06-11 03:16:49 +02:00
private void CreateTitleDirectory()
{
// Creates the directory for the downloaded title...
string currentdir = Directory.GetCurrentDirectory();
if (currentdir.EndsWith(Convert.ToString(Path.DirectorySeparatorChar.ToString())) == false)
currentdir += Path.DirectorySeparatorChar.ToString();
2009-06-11 03:16:49 +02:00
// Get placement directory early...
string titledirectory;
if (titleversion.Text == "")
titledirectory = Path.Combine(currentdir, titleidbox.Text + Path.DirectorySeparatorChar.ToString());
2009-06-11 03:16:49 +02:00
else
titledirectory = Path.Combine(currentdir, titleidbox.Text + "v" + titleversion.Text + Path.DirectorySeparatorChar.ToString());
2009-06-11 03:16:49 +02:00
// Keep local directory if present and checked out...
if ((localuse.Checked) && (Directory.Exists(titledirectory)))
{
//WriteStatus("Using Local Files");
}
else
{
if (Directory.Exists(titledirectory))
Directory.Delete(titledirectory, true);
Directory.CreateDirectory(titledirectory);
}
}
/// <summary>
/// Deletes the title directory.
/// </summary>
2009-06-11 03:16:49 +02:00
private void DeleteTitleDirectory()
{
string currentdir = Directory.GetCurrentDirectory();
if (currentdir.EndsWith(Convert.ToString(Path.DirectorySeparatorChar.ToString())) == false)
currentdir += Path.DirectorySeparatorChar.ToString();
2009-06-11 03:16:49 +02:00
// Get placement directory early...
string titledirectory;
if (titleversion.Text == "")
titledirectory = Path.Combine(currentdir, titleidbox.Text + Path.DirectorySeparatorChar.ToString());
2009-06-11 03:16:49 +02:00
else
titledirectory = Path.Combine(currentdir, titleidbox.Text + "v" + titleversion.Text + Path.DirectorySeparatorChar.ToString());
2009-06-11 03:16:49 +02:00
if (Directory.Exists(titledirectory))
Directory.Delete(titledirectory, true);
//Directory.CreateDirectory(currentdir + titleidbox.Text);
}
/// <summary>
/// Downloads the NUS file.
/// </summary>
/// <param name="titleid">The titleid.</param>
/// <param name="filename">The filename.</param>
/// <param name="placementdir">The placementdir.</param>
/// <param name="sizeinbytes">The sizeinbytes.</param>
/// <param name="iswiititle">if set to <c>true</c> [iswiititle].</param>
2009-06-11 03:16:49 +02:00
private void DownloadNUSFile(string titleid, string filename, string placementdir, int sizeinbytes, bool iswiititle)
{
// Create NUS URL...
string nusfileurl;
if (iswiititle)
nusfileurl = NUSURL + titleid + Path.AltDirectorySeparatorChar.ToString() + filename;
2009-06-11 03:16:49 +02:00
else
nusfileurl = DSiNUSURL + titleid + Path.AltDirectorySeparatorChar.ToString() + filename;
2009-06-11 03:16:49 +02:00
WriteStatus("Grabbing " + filename + "...");
// State size of file...
if (sizeinbytes != 0)
statusbox.Text += " (" + Convert.ToString(sizeinbytes) + " bytes)";
// Download NUS file...
generalWC.DownloadFile(nusfileurl, placementdir + filename);
}
void StatusChange(string status)
{
WriteStatus(status);
}
/// <summary>
/// Packs the WAD.
/// </summary>
/// <param name="titleid">The titleid.</param>
/// <param name="tmdfilename">The tmdfilename.</param>
/// <param name="totaldirectory">The working directory.</param>
public void PackWAD(string titleid, string tmdfilename, string totaldirectory)
2009-06-11 03:16:49 +02:00
{
WriteStatus("Beginning WAD Pack...");
// Obtain Current Directory
string currentdir = Directory.GetCurrentDirectory();
if (!(currentdir.EndsWith(Path.DirectorySeparatorChar.ToString())) || !(currentdir.EndsWith(Path.AltDirectorySeparatorChar.ToString())))
currentdir += Path.DirectorySeparatorChar.ToString();
2009-06-11 03:16:49 +02:00
// Create instance of WAD Packing class
WADPacker packer = new WADPacker();
packer.StatusChanged += WriteStatus;
// Mash together certs into one array.
byte[] certsbuf = new byte[0xA00];
if (!(CertsValid()))
{
WriteStatus("Error: NUSD could not locate cached certs!");
return;
}
for (int c = 0; c < cert_CA.Length; c++)
certsbuf[c] = cert_CA[c];
for (int c = 0; c < cert_CACP.Length; c++)
certsbuf[c + 0x400] = cert_CACP[c];
for (int c = 0; c < cert_CAXS.Length; c++)
certsbuf[c + 0x700] = cert_CAXS[c];
if (!(TotalCertValid(certsbuf)))
{
WriteStatus("Error: Cert array did not hash properly!");
return;
}
packer.Certs = certsbuf;
// Read TMD/TIK into Packer.
packer.Ticket = FileLocationToByteArray(totaldirectory + Path.DirectorySeparatorChar.ToString() + @"cetk");
packer.TMD = FileLocationToByteArray(totaldirectory + Path.DirectorySeparatorChar.ToString() + tmdfilename);
// Get the TMD variables in here instead...
int contentcount = ContentCount(packer.TMD);
string[] contentnames = GetContentNames(packer.TMD, contentcount);
packer.tmdnames = GetContentNames(packer.TMD, contentcount);
packer.tmdsizes = GetContentSizes(packer.TMD, contentcount);
2009-06-11 03:16:49 +02:00
if (wadnamebox.Text.Contains("[v]") == true)
wadnamebox.Text = wadnamebox.Text.Replace("[v]", "v" + titleversion.Text);
if (!(String.IsNullOrEmpty(WAD_Saveas_Filename)))
{
packer.FileName = System.IO.Path.GetFileName(WAD_Saveas_Filename);
packer.Directory = WAD_Saveas_Filename.Replace(packer.FileName, "");
}
else
{
string wad_filename = totaldirectory + Path.DirectorySeparatorChar.ToString() + RemoveIllegalCharacters(wadnamebox.Text);
packer.Directory = totaldirectory;
packer.FileName = System.IO.Path.GetFileName(wad_filename);
}
// Gather contents...
byte[][] contents_array = new byte[contentcount][];
for (int a = 0; a < contentcount; a++)
{
contents_array[a] = FileLocationToByteArray(totaldirectory + contentnames[a]);
}
packer.Contents = contents_array;
// Send operations over to the packer...
packer.PackWAD();
// Delete contents now...
if (deletecontentsbox.Checked)
{
WriteStatus("Deleting contents...");
File.Delete(totaldirectory + Path.DirectorySeparatorChar.ToString() + tmdfilename);
File.Delete(totaldirectory + Path.DirectorySeparatorChar.ToString() + @"cetk");
for (int a = 0; a < contentnames.Length; a++)
File.Delete(totaldirectory + Path.DirectorySeparatorChar.ToString() + contentnames[a]);
WriteStatus(" - Contents have been deleted.");
string[] leftovers = Directory.GetFiles(totaldirectory);
if (leftovers.Length <= 0)
{
WriteStatus(" - Title directory was empty; Deleted.");
Directory.Delete(totaldirectory);
}
WriteStatus("All deletion completed.");
}
2009-06-11 03:16:49 +02:00
}
/// <summary>
/// Returns next 0x40 padded length.
/// </summary>
/// <param name="currentlength">The currentlength.</param>
/// <returns></returns>
2009-06-11 03:16:49 +02:00
private long ByteBoundary(int currentlength)
{
// Gets the next 0x40 offset.
long thelength = currentlength - 1;
long remainder = 1;
while (remainder != 0)
{
thelength += 1;
remainder = thelength % 0x40;
}
//WriteStatus("Initial Size: " + currentlength);
//WriteStatus("0x40 Size: " + thelength);
return (long)thelength;
}
/// <summary>
/// Int -> Byte[] (OLD)
/// </summary>
/// <param name="inte">The int.</param>
/// <param name="arraysize">The array length.</param>
/// <returns></returns>
2009-06-19 03:57:52 +02:00
private byte[] InttoByteArray(int inte, int arraysize)
2009-06-11 03:16:49 +02:00
{
// Take integer and make into byte array
2009-06-19 03:57:52 +02:00
byte[] b = new byte[arraysize];
b = BitConverter.GetBytes(inte);
2009-06-11 03:16:49 +02:00
if (BitConverter.IsLittleEndian)
Array.Reverse(b);
return b;
}
private void radioButton2_CheckedChanged(object sender, EventArgs e)
{
if (radioButton2.Checked == true)
{
// Cannot Pack WADs
packbox.Checked = false;
packbox.Enabled = false;
// Can decrypt if key exists...lulz
if (dsidecrypt == false)
{
decryptbox.Checked = false;
decryptbox.Enabled = false;
}
wadnamebox.Enabled = false;
wadnamebox.Text = "";
}
}
private void radioButton1_CheckedChanged(object sender, EventArgs e)
{
if (radioButton1.Checked == true)
{
// Can pack WADs
// packbox.Checked = true;
packbox.Enabled = true;
decryptbox.Enabled = true;
}
}
private void button2_Click(object sender, EventArgs e)
{
// Display About Text...
statusbox.Text = "";
WriteStatus("NUS Downloader (NUSD)");
WriteStatus("You are running version: " + version);
WriteStatus("This application created by WB3000");
WriteStatus("Various sections contributed by lukegb");
2009-06-11 03:16:49 +02:00
WriteStatus("");
string currentdir = Directory.GetCurrentDirectory();
if (currentdir.EndsWith(Convert.ToString(Path.DirectorySeparatorChar.ToString())) == false)
currentdir += Path.DirectorySeparatorChar.ToString();
2009-06-11 03:16:49 +02:00
if (File.Exists(currentdir + "key.bin") == false)
WriteStatus("Wii Decryption: Need (key.bin)");
else
WriteStatus("Wii Decryption: OK");
2009-06-13 18:04:54 +02:00
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)");
2009-06-11 03:16:49 +02:00
else
WriteStatus("DSi Decryption: OK");
2009-06-13 18:04:54 +02:00
if (File.Exists(currentdir + "database.xml") == false)
WriteStatus("Database: Need (database.xml)");
else
WriteStatus("Database: OK");
if (IsWin7())
WriteStatus("Windows 7 Features: Enabled");
2009-06-11 03:16:49 +02:00
WriteStatus("");
WriteStatus("Special thanks to:");
WriteStatus(" * Crediar for his wadmaker tool + source, and for the advice!");
2009-06-19 03:57:52 +02:00
WriteStatus(" * SquidMan/Galaxy/comex/Xuzz for advice/sources.");
2009-06-13 18:04:54 +02:00
WriteStatus(" * Pasta for database compilation assistance.");
WriteStatus(" * #WiiDev for answering the tough questions.");
WriteStatus(" * Anyone who helped beta test on GBATemp!");
WriteStatus(" * Famfamfam for the Silk Icon Set.");
WriteStatus(" * Wyatt O'Day for the Windows7ProgressBar Control.");
2009-08-25 18:41:49 +02:00
WriteStatus(" * Napo7 for testing proxy usage.");
2009-06-11 03:16:49 +02:00
}
2009-06-11 03:16:49 +02:00
private void packbox_CheckedChanged(object sender, EventArgs e)
{
if (packbox.Checked == true)
{
wadnamebox.Enabled = true;
// Change WAD name if applicable
UpdatePackedName();
2009-06-11 03:16:49 +02:00
}
else
{
wadnamebox.Enabled = false;
wadnamebox.Text = "";
}
2009-06-11 03:16:49 +02:00
}
private void titleidbox_TextChanged(object sender, EventArgs e)
{
UpdatePackedName();
2009-06-11 03:16:49 +02:00
}
private void titleversion_TextChanged(object sender, EventArgs e)
{
UpdatePackedName();
2009-06-11 03:16:49 +02:00
}
/// <summary>
/// Inits the crypto stuffz.
/// </summary>
/// <param name="iv">The iv.</param>
/// <param name="key">The key.</param>
2009-06-11 03:16:49 +02:00
public void initCrypt(byte[] iv, byte[] key)
{
rijndaelCipher = new RijndaelManaged();
rijndaelCipher.Mode = CipherMode.CBC;
rijndaelCipher.Padding = PaddingMode.None;
rijndaelCipher.KeySize = 128;
rijndaelCipher.BlockSize = 128;
rijndaelCipher.Key = key;
rijndaelCipher.IV = iv;
}
/// <summary>
/// Encrypts the specified plain bytes.
/// </summary>
/// <param name="plainBytes">The plain bytes.</param>
/// <returns></returns>
2009-06-11 03:16:49 +02:00
public byte[] Encrypt(byte[] plainBytes)
{
ICryptoTransform transform = rijndaelCipher.CreateEncryptor();
using (MemoryStream ms = new MemoryStream(plainBytes))
{
using (CryptoStream cs = new CryptoStream(ms, transform, CryptoStreamMode.Read))
{
return ReadFully(cs);
}
}
}
/// <summary>
/// Decrypts the specified encrypted data.
/// </summary>
/// <param name="encryptedData">The encrypted data.</param>
/// <returns></returns>
2009-06-11 03:16:49 +02:00
public byte[] Decrypt(byte[] encryptedData)
{
ICryptoTransform transform = rijndaelCipher.CreateDecryptor();
using (MemoryStream ms = new MemoryStream(encryptedData))
{
using (CryptoStream cs = new CryptoStream(ms, transform, CryptoStreamMode.Read))
{
return ReadFully(cs);
}
}
}
/// <summary>
/// Reads the stream.
/// </summary>
/// <param name="stream">The stream.</param>
/// <returns></returns>
2009-06-11 03:16:49 +02:00
public byte[] ReadFully(Stream stream)
{
byte[] buffer = new byte[32768];
using (MemoryStream ms = new MemoryStream())
{
while (true)
{
int read = stream.Read(buffer, 0, buffer.Length);
if (read <= 0)
return ms.ToArray();
ms.Write(buffer, 0, read);
}
}
}
/// <summary>
/// Displays the bytes.
/// </summary>
/// <param name="bytes">The bytes.</param>
/// <param name="spacer">What separates the bytes</param>
/// <returns></returns>
public string DisplayBytes(byte[] bytes, string spacer)
2009-06-11 03:16:49 +02:00
{
string output = "";
for (int i = 0; i < bytes.Length; ++i)
{
output += bytes[i].ToString("X2") + spacer;
2009-06-11 03:16:49 +02:00
}
return output;
}
/// <summary>
/// Computes the SHA-1 Hash.
/// </summary>
/// <param name="data">A byte[].</param>
/// <returns></returns>
2009-06-11 03:16:49 +02:00
static public byte[] ComputeSHA(byte[] data)
{
SHA1 sha = new SHA1CryptoServiceProvider();
// This is one implementation of the abstract class SHA1.
return sha.ComputeHash(data);
}
/// <summary>
/// Loads the common key from disc.
/// </summary>
/// <param name="keyfile">The keyfile filename.</param>
/// <returns></returns>
2009-06-11 03:16:49 +02:00
public byte[] LoadCommonKey(string keyfile)
{
// Directory stuff
string currentdir = Directory.GetCurrentDirectory();
if (!(currentdir.EndsWith(Path.DirectorySeparatorChar.ToString())) || !(currentdir.EndsWith(Path.AltDirectorySeparatorChar.ToString())))
currentdir += Path.DirectorySeparatorChar.ToString();
2009-06-11 03:16:49 +02:00
if (File.Exists(currentdir + keyfile) == true)
{
// Read common key byte[]
return FileLocationToByteArray(currentdir + keyfile);
2009-06-11 03:16:49 +02:00
}
else
return null;
}
/// <summary>
/// Writes/overwrites the common key onto disc.
/// </summary>
/// <param name="keyfile">The keyfile filename.</param>
/// <param name="commonkey">The byte array of the common key.</param>
/// <returns></returns>
public bool WriteCommonKey(string keyfile, byte[] commonkey)
{
// Directory stuff
string currentdir = Directory.GetCurrentDirectory();
if (!(currentdir.EndsWith(Path.DirectorySeparatorChar.ToString())) || !(currentdir.EndsWith(Path.AltDirectorySeparatorChar.ToString())))
currentdir += Path.DirectorySeparatorChar.ToString();
if (File.Exists(currentdir + keyfile) == true)
{
WriteStatus("Overwriting old key.bin...");
}
try
{
FileStream fs = File.OpenWrite(currentdir + keyfile);
fs.Write(commonkey, 0, commonkey.Length);
fs.Close();
WriteStatus("key.bin written - reloading...");
return true;
}
catch (IOException e)
{
2010-06-29 18:06:15 +02:00
WriteStatus("Error: couldn't write key.bin: " + e.Message);
}
return false;
}
2009-06-11 03:16:49 +02:00
private void button4_Click(object sender, EventArgs e)
{
// Open Database button menu...
databaseStrip.Show(databaseButton, 2, 2);
}
/// <summary>
/// Clears the database strip.
/// </summary>
2009-06-11 03:16:49 +02:00
private void ClearDatabaseStrip()
{
SystemMenuList.DropDownItems.Clear();
IOSMenuList.DropDownItems.Clear();
WiiWareMenuList.DropDownItems.Clear();
// VC Games Sections...
C64MenuList.DropDownItems.Clear();
NeoGeoMenuList.DropDownItems.Clear();
NESMenuList.DropDownItems.Clear();
SNESMenuList.DropDownItems.Clear();
N64MenuList.DropDownItems.Clear();
TurboGrafx16MenuList.DropDownItems.Clear();
TurboGrafxCDMenuList.DropDownItems.Clear();
MSXMenuList.DropDownItems.Clear();
SegaMSMenuList.DropDownItems.Clear();
GenesisMenuList.DropDownItems.Clear();
VCArcadeMenuList.DropDownItems.Clear();
}
/// <summary>
/// Fills the database strip.
/// </summary>
2010-06-29 18:06:15 +02:00
private void FillDatabaseStrip(BackgroundWorker worker)
2009-06-11 03:16:49 +02:00
{
XmlDocument xDoc = new XmlDocument();
xDoc.Load("database.xml");
// Variables
string[] XMLNodeTypes = new string[5] { "SYS", "IOS", "VC", "WW", "UPD" };
2009-06-11 03:16:49 +02:00
2010-06-29 18:06:15 +02:00
int totalLength = xDoc.SelectNodes("/database/*").Count;
int rnt = 0;
2009-06-11 03:16:49 +02:00
// Loop through XMLNodeTypes
for (int i = 0; i < XMLNodeTypes.Length; i++)
{
XmlNodeList XMLSpecificNodeTypeList = xDoc.GetElementsByTagName(XMLNodeTypes[i]);
for (int x = 0; x < XMLSpecificNodeTypeList.Count; x++)
{
ToolStripMenuItem XMLToolStripItem = new ToolStripMenuItem();
XmlAttributeCollection XMLAttributes = XMLSpecificNodeTypeList[x].Attributes;
string titleID = "";
string updateScript;
2009-06-11 03:16:49 +02:00
string descname = "";
bool dangerous = false;
bool ticket = true;
2010-06-29 18:06:15 +02:00
// Okay, so now report the progress...
rnt = rnt + 1;
float currentProgress = ((float)rnt / (float)totalLength) * (float)100;
worker.ReportProgress(Convert.ToInt16(Math.Round(currentProgress)));
2009-06-11 03:16:49 +02:00
// Lol.
XmlNodeList ChildrenOfTheNode = XMLSpecificNodeTypeList[x].ChildNodes;
for (int z = 0; z < ChildrenOfTheNode.Count; z++)
{
switch (ChildrenOfTheNode[z].Name)
{
case "name":
descname = ChildrenOfTheNode[z].InnerText;
break;
case "titleID":
titleID = ChildrenOfTheNode[z].InnerText;
break;
case "titleIDs":
updateScript = ChildrenOfTheNode[z].InnerText;
XMLToolStripItem.AccessibleDescription = updateScript; // TODO: Find somewhere better to put this. AND FAST.
break;
2009-06-11 03:16:49 +02:00
case "version":
string[] versions = ChildrenOfTheNode[z].InnerText.Split(',');
// Add to region things?
if (XMLToolStripItem.DropDownItems.Count > 0)
{
for (int b = 0; b < XMLToolStripItem.DropDownItems.Count; b++)
{
if (ChildrenOfTheNode[z].InnerText != "")
{
ToolStripMenuItem regitem = (ToolStripMenuItem)XMLToolStripItem.DropDownItems[b];
regitem.DropDownItems.Add("Latest Version");
for (int y = 0; y < versions.Length; y++)
{
regitem.DropDownItems.Add("v" + versions[y]);
}
regitem.DropDownItemClicked += new ToolStripItemClickedEventHandler(deepitem_clicked);
}
}
}
else
2009-06-11 03:16:49 +02:00
{
XMLToolStripItem.DropDownItems.Add("Latest Version");
if (ChildrenOfTheNode[z].InnerText != "")
2009-06-11 03:16:49 +02:00
{
for (int y = 0; y < versions.Length; y++)
{
XMLToolStripItem.DropDownItems.Add("v" + versions[y]);
}
2009-06-11 03:16:49 +02:00
}
}
break;
case "region":
string[] regions = ChildrenOfTheNode[z].InnerText.Split(',');
if (ChildrenOfTheNode[z].InnerText != "")
{
for (int y = 0; y < regions.Length; y++)
{
XMLToolStripItem.DropDownItems.Add(RegionFromIndex(Convert.ToInt32(regions[y]), xDoc));
}
}
break;
default:
break;
case "ticket":
ticket = Convert.ToBoolean(ChildrenOfTheNode[z].InnerText);
break;
case "danger":
dangerous = true;
XMLToolStripItem.ToolTipText = ChildrenOfTheNode[z].InnerText;
2009-06-11 03:16:49 +02:00
break;
}
XMLToolStripItem.Image = SelectItemImage(ticket, dangerous);
if (titleID != "")
{
XMLToolStripItem.Text = String.Format("{0} - {1}", titleID, descname);
}
else
{
XMLToolStripItem.Text = descname;
}
2009-06-11 03:16:49 +02:00
}
AddToolStripItemToStrip(i, XMLToolStripItem, XMLAttributes);
}
}
}
/// <summary>
/// Adds the tool strip item to strip.
/// </summary>
/// <param name="type">The type.</param>
/// <param name="additionitem">The additionitem.</param>
/// <param name="attributes">The attributes.</param>
2009-06-11 03:16:49 +02:00
void AddToolStripItemToStrip(int type, ToolStripMenuItem additionitem, XmlAttributeCollection attributes)
{
Debug.WriteLine("Adding item...");
// Check if thread-safe
if (this.InvokeRequired)
{
Debug.WriteLine("InvokeRequired...");
AddToolStripItemToStripCallback atsitsc = new AddToolStripItemToStripCallback(AddToolStripItemToStrip);
this.Invoke(atsitsc, new object[] { type, additionitem, attributes });
return;
}
2009-06-11 03:16:49 +02:00
// Deal with VC list depth...
if (type == 2)
{
Debug.WriteLine("Adding:");
Debug.WriteLine(additionitem);
2009-06-11 03:16:49 +02:00
switch (attributes[0].Value)
{
case "C64":
2009-06-11 03:16:49 +02:00
C64MenuList.DropDownItems.Add(additionitem);
break;
case "NEO":
2009-06-11 03:16:49 +02:00
NeoGeoMenuList.DropDownItems.Add(additionitem);
break;
case "NES":
NESMenuList.DropDownItems.Add(additionitem);
break;
case "SNES":
SNESMenuList.DropDownItems.Add(additionitem);
break;
case "N64":
N64MenuList.DropDownItems.Add(additionitem);
break;
case "TG16":
2009-06-11 03:16:49 +02:00
TurboGrafx16MenuList.DropDownItems.Add(additionitem);
break;
case "TGCD":
2009-06-11 03:16:49 +02:00
TurboGrafxCDMenuList.DropDownItems.Add(additionitem);
break;
case "MSX":
MSXMenuList.DropDownItems.Add(additionitem);
break;
case "SMS":
SegaMSMenuList.DropDownItems.Add(additionitem);
break;
case "GEN":
2009-06-11 03:16:49 +02:00
GenesisMenuList.DropDownItems.Add(additionitem);
break;
case "ARC":
2009-06-11 03:16:49 +02:00
VCArcadeMenuList.DropDownItems.Add(additionitem);
break;
default:
break;
}
additionitem.DropDownItemClicked += new ToolStripItemClickedEventHandler(wwitem_regionclicked);
}
else if (type == 4)
{
// I am a brand new combine harvester
//MassUpdateList.DropDownItems.Add(additionitem);
switch (attributes[0].Value)
{
case "KOR":
KoreaMassUpdate.DropDownItems.Add(additionitem);
break;
case "PAL":
PALMassUpdate.DropDownItems.Add(additionitem);
break;
case "NTSC":
NTSCMassUpdate.DropDownItems.Add(additionitem);
break;
default:
Debug.WriteLine("Oops - database error");
return;
}
// SVN was messing with me and i lost track which of these was the latest...
//additionitem.Click += new ToolStripItemClickedEventHandler(upditem_clicked);
//additionitem.Click += new EventHandler(upditem_itemclicked);
2009-06-11 03:16:49 +02:00
}
else
{
// Add SYS, IOS, WW items
// I thought using index would work in .Items, but I
// guess this switch will have to do...
switch (type)
{
case 0:
SystemMenuList.DropDownItems.Add(additionitem);
break;
case 1:
IOSMenuList.DropDownItems.Add(additionitem);
break;
case 3:
WiiWareMenuList.DropDownItems.Add(additionitem);
break;
}
additionitem.DropDownItemClicked += new ToolStripItemClickedEventHandler(sysitem_versionclicked);
}
}
void deepitem_clicked(object sender, ToolStripItemClickedEventArgs e)
{
titleidbox.Text = e.ClickedItem.OwnerItem.OwnerItem.Text.Substring(0, 16);
titleidbox.Text = titleidbox.Text.Replace("XX", e.ClickedItem.OwnerItem.Text.Substring(0, 2));
if (e.ClickedItem.Text != "Latest Version")
{
if (e.ClickedItem.Text.Contains("v"))
{
if (e.ClickedItem.Text.Contains(" "))
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
{
titleversion.Text = "";
}
// Prepare StatusBox...
string titlename = e.ClickedItem.OwnerItem.OwnerItem.Text.Substring(19, (e.ClickedItem.OwnerItem.OwnerItem.Text.Length - 19));
statusbox.Text = " --- " + titlename + " ---";
// Check if a ticket is present...
if ((e.ClickedItem.OwnerItem.OwnerItem.Image) == (orange) || (e.ClickedItem.OwnerItem.OwnerItem.Image) == (redorange))
{
ignoreticket.Checked = true;
WriteStatus("Note: This title has no ticket and cannot be packed/decrypted!");
packbox.Checked = false;
decryptbox.Checked = false;
}
else
{
ignoreticket.Checked = false;
}
// Change WAD name if packed is already checked...
if (packbox.Checked)
{
OfficialWADNaming(titlename);
}
// Check for danger item
if ((e.ClickedItem.OwnerItem.OwnerItem.Image) == (redgreen) || (e.ClickedItem.OwnerItem.OwnerItem.Image) == (redorange))
{
WriteStatus("\r\n" + e.ClickedItem.OwnerItem.OwnerItem.ToolTipText);
}
}
/// <summary>
/// Mods WAD names to be official.
/// </summary>
/// <param name="titlename">The titlename.</param>
public void OfficialWADNaming(string titlename)
{
if (titlename.Contains("IOS"))
wadnamebox.Text = titlename + "-64-[v].wad";
else if (titlename.Contains("System Menu"))
wadnamebox.Text = "RVL-WiiSystemmenu-[v].wad";
else
wadnamebox.Text = titlename + "-NUS-[v].wad";
if (titleversion.Text != "")
wadnamebox.Text = wadnamebox.Text.Replace("[v]", "v" + titleversion.Text);
}
2009-06-11 03:16:49 +02:00
void wwitem_regionclicked(object sender, ToolStripItemClickedEventArgs e)
{
titleidbox.Text = e.ClickedItem.OwnerItem.Text.Substring(0, 16);
titleversion.Text = "";
titleidbox.Text = titleidbox.Text.Replace("XX", e.ClickedItem.Text.Substring(0, 2));
// Prepare StatusBox...
string titlename = e.ClickedItem.OwnerItem.Text.Substring(19, (e.ClickedItem.OwnerItem.Text.Length - 19));
statusbox.Text = " --- " + titlename + " ---";
// Check if a ticket is present...
if ((e.ClickedItem.OwnerItem.Image) == (orange) || (e.ClickedItem.OwnerItem.Image) == (redorange))
2009-06-11 03:16:49 +02:00
{
ignoreticket.Checked = true;
WriteStatus("Note: This title has no ticket and cannot be packed/decrypted!");
packbox.Checked = false;
decryptbox.Checked = false;
}
else
{
ignoreticket.Checked = false;
}
// Change WAD name if packed is already checked...
if (packbox.Checked)
{
OfficialWADNaming(titlename);
}
// Check for danger item
if ((e.ClickedItem.OwnerItem.Image) == (redgreen) || (e.ClickedItem.OwnerItem.Image) == (redorange))
{
WriteStatus("\r\n" + e.ClickedItem.OwnerItem.ToolTipText);
}
2009-06-11 03:16:49 +02:00
}
void upditem_itemclicked(object sender, ToolStripItemClickedEventArgs e)
{
WriteStatus("Preparing to run download script...");
Control.CheckForIllegalCrossThreadCalls = false;
script_mode = true;
statusbox.Text = "";
WriteStatus("Starting script download. Please be patient!");
string[] NUS_Entries = e.ClickedItem.AccessibleDescription.Split('\n'); // TODO: Find somewhere better to put this. AND FAST!
for (int i = 0; i < NUS_Entries.Length; i++)
{
WriteStatus(NUS_Entries[i]);
}
script_filename = "\000";
nusentries = NUS_Entries;
BackgroundWorker scripter = new BackgroundWorker();
scripter.DoWork += new DoWorkEventHandler(RunScript);
scripter.RunWorkerAsync();
}
2009-06-11 03:16:49 +02:00
void sysitem_versionclicked(object sender, ToolStripItemClickedEventArgs e)
{
titleidbox.Text = e.ClickedItem.OwnerItem.Text.Substring(0, 16);
2009-06-11 03:16:49 +02:00
if (e.ClickedItem.Text != "Latest Version")
{
if (e.ClickedItem.Text.Contains("v"))
{
if (e.ClickedItem.Text.Contains(" "))
titleversion.Text = e.ClickedItem.Text.Substring(1, e.ClickedItem.Text.IndexOf(' ') - 1);
2009-06-11 03:16:49 +02:00
else
titleversion.Text = e.ClickedItem.Text.Substring(1, e.ClickedItem.Text.Length - 1);
}
else
{
// Apparently it's a region code..
2009-06-11 03:16:49 +02:00
titleidbox.Text = titleidbox.Text.Replace("XX", e.ClickedItem.Text.Substring(0, 2));
titleversion.Text = "";
}
}
else
{
titleversion.Text = "";
}
// Prepare StatusBox...
string titlename = e.ClickedItem.OwnerItem.Text.Substring(19, (e.ClickedItem.OwnerItem.Text.Length - 19));
statusbox.Text = " --- " + titlename + " ---";
if ((e.ClickedItem.OwnerItem.Image) == (orange) || (e.ClickedItem.OwnerItem.Image) == (redorange))
2009-06-11 03:16:49 +02:00
{
ignoreticket.Checked = true;
WriteStatus("Note: This title has no ticket and cannot be packed/decrypted!");
packbox.Checked = false;
decryptbox.Checked = false;
}
else
{
ignoreticket.Checked = false;
}
// Change WAD name if packed is already checked...
if (packbox.Checked)
{
if (titlename.Contains("IOS"))
wadnamebox.Text = titlename + "-64-[v].wad";
else
wadnamebox.Text = titlename + "-NUS-[v].wad";
if (titleversion.Text != "")
wadnamebox.Text = wadnamebox.Text.Replace("[v]", "v" + titleversion.Text);
}
// Check for danger item
if ((e.ClickedItem.OwnerItem.Image) == (redgreen) || (e.ClickedItem.OwnerItem.Image) == (redorange))
{
WriteStatus("\n" + e.ClickedItem.OwnerItem.ToolTipText);
}
2009-06-11 03:16:49 +02:00
}
/// <summary>
/// Gathers the region based on index
/// </summary>
/// <param name="index">The index.</param>
/// <param name="databasexml">XmlDocument with database inside</param>
/// <returns>Region desc</returns>
2009-06-11 03:16:49 +02:00
string RegionFromIndex(int index, XmlDocument databasexml)
{
/* Typical Region XML
* <REGIONS>
2009-06-13 18:04:54 +02:00
<region index="0">41 (All/System)</region>
2009-06-11 03:16:49 +02:00
<region index=1>44 (German)</region>
<region index=2>45 (USA/NTSC)</region>
<region index=3>46 (French)</region>
<region index=4>4A (Japan)</region>
<region index=5>4B (Korea)</region>
<region index=6>4C (Japanese Import to Europe/Australia/PAL)</region>
<region index=7>4D (American Import to Europe/Australia/PAL)</region>
<region index=8>4E (Japanese Import to USA/NTSC)</region>
<region index=9>50 (Europe/PAL)</region>
<region index=10>51 (Korea w/ Japanese Language)</region>
<region index=11>54 (Korea w/ English Language)</region>
<region index=12>58 (Some Homebrew)</region>
</REGIONS>
*/
XmlNodeList XMLRegionList = databasexml.GetElementsByTagName("REGIONS");
XmlNodeList ChildrenOfTheNode = XMLRegionList[0].ChildNodes;
// For each child node (region node)
for (int z = 0; z < ChildrenOfTheNode.Count; z++)
{
// Gather attributes (index='x')
XmlAttributeCollection XMLAttributes = ChildrenOfTheNode[z].Attributes;
// Return value of node if index matches
if (Convert.ToInt32(XMLAttributes[0].Value) == index)
return ChildrenOfTheNode[z].InnerText;
}
return "XX (Error)";
}
/// <summary>
/// Loads the region codes.
/// </summary>
2009-06-11 03:16:49 +02:00
private void LoadRegionCodes()
{
XmlDocument xDoc = new XmlDocument();
xDoc.Load("database.xml");
XmlNodeList XMLRegionList = xDoc.GetElementsByTagName("REGIONS");
XmlNodeList ChildrenOfTheNode = XMLRegionList[0].ChildNodes;
// For each child node (region node)
for (int z = 0; z < ChildrenOfTheNode.Count; z++)
{
RegionCodesList.DropDownItems.Add(ChildrenOfTheNode[z].InnerText);
}
}
private void RegionCodesList_DropDownItemClicked(object sender, ToolStripItemClickedEventArgs e)
{
if (titleidbox.Text.Length == 16)
titleidbox.Text = titleidbox.Text.Substring(0, 14) + e.ClickedItem.Text.Substring(0, 2);
}
/// <summary>
/// Removes the illegal characters.
/// </summary>
/// <param name="databasestr">removes the illegal chars</param>
/// <returns>legal string</returns>
private string RemoveIllegalCharacters(string databasestr)
{
// Database strings must contain filename-legal characters.
foreach (char illegalchar in System.IO.Path.GetInvalidFileNameChars())
{
if (databasestr.Contains(illegalchar.ToString()))
databasestr = databasestr.Replace(illegalchar, '-');
}
return databasestr;
}
/// <summary>
/// Zeroes the signature in TMD/TIK.
/// </summary>
/// <param name="tmdortik">TMD/TIK</param>
/// <returns>Zeroed TMD/TIK</returns>
private byte[] ZeroSignature(byte[] tmdortik)
{
// Write all 0x00 to signature...
// Sig starts at 0x04 in both TMD/TIK
for (int i = 0; i < 256; i++)
{
tmdortik[i + 4] = 0x00;
}
WriteStatus(" - Signature Emptied...");
return tmdortik;
}
/// <summary>
/// Trucha Signs a TMD/TIK
/// </summary>
/// <param name="tmdortik">The tmdortik.</param>
/// <returns>Fake-signed byte[]</returns>
private byte[] TruchaSign(byte[] tmdortik)
{
// Loop through 2 bytes worth of numbers until hash starts with 0x00...
// Padding starts at 0x104 in both TMD/TIK, seems like a good place to me...
byte[] payload = new byte[2];
byte[] hashobject = new byte[tmdortik.Length - 0x104];
for (int i = 0; i < 65535; i++)
{
payload = incrementAtIndex(payload, 1);
tmdortik[0x104] = payload[0];
tmdortik[0x105] = payload[1];
for (int x = 0; x < (tmdortik.Length - 0x104); x++)
{
hashobject[x] = tmdortik[0x104 + x];
}
if (ComputeSHA(hashobject)[0] == 0x00)
{
WriteStatus(" - Successfully Trucha Signed.");
return tmdortik;
}
}
WriteStatus(" - Sign FAIL!");
return tmdortik;
}
/// <summary>
/// Increments at an index.
/// </summary>
/// <param name="array">The array.</param>
/// <param name="index">The index.</param>
/// <returns></returns>
static public byte[] incrementAtIndex(byte[] array, int index)
{
if (array[index] == byte.MaxValue)
{
array[index] = 0;
if (index > 0)
incrementAtIndex(array, index - 1);
}
else
{
array[index]++;
}
return array;
}
2009-06-19 03:57:52 +02:00
2009-07-04 20:32:52 +02:00
private void button3_Click_1(object sender, EventArgs e)
{
// Clear Statusbox.text
statusbox.Text = "";
}
/// <summary>
/// Makes everything disabled/enabled.
/// </summary>
/// <param name="enabled">if set to <c>true</c> [enabled].</param>
private void SetEnableforDownload(bool enabled)
{
// Disable things the user should not mess with during download...
downloadstartbtn.Enabled = enabled;
titleidbox.Enabled = enabled;
titleversion.Enabled = enabled;
Extrasbtn.Enabled = enabled;
databaseButton.Enabled = enabled;
packbox.Enabled = enabled;
localuse.Enabled = enabled;
ignoreticket.Enabled = enabled;
decryptbox.Enabled = enabled;
}
/// <summary>
/// Makes tooltips disappear in the database, as many contain danger tag info.
/// </summary>
/// <param name="enabled">if set to <c>true</c> [enabled].</param>
private void ShowInnerToolTips(bool enabled)
{
// Force tooltips to GTFO in sub menus...
foreach (ToolStripItem item in databaseStrip.Items)
{
try
{
ToolStripMenuItem menuitem = (ToolStripMenuItem)item;
menuitem.DropDown.ShowItemToolTips = false;
}
catch (Exception)
{
// Do nothing, some objects will not cast.
}
}
}
/// <summary>
/// Selects the database item image.
/// </summary>
/// <param name="ticket">if set to <c>true</c> [ticket].</param>
/// <param name="danger">if set to <c>true</c> [danger].</param>
/// <returns>Correct Image</returns>
private System.Drawing.Image SelectItemImage(bool ticket, bool danger)
{
// All is good, go green...
if ((ticket) && (!danger))
return green;
// There's no ticket, but danger is clear...
if ((!ticket) && (!danger))
return orange;
// DANGER WILL ROBINSON...
if ((ticket) && (danger))
return redgreen;
// Double bad...
if ((!ticket) && (danger))
return redorange;
return null;
}
/// <summary>
/// Loads a file into a byte[]
/// </summary>
/// <param name="filename">The filename.</param>
/// <returns>byte[] of file contents</returns>
private byte[] FileLocationToByteArray(string filename)
{
FileStream fs = File.OpenRead(filename);
byte[] filebytearray = ReadFully(fs, 460);
fs.Close();
return filebytearray;
}
/// <summary>
/// Updates the name of the packed WAD in the textbox.
/// </summary>
private void UpdatePackedName()
{
// Change WAD name if applicable
string title_name = null;
if ((titleidbox.Enabled == true) && (packbox.Checked == true))
{
if (titleversion.Text != "")
{
wadnamebox.Text = titleidbox.Text + "-NUS-v" + titleversion.Text + ".wad";
}
else
{
wadnamebox.Text = titleidbox.Text + "-NUS-[v]" + titleversion.Text + ".wad";
}
if ((File.Exists("database.xml") == true) && (titleidbox.Text.Length == 16))
title_name = NameFromDatabase(titleidbox.Text);
if (title_name != null)
{
wadnamebox.Text = wadnamebox.Text.Replace(titleidbox.Text, title_name);
OfficialWADNaming(title_name);
}
}
wadnamebox.Text = RemoveIllegalCharacters(wadnamebox.Text);
}
/// <summary>
/// Generates a ticket from TitleKey/ID
/// </summary>
/// <param name="EncTitleKey">The enc title key.</param>
/// <param name="TitleID">The title ID.</param>
/// <returns>New Ticket</returns>
private byte[] GenerateTicket(byte[] EncTitleKey, byte[] TitleID)
{
byte[] Ticket = new byte[0x2A4];
// RSA Signature Heading...
Ticket[1] = 0x01; Ticket[3] = 0x01;
// Signature Issuer... (Root-CA00000001-XS00000003)
byte[] SignatureIssuer = new byte[0x1A] { 0x52, 0x6F, 0x6F, 0x74, 0x2D, 0x43, 0x41, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x2D, 0x58, 0x53, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x33 };
for (int a = 0; a < 0x40; a++)
{
Ticket[0x140 + a] = SignatureIssuer[a];
}
// Encrypted TitleKey...
for (int b = 0; b < 0x10; b++)
{
Ticket[0x1BF + b] = EncTitleKey[b];
}
// Ticket ID...
for (int c = 0; c < 0x08; c++)
{
Ticket[0x1D0 + c] = 0x49;
}
// Title ID...
for (int d = 0; d < 0x08; d++)
{
Ticket[0x1DC + d] = TitleID[d];
}
// Misc FF...
Ticket[0x1E4] = 0xFF; Ticket[0x1E5] = 0xFF;
Ticket[0x1E6] = 0xFF; Ticket[0x1E7] = 0xFF;
// Unknown 0x01...
Ticket[0x221] = 0x01;
// Misc FF...
for (int e = 0; e < 0x20; e++)
{
Ticket[0x222 + e] = 0xFF;
}
return Ticket;
}
/// <summary>
/// Checks for a hex string.
/// </summary>
/// <param name="test">The test string</param>
/// <returns>Whether string is hex or not.</returns>
public bool OnlyHexInString(string test)
{
return System.Text.RegularExpressions.Regex.IsMatch(test, @"\A\b[0-9a-fA-F]+\b\Z");
}
2009-07-23 00:22:04 +02:00
/// <summary>
/// Pads to multiple of....
/// </summary>
/// <param name="src">The binary.</param>
/// <param name="pad">The pad amount.</param>
/// <returns>Padded byte[]</returns>
private byte[] PadToMultipleOf(byte[] src, int pad)
{
int len = (src.Length + pad - 1) / pad * pad;
Array.Resize(ref src, len);
return src;
}
/// <summary>
/// Determines whether OS is win7.
/// </summary>
/// <returns>
/// <c>true</c> if OS = win7; otherwise, <c>false</c>.
/// </returns>
private bool IsWin7()
{
return (Environment.OSVersion.VersionString.Contains("6.1") == true);
}
private byte[] NewIntegertoByteArray(int theInt, int arrayLen)
{
byte[] resultArray = new byte[arrayLen];
for(int i = arrayLen - 1 ; i >= 0; i--)
{
resultArray[i] = (byte)((theInt >> (8 * i)) & 0xFF);
}
Array.Reverse(resultArray);
// Fix duplication, rewrite extra to 0x00;
if (arrayLen > 4)
{
for (int i = 0; i < (arrayLen - 4); i++)
{
resultArray[i] = 0x00;
}
}
return resultArray;
}
/// <summary>
/// Does byte[] contain byte[]?
/// </summary>
/// <param name="bigboy">The large byte[].</param>
/// <param name="littleman">Small byte[] which may be in large one.</param>
/// <returns>messed up int[] with offsets.</returns>
private int[] ByteArrayContainsByteArray(byte[] bigboy, byte[] littleman)
{
// bigboy.Contains(littleman);
// returns offset { cnt , ofst };
int[] offset = new int[5];
for (int a = 0; a < (bigboy.Length - littleman.Length); a++)
{
int matches = 0;
for (int b = 0; b < littleman.Length; b++)
{
if (bigboy[a + b] == littleman[b])
matches += 1;
}
if (matches == littleman.Length)
{
offset[offset[0] + 1] = a;
offset[0] += 1;
}
}
return offset;
}
/// <summary>
/// Retrieves the new database via WiiBrew.
/// </summary>
/// <returns>Database as a String</returns>
2010-06-29 18:06:15 +02:00
private void RetrieveNewDatabase(object sender, DoWorkEventArgs e)
{
// Retrieve Wiibrew database page source code
WebClient databasedl = new WebClient();
2010-06-29 18:06:15 +02:00
//statusbox.Refresh();
// Proxy
if (!(String.IsNullOrEmpty(proxy_url)))
{
WebProxy customproxy = new WebProxy();
customproxy.Address = new Uri(proxy_url);
if (String.IsNullOrEmpty(proxy_usr))
customproxy.UseDefaultCredentials = true;
else
{
NetworkCredential cred = new NetworkCredential();
cred.UserName = proxy_usr;
if (!(String.IsNullOrEmpty(proxy_pwd)))
cred.Password = proxy_pwd;
customproxy.Credentials = cred;
}
databasedl.Proxy = customproxy;
WriteStatus(" - Custom proxy settings applied!");
}
else
{
databasedl.Proxy = WebRequest.GetSystemWebProxy();
databasedl.UseDefaultCredentials = true;
}
string wiibrewsource = databasedl.DownloadString("http://www.wiibrew.org/wiki/NUS_Downloader/database?cachesmash=" + System.DateTime.Now.ToString());
2010-06-29 18:06:15 +02:00
//statusbox.Refresh();
// Strip out HTML
wiibrewsource = Regex.Replace(wiibrewsource, @"<(.|\n)*?>", "");
// Shrink to fix only the database
string startofdatabase = "&lt;database v";
string endofdatabase = "&lt;/database&gt;";
wiibrewsource = wiibrewsource.Substring(wiibrewsource.IndexOf(startofdatabase), wiibrewsource.Length - wiibrewsource.IndexOf(startofdatabase));
wiibrewsource = wiibrewsource.Substring(0, wiibrewsource.IndexOf(endofdatabase) + endofdatabase.Length);
// Fix ", <, >, and spaces
wiibrewsource = wiibrewsource.Replace("&lt;","<");
wiibrewsource = wiibrewsource.Replace("&gt;",">");
wiibrewsource = wiibrewsource.Replace("&quot;",'"'.ToString());
wiibrewsource = wiibrewsource.Replace("&nbsp;"," "); // Shouldn't occur, but they happen...
// Return parsed xml database...
2010-06-29 18:06:15 +02:00
e.Result = wiibrewsource;
}
2010-06-29 18:06:15 +02:00
private void RetrieveNewDatabase_Completed(object sender, RunWorkerCompletedEventArgs e)
{
2010-06-29 18:06:15 +02:00
string database = e.Result.ToString();
string currentversion = GetDatabaseVersion("database.xml");
string onlineversion = GetDatabaseVersion(database);
WriteStatus(" - Database successfully parsed!");
WriteStatus(" - Current Database Version: " + currentversion);
WriteStatus(" - Online Database Version: " + onlineversion);
if (currentversion == onlineversion)
{
WriteStatus(" - You have the latest database version!");
return;
}
2010-06-29 18:06:15 +02:00
bool isCreation = false;
if (File.Exists("database.xml"))
{
WriteStatus(" - Overwriting your current database.xml...");
WriteStatus(" - The old database will become 'olddatabase.xml' in case the new one is faulty.");
2010-06-29 18:06:15 +02:00
string olddatabase = File.ReadAllText("database.xml");
File.WriteAllText("olddatabase.xml", olddatabase);
File.Delete("database.xml");
File.WriteAllText("database.xml", database);
}
else
{
WriteStatus(" - database.xml has been created.");
File.WriteAllText("database.xml", database);
isCreation = true;
}
// Load it up...
2010-06-29 18:06:15 +02:00
this.fds.RunWorkerAsync();
if (isCreation)
{
WriteStatus("Database successfully created!");
databaseButton.Visible = true;
databaseButton.Enabled = false;
Extrasbtn.Size = new System.Drawing.Size(55, 20);
updateDatabaseToolStripMenuItem.Text = "Download Database";
2010-06-29 18:06:15 +02:00
}
else
{
WriteStatus("Database successfully updated!");
}
2010-06-29 18:06:15 +02:00
}
private void updateDatabaseToolStripMenuItem_Click(object sender, EventArgs e)
{
statusbox.Text = "";
WriteStatus("Updating your database.xml from Wiibrew.org");
BackgroundWorker dbFetcher = new BackgroundWorker();
dbFetcher.DoWork += new DoWorkEventHandler(RetrieveNewDatabase);
dbFetcher.RunWorkerCompleted += new RunWorkerCompletedEventHandler(RetrieveNewDatabase_Completed);
dbFetcher.RunWorkerAsync();
/*while (dbFetcher.IsBusy)
2010-06-29 18:06:15 +02:00
{
statusbox.Text += ".";
}*/
}
private void loadInfoFromTMDToolStripMenuItem_Click(object sender, EventArgs e)
{
// Extras menu -> Load TMD...
LoadTitleFromTMD();
}
/// <summary>
/// Sends the SOAP request to NUS.
/// </summary>
/// <param name="soap_xml">The Request</param>
/// <returns></returns>
public string SendSOAPRequest(string soap_xml)
{
System.Net.HttpWebRequest req = (System.Net.HttpWebRequest)System.Net.HttpWebRequest.Create("http://nus.shop.wii.com:80/nus/services/NetUpdateSOAP");
req.Method = "POST";
req.UserAgent = "wii libnup/1.0";
req.Headers.Add("SOAPAction", '"' + "urn:nus.wsapi.broadon.com/" + '"');
// Proxy
if (!(String.IsNullOrEmpty(proxy_url)))
{
WebProxy customproxy = new WebProxy();
customproxy.Address = new Uri(proxy_url);
if (String.IsNullOrEmpty(proxy_usr))
customproxy.UseDefaultCredentials = true;
else
{
NetworkCredential cred = new NetworkCredential();
cred.UserName = proxy_usr;
if (!(String.IsNullOrEmpty(proxy_pwd)))
cred.Password = proxy_pwd;
customproxy.Credentials = cred;
}
req.Proxy = customproxy;
WriteStatus(" - Custom proxy settings applied!");
}
else
{
req.Proxy = WebRequest.GetSystemWebProxy();
req.UseDefaultCredentials = true;
}
Stream writeStream = req.GetRequestStream();
UTF8Encoding encoding = new UTF8Encoding();
byte[] bytes = encoding.GetBytes(soap_xml);
req.ContentType = "text/xml; charset=utf-8";
//req.ContentLength = bytes.Length;
writeStream.Write(bytes, 0, bytes.Length);
writeStream.Close();
Application.DoEvents();
try
{
string result;
System.Net.HttpWebResponse resp = (System.Net.HttpWebResponse)req.GetResponse();
using (Stream responseStream = resp.GetResponseStream())
{
using (StreamReader readStream = new StreamReader(responseStream, Encoding.UTF8))
{
result = readStream.ReadToEnd();
}
}
req.Abort();
Application.DoEvents();
return result;
}
catch (Exception ex)
{
req.Abort();
return ex.Message.ToString();
}
}
private void emulateUpdate_DropDownItemClicked(object sender, ToolStripItemClickedEventArgs e)
{
// Begin Wii System Update
statusbox.Text = "";
WriteStatus("Starting Wii System Update...");
extrasStrip.Close();
string deviceID = "4362227770";
string messageID = "13198105123219138";
string attr = "2";
string RegionID = e.ClickedItem.Text.Substring(0, 3);
if (RegionID == "JAP") // Japan fix, only region not w/ 1st 3 letters same as ID.
RegionID = "JPN";
string CountryCode = RegionID.Substring(0, 2);
/* [14:26] <Galaxy|> RegionID: USA, Country: US;
RegionID: JPN, Country: JP;
RegionID: EUR, Country: EU;
RegionID: KOR, Country: KO; */
string soap_req = "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\"" +
" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"" +
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n" +
"<soapenv:Body>\n<GetSystemUpdateRequest xmlns=\"urn:nus.wsapi.broadon.com\">\n" +
"<Version>1.0</Version>\n<MessageId>" + messageID + "</MessageId>\n<DeviceId>" + deviceID + "</DeviceId>\n" +
"<RegionId>" + RegionID + "</RegionId>\n<CountryCode>" + CountryCode + "</CountryCode>\n<TitleVersion>\n<TitleId>0000000100000001</TitleId>\n" +
"<Version>2</Version>\n</TitleVersion>\n<TitleVersion>\n<TitleId>0000000100000002</TitleId>\n" +
"<Version>33</Version>\n</TitleVersion>\n<TitleVersion>\n<TitleId>0000000100000009</TitleId>\n" +
"<Version>516</Version>\n</TitleVersion>\n<Attribute>" + attr + "</Attribute>\n<AuditData></AuditData>\n" +
"</GetSystemUpdateRequest>\n</soapenv:Body>\n</soapenv:Envelope>";
WriteStatus(" - Sending SOAP Request to NUS...");
WriteStatus(" - Region: " + RegionID);
string update_xml = SendSOAPRequest(soap_req);
if (update_xml != null)
WriteStatus(" - Recieved Update Info!");
else
{
WriteStatus(" - Fail.");
return;
}
WriteStatus(" - Title information:");
string script_text = "";
XmlDocument xDoc = new XmlDocument();
xDoc.LoadXml(update_xml);
XmlNodeList TitleList = xDoc.GetElementsByTagName("TitleVersion");
for (int a = 0; a < TitleList.Count; a++)
{
XmlNodeList TitleInfo = TitleList[a].ChildNodes;
string TitleID = "";
string Version = "";
for (int b = 0; b < TitleInfo.Count; b++)
{
switch (TitleInfo[b].Name)
{
case "TitleId":
TitleID = TitleInfo[b].InnerText;
break;
case "Version":
Version = TitleInfo[b].InnerText;
break;
default:
break;
}
}
WriteStatus(String.Format(" - {0} [v{1}]", TitleID, Version));
if ((File.Exists("database.xml") == true) && ((!(String.IsNullOrEmpty(NameFromDatabase(TitleID))))))
statusbox.Text += String.Format(" [{0}]", NameFromDatabase(TitleID));
script_text += String.Format("{0} {1}\n", TitleID, DisplayBytes(NewIntegertoByteArray(Convert.ToInt32(Version), 2), ""));
}
WriteStatus(" - Outputting results to NUS script...");
// Current directory...
string currentdir = Directory.GetCurrentDirectory();
if (!(currentdir.EndsWith(Path.DirectorySeparatorChar.ToString())) || !(currentdir.EndsWith(Path.AltDirectorySeparatorChar.ToString())))
currentdir += Path.DirectorySeparatorChar.ToString();
if (!(Directory.Exists(currentdir + "scripts")))
{
Directory.CreateDirectory(currentdir + "scripts");
WriteStatus(" - Created 'scripts\' directory.");
}
string time = RemoveIllegalCharacters(DateTime.Now.ToShortTimeString());
File.WriteAllText(String.Format(currentdir + "scripts\\{0}_Update_{1}_{2}_{3} {4}.nus", RegionID, DateTime.Now.Month, DateTime.Now.Day, DateTime.Now.Year, time), script_text);
WriteStatus(" - Script written!");
WriteStatus(" - Run this script if you feel like downloading the update!");
}
/// <summary>
/// Scans for certs in TMD/TIK.
/// </summary>
/// <param name="tmdortik">The tmdortik.</param>
private void ScanForCerts(byte[] tmdortik)
{
// For some reason a few 00s are cut off, so pad it up to be safe.
tmdortik = PadToMultipleOf(tmdortik, 16);
// Search for cert_CACP
if (!(tmdortik.Length < 0x300))
for (int a = 0; a < (tmdortik.Length - 0x300); a++)
{
byte[] chunk = new byte[0x300];
for (int b = 0; b < 0x300; b++)
{
chunk[b] = tmdortik[a + b];
}
if (Convert.ToBase64String(ComputeSHA(chunk)) == Convert.ToBase64String(cert_CACP_sha1))
{
cert_CACP = chunk;
WriteStatus(" - Cert CA-CP Located!");
break;
}
}
// Search for cert_CAXS
if (!(tmdortik.Length < 0x300))
for (int a = 0; a < (tmdortik.Length - 0x300); a++)
{
byte[] chunk = new byte[0x300];
for (int b = 0; b < 0x300; b++)
{
chunk[b] = tmdortik[a + b];
}
if (Convert.ToBase64String(ComputeSHA(chunk)) == Convert.ToBase64String(cert_CAXS_sha1))
{
cert_CAXS = chunk;
WriteStatus(" - Cert CA-XS Located!");
break;
}
}
// Search for cert_CA
if ((!(tmdortik.Length < 0x400)) && ((Convert.ToBase64String(ComputeSHA(cert_CA)) != Convert.ToBase64String(cert_CA_sha1))))
{
for (int a = 0; a < (tmdortik.Length - 0x400); a++)
{
byte[] chunk = new byte[0x400];
for (int b = 0; b < 0x400; b++)
{
chunk[b] = tmdortik[a + b];
}
if (Convert.ToBase64String(ComputeSHA(chunk)) == Convert.ToBase64String(cert_CA_sha1))
{
cert_CA = chunk;
WriteStatus(" - Cert CA Located!");
break;
}
}
}
}
/// <summary>
/// Checks whether the certs are obtained.
/// </summary>
/// <returns></returns>
private bool CertsValid()
{
if (Convert.ToBase64String(ComputeSHA(cert_CA)) != Convert.ToBase64String(cert_CA_sha1))
return false;
if (Convert.ToBase64String(ComputeSHA(cert_CACP)) != Convert.ToBase64String(cert_CACP_sha1))
return false;
if (Convert.ToBase64String(ComputeSHA(cert_CAXS)) != Convert.ToBase64String(cert_CAXS_sha1))
return false;
return true;
}
/// <summary>
/// Checks the whole cert file for validity.
/// </summary>
/// <param name="cert_sys">The cert_sys.</param>
/// <returns>Valid Cert state.</returns>
private bool TotalCertValid(byte[] cert_sys)
{
if (Convert.ToBase64String(ComputeSHA(cert_sys)) != Convert.ToBase64String(cert_total_sha1))
return false;
return true;
}
/// <summary>
/// Looks for a title's name by TitleID in Database.
/// </summary>
/// <param name="titleid">The titleid.</param>
/// <returns>Existing name; else null</returns>
private string NameFromDatabase(string titleid)
{
// DANGER: BAD h4x HERE!!
// Fix MIOS/BC naming
if (titleid == "0000000100000101")
return "MIOS";
else if (titleid == "0000000100000100")
return "BC";
XmlDocument xDoc = new XmlDocument();
xDoc.Load("database.xml");
// Variables
string[] XMLNodeTypes = new string[4] { "SYS", "IOS", "VC", "WW" };
// Loop through XMLNodeTypes
for (int i = 0; i < XMLNodeTypes.Length; i++) // FOR THE FOUR TYPES OF NODES
{
XmlNodeList XMLSpecificNodeTypeList = xDoc.GetElementsByTagName(XMLNodeTypes[i]);
for (int x = 0; x < XMLSpecificNodeTypeList.Count; x++) // FOR EACH ITEM IN THE LIST OF A NODE TYPE
{
bool found_it = false;
// Lol.
XmlNodeList ChildrenOfTheNode = XMLSpecificNodeTypeList[x].ChildNodes;
for (int z = 0; z < ChildrenOfTheNode.Count; z++) // FOR EACH CHILD NODE
{
switch (ChildrenOfTheNode[z].Name)
{
case "titleID":
if (ChildrenOfTheNode[z].InnerText == titleid)
found_it = true;
else if ((ChildrenOfTheNode[z].InnerText.Substring(0, 14) + "XX") == (titleid.Substring(0, 14) + "XX") && (titleid.Substring(0, 14) != "00000001000000"))
found_it = true;
else
found_it = false;
break;
default:
break;
}
}
if (found_it)
{
for (int z = 0; z < ChildrenOfTheNode.Count; z++) // FOR EACH CHILD NODE
{
switch (ChildrenOfTheNode[z].Name)
{
case "name":
return ChildrenOfTheNode[z].InnerText;
default:
break;
}
}
}
}
}
return null;
}
private void packbox_EnabledChanged(object sender, EventArgs e)
{
saveaswadbox.Enabled = packbox.Enabled;
deletecontentsbox.Enabled = packbox.Enabled;
}
2009-08-19 18:40:43 +02:00
private void saveaswadbox_Paint(object sender, PaintEventArgs e)
{
//e.Graphics.
/*Rectangle rect = new Rectangle(0, 0, 16, 16);
if (saveaswadbox.Checked)
e.Graphics.DrawImageUnscaledAndClipped(green, rect);
else
e.Graphics.DrawImageUnscaled(orange, -7, -5); */
}
2009-08-20 19:41:08 +02:00
private void SaveProxyBtn_Click(object sender, EventArgs e)
{
// Current directory...
string currentdir = Directory.GetCurrentDirectory();
if (!(currentdir.EndsWith(Path.DirectorySeparatorChar.ToString())) || !(currentdir.EndsWith(Path.AltDirectorySeparatorChar.ToString())))
currentdir += Path.DirectorySeparatorChar.ToString();
2009-08-20 19:41:08 +02:00
if ((String.IsNullOrEmpty(ProxyURL.Text)) && (String.IsNullOrEmpty(ProxyUser.Text)) && ((File.Exists(currentdir + "proxy.txt"))))
{
File.Delete(currentdir + "proxy.txt");
proxyBox.Visible = false;
proxy_usr = ""; proxy_url = ""; proxy_pwd = "";
WriteStatus("Proxy settings deleted!");
return;
}
else if ((String.IsNullOrEmpty(ProxyURL.Text)) && (String.IsNullOrEmpty(ProxyUser.Text)) && ((!(File.Exists(currentdir + "proxy.txt")))))
{
proxyBox.Visible = false;
WriteStatus("No proxy settings saved!");
return;
}
string proxy_file = "";
if (!(String.IsNullOrEmpty(ProxyURL.Text)))
{
proxy_file += ProxyURL.Text + "\n";
proxy_url = ProxyURL.Text;
}
if (!(String.IsNullOrEmpty(ProxyUser.Text)))
{
proxy_file += ProxyUser.Text;
proxy_usr = ProxyUser.Text;
}
if (!(String.IsNullOrEmpty(proxy_file)))
{
File.WriteAllText(currentdir + "proxy.txt", proxy_file);
WriteStatus("Proxy settings saved!");
}
proxyBox.Visible = false;
SetAllEnabled(false);
ProxyVerifyBox.Visible = true; ProxyVerifyBox.Enabled = true;
ProxyPwdBox.Enabled = true; SaveProxyBtn.Enabled = true;
ProxyVerifyBox.Select();
}
private void proxySettingsToolStripMenuItem_Click(object sender, EventArgs e)
{
// Current directory...
string currentdir = Directory.GetCurrentDirectory();
if (!(currentdir.EndsWith(Path.DirectorySeparatorChar.ToString())) || !(currentdir.EndsWith(Path.AltDirectorySeparatorChar.ToString())))
currentdir += Path.DirectorySeparatorChar.ToString();
2009-08-20 19:41:08 +02:00
// Check for Proxy Settings file...
if (File.Exists(currentdir + "proxy.txt") == true)
{
string[] proxy_file = File.ReadAllLines(currentdir + "proxy.txt");
ProxyURL.Text = proxy_file[0];
if (proxy_file.Length > 1)
{
ProxyUser.Text = proxy_file[1];
}
}
proxyBox.Visible = true;
}
private void button18_Click(object sender, EventArgs e)
{
proxy_pwd = ProxyPwdBox.Text;
ProxyVerifyBox.Visible = false;
SetAllEnabled(true);
}
private void ProxyPwdBox_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar == Convert.ToChar(Keys.Enter))
button18_Click("LOLWUT", EventArgs.Empty);
}
private void ProxyAssistBtn_Click(object sender, EventArgs e)
{
MessageBox.Show("If you are behind a proxy, set these settings to get through to NUS." +
" If you have an alternate port for accessing your proxy, add ':' followed by the port." +
" You will be prompted for your password each time you run NUSD, for privacy purposes.");
}
private void loadNUSScriptToolStripMenuItem_Click(object sender, EventArgs e)
{
// Current directory...
string currentdir = Directory.GetCurrentDirectory();
if (!(currentdir.EndsWith(Path.DirectorySeparatorChar.ToString())) || !(currentdir.EndsWith(Path.AltDirectorySeparatorChar.ToString())))
currentdir += Path.DirectorySeparatorChar.ToString();
// Open a NUS script.
OpenFileDialog ofd = new OpenFileDialog();
ofd.Multiselect = false;
ofd.Filter = "NUS Scripts|*.nus|All Files|*.*";
if (Directory.Exists(currentdir + "scripts"))
ofd.InitialDirectory = currentdir + "scripts";
ofd.Title = "Load a NUS/Wiimpersonator script.";
if (ofd.ShowDialog() != DialogResult.Cancel)
{
script_filename = ofd.FileName;
BackgroundWorker scripter = new BackgroundWorker();
scripter.DoWork += new DoWorkEventHandler(RunScript);
scripter.RunWorkerAsync();
}
}
/// <summary>
/// Runs a NUS script (BG).
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="System.ComponentModel.DoWorkEventArgs"/> instance containing the event data.</param>
private void RunScript(object sender, System.ComponentModel.DoWorkEventArgs e)
{
Control.CheckForIllegalCrossThreadCalls = false;
script_mode = true;
statusbox.Text = "";
WriteStatus("Starting script download. Please be patient!");
string[] NUS_Entries;
if (script_filename != "\000")
{
NUS_Entries = File.ReadAllLines(script_filename);
}
else
{
NUS_Entries = nusentries;
}
WriteStatus(String.Format(" - Script loaded ({0} Titles)", NUS_Entries.Length));
for (int a = 0; a < NUS_Entries.Length; a++)
{
// Download the title
WriteStatus(String.Format("===== Running Download ({0}/{1}) =====", a+1, NUS_Entries.Length));
string[] title_info = NUS_Entries[a].Split(' ');
// don't let the delete issue reappear...
if (string.IsNullOrEmpty(title_info[0]))
break;
titleidbox.Text = title_info[0];
titleversion.Text = Convert.ToString(256*(byte.Parse(title_info[1].Substring(0, 2), System.Globalization.NumberStyles.HexNumber)));
titleversion.Text = Convert.ToString(Convert.ToInt32(titleversion.Text) + byte.Parse(title_info[1].Substring(2, 2), System.Globalization.NumberStyles.HexNumber));
button3_Click("Scripter", EventArgs.Empty);
Thread.Sleep(1000);
while (NUSDownloader.IsBusy)
{
Thread.Sleep(1000);
}
}
script_mode = false;
WriteStatus("Script completed!");
}
private void getCommonKeyMenuItem_Click(object sender, EventArgs e)
{
WriteStatus("Preparing to retrieve common key...");
// Begin the epic grab for freedom
WebClient databasedl = new WebClient();
statusbox.Refresh();
// Proxy
if (!(String.IsNullOrEmpty(proxy_url)))
{
WebProxy customproxy = new WebProxy();
customproxy.Address = new Uri(proxy_url);
if (String.IsNullOrEmpty(proxy_usr))
customproxy.UseDefaultCredentials = true;
else
{
NetworkCredential cred = new NetworkCredential();
cred.UserName = proxy_usr;
if (!(String.IsNullOrEmpty(proxy_pwd)))
cred.Password = proxy_pwd;
customproxy.Credentials = cred;
}
databasedl.Proxy = customproxy;
WriteStatus(" - Custom proxy settings applied!");
}
else
{
databasedl.Proxy = WebRequest.GetSystemWebProxy();
databasedl.UseDefaultCredentials = true;
}
string keyspostsource = databasedl.DownloadString("http://hackmii.com/2008/04/keys-keys-keys/");
statusbox.Refresh();
// Find our start point
string startofcommonkey = "Common key (";
keyspostsource = keyspostsource.Substring(keyspostsource.IndexOf(startofcommonkey) + startofcommonkey.Length, 32);
WriteStatus("Got the common key as: " + keyspostsource);
byte[] commonkey = HexStringToByteArray(keyspostsource);
if (WriteCommonKey("key.bin", commonkey))
{
BootChecks();
}
}
public static string ByteArrayToHexString(byte[] Bytes)
{
StringBuilder Result = new StringBuilder();
string HexAlphabet = "0123456789ABCDEF";
foreach (byte B in Bytes)
{
Result.Append(HexAlphabet[(int)(B >> 4)]);
Result.Append(HexAlphabet[(int)(B & 0xF)]);
}
return Result.ToString();
}
public static byte[] HexStringToByteArray(string Hex)
{
byte[] Bytes = new byte[Hex.Length / 2];
2010-06-29 18:06:15 +02:00
int[] HexValue = new int[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x0B, 0x0C, 0x0D,
0x0E, 0x0F };
for (int x = 0, i = 0; i < Hex.Length; i += 2, x += 1)
{
Bytes[x] = (byte)(HexValue[Char.ToUpper(Hex[i + 0]) - '0'] << 4 |
HexValue[Char.ToUpper(Hex[i + 1]) - '0']);
}
return Bytes;
2010-06-29 18:06:15 +02:00
}
2009-06-11 03:16:49 +02:00
}
}