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); }
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); }
private Stream compress(Stream inFile) { if (Lz77.IsLz77Compressed(inFile)) { return(inFile); } inFile.Seek(0, SeekOrigin.Begin); int textSize = 0; int codeSize = 0; int i, c, r, s, length, lastMatchLength, codeBufferPointer, mask; int[] codeBuffer = new int[17]; uint fileSize = ((Convert.ToUInt32(inFile.Length)) << 8) + 0x10; MemoryStream outFile = new MemoryStream(); outFile.Write(BitConverter.GetBytes(Shared.Swap(lz77Magic)), 0, 4); outFile.Write(BitConverter.GetBytes(fileSize), 0, 4); InitTree(); codeBuffer[0] = 0; codeBufferPointer = 1; mask = 0x80; s = 0; r = N - F; for (i = s; i < r; i++) { textBuffer[i] = 0xffff; } for (length = 0; length < F && (c = (int)inFile.ReadByte()) != -1; length++) { textBuffer[r + length] = (ushort)c; } if ((textSize = length) == 0) { return(inFile); } for (i = 1; i <= F; i++) { InsertNode(r - i); } InsertNode(r); do { if (matchLength > length) { matchLength = length; } if (matchLength <= threshold) { matchLength = 1; codeBuffer[codeBufferPointer++] = textBuffer[r]; } else { codeBuffer[0] |= mask; codeBuffer[codeBufferPointer++] = (char) (((r - matchPosition - 1) >> 8) & 0x0f) | ((matchLength - (threshold + 1)) << 4); codeBuffer[codeBufferPointer++] = (char)((r - matchPosition - 1) & 0xff); } if ((mask >>= 1) == 0) { for (i = 0; i < codeBufferPointer; i++) { outFile.WriteByte((byte)codeBuffer[i]); } codeSize += codeBufferPointer; codeBuffer[0] = 0; codeBufferPointer = 1; mask = 0x80; } lastMatchLength = matchLength; for (i = 0; i < lastMatchLength && (c = (int)inFile.ReadByte()) != -1; i++) { DeleteNode(s); textBuffer[s] = (ushort)c; if (s < F - 1) { textBuffer[s + N] = (ushort)c; } s = (s + 1) & (N - 1); r = (r + 1) & (N - 1); InsertNode(r); } while (i++ < lastMatchLength) { DeleteNode(s); s = (s + 1) & (N - 1); r = (r + 1) & (N - 1); if (--length != 0) { InsertNode(r); } } } while (length > 0); if (codeBufferPointer > 1) { for (i = 0; i < codeBufferPointer; i++) { outFile.WriteByte((byte)codeBuffer[i]); } codeSize += codeBufferPointer; } if (codeSize % 4 != 0) { for (i = 0; i < 4 - (codeSize % 4); i++) { outFile.WriteByte(0x00); } } return(outFile); }
/// <summary> /// Checks whether a file is Lz77 compressed or not. /// </summary> /// <param name="file"></param> /// <returns></returns> public static bool IsLz77Compressed(byte[] file) { Headers.HeaderType h = Headers.DetectHeader(file); return(Shared.Swap(BitConverter.ToUInt32(file, (int)h)) == lz77Magic); }
private Stream decompress(Stream inFile) { if (!Lz77.IsLz77Compressed(inFile)) { return(inFile); } inFile.Seek(0, SeekOrigin.Begin); int i, j, k, r, c, z; uint flags, decompressedSize, currentSize = 0; Headers.HeaderType h = Headers.DetectHeader(inFile); byte[] temp = new byte[8]; inFile.Seek((int)h, SeekOrigin.Begin); inFile.Read(temp, 0, 8); if (Shared.Swap(BitConverter.ToUInt32(temp, 0)) != lz77Magic) { inFile.Dispose(); throw new Exception("Invaild Magic!"); } if (temp[4] != 0x10) { inFile.Dispose(); throw new Exception("Unsupported Compression Type!"); } decompressedSize = (BitConverter.ToUInt32(temp, 4)) >> 8; for (i = 0; i < N - F; i++) { textBuffer[i] = 0xdf; } r = N - F; flags = 7; z = 7; MemoryStream outFile = new MemoryStream(); while (true) { flags <<= 1; z++; if (z == 8) { if ((c = inFile.ReadByte()) == -1) { break; } flags = (uint)c; z = 0; } if ((flags & 0x80) == 0) { if ((c = inFile.ReadByte()) == inFile.Length - 1) { break; } if (currentSize < decompressedSize) { outFile.WriteByte((byte)c); } textBuffer[r++] = (byte)c; r &= (N - 1); currentSize++; } else { if ((i = inFile.ReadByte()) == -1) { break; } if ((j = inFile.ReadByte()) == -1) { break; } j = j | ((i << 8) & 0xf00); i = ((i >> 4) & 0x0f) + threshold; for (k = 0; k <= i; k++) { c = textBuffer[(r - j - 1) & (N - 1)]; if (currentSize < decompressedSize) { outFile.WriteByte((byte)c); } textBuffer[r++] = (byte)c; r &= (N - 1); currentSize++; } } } return(outFile); }