diff --git a/NUS Downloader/Form1.cs b/NUS Downloader/Form1.cs index 52df8d4..ef60d01 100644 --- a/NUS Downloader/Form1.cs +++ b/NUS Downloader/Form1.cs @@ -867,6 +867,34 @@ namespace NUS_Downloader private void NUSDownloader_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e) { + Control.CheckForIllegalCrossThreadCalls = false; // this function would need major rewriting to get rid of this... + if (!(script_mode)) + WriteStatus("Starting NUS Download. Please be patient!"); + SetEnableforDownload(false); + downloadstartbtn.Text = "Starting NUS Download!"; + + // WebClient configuration + WebClient nusWC = new WebClient(); + nusWC = ConfigureWithProxy(nusWC); + nusWC.Headers.Add("User-Agent", "wii libnup/1.0"); // Set UserAgent to Wii value + + // Create\Configure NusClient + libWiiSharp.NusClient nusClient = new libWiiSharp.NusClient(); + nusClient.ConfigureNusClient(nusWC); + nusClient.UseLocalFiles = localuse.Checked; + nusClient.ContinueWithoutTicket = true; + nusClient.Debug += new EventHandler(nusClient_Debug); + + libWiiSharp.StoreType[] storeTypes = new libWiiSharp.StoreType[3]; + if (packbox.Checked) storeTypes[0] = libWiiSharp.StoreType.WAD; else storeTypes[0] = libWiiSharp.StoreType.Empty; + if (decryptbox.Checked) storeTypes[1] = libWiiSharp.StoreType.DecryptedContent; else storeTypes[1] = libWiiSharp.StoreType.Empty; + if (keepenccontents.Checked) storeTypes[2] = libWiiSharp.StoreType.EncryptedContent; else storeTypes[2] = libWiiSharp.StoreType.Empty; + + nusClient.DownloadTitle(titleidbox.Text, titleversion.Text, Path.Combine(CURRENT_DIR, "titles"), wadnamebox.Text, storeTypes); + + WriteStatus("NUS Download Finished."); + + /* THIS CODE WAD PRE-libWiiSharp // Preparations for Downloading Control.CheckForIllegalCrossThreadCalls = false; // this function would need major rewriting to get rid of this... if (!(script_mode)) @@ -958,12 +986,7 @@ namespace NUS_Downloader catch (Exception ex) { //WriteStatus("Download Failed: cetk"); - /*WriteStatus("You may be able to retrieve the contents by Ignoring the Ticket (Check below)"); - SetEnableforDownload(true); - downloadstartbtn.Text = "Start NUS Download!"; - dlprogress.Value = 0; - DeleteTitleDirectory(); - return;*/ + WriteStatus("Ticket not found! Continuing, however WAD packing and decryption are not possible!"); WriteStatus(" - Reason: " + @@ -1148,12 +1171,7 @@ namespace NUS_Downloader initCrypt(iv, titlekey); - /* Create decrypted file - 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( @@ -1191,7 +1209,12 @@ namespace NUS_Downloader if ((packbox.Checked == true) && (wiimode == true)) { PackWAD(titleid, tmdfull, titledirectory); - } + } */ + } + + void nusClient_Debug(object sender, libWiiSharp.MessageEventArgs e) + { + WriteStatus(e.Message); } private void NUSDownloader_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) diff --git a/NUS Downloader/MessageEventArgs.cs b/NUS Downloader/MessageEventArgs.cs new file mode 100644 index 0000000..7191ea0 --- /dev/null +++ b/NUS Downloader/MessageEventArgs.cs @@ -0,0 +1,29 @@ +/* This file is part of libWiiSharp + * Copyright (C) 2009 Leathl + * + * libWiiSharp is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libWiiSharp is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; + +namespace libWiiSharp +{ + public class MessageEventArgs : EventArgs + { + private string message; + public string Message { get { return message; } } + + public MessageEventArgs(string message) { this.message = message; } + } +} diff --git a/NUS Downloader/NUS Downloader.csproj b/NUS Downloader/NUS Downloader.csproj index 07b7729..2bbcf91 100644 --- a/NUS Downloader/NUS Downloader.csproj +++ b/NUS Downloader/NUS Downloader.csproj @@ -83,6 +83,7 @@ + diff --git a/NUS Downloader/NusClient.cs b/NUS Downloader/NusClient.cs index 2f6bcc3..c2a3dd1 100644 --- a/NUS Downloader/NusClient.cs +++ b/NUS Downloader/NusClient.cs @@ -15,17 +15,6 @@ * along with this program. If not, see . */ -/* Further modifications have been made for the purposes of NUS Downloader. - * See SVN changelog for further details. - */ - -/////////////////////////////////////// -// NUS Downloader: NusClient.cs // -// $Rev:: $ // -// $Author:: $ // -// $Date:: $ // -/////////////////////////////////////// - using System; using System.ComponentModel; using System.IO; @@ -34,12 +23,21 @@ using System.Security.Cryptography; namespace libWiiSharp { + public enum StoreType + { + EncryptedContent = 0, + DecryptedContent = 1, + WAD = 2, + All = 3, + Empty = 4 + } + public class NusClient : IDisposable { - //private const string nusUrl = "http://nus.cdn.shop.wii.com/ccs/download/"; + private const string nusUrl = "http://nus.cdn.shop.wii.com/ccs/download/"; private WebClient wcNus = new WebClient(); private bool useLocalFiles = false; - //private bool continueWithoutTicket = false; + private bool continueWithoutTicket = false; /// /// If true, existing local files will be used. @@ -48,7 +46,7 @@ namespace libWiiSharp /// /// If true, the download will be continued even if no ticket for the title is avaiable (WAD packaging and decryption are disabled). /// - //public bool ContinueWithoutTicket { get { return continueWithoutTicket; } set { continueWithoutTicket = value; } } + public bool ContinueWithoutTicket { get { return continueWithoutTicket; } set { continueWithoutTicket = value; } } #region IDisposable Members private bool isDisposed = false; @@ -82,6 +80,21 @@ namespace libWiiSharp wcNus = wcReady; } + + /// + /// Grabs a title from NUS, you can define several store types. + /// Leave the title version empty for the latest. + /// + /// + /// + /// + /// + public void DownloadTitle(string titleId, string titleVersion, string outputDir, string wadName, params StoreType[] storeTypes) + { + if (titleId.Length != 16) throw new Exception("Title ID must be 16 characters long!"); + downloadTitle(titleId, titleVersion, outputDir, wadName, storeTypes); + } + /// /// Grabs a TMD from NUS. /// Leave the title version empty for the latest. @@ -89,10 +102,10 @@ namespace libWiiSharp /// /// /// - public byte[] DownloadTMD(string titleId, string titleVersion, string nusUrl) + public TMD DownloadTMD(string titleId, string titleVersion) { if (titleId.Length != 16) throw new Exception("Title ID must be 16 characters long!"); - return downloadTmd(titleId, titleVersion, nusUrl); + return downloadTmd(titleId, titleVersion); } /// @@ -100,10 +113,10 @@ namespace libWiiSharp /// /// /// - public byte[] DownloadTicket(string titleId, string nusUrl) + public Ticket DownloadTicket(string titleId) { if (titleId.Length != 16) throw new Exception("Title ID must be 16 characters long!"); - return downloadTicket(titleId, nusUrl); + return downloadTicket(titleId); } /// @@ -114,15 +127,33 @@ namespace libWiiSharp /// /// /// - public byte[] DownloadSingleContent(string titleId, string titleVersion, string contentId, string nusUrl) + public byte[] DownloadSingleContent(string titleId, string titleVersion, string contentId) { if (titleId.Length != 16) throw new Exception("Title ID must be 16 characters long!"); - return downloadSingleContent(titleId, titleVersion, contentId, nusUrl); + return downloadSingleContent(titleId, titleVersion, contentId); + } + + /// + /// Grabs a single content file and decrypts it. + /// Leave the title version empty for the latest. + /// + /// + /// + /// + /// + public void DownloadSingleContent(string titleId, string titleVersion, string contentId, string savePath) + { + if (titleId.Length != 16) throw new Exception("Title ID must be 16 characters long!"); + if (!Directory.Exists(Path.GetDirectoryName(savePath))) Directory.CreateDirectory(Path.GetDirectoryName(savePath)); + if (File.Exists(savePath)) File.Delete(savePath); + + byte[] content = downloadSingleContent(titleId, titleVersion, contentId); + File.WriteAllBytes(savePath, content); } #endregion #region Private Functions - private byte[] downloadSingleContent(string titleId, string titleVersion, string contentId, string nusUrl) + private byte[] downloadSingleContent(string titleId, string titleVersion, string contentId) { uint cId = uint.Parse(contentId, System.Globalization.NumberStyles.HexNumber); contentId = cId.ToString("x8"); @@ -135,20 +166,20 @@ namespace libWiiSharp fireProgress(0); - //string tmdFile = "tmd" + (string.IsNullOrEmpty(titleVersion) ? string.Empty : string.Format(".{0}", titleVersion)); + string tmdFile = "tmd" + (string.IsNullOrEmpty(titleVersion) ? string.Empty : string.Format(".{0}", titleVersion)); string titleUrl = string.Format("{0}{1}/", nusUrl, titleId); - string contentIdString = contentId; - //int cIndex = 0; + string contentIdString = string.Empty; + int cIndex = 0; - /*Download TMD + //Download TMD fireDebug(" Downloading TMD..."); byte[] tmdArray = wcNus.DownloadData(titleUrl + tmdFile); fireDebug(" Parsing TMD..."); - TMD tmd = TMD.Load(tmdArray);*/ + TMD tmd = TMD.Load(tmdArray); - //fireProgress(20); + fireProgress(20); - /*Search for Content ID in TMD + //Search for Content ID in TMD fireDebug(" Looking for Content ID {0} in TMD...", contentId); bool foundContentId = false; for (int i = 0; i < tmd.Contents.Length; i++) @@ -172,13 +203,12 @@ namespace libWiiSharp fireProgress(40); - fireDebug(" Downloading Content... ({0} bytes)", tmd.Contents[cIndex].Size); */ - + //Download and Decrypt Content + fireDebug(" Downloading Content... ({0} bytes)", tmd.Contents[cIndex].Size); byte[] encryptedContent = wcNus.DownloadData(titleUrl + contentIdString); fireProgress(80); - /* fireDebug(" Decrypting Content..."); byte[] decryptedContent = decryptContent(encryptedContent, cIndex, tik, tmd); Array.Resize(ref decryptedContent, (int)tmd.Contents[cIndex].Size); @@ -193,11 +223,10 @@ namespace libWiiSharp fireProgress(100); fireDebug("Downloading Content (Content ID: {0}) of Title {1} v{2} Finished...", contentId, titleId, (string.IsNullOrEmpty(titleVersion)) ? "[Latest]" : titleVersion); - return decryptedContent;*/ - return encryptedContent; + return decryptedContent; } - private byte[] downloadTicket(string titleId, string nusUrl) + private Ticket downloadTicket(string titleId) { if (!CheckInet()) throw new Exception("You're not connected to the internet!"); @@ -205,10 +234,10 @@ namespace libWiiSharp string titleUrl = string.Format("{0}{1}/", nusUrl, titleId); byte[] tikArray = wcNus.DownloadData(titleUrl + "cetk"); - return tikArray; + return Ticket.Load(tikArray); } - private byte[] downloadTmd(string titleId, string titleVersion, string nusUrl) + private TMD downloadTmd(string titleId, string titleVersion) { if (!CheckInet()) throw new Exception("You're not connected to the internet!"); @@ -218,10 +247,10 @@ namespace libWiiSharp byte[] tmdArray = wcNus.DownloadData(titleUrl + tmdFile); - return tmdArray; + return TMD.Load(tmdArray); } - /*private void downloadTitle(string titleId, string titleVersion, string outputDir, StoreType[] storeTypes) + private void downloadTitle(string titleId, string titleVersion, string outputDir, string wadName, StoreType[] storeTypes) { fireDebug("Downloading Title {0} v{1}...", titleId, (string.IsNullOrEmpty(titleVersion)) ? "[Latest]" : titleVersion); @@ -259,6 +288,8 @@ namespace libWiiSharp storeEncrypted = true; storeWad = true; break; + case StoreType.Empty: + break; } } @@ -266,26 +297,40 @@ namespace libWiiSharp if (!CheckInet()) { fireDebug(" Connection not found..."); throw new Exception("You're not connected to the internet!"); } - if (outputDir[outputDir.Length - 1] != Path.DirectorySeparatorChar) outputDir += Path.DirectorySeparatorChar; + //if (outputDir[outputDir.Length - 1] != Path.DirectorySeparatorChar) outputDir += Path.DirectorySeparatorChar; if (!Directory.Exists(outputDir)) Directory.CreateDirectory(outputDir); + if (!Directory.Exists(Path.Combine(outputDir, titleId))) Directory.CreateDirectory(Path.Combine(outputDir, titleId)); + outputDir = Path.Combine(outputDir, titleId); string tmdFile = "tmd" + (string.IsNullOrEmpty(titleVersion) ? string.Empty : string.Format(".{0}", titleVersion)); //Download TMD fireDebug(" Downloading TMD..."); + TMD tmd; try { - wcNus.DownloadFile(titleUrl + tmdFile, outputDir + tmdFile); + tmd = TMD.Load(wcNus.DownloadData(titleUrl + tmdFile)); } catch (Exception ex) { fireDebug(" Downloading TMD Failed..."); throw new Exception("Downloading TMD Failed:\n" + ex.Message); } + //Parse TMD + fireDebug(" Parsing TMD..."); + + if (string.IsNullOrEmpty(titleVersion)) { fireDebug(" -> Title Version: {0}", tmd.TitleVersion); } + fireDebug(" -> {0} Contents", tmd.NumOfContents); + + if (!Directory.Exists(Path.Combine(outputDir, tmd.TitleVersion.ToString()))) Directory.CreateDirectory(Path.Combine(outputDir, tmd.TitleVersion.ToString())); + outputDir = Path.Combine(outputDir, tmd.TitleVersion.ToString()); + + File.WriteAllBytes(Path.Combine(outputDir, tmdFile), tmd.ToByteArray()); + fireProgress(5); //Download cetk fireDebug(" Downloading Ticket..."); try { - wcNus.DownloadFile(titleUrl + "cetk", outputDir + "cetk"); + wcNus.DownloadFile(Path.Combine(titleUrl, "cetk"), Path.Combine(outputDir, "cetk")); } catch (Exception ex) { @@ -301,15 +346,9 @@ namespace libWiiSharp fireProgress(10); - //Parse TMD and Ticket - fireDebug(" Parsing TMD..."); - TMD tmd = TMD.Load(outputDir + tmdFile); - - if (string.IsNullOrEmpty(titleVersion)) { fireDebug(" -> Title Version: {0}", tmd.TitleVersion); } - fireDebug(" -> {0} Contents", tmd.NumOfContents); - + // Parse Ticket fireDebug(" Parsing Ticket..."); - Ticket tik = Ticket.Load(outputDir + "cetk"); + Ticket tik = Ticket.Load(Path.Combine(outputDir, "cetk")); string[] encryptedContents = new string[tmd.NumOfContents]; @@ -319,13 +358,13 @@ namespace libWiiSharp fireDebug(" Downloading Content #{0} of {1}... ({2} bytes)", i + 1, tmd.NumOfContents, tmd.Contents[i].Size); fireProgress(((i + 1) * 60 / tmd.NumOfContents) + 10); - if (useLocalFiles && File.Exists(outputDir + tmd.Contents[i].ContentID.ToString("x8"))) + if (useLocalFiles && File.Exists(Path.Combine(outputDir, tmd.Contents[i].ContentID.ToString("x8")))) { fireDebug(" Using Local File, Skipping..."); continue; } try { wcNus.DownloadFile(titleUrl + tmd.Contents[i].ContentID.ToString("x8"), - outputDir + tmd.Contents[i].ContentID.ToString("x8")); + Path.Combine(outputDir, tmd.Contents[i].ContentID.ToString("x8"))); encryptedContents[i] = tmd.Contents[i].ContentID.ToString("x8"); } @@ -344,7 +383,7 @@ namespace libWiiSharp //Decrypt Content byte[] decryptedContent = - decryptContent(File.ReadAllBytes(outputDir + tmd.Contents[i].ContentID.ToString("x8")), i, tik, tmd); + decryptContent(File.ReadAllBytes(Path.Combine(outputDir, tmd.Contents[i].ContentID.ToString("x8"))), i, tik, tmd); Array.Resize(ref decryptedContent, (int)tmd.Contents[i].Size); //Check SHA1 @@ -353,7 +392,7 @@ namespace libWiiSharp { fireDebug(@"/!\ /!\ /!\ Hashes do not match /!\ /!\ /!\"); throw new Exception(string.Format("Content #{0}: Hashes do not match!", i)); } //Write Decrypted Content - File.WriteAllBytes(outputDir + tmd.Contents[i].ContentID.ToString("x8") + ".app", decryptedContent); + File.WriteAllBytes(Path.Combine(outputDir, (tmd.Contents[i].ContentID.ToString("x8") + ".app")), decryptedContent); } s.Clear(); @@ -363,16 +402,16 @@ namespace libWiiSharp if (storeWad) { fireDebug(" Building Certificate Chain..."); - CertificateChain cert = CertificateChain.FromTikTmd(outputDir + "cetk", outputDir + tmdFile); + CertificateChain cert = CertificateChain.FromTikTmd(Path.Combine(outputDir, "cetk"), Path.Combine(outputDir, tmdFile)); byte[][] contents = new byte[tmd.NumOfContents][]; for (int i = 0; i < tmd.NumOfContents; i++) - contents[i] = File.ReadAllBytes(outputDir + tmd.Contents[i].ContentID.ToString("x8") + ".app"); + contents[i] = File.ReadAllBytes(Path.Combine(outputDir, (tmd.Contents[i].ContentID.ToString("x8") + ".app"))); fireDebug(" Creating WAD..."); WAD wad = WAD.Create(cert, tik, tmd, contents); - wad.Save(outputDir + tmd.TitleID.ToString("x16") + "v" + tmd.TitleVersion.ToString() + ".wad"); + wad.Save(Path.Combine(outputDir, wadName)); } //Delete not wanted files @@ -380,28 +419,28 @@ namespace libWiiSharp { fireDebug(" Deleting Encrypted Contents..."); for (int i = 0; i < encryptedContents.Length; i++) - if (File.Exists(outputDir + encryptedContents[i])) File.Delete(outputDir + encryptedContents[i]); + if (File.Exists(Path.Combine(outputDir, encryptedContents[i]))) File.Delete(Path.Combine(outputDir, encryptedContents[i])); } if (storeWad && !storeDecrypted) { fireDebug(" Deleting Decrypted Contents..."); for (int i = 0; i < encryptedContents.Length; i++) - if (File.Exists(outputDir + encryptedContents[i] + ".app")) File.Delete(outputDir + encryptedContents[i] + ".app"); + if (File.Exists(Path.Combine(outputDir, (encryptedContents[i] + ".app")))) File.Delete(Path.Combine(outputDir, (encryptedContents[i] + ".app"))); } if (!storeDecrypted && !storeEncrypted) { fireDebug(" Deleting TMD and Ticket..."); - File.Delete(outputDir + tmdFile); - File.Delete(outputDir + "cetk"); + File.Delete(Path.Combine(outputDir, tmdFile)); + File.Delete(Path.Combine(outputDir, "cetk")); } fireDebug("Downloading Title {0} v{1} Finished...", titleId, (string.IsNullOrEmpty(titleVersion)) ? "[Latest]" : titleVersion); fireProgress(100); } - /*private byte[] decryptContent(byte[] content, int contentIndex, Ticket tik, TMD tmd) + private byte[] decryptContent(byte[] content, int contentIndex, Ticket tik, TMD tmd) { Array.Resize(ref content, Shared.AddPadding(content.Length, 16)); byte[] titleKey = tik.TitleKey; @@ -431,7 +470,7 @@ namespace libWiiSharp ms.Dispose(); return decCont; - }*/ + } private bool CheckInet() { @@ -470,15 +509,3 @@ namespace libWiiSharp #endregion } } - -namespace libWiiSharp -{ - public class MessageEventArgs : EventArgs - { - private string message; - public string Message { get { return message; } } - - public MessageEventArgs(string message) { this.message = message; } - } -} -