// Decompiled with JetBrains decompiler // Type: libWiiSharp.WAD // Assembly: NUS Downloader, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null // MVID: DDAF9FEC-76DE-4BD8-8A6D-D7CAD5827AC6 // Assembly location: C:\dotpeek\NUS Downloader.exe using System; using System.Collections.Generic; using System.ComponentModel; using System.IO; using System.Security.Cryptography; using System.Text; namespace libWiiSharp { public class WAD : IDisposable { private SHA1 sha = SHA1.Create(); private DateTime creationTimeUTC = new DateTime(1970, 1, 1); private bool hasBanner; private bool lz77CompressBannerAndIcon = true; private bool lz77DecompressBannerAndIcon; private bool keepOriginalFooter; private WAD_Header wadHeader; private CertificateChain cert = new CertificateChain(); private Ticket tik = new Ticket(); private TMD tmd = new TMD(); private List contents; private U8 bannerApp = new U8(); private byte[] footer = new byte[0]; private bool isDisposed; public Region Region { get => this.tmd.Region; set => this.tmd.Region = value; } public int NumOfContents => (int) this.tmd.NumOfContents; public byte[][] Contents => this.contents.ToArray(); public bool FakeSign { get => this.tik.FakeSign && this.tmd.FakeSign; set { this.tik.FakeSign = value; this.tmd.FakeSign = value; } } public U8 BannerApp { get => this.bannerApp; set => this.bannerApp = value; } public ulong StartupIOS { get => this.tmd.StartupIOS; set => this.tmd.StartupIOS = value; } public ulong TitleID { get => this.tik.TitleID; set { this.tik.TitleID = value; this.tmd.TitleID = value; } } public string UpperTitleID => this.tik.GetUpperTitleID(); public ushort TitleVersion { get => this.tmd.TitleVersion; set => this.tmd.TitleVersion = value; } public ushort BootIndex { get => this.tmd.BootIndex; set => this.tmd.BootIndex = value; } public DateTime CreationTimeUTC => this.creationTimeUTC; public bool HasBanner => this.hasBanner; public bool Lz77CompressBannerAndIcon { get => this.lz77CompressBannerAndIcon; set { this.lz77CompressBannerAndIcon = value; if (!value) return; this.lz77DecompressBannerAndIcon = false; } } public bool Lz77DecompressBannerAndIcon { get => this.lz77DecompressBannerAndIcon; set { this.lz77DecompressBannerAndIcon = value; if (!value) return; this.lz77CompressBannerAndIcon = false; } } public string NandBlocks => this.tmd.GetNandBlocks(); public string[] ChannelTitles { get => this.hasBanner ? ((Headers.IMET) this.bannerApp.Header).AllTitles : new string[0]; set => this.ChangeChannelTitles(value); } public bool KeepOriginalFooter { get => this.keepOriginalFooter; set => this.keepOriginalFooter = value; } public TMD_Content[] TmdContents => this.tmd.Contents; public WAD() { this.cert.Debug += new EventHandler(this.cert_Debug); this.tik.Debug += new EventHandler(this.tik_Debug); this.tmd.Debug += new EventHandler(this.tmd_Debug); this.bannerApp.Debug += new EventHandler(this.bannerApp_Debug); this.bannerApp.Warning += new EventHandler(this.bannerApp_Warning); } ~WAD() => this.Dispose(false); public void Dispose() { this.Dispose(true); GC.SuppressFinalize((object) this); } protected virtual void Dispose(bool disposing) { if (disposing && !this.isDisposed) { this.sha.Clear(); this.sha = (SHA1) null; this.wadHeader = (WAD_Header) null; this.cert.Dispose(); this.tik.Dispose(); this.tmd.Dispose(); this.contents.Clear(); this.contents = (List) null; this.bannerApp.Dispose(); this.footer = (byte[]) null; } this.isDisposed = true; } public static WAD Load(string pathToWad) => WAD.Load(File.ReadAllBytes(pathToWad)); public static WAD Load(byte[] wadFile) { WAD wad = new WAD(); MemoryStream wadFile1 = new MemoryStream(wadFile); try { wad.parseWad((Stream) wadFile1); } catch { wadFile1.Dispose(); throw; } wadFile1.Dispose(); return wad; } public static WAD Load(Stream wad) { WAD wad1 = new WAD(); wad1.parseWad(wad); return wad1; } public static WAD Create(string contentDir) { string[] files1 = Directory.GetFiles(contentDir, "*cert*"); string[] files2 = Directory.GetFiles(contentDir, "*tik*"); string[] files3 = Directory.GetFiles(contentDir, "*tmd*"); CertificateChain cert = CertificateChain.Load(files1[0]); Ticket tik = Ticket.Load(files2[0]); TMD tmd = TMD.Load(files3[0]); bool flag = true; for (int index = 0; index < tmd.Contents.Length; ++index) { if (!File.Exists(contentDir + (object) Path.DirectorySeparatorChar + tmd.Contents[index].ContentID.ToString("x8") + ".app")) { flag = false; break; } } if (!flag) { for (int index = 0; index < tmd.Contents.Length; ++index) { if (!File.Exists(contentDir + (object) Path.DirectorySeparatorChar + tmd.Contents[index].Index.ToString("x8") + ".app")) throw new Exception("Couldn't find all content files!"); } } byte[][] contents = new byte[tmd.Contents.Length][]; for (int index = 0; index < tmd.Contents.Length; ++index) { string path = contentDir + (object) Path.DirectorySeparatorChar + (flag ? (object) tmd.Contents[index].ContentID.ToString("x8") : (object) tmd.Contents[index].Index.ToString("x8")) + ".app"; contents[index] = File.ReadAllBytes(path); } return WAD.Create(cert, tik, tmd, contents); } public static WAD Create( string pathToCert, string pathToTik, string pathToTmd, string contentDir) { CertificateChain cert = CertificateChain.Load(pathToCert); Ticket tik = Ticket.Load(pathToTik); TMD tmd = TMD.Load(pathToTmd); bool flag = true; for (int index = 0; index < tmd.Contents.Length; ++index) { if (!File.Exists(contentDir + (object) Path.DirectorySeparatorChar + tmd.Contents[index].ContentID.ToString("x8") + ".app")) { flag = false; break; } } if (!flag) { for (int index = 0; index < tmd.Contents.Length; ++index) { if (!File.Exists(contentDir + (object) Path.DirectorySeparatorChar + tmd.Contents[index].Index.ToString("x8") + ".app")) throw new Exception("Couldn't find all content files!"); } } byte[][] contents = new byte[tmd.Contents.Length][]; for (int index = 0; index < tmd.Contents.Length; ++index) { string path = contentDir + (object) Path.DirectorySeparatorChar + (flag ? (object) tmd.Contents[index].ContentID.ToString("x8") : (object) tmd.Contents[index].Index.ToString("x8")) + ".app"; contents[index] = File.ReadAllBytes(path); } return WAD.Create(cert, tik, tmd, contents); } public static WAD Create(byte[] cert, byte[] tik, byte[] tmd, byte[][] contents) => WAD.Create(CertificateChain.Load(cert), Ticket.Load(tik), TMD.Load(tmd), contents); public static WAD Create(CertificateChain cert, Ticket tik, TMD tmd, byte[][] contents) { WAD wad = new WAD() { cert = cert, tik = tik, tmd = tmd, contents = new List((IEnumerable) contents), wadHeader = new WAD_Header() }; wad.wadHeader.TmdSize = (uint) (484 + tmd.Contents.Length * 36); int num1 = 0; for (int index = 0; index < contents.Length - 1; ++index) num1 += Shared.AddPadding(contents[index].Length); int num2 = num1 + contents[contents.Length - 1].Length; wad.wadHeader.ContentSize = (uint) num2; for (int index = 0; index < wad.tmd.Contents.Length; ++index) { if (wad.tmd.Contents[index].Index == (ushort) 0) { try { wad.bannerApp.LoadFile(contents[index]); wad.hasBanner = true; break; } catch { wad.hasBanner = false; break; } } } return wad; } public void LoadFile(string pathToWad) => this.LoadFile(File.ReadAllBytes(pathToWad)); public void LoadFile(byte[] wadFile) { MemoryStream wadFile1 = new MemoryStream(wadFile); try { this.parseWad((Stream) wadFile1); } catch { wadFile1.Dispose(); throw; } wadFile1.Dispose(); } public void LoadFile(Stream wad) => this.parseWad(wad); public void CreateNew(string contentDir) { string[] files1 = Directory.GetFiles(contentDir, "*cert*"); string[] files2 = Directory.GetFiles(contentDir, "*tik*"); string[] files3 = Directory.GetFiles(contentDir, "*tmd*"); CertificateChain cert = CertificateChain.Load(files1[0]); Ticket tik = Ticket.Load(files2[0]); TMD tmd = TMD.Load(files3[0]); bool flag = true; for (int index = 0; index < tmd.Contents.Length; ++index) { if (!File.Exists(contentDir + (object) Path.DirectorySeparatorChar + tmd.Contents[index].ContentID.ToString("x8") + ".app")) { flag = false; break; } } if (!flag) { for (int index = 0; index < tmd.Contents.Length; ++index) { if (!File.Exists(contentDir + (object) Path.DirectorySeparatorChar + tmd.Contents[index].Index.ToString("x8") + ".app")) throw new Exception("Couldn't find all content files!"); } } byte[][] contents = new byte[tmd.Contents.Length][]; for (int index = 0; index < tmd.Contents.Length; ++index) { string path = contentDir + (object) Path.DirectorySeparatorChar + (flag ? (object) tmd.Contents[index].ContentID.ToString("x8") : (object) tmd.Contents[index].Index.ToString("x8")) + ".app"; contents[index] = File.ReadAllBytes(path); } this.CreateNew(cert, tik, tmd, contents); } public void CreateNew( string pathToCert, string pathToTik, string pathToTmd, string contentDir) { CertificateChain cert = CertificateChain.Load(pathToCert); Ticket tik = Ticket.Load(pathToTik); TMD tmd = TMD.Load(pathToTmd); bool flag = true; for (int index = 0; index < tmd.Contents.Length; ++index) { if (!File.Exists(contentDir + (object) Path.DirectorySeparatorChar + tmd.Contents[index].ContentID.ToString("x8") + ".app")) { flag = false; break; } } if (!flag) { for (int index = 0; index < tmd.Contents.Length; ++index) { if (!File.Exists(contentDir + (object) Path.DirectorySeparatorChar + tmd.Contents[index].Index.ToString("x8") + ".app")) throw new Exception("Couldn't find all content files!"); } } byte[][] contents = new byte[tmd.Contents.Length][]; for (int index = 0; index < tmd.Contents.Length; ++index) { string path = contentDir + (object) Path.DirectorySeparatorChar + (flag ? (object) tmd.Contents[index].ContentID.ToString("x8") : (object) tmd.Contents[index].Index.ToString("x8")) + ".app"; contents[index] = File.ReadAllBytes(path); } this.CreateNew(cert, tik, tmd, contents); } public void CreateNew(byte[] cert, byte[] tik, byte[] tmd, byte[][] contents) => this.CreateNew(CertificateChain.Load(cert), Ticket.Load(tik), TMD.Load(tmd), contents); public void CreateNew(CertificateChain cert, Ticket tik, TMD tmd, byte[][] contents) { this.cert = cert; this.tik = tik; this.tmd = tmd; this.contents = new List((IEnumerable) contents); this.wadHeader = new WAD_Header(); this.wadHeader.TmdSize = (uint) (484 + tmd.Contents.Length * 36); int num = 0; for (int index = 0; index < contents.Length - 1; ++index) num += Shared.AddPadding(contents[index].Length); this.wadHeader.ContentSize = (uint) (num + contents[contents.Length - 1].Length); for (int index = 0; index < this.tmd.Contents.Length; ++index) { if (this.tmd.Contents[index].Index == (ushort) 0) { try { this.bannerApp.LoadFile(contents[index]); this.hasBanner = true; break; } catch { this.hasBanner = false; break; } } } } public void Save(string savePath) { if (File.Exists(savePath)) File.Delete(savePath); using (FileStream writeStream = new FileStream(savePath, FileMode.Create)) this.writeToStream((Stream) writeStream); } public MemoryStream ToMemoryStream() { MemoryStream writeStream = new MemoryStream(); try { this.writeToStream((Stream) writeStream); } catch { writeStream.Dispose(); throw; } return writeStream; } public byte[] ToByteArray() { MemoryStream writeStream = new MemoryStream(); try { this.writeToStream((Stream) writeStream); } catch { writeStream.Dispose(); throw; } byte[] array = writeStream.ToArray(); writeStream.Dispose(); return array; } public void ChangeTitleID(LowerTitleID lowerID, string upperID) { uint num1 = upperID.Length == 4 ? BitConverter.ToUInt32(new byte[4] { (byte) upperID[3], (byte) upperID[2], (byte) upperID[1], (byte) upperID[0] }, 0) : throw new Exception("Upper Title ID must be 4 characters long!"); ulong num2 = (ulong) lowerID << 32 | (ulong) num1; this.tik.TitleID = num2; this.tmd.TitleID = num2; } public void ChangeStartupIOS(int newIos) => this.StartupIOS = 4294967296UL | (ulong) (uint) newIos; public void ChangeTitleKey(string newTitleKey) => this.tik.SetTitleKey(newTitleKey); public void ChangeTitleKey(char[] newTitleKey) => this.tik.SetTitleKey(newTitleKey); public void ChangeTitleKey(byte[] newTitleKey) => this.tik.SetTitleKey(newTitleKey); public byte[] GetContentByIndex(int index) { for (int index1 = 0; index1 < (int) this.tmd.NumOfContents; ++index1) { if ((int) this.tmd.Contents[index1].Index == index) return this.contents[index1]; } throw new Exception(string.Format("Content with index {0} not found!", (object) index)); } public byte[] GetContentByID(int contentID) { for (int index = 0; index < (int) this.tmd.NumOfContents; ++index) { if ((int) this.tmd.Contents[index].Index == contentID) return this.contents[index]; } throw new Exception(string.Format("Content with content ID {0} not found!", (object) contentID)); } public void ChangeChannelTitles(params string[] newTitles) { if (!this.hasBanner) return; ((Headers.IMET) this.bannerApp.Header).ChangeTitles(newTitles); } public void AddContent(byte[] newContent, int contentID, int index, ContentType type = ContentType.Normal) { this.tmd.AddContent(new TMD_Content() { ContentID = (uint) contentID, Index = (ushort) index, Type = type, Size = (ulong) newContent.Length, Hash = this.sha.ComputeHash(newContent) }); this.contents.Add(newContent); this.wadHeader.TmdSize = (uint) (484 + (int) this.tmd.NumOfContents * 36); } public void RemoveContent(int index) { for (int index1 = 0; index1 < this.tmd.Contents.Length; ++index1) { if ((int) this.tmd.Contents[index1].Index == index) { this.tmd.RemoveContent(index); this.contents.RemoveAt(index1); this.wadHeader.TmdSize = (uint) (484 + (int) this.tmd.NumOfContents * 36); return; } } throw new Exception(string.Format("Content with index {0} not found!", (object) index)); } public void RemoveContentByID(int contentID) { for (int index = 0; index < this.tmd.Contents.Length; ++index) { if ((int) this.tmd.Contents[index].Index == contentID) { this.tmd.RemoveContentByID(contentID); this.contents.RemoveAt(index); this.wadHeader.TmdSize = (uint) (484 + (int) this.tmd.NumOfContents * 36); return; } } throw new Exception(string.Format("Content with content ID {0} not found!", (object) contentID)); } public void RemoveAllContents() { if (!this.hasBanner) { this.tmd.Contents = new TMD_Content[0]; this.contents = new List(); this.wadHeader.TmdSize = (uint) (484 + (int) this.tmd.NumOfContents * 36); } else { for (int index = 0; index < (int) this.tmd.NumOfContents; ++index) { if (this.tmd.Contents[index].Index == (ushort) 0) { byte[] content1 = this.contents[index]; TMD_Content content2 = this.tmd.Contents[index]; this.tmd.Contents = new TMD_Content[0]; this.contents = new List(); this.tmd.AddContent(content2); this.contents.Add(content1); this.wadHeader.TmdSize = (uint) (484 + (int) this.tmd.NumOfContents * 36); break; } } } } public void Unpack(string unpackDir, bool nameContentID = false) => this.unpackAll(unpackDir, nameContentID); public void RemoveFooter() { this.footer = new byte[0]; this.wadHeader.FooterSize = 0U; this.keepOriginalFooter = true; } public void AddFooter(byte[] footer) => this.ChangeFooter(footer); public void ChangeFooter(byte[] newFooter) { if (newFooter.Length % 64 != 0) Array.Resize(ref newFooter, Shared.AddPadding(newFooter.Length)); this.footer = newFooter; this.wadHeader.FooterSize = (uint) newFooter.Length; this.keepOriginalFooter = true; } private void writeToStream(Stream writeStream) { this.fireDebug("Writing Wad..."); if (!this.keepOriginalFooter) { this.fireDebug(" Building Footer Timestamp..."); this.createFooterTimestamp(); } if (this.hasBanner) { if (this.lz77CompressBannerAndIcon || this.lz77DecompressBannerAndIcon) { for (int index = 0; index < this.bannerApp.Nodes.Count; ++index) { if (this.bannerApp.StringTable[index].ToLower() == "icon.bin" || this.bannerApp.StringTable[index].ToLower() == "banner.bin") { if (!Lz77.IsLz77Compressed(this.bannerApp.Data[index]) && this.lz77CompressBannerAndIcon) { this.fireDebug(" Compressing {0}...", (object) this.bannerApp.StringTable[index]); byte[] numArray1 = new byte[this.bannerApp.Data[index].Length - 32]; Array.Copy((Array) this.bannerApp.Data[index], 32, (Array) numArray1, 0, numArray1.Length); byte[] numArray2 = Headers.IMD5.AddHeader(new Lz77().Compress(numArray1)); this.bannerApp.Data[index] = numArray2; this.bannerApp.Nodes[index].SizeOfData = (uint) numArray2.Length; } else if (Lz77.IsLz77Compressed(this.bannerApp.Data[index]) && this.lz77DecompressBannerAndIcon) { this.fireDebug(" Decompressing {0}...", (object) this.bannerApp.StringTable[index]); byte[] numArray3 = new byte[this.bannerApp.Data[index].Length - 32]; Array.Copy((Array) this.bannerApp.Data[index], 32, (Array) numArray3, 0, numArray3.Length); byte[] numArray4 = Headers.IMD5.AddHeader(new Lz77().Decompress(numArray3)); this.bannerApp.Data[index] = numArray4; this.bannerApp.Nodes[index].SizeOfData = (uint) numArray4.Length; } } } } for (int index = 0; index < this.contents.Count; ++index) { if (this.tmd.Contents[index].Index == (ushort) 0) { this.fireDebug(" Saving Banner App..."); this.contents[index] = this.bannerApp.ToByteArray(); break; } } } this.fireDebug(" Updating Header..."); int num = 0; for (int index = 0; index < this.contents.Count - 1; ++index) num += Shared.AddPadding(this.contents[index].Length); this.wadHeader.ContentSize = (uint) (num + this.contents[this.contents.Count - 1].Length); this.wadHeader.TmdSize = (uint) (484 + (int) this.tmd.NumOfContents * 36); this.fireDebug(" Updating TMD Contents..."); this.tmd.UpdateContents(this.contents.ToArray()); this.fireDebug(" Writing Wad Header... (Offset: 0x{0})", (object) writeStream.Position.ToString("x8").ToUpper()); writeStream.Seek(0L, SeekOrigin.Begin); this.wadHeader.Write(writeStream); this.fireDebug(" Writing Certificate Chain... (Offset: 0x{0})", (object) writeStream.Position.ToString("x8").ToUpper()); writeStream.Seek((long) Shared.AddPadding((int) writeStream.Position), SeekOrigin.Begin); byte[] byteArray1 = this.cert.ToByteArray(); writeStream.Write(byteArray1, 0, byteArray1.Length); this.fireDebug(" Writing Ticket... (Offset: 0x{0})", (object) writeStream.Position.ToString("x8").ToUpper()); writeStream.Seek((long) Shared.AddPadding((int) writeStream.Position), SeekOrigin.Begin); byte[] byteArray2 = this.tik.ToByteArray(); writeStream.Write(byteArray2, 0, byteArray2.Length); this.fireDebug(" Writing TMD... (Offset: 0x{0})", (object) writeStream.Position.ToString("x8").ToUpper()); writeStream.Seek((long) Shared.AddPadding((int) writeStream.Position), SeekOrigin.Begin); byte[] byteArray3 = this.tmd.ToByteArray(); writeStream.Write(byteArray3, 0, byteArray3.Length); List contentIndicesList = new List(); for (int index = 0; index < this.tmd.Contents.Length; ++index) contentIndicesList.Add(new ContentIndices(index, (int) this.tmd.Contents[index].Index)); contentIndicesList.Sort(); for (int index = 0; index < contentIndicesList.Count; ++index) { writeStream.Seek((long) Shared.AddPadding((int) writeStream.Position), SeekOrigin.Begin); this.fireProgress((index + 1) * 100 / this.contents.Count); this.fireDebug(" Writing Content #{1} of {2}... (Offset: 0x{0})", (object) writeStream.Position.ToString("x8").ToUpper(), (object) (index + 1), (object) this.contents.Count); this.fireDebug(" -> Content ID: 0x{0}", (object) this.tmd.Contents[contentIndicesList[index].Index].ContentID.ToString("x8")); this.fireDebug(" -> Index: 0x{0}", (object) this.tmd.Contents[contentIndicesList[index].Index].Index.ToString("x4")); this.fireDebug(" -> Type: 0x{0} ({1})", (object) ((ushort) this.tmd.Contents[contentIndicesList[index].Index].Type).ToString("x4"), (object) this.tmd.Contents[contentIndicesList[index].Index].Type.ToString()); this.fireDebug(" -> Size: {0} bytes", (object) this.tmd.Contents[contentIndicesList[index].Index].Size); this.fireDebug(" -> Hash: {0}", (object) Shared.ByteArrayToString(this.tmd.Contents[contentIndicesList[index].Index].Hash)); byte[] buffer = this.encryptContent(this.contents[contentIndicesList[index].Index], contentIndicesList[index].Index); writeStream.Write(buffer, 0, buffer.Length); } if (this.wadHeader.FooterSize > 0U) { this.fireDebug(" Writing Footer... (Offset: 0x{0})", (object) writeStream.Position.ToString("x8").ToUpper()); writeStream.Seek((long) Shared.AddPadding((int) writeStream.Position), SeekOrigin.Begin); writeStream.Write(this.footer, 0, this.footer.Length); } while (writeStream.Position % 64L != 0L) writeStream.WriteByte((byte) 0); this.fireDebug("Writing Wad Finished... (Written Bytes: {0})", (object) writeStream.Position); } private void unpackAll(string unpackDir, bool nameContentId) { this.fireDebug("Unpacking Wad to: {0}", (object) unpackDir); if (!Directory.Exists(unpackDir)) Directory.CreateDirectory(unpackDir); string str = this.tik.TitleID.ToString("x16"); this.fireDebug(" Saving Certificate Chain: {0}.cert", (object) str); this.cert.Save(unpackDir + (object) Path.DirectorySeparatorChar + str + ".cert"); this.fireDebug(" Saving Ticket: {0}.tik", (object) str); this.tik.Save(unpackDir + (object) Path.DirectorySeparatorChar + str + ".tik"); this.fireDebug(" Saving TMD: {0}.tmd", (object) str); this.tmd.Save(unpackDir + (object) Path.DirectorySeparatorChar + str + ".tmd"); for (int index = 0; index < (int) this.tmd.NumOfContents; ++index) { this.fireProgress((index + 1) * 100 / (int) this.tmd.NumOfContents); this.fireDebug(" Saving Content #{0} of {1}: {2}.app", (object) (index + 1), (object) this.tmd.NumOfContents, nameContentId ? (object) this.tmd.Contents[index].ContentID.ToString("x8") : (object) this.tmd.Contents[index].Index.ToString("x8")); this.fireDebug(" -> Content ID: 0x{0}", (object) this.tmd.Contents[index].ContentID.ToString("x8")); this.fireDebug(" -> Index: 0x{0}", (object) this.tmd.Contents[index].Index.ToString("x4")); this.fireDebug(" -> Type: 0x{0} ({1})", (object) ((ushort) this.tmd.Contents[index].Type).ToString("x4"), (object) this.tmd.Contents[index].Type.ToString()); this.fireDebug(" -> Size: {0} bytes", (object) this.tmd.Contents[index].Size); this.fireDebug(" -> Hash: {0}", (object) Shared.ByteArrayToString(this.tmd.Contents[index].Hash)); using (FileStream fileStream = new FileStream(unpackDir + (object) Path.DirectorySeparatorChar + (nameContentId ? (object) this.tmd.Contents[index].ContentID.ToString("x8") : (object) this.tmd.Contents[index].Index.ToString("x8")) + ".app", FileMode.Create)) fileStream.Write(this.contents[index], 0, this.contents[index].Length); } this.fireDebug(" Saving Footer: {0}.footer", (object) str); using (FileStream fileStream = new FileStream(unpackDir + (object) Path.DirectorySeparatorChar + str + ".footer", FileMode.Create)) fileStream.Write(this.footer, 0, this.footer.Length); this.fireDebug("Unpacking Wad Finished..."); } private void parseWad(Stream wadFile) { this.fireDebug("Parsing Wad..."); wadFile.Seek(0L, SeekOrigin.Begin); byte[] buffer = new byte[4]; this.wadHeader = new WAD_Header(); this.contents = new List(); this.fireDebug(" Parsing Header... (Offset: 0x{0})", (object) wadFile.Position.ToString("x8").ToUpper()); wadFile.Read(buffer, 0, 4); if ((int) Shared.Swap(BitConverter.ToUInt32(buffer, 0)) != (int) this.wadHeader.HeaderSize) throw new Exception("Invalid Headersize!"); wadFile.Read(buffer, 0, 4); this.wadHeader.WadType = Shared.Swap(BitConverter.ToUInt32(buffer, 0)); wadFile.Seek(12L, SeekOrigin.Current); wadFile.Read(buffer, 0, 4); this.wadHeader.TmdSize = Shared.Swap(BitConverter.ToUInt32(buffer, 0)); wadFile.Read(buffer, 0, 4); this.wadHeader.ContentSize = Shared.Swap(BitConverter.ToUInt32(buffer, 0)); wadFile.Read(buffer, 0, 4); this.wadHeader.FooterSize = Shared.Swap(BitConverter.ToUInt32(buffer, 0)); this.fireDebug(" Parsing Certificate Chain... (Offset: 0x{0})", (object) wadFile.Position.ToString("x8").ToUpper()); wadFile.Seek((long) Shared.AddPadding((int) wadFile.Position), SeekOrigin.Begin); byte[] numArray1 = new byte[(IntPtr) this.wadHeader.CertSize]; wadFile.Read(numArray1, 0, numArray1.Length); this.cert.LoadFile(numArray1); this.fireDebug(" Parsing Ticket... (Offset: 0x{0})", (object) wadFile.Position.ToString("x8").ToUpper()); wadFile.Seek((long) Shared.AddPadding((int) wadFile.Position), SeekOrigin.Begin); byte[] numArray2 = new byte[(IntPtr) this.wadHeader.TicketSize]; wadFile.Read(numArray2, 0, numArray2.Length); this.tik.LoadFile(numArray2); this.fireDebug(" Parsing TMD... (Offset: 0x{0})", (object) wadFile.Position.ToString("x8").ToUpper()); wadFile.Seek((long) Shared.AddPadding((int) wadFile.Position), SeekOrigin.Begin); byte[] numArray3 = new byte[(IntPtr) this.wadHeader.TmdSize]; wadFile.Read(numArray3, 0, numArray3.Length); this.tmd.LoadFile(numArray3); if ((long) this.tmd.TitleID != (long) this.tik.TitleID) this.fireWarning("The Title ID in the Ticket doesn't match the one in the TMD!"); for (int contentIndex = 0; contentIndex < (int) this.tmd.NumOfContents; ++contentIndex) { this.fireProgress((contentIndex + 1) * 100 / (int) this.tmd.NumOfContents); this.fireDebug(" Reading Content #{0} of {1}... (Offset: 0x{2})", (object) (contentIndex + 1), (object) this.tmd.NumOfContents, (object) wadFile.Position.ToString("x8").ToUpper()); this.fireDebug(" -> Content ID: 0x{0}", (object) this.tmd.Contents[contentIndex].ContentID.ToString("x8")); this.fireDebug(" -> Index: 0x{0}", (object) this.tmd.Contents[contentIndex].Index.ToString("x4")); this.fireDebug(" -> Type: 0x{0} ({1})", (object) ((ushort) this.tmd.Contents[contentIndex].Type).ToString("x4"), (object) this.tmd.Contents[contentIndex].Type.ToString()); this.fireDebug(" -> Size: {0} bytes", (object) this.tmd.Contents[contentIndex].Size); this.fireDebug(" -> Hash: {0}", (object) Shared.ByteArrayToString(this.tmd.Contents[contentIndex].Hash)); wadFile.Seek((long) Shared.AddPadding((int) wadFile.Position), SeekOrigin.Begin); byte[] array = new byte[Shared.AddPadding((int) this.tmd.Contents[contentIndex].Size, 16)]; wadFile.Read(array, 0, array.Length); array = this.decryptContent(array, contentIndex); Array.Resize(ref array, (int) this.tmd.Contents[contentIndex].Size); if (!Shared.CompareByteArrays(this.tmd.Contents[contentIndex].Hash, this.sha.ComputeHash(array, 0, (int) this.tmd.Contents[contentIndex].Size))) { this.fireDebug("/!\\ /!\\ /!\\ Hashes do not match /!\\ /!\\ /!\\"); this.fireWarning(string.Format("Content #{0} (Content ID: 0x{1}; Index: 0x{2}): Hashes do not match! The content might be corrupted!", (object) (contentIndex + 1), (object) this.tmd.Contents[contentIndex].ContentID.ToString("x8"), (object) this.tmd.Contents[contentIndex].Index.ToString("x4"))); } this.contents.Add(array); if (this.tmd.Contents[contentIndex].Index == (ushort) 0) { try { this.bannerApp.LoadFile(array); this.hasBanner = true; } catch { this.hasBanner = false; } } } if (this.wadHeader.FooterSize > 0U) { this.fireDebug(" Reading Footer... (Offset: 0x{0})", (object) wadFile.Position.ToString("x8").ToUpper()); this.footer = new byte[(IntPtr) this.wadHeader.FooterSize]; wadFile.Seek((long) Shared.AddPadding((int) wadFile.Position), SeekOrigin.Begin); wadFile.Read(this.footer, 0, this.footer.Length); this.parseFooterTimestamp(); } this.fireDebug("Parsing Wad Finished..."); } private byte[] decryptContent(byte[] content, int contentIndex) { int length = content.Length; Array.Resize(ref content, Shared.AddPadding(content.Length, 16)); byte[] titleKey = this.tik.TitleKey; byte[] numArray = new byte[16]; byte[] bytes = BitConverter.GetBytes(this.tmd.Contents[contentIndex].Index); numArray[0] = bytes[1]; numArray[1] = bytes[0]; RijndaelManaged rijndaelManaged = new RijndaelManaged(); rijndaelManaged.Mode = CipherMode.CBC; rijndaelManaged.Padding = PaddingMode.None; rijndaelManaged.KeySize = 128; rijndaelManaged.BlockSize = 128; rijndaelManaged.Key = titleKey; rijndaelManaged.IV = numArray; ICryptoTransform decryptor = rijndaelManaged.CreateDecryptor(); MemoryStream memoryStream = new MemoryStream(content); CryptoStream cryptoStream = new CryptoStream((Stream) memoryStream, decryptor, CryptoStreamMode.Read); byte[] buffer = new byte[length]; cryptoStream.Read(buffer, 0, buffer.Length); cryptoStream.Dispose(); memoryStream.Dispose(); return buffer; } private byte[] encryptContent(byte[] content, int contentIndex) { Array.Resize(ref content, Shared.AddPadding(content.Length, 16)); byte[] titleKey = this.tik.TitleKey; byte[] numArray = new byte[16]; byte[] bytes = BitConverter.GetBytes(this.tmd.Contents[contentIndex].Index); numArray[0] = bytes[1]; numArray[1] = bytes[0]; RijndaelManaged rijndaelManaged = new RijndaelManaged(); rijndaelManaged.Mode = CipherMode.CBC; rijndaelManaged.Padding = PaddingMode.None; rijndaelManaged.KeySize = 128; rijndaelManaged.BlockSize = 128; rijndaelManaged.Key = titleKey; rijndaelManaged.IV = numArray; ICryptoTransform encryptor = rijndaelManaged.CreateEncryptor(); MemoryStream memoryStream = new MemoryStream(content); CryptoStream cryptoStream = new CryptoStream((Stream) memoryStream, encryptor, CryptoStreamMode.Read); byte[] buffer = new byte[content.Length]; cryptoStream.Read(buffer, 0, buffer.Length); cryptoStream.Dispose(); memoryStream.Dispose(); return buffer; } private void createFooterTimestamp() { byte[] bytes = new ASCIIEncoding().GetBytes("TmStmp" + ((int) (DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds).ToString()); Array.Resize(ref bytes, 64); this.wadHeader.FooterSize = (uint) bytes.Length; this.footer = bytes; } private void parseFooterTimestamp() { this.creationTimeUTC = new DateTime(1970, 1, 1); if ((this.footer[0] != (byte) 67 || this.footer[1] != (byte) 77 || this.footer[2] != (byte) 105 || this.footer[3] != (byte) 105 || this.footer[4] != (byte) 85 || this.footer[5] != (byte) 84) && (this.footer[0] != (byte) 84 || this.footer[1] != (byte) 109 || this.footer[2] != (byte) 83 || this.footer[3] != (byte) 116 || this.footer[4] != (byte) 109 || this.footer[5] != (byte) 112)) return; string s = new ASCIIEncoding().GetString(this.footer, 6, 10); int result = 0; if (!int.TryParse(s, out result)) return; this.creationTimeUTC = this.creationTimeUTC.AddSeconds((double) result); } public event EventHandler Progress; public event EventHandler Warning; public event EventHandler Debug; private void fireDebug(string debugMessage, params object[] args) { EventHandler debug = this.Debug; if (debug == null) return; debug(new object(), new MessageEventArgs(string.Format(debugMessage, args))); } private void fireWarning(string warningMessage) { EventHandler warning = this.Warning; if (warning == null) return; warning(new object(), new MessageEventArgs(warningMessage)); } private void fireProgress(int progressPercentage) { EventHandler progress = this.Progress; if (progress == null) return; progress(new object(), new ProgressChangedEventArgs(progressPercentage, (object) string.Empty)); } private void cert_Debug(object sender, MessageEventArgs e) => this.fireDebug(" Certificate Chain: {0}", (object) e.Message); private void tik_Debug(object sender, MessageEventArgs e) => this.fireDebug(" Ticket: {0}", (object) e.Message); private void tmd_Debug(object sender, MessageEventArgs e) => this.fireDebug(" TMD: {0}", (object) e.Message); private void bannerApp_Debug(object sender, MessageEventArgs e) => this.fireDebug(" BannerApp: {0}", (object) e.Message); private void bannerApp_Warning(object sender, MessageEventArgs e) => this.fireWarning(e.Message); } }