private TMD downloadTmd() { if (!CheckInet()) { throw new Exception("You're not connected to the internet!"); } string titleUrl = getTmdUrl(); string filePath = Path.Combine(mOutputDir, TMD_FILE_NAME); FileInfo fi = new FileInfo(filePath); if (mUseLocalFiles && fi.Exists && fi.Length > 0) { // has file } else { mWcNus.DownloadFile(titleUrl, filePath); } mTmd = TMD.Load(filePath); return(mTmd); }
private byte[] decryptContent(byte[] content, int contentIndex, Ticket tik, TMD tmd) { Array.Resize(ref content, Shared.AddPadding(content.Length, 16)); byte[] titleKey = tik.TitleKey; byte[] iv = new byte[16]; byte[] tmp = BitConverter.GetBytes(tmd.Contents[contentIndex].Index); iv[0] = tmp[1]; iv[1] = tmp[0]; RijndaelManaged rm = new RijndaelManaged(); rm.Mode = CipherMode.CBC; rm.Padding = PaddingMode.None; rm.KeySize = 128; rm.BlockSize = 128; rm.Key = titleKey; rm.IV = iv; ICryptoTransform decryptor = rm.CreateDecryptor(); MemoryStream ms = new MemoryStream(content); CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read); byte[] decCont = new byte[content.Length]; cs.Read(decCont, 0, decCont.Length); cs.Dispose(); ms.Dispose(); return(decCont); }
/// <summary> /// Loads a tmd file. /// </summary> /// <param name="tmd"></param> /// <returns></returns> public static TMD Load(Stream tmd) { TMD t = new TMD(); t.parseTmd(tmd); return(t); }
private byte[] DecryptContent(byte[] content, int contentIndex, Ticket tik, TMD tmd) { Array.Resize(ref content, Shared.AddPadding(content.Length, 16)); var titleKey = tik.TitleKey; var iv = new byte[16]; var tmp = BitConverter.GetBytes(tmd.Contents[contentIndex].Index); iv[0] = tmp[1]; iv[1] = tmp[0]; var rm = new RijndaelManaged() { Mode = CipherMode.CBC, Padding = PaddingMode.None, KeySize = 128, BlockSize = 128, Key = titleKey, IV = iv }; var decryptor = rm.CreateDecryptor(); var ms = new MemoryStream(content); var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read); var decCont = new byte[content.Length]; cs.Read(decCont, 0, decCont.Length); cs.Dispose(); ms.Dispose(); return(decCont); }
/// <summary> /// Loads a tmd file. /// </summary> /// <param name="tmdFile"></param> /// <returns></returns> public static TMD Load(byte[] tmdFile) { TMD t = new TMD(); MemoryStream ms = new MemoryStream(tmdFile); try { t.parseTmd(ms); } catch { ms.Dispose(); throw; } ms.Dispose(); return(t); }
private TMD downloadTmd(string titleId, string titleVersion) { if (!CheckInet()) { throw new Exception("You're not connected to the internet!"); } string titleUrl = string.Format("{0}{1}/", nusUrl, titleId); string tmdFile = "tmd" + (string.IsNullOrEmpty(titleVersion) ? string.Empty : string.Format(".{0}", titleVersion)); byte[] tmdArray = wcNus.DownloadData(titleUrl + tmdFile); return(TMD.Load(tmdArray)); }
private TMD downloadTmdToMemory() { if (!CheckInet()) { throw new Exception("You're not connected to the internet!"); } string titleUrl = getTmdUrl(); byte[] data = mWcNus.DownloadData(titleUrl); mTmd = TMD.Load(data); return(mTmd); }
public void exportTitle() { // Create dirs if (!Directory.Exists(mOutputDirBase)) { Directory.CreateDirectory(mOutputDirBase); } //Download TMD fireDebug(" - TMD... "); TMD tmd = downloadTmdToMemory(); //fireDebug(" {0} Contents.\r\n", tmd.NumOfContents); mBytesTotal = 0; for (int i = 0; i < tmd.NumOfContents; i++) { mBytesTotal += (long)tmd.Contents[i].Size; } fireDebug(" Contents:" + tmd.NumOfContents + ", Size:" + ConvertUnit(mBytesTotal) + "\r\n"); string[] encryptedContents = new string[tmd.NumOfContents]; mBytesTotalDone = 0; var listFilePath = Path.Combine(mOutputDirBase, mTitleId + "_LIST_" + ConvertUnit(mBytesTotal, "") + ".txt"); StringWriter sw = new StringWriter(); sw.WriteLine(getTmdUrl()); //Export Content for (int i = 0; i < tmd.NumOfContents; i++) { TMD_Content content = tmd.Contents[i]; var list = getContentDownloadUrls(content); foreach (var j in list) { sw.WriteLine(j); } } File.WriteAllText(listFilePath, sw.ToString()); }
private void downloadTitle(string titleId, string titleVersion, string outputDir, StoreType[] storeTypes) { fireDebug("Downloading Title {0} v{1}...", titleId, (string.IsNullOrEmpty(titleVersion)) ? "[Latest]" : titleVersion); if (storeTypes.Length < 1) { fireDebug(" No store types were defined..."); throw new Exception("You must at least define one store type!"); } string titleUrl = string.Format("{0}{1}/", nusUrl, titleId); bool storeEncrypted = false; bool storeDecrypted = false; bool storeWad = false; fireProgress(0); foreach (StoreType st in storeTypes) { switch (st) { case StoreType.DecryptedContent: fireDebug(" -> Storing Decrypted Content..."); storeDecrypted = true; break; case StoreType.EncryptedContent: fireDebug(" -> Storing Encrypted Content..."); storeEncrypted = true; break; case StoreType.WAD: fireDebug(" -> Storing WAD..."); storeWad = true; break; case StoreType.All: fireDebug(" -> Storing Decrypted Content..."); fireDebug(" -> Storing Encrypted Content..."); fireDebug(" -> Storing WAD..."); storeDecrypted = true; storeEncrypted = true; storeWad = true; break; } } fireDebug(" Checking for Internet connection..."); 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 (!Directory.Exists(outputDir)) { Directory.CreateDirectory(outputDir); } string tmdFile = "tmd" + (string.IsNullOrEmpty(titleVersion) ? string.Empty : string.Format(".{0}", titleVersion)); //Download TMD fireDebug(" Downloading TMD..."); try { wcNus.DownloadFile(titleUrl + tmdFile, outputDir + tmdFile); } catch (Exception ex) { fireDebug(" Downloading TMD Failed..."); throw new Exception("Downloading TMD Failed:\n" + ex.Message); } fireProgress(5); //Download cetk fireDebug(" Downloading Ticket..."); try { wcNus.DownloadFile(titleUrl + "cetk", outputDir + "cetk"); } catch (Exception ex) { if (!continueWithoutTicket || !storeEncrypted) { fireDebug(" Downloading Ticket Failed..."); throw new Exception("Downloading Ticket Failed:\n" + ex.Message); } storeDecrypted = false; storeWad = false; } 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); fireDebug(" Parsing Ticket..."); Ticket tik = Ticket.Load(outputDir + "cetk"); string[] encryptedContents = new string[tmd.NumOfContents]; //Download Content for (int i = 0; i < tmd.NumOfContents; i++) { 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"))) { fireDebug(" Using Local File, Skipping..."); continue; } try { wcNus.DownloadFile(titleUrl + tmd.Contents[i].ContentID.ToString("x8"), outputDir + tmd.Contents[i].ContentID.ToString("x8")); encryptedContents[i] = tmd.Contents[i].ContentID.ToString("x8"); } catch (Exception ex) { fireDebug(" Downloading Content #{0} of {1} failed...", i + 1, tmd.NumOfContents); throw new Exception("Downloading Content Failed:\n" + ex.Message); } } //Decrypt Content if (storeDecrypted || storeWad) { SHA1 s = SHA1.Create(); for (int i = 0; i < tmd.NumOfContents; i++) { fireDebug(" Decrypting Content #{0} of {1}...", i + 1, tmd.NumOfContents); fireProgress(((i + 1) * 20 / tmd.NumOfContents) + 75); //Decrypt Content byte[] decryptedContent = decryptContent(File.ReadAllBytes(outputDir + tmd.Contents[i].ContentID.ToString("x8")), i, tik, tmd); Array.Resize(ref decryptedContent, (int)tmd.Contents[i].Size); //Check SHA1 byte[] newSha = s.ComputeHash(decryptedContent); if (!Shared.CompareByteArrays(newSha, tmd.Contents[i].Hash)) { 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); } s.Clear(); } //Pack Wad if (storeWad) { fireDebug(" Building Certificate Chain..."); CertificateChain cert = CertificateChain.FromTikTmd(outputDir + "cetk", 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"); } fireDebug(" Creating WAD..."); WAD wad = WAD.Create(cert, tik, tmd, contents); wad.Save(outputDir + tmd.TitleID.ToString("x16") + "v" + tmd.TitleVersion.ToString() + ".wad"); } //Delete not wanted files if (!storeEncrypted) { fireDebug(" Deleting Encrypted Contents..."); for (int i = 0; i < encryptedContents.Length; i++) { if (File.Exists(outputDir + encryptedContents[i])) { File.Delete(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 (!storeDecrypted && !storeEncrypted) { fireDebug(" Deleting TMD and Ticket..."); File.Delete(outputDir + tmdFile); File.Delete(outputDir + "cetk"); } fireDebug("Downloading Title {0} v{1} Finished...", titleId, (string.IsNullOrEmpty(titleVersion)) ? "[Latest]" : titleVersion); fireProgress(100); }
private byte[] downloadSingleContent(string titleId, string titleVersion, string contentId) { uint cId = uint.Parse(contentId, System.Globalization.NumberStyles.HexNumber); contentId = cId.ToString("x8"); fireDebug("Downloading Content (Content ID: {0}) of Title {1} v{2}...", contentId, titleId, (string.IsNullOrEmpty(titleVersion)) ? "[Latest]" : titleVersion); fireDebug(" Checking for Internet connection..."); if (!CheckInet()) { fireDebug(" Connection not found..."); throw new Exception("You're not connected to the internet!"); } fireProgress(0); string tmdFile = "tmd" + (string.IsNullOrEmpty(titleVersion) ? string.Empty : string.Format(".{0}", titleVersion)); string titleUrl = string.Format("{0}{1}/", nusUrl, titleId); string contentIdString = string.Empty; int cIndex = 0; //Download TMD fireDebug(" Downloading TMD..."); byte[] tmdArray = wcNus.DownloadData(titleUrl + tmdFile); fireDebug(" Parsing TMD..."); TMD tmd = TMD.Load(tmdArray); fireProgress(20); //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++) { if (tmd.Contents[i].ContentID == cId) { fireDebug(" Content ID {0} found in TMD...", contentId); foundContentId = true; contentIdString = tmd.Contents[i].ContentID.ToString("x8"); cIndex = i; break; } } if (!foundContentId) { fireDebug(" Content ID {0} wasn't found in TMD...", contentId); throw new Exception("Content ID wasn't found in the TMD!"); } //Download Ticket fireDebug(" Downloading Ticket..."); byte[] tikArray = wcNus.DownloadData(titleUrl + "cetk"); fireDebug(" Parsing Ticket..."); Ticket tik = Ticket.Load(tikArray); fireProgress(40); //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); //Check SHA1 SHA1 s = SHA1.Create(); byte[] newSha = s.ComputeHash(decryptedContent); if (!Shared.CompareByteArrays(newSha, tmd.Contents[cIndex].Hash)) { fireDebug(@"/!\ /!\ /!\ Hashes do not match /!\ /!\ /!\"); throw new Exception("Hashes do not match!"); } fireProgress(100); fireDebug("Downloading Content (Content ID: {0}) of Title {1} v{2} Finished...", contentId, titleId, (string.IsNullOrEmpty(titleVersion)) ? "[Latest]" : titleVersion); return(decryptedContent); }
/// <summary> /// Creates a WAD file from contents. /// </summary> /// <param name="cert"></param> /// <param name="tik"></param> /// <param name="tmd"></param> /// <param name="contents"></param> public void CreateNew(CertificateChain cert, Ticket tik, TMD tmd, byte[][] contents) { this.cert = cert; this.tik = tik; this.tmd = tmd; this.contents = new List<byte[]>(contents); this.wadHeader = new WAD_Header(); this.wadHeader.TmdSize = (uint)(484 + (tmd.Contents.Length * 36)); int contentSize = 0; for (int i = 0; i < contents.Length - 1; i++) contentSize += Shared.AddPadding(contents[i].Length); contentSize += contents[contents.Length - 1].Length; this.wadHeader.ContentSize = (uint)contentSize; for (int i = 0; i < this.tmd.Contents.Length; i++) if (this.tmd.Contents[i].Index == 0x0000) { try { this.bannerApp.LoadFile(contents[i]); hasBanner = true; } catch { hasBanner = false; } //Probably System Wad => No Banner App... break; } }
/// <summary> /// Loads a tmd file. /// </summary> /// <param name="tmd"></param> /// <returns></returns> public static TMD Load(Stream tmd) { TMD t = new TMD(); t.parseTmd(tmd); return t; }
/// <summary> /// Loads a tmd file. /// </summary> /// <param name="tmdFile"></param> /// <returns></returns> public static TMD Load(byte[] tmdFile) { TMD t = new TMD(); MemoryStream ms = new MemoryStream(tmdFile); try { t.parseTmd(ms); } catch { ms.Dispose(); throw; } ms.Dispose(); return t; }
/// <summary> /// Grabs a title from NUS, you can define several store types. /// Leave the title version empty for the latest. /// </summary> /// <param name="titleId"></param> /// <param name="titleVersion"></param> /// <param name="outputDir"></param> /// <param name="storeTypes"></param> public void DownloadTitle(string titleId, string titleVersion, string outputDir, StoreType[] storeTypes) { FireDebug("Downloading Title {0} v{1}...", titleId, (string.IsNullOrEmpty(titleVersion)) ? "[Latest]" : titleVersion); if (storeTypes.Length < 1) { FireDebug(" No store types were defined..."); throw new Exception("You must at least define one store type!"); } var titleInfo = Database.GetTitle(titleId); string titleUrl = $"{nusUrl}{titleId}/"; string titleUrl2 = $"{nusUrl2}{titleId}/"; bool storeEncrypted = false; bool storeDecrypted = false; FireProgress(0); foreach (StoreType st in storeTypes) { switch (st) { case StoreType.DecryptedContent: FireDebug(" [=] Storing Decrypted Content..."); storeDecrypted = true; break; case StoreType.EncryptedContent: FireDebug(" [=] Storing Encrypted Content..."); storeEncrypted = true; break; case StoreType.All: FireDebug(" [=] Storing Decrypted Content..."); FireDebug(" [=] Storing Encrypted Content..."); FireDebug(" [=] Storing WAD..."); storeDecrypted = true; storeEncrypted = true; break; case StoreType.Empty: break; } } FireDebug(" - Checking for Internet connection..."); if (!CheckInet()) { FireDebug(" + Connection not found..."); throw new Exception("You're not connected to the internet!"); } if (!Directory.Exists(outputDir)) { Directory.CreateDirectory(outputDir); } if (!Directory.Exists(Path.Combine(outputDir, titleInfo.Name))) { Directory.CreateDirectory(Path.Combine(outputDir, titleInfo.Name)); } outputDir = Path.Combine(outputDir, titleInfo.Name); string tmdFile = "tmd" + (string.IsNullOrEmpty(titleVersion) ? string.Empty : string.Format(".{0}", titleVersion)); //Download TMD FireDebug(" - Downloading TMD..."); TMD tmd; byte[] tmdFileWithCerts; try { tmdFileWithCerts = wcNus.DownloadData(titleUrl + tmdFile); tmd = TMD.Load(tmdFileWithCerts); } 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()); titleversion = tmd.TitleVersion; File.WriteAllBytes(Path.Combine(outputDir, tmdFile), tmdFileWithCerts); FireProgress(5); //Download cetk FireDebug(" - Downloading Ticket..."); try { wcNus.DownloadFile(Path.Combine(titleUrl, "cetk"), Path.Combine(outputDir, "cetk")); } catch (Exception ex) { try { if (titleInfo.Ticket == "1") { var cetkUrl = $"{WII_TIK_URL}{titleId.ToLower()}.tik"; wcNus.DownloadFile(cetkUrl, Path.Combine(outputDir, "cetk")); } } catch { continueWithoutTicket = false; if (!continueWithoutTicket || !storeEncrypted) { FireDebug(" + Downloading Ticket Failed..."); throw new Exception("Downloading Ticket Failed:\n" + ex.Message); } if (!(File.Exists(Path.Combine(outputDir, "cetk")))) { storeDecrypted = false; } } } FireProgress(10); // Parse Ticket Ticket tik = new Ticket(); if (File.Exists(Path.Combine(outputDir, "cetk"))) { FireDebug(" + Parsing Ticket..."); tik = Ticket.Load(Path.Combine(outputDir, "cetk")); // DSi ticket? Must make sure to use DSi Key :D if (nusUrl == DSI_NUS_URL) { tik.DSiTicket = true; } } else { FireDebug(" + Ticket Unavailable..."); } string[] encryptedContents = new string[tmd.NumOfContents]; //Download Content for (int i = 0; i < tmd.NumOfContents; i++) { Form1.token.ThrowIfCancellationRequested(); var size = Toolbelt.SizeSuffix(tmd.Contents[i].Size); FireDebug(" - Downloading Content #{0} of {1}... ({2} bytes)", i + 1, tmd.NumOfContents, size); FireProgress(((i + 1) * 60 / tmd.NumOfContents) + 10); var contentPath = Path.Combine(outputDir, tmd.Contents[i].ContentID.ToString("x8")); if (useLocalFiles && Toolbelt.IsValid(tmd.Contents[i], contentPath)) { FireDebug(" + Using Local File, Skipping..."); continue; } try { var downloadUrl = titleUrl + tmd.Contents[i].ContentID.ToString("x8"); var outputdir = Path.Combine(outputDir, tmd.Contents[i].ContentID.ToString("x8")); wcNus.DownloadFile(downloadUrl, outputdir); encryptedContents[i] = tmd.Contents[i].ContentID.ToString("x8"); } catch (Exception ex) { FireDebug(" - Downloading Content #{0} of {1} failed...", i + 1, tmd.NumOfContents); throw new Exception("Downloading Content Failed:\n" + ex.Message); } } //Decrypt Content if (storeDecrypted) { FireDebug(" - Decrypting Content..."); Toolbelt.CDecrypt(this, outputDir); } //Delete not wanted files if (!storeEncrypted) { FireDebug(" - Deleting Encrypted Contents..."); for (int i = 0; i < tmd.Contents.Length; i++) { if (File.Exists(Path.Combine(outputDir, tmd.Contents[i].ContentID.ToString("x8")))) { File.Delete(Path.Combine(outputDir, tmd.Contents[i].ContentID.ToString("x8"))); } } } if (!storeDecrypted && !storeEncrypted) { FireDebug(" - Deleting TMD and Ticket..."); File.Delete(Path.Combine(outputDir, tmdFile)); File.Delete(Path.Combine(outputDir, "cetk")); } FireDebug("Downloading Title {0} v{1} Finished...", titleId, tmd.TitleVersion /*(string.IsNullOrEmpty(titleVersion)) ? "[Latest]" : titleVersion*/); FireProgress(100); }
public void downloadTitle() { //fireDebug("Downloading Title {0} {1}...", mTitleId, mTitleVersion); // Create dirs if (!Directory.Exists(mOutputDir)) { Directory.CreateDirectory(mOutputDir); } //Download TMD fireDebug(" - TMD... "); TMD tmd = downloadTmd(); //fireDebug(" {0} Contents.\r\n", tmd.NumOfContents); mBytesTotal = 0; for (int i = 0; i < tmd.NumOfContents; i++) { mBytesTotal += (long)tmd.Contents[i].Size; } fireDebug(" Contents:" + tmd.NumOfContents + ", Size:" + ConvertUnit(mBytesTotal) + "\r\n"); fireDebug(" - Ticket..."); //Download cetk try { downloadTicket(); fireDebug(" OK! \r\n"); } catch (Exception ex) { fireDebug(" Fail! \r\n"); if (!mContinueWithoutTicket) { fireDebug(" + Downloading Ticket Failed..."); throw new Exception("Downloading Ticket Failed:\n" + ex.Message); } } string[] encryptedContents = new string[tmd.NumOfContents]; mBytesTotalDone = 0; //Download Content for (int i = 0; i < tmd.NumOfContents; i++) { TMD_Content content = tmd.Contents[i]; fireDebug(" - Content:[{0}/{1}], ID:{2}, Size:{3} ...", i + 1, tmd.NumOfContents, content.ContentID.ToString("x8"), ConvertUnit((long)content.Size)); fireTotalProgress(mBytesTotalDone, mBytesTotal); downloadContent(content); if (!cancelDownload) { mBytesTotalDone += (long)tmd.Contents[i].Size; fireTotalProgress(mBytesTotalDone, mBytesTotal); fireDebug(" OK! \r\n"); } else { fireDebug("Canceled!\r\n"); throw new OperationCanceledException(); } } // Create cert file createCert(); }
/// <summary> /// Creates a WAD file from contents. /// </summary> /// <param name="cert"></param> /// <param name="tik"></param> /// <param name="tmd"></param> /// <param name="contents"></param> /// <returns></returns> public static WAD Create(CertificateChain cert, Ticket tik, TMD tmd, byte[][] contents) { WAD w = new WAD(); w.cert = cert; w.tik = tik; w.tmd = tmd; w.contents = new List<byte[]>(contents); w.wadHeader = new WAD_Header(); w.wadHeader.TmdSize = (uint)(484 + (tmd.Contents.Length * 36)); int contentSize = 0; for (int i = 0; i < contents.Length - 1; i++) contentSize += Shared.AddPadding(contents[i].Length); contentSize += contents[contents.Length - 1].Length; w.wadHeader.ContentSize = (uint)contentSize; for (int i = 0; i < w.tmd.Contents.Length; i++) if (w.tmd.Contents[i].Index == 0x0000) { try { w.bannerApp.LoadFile(contents[i]); w.hasBanner = true; } catch { w.hasBanner = false; } //Probably System Wad => No Banner App... break; } return w; }
private byte[] decryptContent(byte[] content, int contentIndex, Ticket tik, TMD tmd) { Array.Resize(ref content, Shared.AddPadding(content.Length, 16)); byte[] titleKey = tik.TitleKey; byte[] iv = new byte[16]; byte[] tmp = BitConverter.GetBytes(tmd.Contents[contentIndex].Index); iv[0] = tmp[1]; iv[1] = tmp[0]; RijndaelManaged rm = new RijndaelManaged(); rm.Mode = CipherMode.CBC; rm.Padding = PaddingMode.None; rm.KeySize = 128; rm.BlockSize = 128; rm.Key = titleKey; rm.IV = iv; ICryptoTransform decryptor = rm.CreateDecryptor(); MemoryStream ms = new MemoryStream(content); CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read); byte[] decCont = new byte[content.Length]; cs.Read(decCont, 0, decCont.Length); cs.Dispose(); ms.Dispose(); return decCont; }
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); if (storeTypes.Length < 1) { fireDebug(" No store types were defined..."); throw new Exception("You must at least define one store type!"); } string titleUrl = string.Format("{0}{1}/", nusUrl, titleId); bool storeEncrypted = false; bool storeDecrypted = false; bool storeWad = false; fireProgress(0); foreach (StoreType st in storeTypes) { switch (st) { case StoreType.DecryptedContent: fireDebug(" [=] Storing Decrypted Content..."); storeDecrypted = true; break; case StoreType.EncryptedContent: fireDebug(" [=] Storing Encrypted Content..."); storeEncrypted = true; break; case StoreType.WAD: fireDebug(" [=] Storing WAD..."); storeWad = true; break; case StoreType.All: fireDebug(" [=] Storing Decrypted Content..."); fireDebug(" [=] Storing Encrypted Content..."); fireDebug(" [=] Storing WAD..."); storeDecrypted = true; storeEncrypted = true; storeWad = true; break; case StoreType.Empty: break; } } /* * fireDebug(" - Checking for Internet connection..."); * if (!CheckInet()) * { fireDebug(" + Connection not found..."); throw new Exception("You're not connected to the internet!"); } */ 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; byte[] tmdFileWithCerts; try { tmdFileWithCerts = wcNus.DownloadData(titleUrl + tmdFile); tmd = TMD.Load(tmdFileWithCerts); } 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()); this.titleversion = tmd.TitleVersion; File.WriteAllBytes(Path.Combine(outputDir, tmdFile), tmd.ToByteArray()); fireProgress(5); //Download cetk fireDebug(" - Downloading Ticket..."); try { wcNus.DownloadFile(Path.Combine(titleUrl, "cetk"), Path.Combine(outputDir, "cetk")); } catch (Exception ex) { if (!continueWithoutTicket || !storeEncrypted) { fireDebug(" + Downloading Ticket Failed..."); throw new Exception("Downloading Ticket Failed:\n" + ex.Message); } if (!(File.Exists(Path.Combine(outputDir, "cetk")))) { storeDecrypted = false; storeWad = false; } } fireProgress(10); // Parse Ticket Ticket tik = new Ticket(); if (File.Exists(Path.Combine(outputDir, "cetk"))) { fireDebug(" + Parsing Ticket..."); tik = Ticket.Load(Path.Combine(outputDir, "cetk")); // DSi ticket? Must make sure to use DSi Key :D if (nusUrl == DSI_NUS_URL) { tik.DSiTicket = true; } } else { fireDebug(" + Ticket Unavailable..."); } string[] encryptedContents = new string[tmd.NumOfContents]; //Download Content for (int i = 0; i < tmd.NumOfContents; i++) { 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(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"), Path.Combine(outputDir, tmd.Contents[i].ContentID.ToString("x8"))); encryptedContents[i] = tmd.Contents[i].ContentID.ToString("x8"); } catch (Exception ex) { fireDebug(" - Downloading Content #{0} of {1} failed...", i + 1, tmd.NumOfContents); throw new Exception("Downloading Content Failed:\n" + ex.Message); } } //Decrypt Content if (storeDecrypted || storeWad) { SHA1 s = SHA1.Create(); for (int i = 0; i < tmd.NumOfContents; i++) { fireDebug(" - Decrypting Content #{0} of {1}...", i + 1, tmd.NumOfContents); fireProgress(((i + 1) * 20 / tmd.NumOfContents) + 75); //Decrypt Content byte[] decryptedContent = 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 byte[] newSha = s.ComputeHash(decryptedContent); if (!Shared.CompareByteArrays(newSha, tmd.Contents[i].Hash)) { fireDebug(@" + Hashes do not match! (Invalid Output)"); //throw new Exception(string.Format("Content #{0}: Hashes do not match!", i)); } //Write Decrypted Content File.WriteAllBytes(Path.Combine(outputDir, (tmd.Contents[i].ContentID.ToString("x8") + ".app")), decryptedContent); } s.Clear(); } //Pack Wad if (storeWad) { fireDebug(" - Building Certificate Chain..."); CertificateChain cert = CertificateChain.FromTikTmd(Path.Combine(outputDir, "cetk"), tmdFileWithCerts); byte[][] contents = new byte[tmd.NumOfContents][]; for (int i = 0; i < tmd.NumOfContents; i++) { 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.RemoveFooter(); wadName = wadName.Replace("[v]", "v" + this.TitleVersion.ToString()); // fix by madri2 if (Path.DirectorySeparatorChar.ToString() != "/" && Path.AltDirectorySeparatorChar.ToString() != "/") { wadName = wadName.Replace("/", ""); } if (wadName.Contains(Path.DirectorySeparatorChar.ToString()) || wadName.Contains(Path.AltDirectorySeparatorChar.ToString())) { wad.Save(wadName); } else { wad.Save(Path.Combine(outputDir, wadName)); } } //Delete not wanted files if (!storeEncrypted) { fireDebug(" - Deleting Encrypted Contents..."); for (int i = 0; i < encryptedContents.Length; 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(Path.Combine(outputDir, (encryptedContents[i] + ".app")))) { File.Delete(Path.Combine(outputDir, (encryptedContents[i] + ".app"))); } } } if (!storeDecrypted && !storeEncrypted) { fireDebug(" - Deleting TMD and Ticket..."); File.Delete(Path.Combine(outputDir, tmdFile)); File.Delete(Path.Combine(outputDir, "cetk")); } fireDebug("Downloading Title {0} v{1} Finished...", titleId, tmd.TitleVersion /*(string.IsNullOrEmpty(titleVersion)) ? "[Latest]" : titleVersion*/); fireProgress(100); }