diff --git a/AssemblyInfo.cs b/AssemblyInfo.cs new file mode 100644 index 0000000..e6fb5e9 --- /dev/null +++ b/AssemblyInfo.cs @@ -0,0 +1,19 @@ +using System.Reflection; +using System.Resources; +using System.Runtime.InteropServices; +using System.Security.Permissions; + +[assembly: AssemblyTitle("libWiiSharp_ModMii")] +[assembly: AssemblyDescription("a wii related .NET library modified for ModMii")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Leathl,SC reproductions")] +[assembly: AssemblyProduct("libWiiSharp_ModMii")] +[assembly: AssemblyCopyright("Copyright © Leathl 2011")] +[assembly: AssemblyTrademark("")] +[assembly: ComVisible(false)] +[assembly: Guid("af701263-5875-4866-9c09-d7f62e9f0ff0")] +[assembly: AssemblyFileVersion("0.4.0.0")] +[assembly: NeutralResourcesLanguage("en")] +[assembly: AssemblyVersion("0.4.0.0")] +[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] +[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] diff --git a/BNS.cs b/BNS.cs new file mode 100644 index 0000000..14a6838 --- /dev/null +++ b/BNS.cs @@ -0,0 +1,514 @@ +// Decompiled with JetBrains decompiler +// Type: libWiiSharp.BNS +// Assembly: libWiiSharp, Version=0.4.0.0, Culture=neutral, PublicKeyToken=null +// MVID: FBF36F3D-B5D6-481F-B5F5-1BD3C19E13B2 +// Assembly location: C:\Users\theso\Downloads\NCPatcher\pack\libWiiSharp.dll + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.IO; + +namespace libWiiSharp +{ + public class BNS : IDisposable + { + private BNS_Header bnsHeader = new BNS_Header(); + private BNS_Info bnsInfo = new BNS_Info(); + private BNS_Data bnsData = new BNS_Data(); + private int[,] lSamples = new int[2, 2]; + private int[,] rlSamples = new int[2, 2]; + private int[] tlSamples = new int[2]; + private int[] hbcDefTbl = new int[16] + { + 674, + 1040, + 3598, + -1738, + 2270, + -583, + 3967, + -1969, + 1516, + 381, + 3453, + -1468, + 2606, + -617, + 3795, + -1759 + }; + private int[] defTbl = new int[16] + { + 1820, + -856, + 3238, + -1514, + 2333, + -550, + 3336, + -1376, + 2444, + -949, + 3666, + -1764, + 2654, + -701, + 3420, + -1398 + }; + private int[] pHist1 = new int[2]; + private int[] pHist2 = new int[2]; + private int tempSampleCount; + private byte[] waveFile; + private bool loopFromWave; + private bool converted; + private bool toMono; + private bool isDisposed; + + public bool HasLoop + { + get => this.bnsInfo.HasLoop == (byte) 1; + set => this.bnsInfo.HasLoop = value ? (byte) 1 : (byte) 0; + } + + public uint LoopStart + { + get => this.bnsInfo.LoopStart; + set => this.bnsInfo.LoopStart = value; + } + + public uint TotalSampleCount + { + get => this.bnsInfo.LoopEnd; + set => this.bnsInfo.LoopEnd = value; + } + + public bool StereoToMono + { + get => this.toMono; + set => this.toMono = value; + } + + public event EventHandler Progress; + + protected BNS() + { + } + + public BNS(string waveFile) => this.waveFile = File.ReadAllBytes(waveFile); + + public BNS(string waveFile, bool loopFromWave) + { + this.waveFile = File.ReadAllBytes(waveFile); + this.loopFromWave = loopFromWave; + } + + public BNS(byte[] waveFile) => this.waveFile = waveFile; + + public BNS(byte[] waveFile, bool loopFromWave) + { + this.waveFile = waveFile; + this.loopFromWave = loopFromWave; + } + + ~BNS() => this.Dispose(false); + + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize((object) this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing && !this.isDisposed) + { + this.bnsHeader = (BNS_Header) null; + this.bnsInfo = (BNS_Info) null; + this.bnsData = (BNS_Data) null; + this.lSamples = (int[,]) null; + this.rlSamples = (int[,]) null; + this.tlSamples = (int[]) null; + this.hbcDefTbl = (int[]) null; + this.pHist1 = (int[]) null; + this.pHist2 = (int[]) null; + this.waveFile = (byte[]) null; + } + this.isDisposed = true; + } + + public static int GetBnsLength(byte[] bnsFile) + { + uint num = (uint) Shared.Swap(BitConverter.ToUInt16(bnsFile, 44)); + return (int) (Shared.Swap(BitConverter.ToUInt32(bnsFile, 52)) / num); + } + + public void Convert() => this.convert(this.waveFile, this.loopFromWave); + + public byte[] ToByteArray() => this.ToMemoryStream().ToArray(); + + public MemoryStream ToMemoryStream() + { + if (!this.converted) + this.convert(this.waveFile, this.loopFromWave); + MemoryStream memoryStream = new MemoryStream(); + try + { + this.bnsHeader.Write((Stream) memoryStream); + this.bnsInfo.Write((Stream) memoryStream); + this.bnsData.Write((Stream) memoryStream); + return memoryStream; + } + catch + { + memoryStream.Dispose(); + throw; + } + } + + public void Save(string destionationFile) + { + if (File.Exists(destionationFile)) + File.Delete(destionationFile); + using (FileStream fileStream = new FileStream(destionationFile, FileMode.Create)) + { + byte[] array = this.ToMemoryStream().ToArray(); + fileStream.Write(array, 0, array.Length); + } + } + + public void SetLoop(int loopStartSample) + { + this.bnsInfo.HasLoop = (byte) 1; + this.bnsInfo.LoopStart = (uint) loopStartSample; + } + + private void convert(byte[] waveFile, bool loopFromWave) + { + Wave wave = new Wave(waveFile); + int numLoops = wave.NumLoops; + int loopStart = wave.LoopStart; + this.bnsInfo.ChannelCount = (byte) wave.NumChannels; + this.bnsInfo.SampleRate = (ushort) wave.SampleRate; + if (this.bnsInfo.ChannelCount > (byte) 2 || this.bnsInfo.ChannelCount < (byte) 1) + throw new Exception("Unsupported Amount of Channels!"); + if (wave.BitDepth != 16) + throw new Exception("Only 16bit Wave files are supported!"); + this.bnsData.Data = wave.DataFormat == 1 ? this.Encode(wave.SampleData) : throw new Exception("The format of this Wave file is not supported!"); + if (this.bnsInfo.ChannelCount == (byte) 1) + { + this.bnsHeader.InfoLength = 96U; + this.bnsHeader.DataOffset = 128U; + this.bnsInfo.Size = 96U; + this.bnsInfo.Channel1StartOffset = 28U; + this.bnsInfo.Channel2StartOffset = 0U; + this.bnsInfo.Channel1Start = 40U; + this.bnsInfo.Coefficients1Offset = 0U; + } + this.bnsData.Size = (uint) (this.bnsData.Data.Length + 8); + this.bnsHeader.DataLength = this.bnsData.Size; + this.bnsHeader.FileSize = (uint) this.bnsHeader.Size + this.bnsInfo.Size + this.bnsData.Size; + if (loopFromWave && numLoops == 1 && loopStart != -1) + { + this.bnsInfo.LoopStart = (uint) loopStart; + this.bnsInfo.HasLoop = (byte) 1; + } + this.bnsInfo.LoopEnd = (uint) this.tempSampleCount; + for (int index = 0; index < 16; ++index) + { + this.bnsInfo.Coefficients1[index] = this.defTbl[index]; + if (this.bnsInfo.ChannelCount == (byte) 2) + this.bnsInfo.Coefficients2[index] = this.defTbl[index]; + } + this.converted = true; + } + + private byte[] Encode(byte[] inputFrames) + { + int[] inputBuffer = new int[14]; + this.tempSampleCount = inputFrames.Length / (this.bnsInfo.ChannelCount == (byte) 2 ? 4 : 2); + int num1 = inputFrames.Length / (this.bnsInfo.ChannelCount == (byte) 2 ? 4 : 2) % 14; + Array.Resize(ref inputFrames, inputFrames.Length + (14 - num1) * (this.bnsInfo.ChannelCount == (byte) 2 ? 4 : 2)); + int num2 = inputFrames.Length / (this.bnsInfo.ChannelCount == (byte) 2 ? 4 : 2); + int num3 = (num2 + 13) / 14; + List intList1 = new List(); + List intList2 = new List(); + int startIndex = 0; + if (this.toMono && this.bnsInfo.ChannelCount == (byte) 2) + this.bnsInfo.ChannelCount = (byte) 1; + else if (this.toMono) + this.toMono = false; + for (int index = 0; index < num2; ++index) + { + intList1.Add((int) BitConverter.ToInt16(inputFrames, startIndex)); + startIndex += 2; + if (this.bnsInfo.ChannelCount == (byte) 2 || this.toMono) + { + intList2.Add((int) BitConverter.ToInt16(inputFrames, startIndex)); + startIndex += 2; + } + } + byte[] numArray1 = new byte[this.bnsInfo.ChannelCount == (byte) 2 ? num3 * 16 : num3 * 8]; + int num4 = 0; + int num5 = num3 * 8; + this.bnsInfo.Channel2Start = this.bnsInfo.ChannelCount == (byte) 2 ? (uint) num5 : 0U; + int[] array1 = intList1.ToArray(); + int[] array2 = intList2.ToArray(); + for (int index1 = 0; index1 < num3; ++index1) + { + try + { + if (index1 % (num3 / 100) != 0) + { + if (index1 + 1 != num3) + goto label_14; + } + this.ChangeProgress((index1 + 1) * 100 / num3); + } + catch + { + } +label_14: + for (int index2 = 0; index2 < 14; ++index2) + inputBuffer[index2] = array1[index1 * 14 + index2]; + byte[] numArray2 = this.RepackAdpcm(0, this.defTbl, inputBuffer); + for (int index2 = 0; index2 < 8; ++index2) + numArray1[num4 + index2] = numArray2[index2]; + num4 += 8; + if (this.bnsInfo.ChannelCount == (byte) 2) + { + for (int index2 = 0; index2 < 14; ++index2) + inputBuffer[index2] = array2[index1 * 14 + index2]; + byte[] numArray3 = this.RepackAdpcm(1, this.defTbl, inputBuffer); + for (int index2 = 0; index2 < 8; ++index2) + numArray1[num5 + index2] = numArray3[index2]; + num5 += 8; + } + } + this.bnsInfo.LoopEnd = (uint) (num3 * 7); + return numArray1; + } + + private byte[] RepackAdpcm(int index, int[] table, int[] inputBuffer) + { + byte[] numArray1 = new byte[8]; + int[] numArray2 = new int[2]; + double num1 = 999999999.0; + for (int tableIndex = 0; tableIndex < 8; ++tableIndex) + { + double outError; + byte[] numArray3 = this.CompressAdpcm(index, table, tableIndex, inputBuffer, out outError); + if (outError < num1) + { + num1 = outError; + for (int index1 = 0; index1 < 8; ++index1) + numArray1[index1] = numArray3[index1]; + for (int index1 = 0; index1 < 2; ++index1) + numArray2[index1] = this.tlSamples[index1]; + } + } + for (int index1 = 0; index1 < 2; ++index1) + { + int[,] rlSamples = this.rlSamples; + int num2 = index1; + int index2 = index; + int index3 = num2; + int num3 = numArray2[index1]; + rlSamples[index2, index3] = num3; + } + return numArray1; + } + + private byte[] CompressAdpcm( + int index, + int[] table, + int tableIndex, + int[] inputBuffer, + out double outError) + { + byte[] numArray = new byte[8]; + int num1 = 0; + int num2 = table[2 * tableIndex]; + int num3 = table[2 * tableIndex + 1]; + int stdExponent = this.DetermineStdExponent(index, table, tableIndex, inputBuffer); + while (stdExponent <= 15) + { + bool flag = false; + num1 = 0; + numArray[0] = (byte) (stdExponent | tableIndex << 4); + for (int index1 = 0; index1 < 2; ++index1) + this.tlSamples[index1] = this.rlSamples[index, index1]; + int num4 = 0; + for (int index1 = 0; index1 < 14; ++index1) + { + int num5 = this.tlSamples[1] * num2 + this.tlSamples[0] * num3 >> 11; + int input1 = inputBuffer[index1] - num5 >> stdExponent; + if (input1 <= 7 && input1 >= -8) + { + int num6 = this.Clamp(input1, -8, 7); + numArray[index1 / 2 + 1] = (index1 & 1) == 0 ? (byte) (num6 << 4) : (byte) ((uint) numArray[index1 / 2 + 1] | (uint) (num6 & 15)); + int input2 = num5 + (num6 << stdExponent); + this.tlSamples[0] = this.tlSamples[1]; + this.tlSamples[1] = this.Clamp(input2, (int) short.MinValue, (int) short.MaxValue); + num1 += (int) Math.Pow((double) (this.tlSamples[1] - inputBuffer[index1]), 2.0); + } + else + { + ++stdExponent; + flag = true; + break; + } + } + if (!flag) + num4 = 14; + if (num4 == 14) + break; + } + outError = (double) num1; + return numArray; + } + + private int DetermineStdExponent(int index, int[] table, int tableIndex, int[] inputBuffer) + { + int[] numArray = new int[2]; + int num1 = 0; + int num2 = table[2 * tableIndex]; + int num3 = table[2 * tableIndex + 1]; + for (int index1 = 0; index1 < 2; ++index1) + numArray[index1] = this.rlSamples[index, index1]; + for (int index1 = 0; index1 < 14; ++index1) + { + int num4 = numArray[1] * num2 + numArray[0] * num3 >> 11; + int num5 = inputBuffer[index1] - num4; + if (num5 > num1) + num1 = num5; + numArray[0] = numArray[1]; + numArray[1] = inputBuffer[index1]; + } + return this.FindExponent((double) num1); + } + + private int FindExponent(double residual) + { + int num = 0; + for (; residual > 7.5 || residual < -8.5; residual /= 2.0) + ++num; + return num; + } + + private int Clamp(int input, int min, int max) + { + if (input < min) + return min; + return input > max ? max : input; + } + + private void ChangeProgress(int progressPercentage) + { + EventHandler progress = this.Progress; + if (progress == null) + return; + progress(new object(), new ProgressChangedEventArgs(progressPercentage, new object())); + } + + public static Wave BnsToWave(Stream inputFile) + { + BNS bns = new BNS(); + byte[] samples = bns.Read(inputFile); + Wave wave = new Wave((int) bns.bnsInfo.ChannelCount, 16, (int) bns.bnsInfo.SampleRate, samples); + if (bns.bnsInfo.HasLoop == (byte) 1) + wave.AddLoop((int) bns.bnsInfo.LoopStart); + return wave; + } + + public static Wave BnsToWave(string pathToFile) + { + BNS bns = new BNS(); + byte[] samples = (byte[]) null; + using (FileStream fileStream = new FileStream(pathToFile, FileMode.Open)) + samples = bns.Read((Stream) fileStream); + Wave wave = new Wave((int) bns.bnsInfo.ChannelCount, 16, (int) bns.bnsInfo.SampleRate, samples); + if (bns.bnsInfo.HasLoop == (byte) 1) + wave.AddLoop((int) bns.bnsInfo.LoopStart); + return wave; + } + + public static Wave BnsToWave(byte[] bnsFile) + { + BNS bns = new BNS(); + byte[] samples = (byte[]) null; + using (MemoryStream memoryStream = new MemoryStream(bnsFile)) + samples = bns.Read((Stream) memoryStream); + Wave wave = new Wave((int) bns.bnsInfo.ChannelCount, 16, (int) bns.bnsInfo.SampleRate, samples); + if (bns.bnsInfo.HasLoop == (byte) 1) + wave.AddLoop((int) bns.bnsInfo.LoopStart); + return wave; + } + + private byte[] Read(Stream input) + { + input.Seek(0L, SeekOrigin.Begin); + this.bnsHeader.Read(input); + this.bnsInfo.Read(input); + this.bnsData.Read(input); + return this.Decode(); + } + + private byte[] Decode() + { + List byteList = new List(); + int num = this.bnsData.Data.Length / (this.bnsInfo.ChannelCount == (byte) 2 ? 16 : 8); + int dataOffset1 = 0; + int dataOffset2 = num * 8; + byte[] numArray1 = new byte[0]; + byte[] numArray2 = new byte[0]; + for (int index1 = 0; index1 < num; ++index1) + { + byte[] numArray3 = this.DecodeAdpcm(0, dataOffset1); + if (this.bnsInfo.ChannelCount == (byte) 2) + numArray2 = this.DecodeAdpcm(1, dataOffset2); + for (int index2 = 0; index2 < 14; ++index2) + { + byteList.Add(numArray3[index2 * 2]); + byteList.Add(numArray3[index2 * 2 + 1]); + if (this.bnsInfo.ChannelCount == (byte) 2) + { + byteList.Add(numArray2[index2 * 2]); + byteList.Add(numArray2[index2 * 2 + 1]); + } + } + dataOffset1 += 8; + if (this.bnsInfo.ChannelCount == (byte) 2) + dataOffset2 += 8; + } + return byteList.ToArray(); + } + + private byte[] DecodeAdpcm(int channel, int dataOffset) + { + byte[] numArray = new byte[28]; + int num1 = (int) this.bnsData.Data[dataOffset] >> 4 & 15; + int num2 = 1 << ((int) this.bnsData.Data[dataOffset] & 15); + int num3 = this.pHist1[channel]; + int num4 = this.pHist2[channel]; + int num5 = channel == 0 ? this.bnsInfo.Coefficients1[num1 * 2] : this.bnsInfo.Coefficients2[num1 * 2]; + int num6 = channel == 0 ? this.bnsInfo.Coefficients1[num1 * 2 + 1] : this.bnsInfo.Coefficients2[num1 * 2 + 1]; + for (int index = 0; index < 14; ++index) + { + short num7 = (short) this.bnsData.Data[dataOffset + (index / 2 + 1)]; + int num8 = (index & 1) != 0 ? (int) num7 & 15 : (int) num7 >> 4; + if (num8 >= 8) + num8 -= 16; + int num9 = this.Clamp((num2 * num8 << 11) + (num5 * num3 + num6 * num4) + 1024 >> 11, (int) short.MinValue, (int) short.MaxValue); + numArray[index * 2] = (byte) ((uint) (short) num9 & (uint) byte.MaxValue); + numArray[index * 2 + 1] = (byte) ((uint) (short) num9 >> 8); + num4 = num3; + num3 = num9; + } + this.pHist1[channel] = num3; + this.pHist2[channel] = num4; + return numArray; + } + } +} diff --git a/BNS_Data.cs b/BNS_Data.cs new file mode 100644 index 0000000..5f68ebb --- /dev/null +++ b/BNS_Data.cs @@ -0,0 +1,51 @@ +// Decompiled with JetBrains decompiler +// Type: libWiiSharp.BNS_Data +// Assembly: libWiiSharp, Version=0.4.0.0, Culture=neutral, PublicKeyToken=null +// MVID: FBF36F3D-B5D6-481F-B5F5-1BD3C19E13B2 +// Assembly location: C:\Users\theso\Downloads\NCPatcher\pack\libWiiSharp.dll + +using System; +using System.IO; + +namespace libWiiSharp +{ + internal class BNS_Data + { + private byte[] magic = new byte[4] + { + (byte) 68, + (byte) 65, + (byte) 84, + (byte) 65 + }; + private uint size = 315392; + private byte[] data; + + public uint Size + { + get => this.size; + set => this.size = value; + } + + public byte[] Data + { + get => this.data; + set => this.data = value; + } + + public void Write(Stream outStream) + { + byte[] bytes = BitConverter.GetBytes(Shared.Swap(this.size)); + outStream.Write(this.magic, 0, this.magic.Length); + outStream.Write(bytes, 0, bytes.Length); + outStream.Write(this.data, 0, this.data.Length); + } + + public void Read(Stream input) + { + BinaryReader binaryReader = new BinaryReader(input); + this.size = Shared.CompareByteArrays(this.magic, binaryReader.ReadBytes(4)) ? Shared.Swap(binaryReader.ReadUInt32()) : throw new Exception("This is not a valid BNS audfo file!"); + this.data = binaryReader.ReadBytes((int) this.size - 8); + } + } +} diff --git a/BNS_Header.cs b/BNS_Header.cs new file mode 100644 index 0000000..84db688 --- /dev/null +++ b/BNS_Header.cs @@ -0,0 +1,108 @@ +// Decompiled with JetBrains decompiler +// Type: libWiiSharp.BNS_Header +// Assembly: libWiiSharp, Version=0.4.0.0, Culture=neutral, PublicKeyToken=null +// MVID: FBF36F3D-B5D6-481F-B5F5-1BD3C19E13B2 +// Assembly location: C:\Users\theso\Downloads\NCPatcher\pack\libWiiSharp.dll + +using System; +using System.IO; + +namespace libWiiSharp +{ + internal class BNS_Header + { + private byte[] magic = new byte[4] + { + (byte) 66, + (byte) 78, + (byte) 83, + (byte) 32 + }; + private uint flags = 4278124800; + private uint fileSize = 315584; + private ushort size = 32; + private ushort chunkCount = 2; + private uint infoOffset = 32; + private uint infoLength = 160; + private uint dataOffset = 192; + private uint dataLength = 315392; + + public uint DataOffset + { + get => this.dataOffset; + set => this.dataOffset = value; + } + + public uint InfoLength + { + get => this.infoLength; + set => this.infoLength = value; + } + + public ushort Size + { + get => this.size; + set => this.size = value; + } + + public uint DataLength + { + get => this.dataLength; + set => this.dataLength = value; + } + + public uint FileSize + { + get => this.fileSize; + set => this.fileSize = value; + } + + public void Write(Stream outStream) + { + outStream.Write(this.magic, 0, this.magic.Length); + byte[] bytes1 = BitConverter.GetBytes(this.flags); + Array.Reverse((Array) bytes1); + outStream.Write(bytes1, 0, bytes1.Length); + byte[] bytes2 = BitConverter.GetBytes(this.fileSize); + Array.Reverse((Array) bytes2); + outStream.Write(bytes2, 0, bytes2.Length); + byte[] bytes3 = BitConverter.GetBytes(this.size); + Array.Reverse((Array) bytes3); + outStream.Write(bytes3, 0, bytes3.Length); + byte[] bytes4 = BitConverter.GetBytes(this.chunkCount); + Array.Reverse((Array) bytes4); + outStream.Write(bytes4, 0, bytes4.Length); + byte[] bytes5 = BitConverter.GetBytes(this.infoOffset); + Array.Reverse((Array) bytes5); + outStream.Write(bytes5, 0, bytes5.Length); + byte[] bytes6 = BitConverter.GetBytes(this.infoLength); + Array.Reverse((Array) bytes6); + outStream.Write(bytes6, 0, bytes6.Length); + byte[] bytes7 = BitConverter.GetBytes(this.dataOffset); + Array.Reverse((Array) bytes7); + outStream.Write(bytes7, 0, bytes7.Length); + byte[] bytes8 = BitConverter.GetBytes(this.dataLength); + Array.Reverse((Array) bytes8); + outStream.Write(bytes8, 0, bytes8.Length); + } + + public void Read(Stream input) + { + BinaryReader binaryReader = new BinaryReader(input); + if (!Shared.CompareByteArrays(this.magic, binaryReader.ReadBytes(4))) + { + binaryReader.BaseStream.Seek(28L, SeekOrigin.Current); + if (!Shared.CompareByteArrays(this.magic, binaryReader.ReadBytes(4))) + throw new Exception("This is not a valid BNS audfo file!"); + } + this.flags = Shared.Swap(binaryReader.ReadUInt32()); + this.fileSize = Shared.Swap(binaryReader.ReadUInt32()); + this.size = Shared.Swap(binaryReader.ReadUInt16()); + this.chunkCount = Shared.Swap(binaryReader.ReadUInt16()); + this.infoOffset = Shared.Swap(binaryReader.ReadUInt32()); + this.infoLength = Shared.Swap(binaryReader.ReadUInt32()); + this.dataOffset = Shared.Swap(binaryReader.ReadUInt32()); + this.dataLength = Shared.Swap(binaryReader.ReadUInt32()); + } + } +} diff --git a/BNS_Info.cs b/BNS_Info.cs new file mode 100644 index 0000000..caa54b8 --- /dev/null +++ b/BNS_Info.cs @@ -0,0 +1,351 @@ +// Decompiled with JetBrains decompiler +// Type: libWiiSharp.BNS_Info +// Assembly: libWiiSharp, Version=0.4.0.0, Culture=neutral, PublicKeyToken=null +// MVID: FBF36F3D-B5D6-481F-B5F5-1BD3C19E13B2 +// Assembly location: C:\Users\theso\Downloads\NCPatcher\pack\libWiiSharp.dll + +using System; +using System.IO; + +namespace libWiiSharp +{ + internal class BNS_Info + { + private byte[] magic = new byte[4] + { + (byte) 73, + (byte) 78, + (byte) 70, + (byte) 79 + }; + private uint size = 160; + private byte codec; + private byte hasLoop; + private byte channelCount = 2; + private byte zero; + private ushort sampleRate = 44100; + private ushort pad0; + private uint loopStart; + private uint loopEnd; + private uint offsetToChannelStart = 24; + private uint pad1; + private uint channel1StartOffset = 32; + private uint channel2StartOffset = 44; + private uint channel1Start; + private uint coefficients1Offset = 56; + private uint pad2; + private uint channel2Start; + private uint coefficients2Offset = 104; + private uint pad3; + private int[] coefficients1 = new int[16]; + private ushort channel1Gain; + private ushort channel1PredictiveScale; + private ushort channel1PreviousValue; + private ushort channel1NextPreviousValue; + private ushort channel1LoopPredictiveScale; + private ushort channel1LoopPreviousValue; + private ushort channel1LoopNextPreviousValue; + private ushort channel1LoopPadding; + private int[] coefficients2 = new int[16]; + private ushort channel2Gain; + private ushort channel2PredictiveScale; + private ushort channel2PreviousValue; + private ushort channel2NextPreviousValue; + private ushort channel2LoopPredictiveScale; + private ushort channel2LoopPreviousValue; + private ushort channel2LoopNextPreviousValue; + private ushort channel2LoopPadding; + + public byte HasLoop + { + get => this.hasLoop; + set => this.hasLoop = value; + } + + public uint Coefficients1Offset + { + get => this.coefficients1Offset; + set => this.coefficients1Offset = value; + } + + public uint Channel1StartOffset + { + get => this.channel1StartOffset; + set => this.channel1StartOffset = value; + } + + public uint Channel2StartOffset + { + get => this.channel2StartOffset; + set => this.channel2StartOffset = value; + } + + public uint Size + { + get => this.size; + set => this.size = value; + } + + public ushort SampleRate + { + get => this.sampleRate; + set => this.sampleRate = value; + } + + public byte ChannelCount + { + get => this.channelCount; + set => this.channelCount = value; + } + + public uint Channel1Start + { + get => this.channel1Start; + set => this.channel1Start = value; + } + + public uint Channel2Start + { + get => this.channel2Start; + set => this.channel2Start = value; + } + + public uint LoopStart + { + get => this.loopStart; + set => this.loopStart = value; + } + + public uint LoopEnd + { + get => this.loopEnd; + set => this.loopEnd = value; + } + + public int[] Coefficients1 + { + get => this.coefficients1; + set => this.coefficients1 = value; + } + + public int[] Coefficients2 + { + get => this.coefficients2; + set => this.coefficients2 = value; + } + + public void Write(Stream outStream) + { + outStream.Write(this.magic, 0, this.magic.Length); + byte[] bytes1 = BitConverter.GetBytes(this.size); + Array.Reverse((Array) bytes1); + outStream.Write(bytes1, 0, bytes1.Length); + outStream.WriteByte(this.codec); + outStream.WriteByte(this.hasLoop); + outStream.WriteByte(this.channelCount); + outStream.WriteByte(this.zero); + byte[] bytes2 = BitConverter.GetBytes(this.sampleRate); + Array.Reverse((Array) bytes2); + outStream.Write(bytes2, 0, bytes2.Length); + byte[] bytes3 = BitConverter.GetBytes(this.pad0); + Array.Reverse((Array) bytes3); + outStream.Write(bytes3, 0, bytes3.Length); + byte[] bytes4 = BitConverter.GetBytes(this.loopStart); + Array.Reverse((Array) bytes4); + outStream.Write(bytes4, 0, bytes4.Length); + byte[] bytes5 = BitConverter.GetBytes(this.loopEnd); + Array.Reverse((Array) bytes5); + outStream.Write(bytes5, 0, bytes5.Length); + byte[] bytes6 = BitConverter.GetBytes(this.offsetToChannelStart); + Array.Reverse((Array) bytes6); + outStream.Write(bytes6, 0, bytes6.Length); + byte[] bytes7 = BitConverter.GetBytes(this.pad1); + Array.Reverse((Array) bytes7); + outStream.Write(bytes7, 0, bytes7.Length); + byte[] bytes8 = BitConverter.GetBytes(this.channel1StartOffset); + Array.Reverse((Array) bytes8); + outStream.Write(bytes8, 0, bytes8.Length); + byte[] bytes9 = BitConverter.GetBytes(this.channel2StartOffset); + Array.Reverse((Array) bytes9); + outStream.Write(bytes9, 0, bytes9.Length); + byte[] bytes10 = BitConverter.GetBytes(this.channel1Start); + Array.Reverse((Array) bytes10); + outStream.Write(bytes10, 0, bytes10.Length); + byte[] bytes11 = BitConverter.GetBytes(this.coefficients1Offset); + Array.Reverse((Array) bytes11); + outStream.Write(bytes11, 0, bytes11.Length); + if (this.channelCount == (byte) 2) + { + byte[] bytes12 = BitConverter.GetBytes(this.pad2); + Array.Reverse((Array) bytes12); + outStream.Write(bytes12, 0, bytes12.Length); + byte[] bytes13 = BitConverter.GetBytes(this.channel2Start); + Array.Reverse((Array) bytes13); + outStream.Write(bytes13, 0, bytes13.Length); + byte[] bytes14 = BitConverter.GetBytes(this.coefficients2Offset); + Array.Reverse((Array) bytes14); + outStream.Write(bytes14, 0, bytes14.Length); + byte[] bytes15 = BitConverter.GetBytes(this.pad3); + Array.Reverse((Array) bytes15); + outStream.Write(bytes15, 0, bytes15.Length); + foreach (int num in this.coefficients1) + { + byte[] bytes16 = BitConverter.GetBytes(num); + Array.Reverse((Array) bytes16); + outStream.Write(bytes16, 2, bytes16.Length - 2); + } + byte[] bytes17 = BitConverter.GetBytes(this.channel1Gain); + Array.Reverse((Array) bytes17); + outStream.Write(bytes17, 0, bytes17.Length); + byte[] bytes18 = BitConverter.GetBytes(this.channel1PredictiveScale); + Array.Reverse((Array) bytes18); + outStream.Write(bytes18, 0, bytes18.Length); + byte[] bytes19 = BitConverter.GetBytes(this.channel1PreviousValue); + Array.Reverse((Array) bytes19); + outStream.Write(bytes19, 0, bytes19.Length); + byte[] bytes20 = BitConverter.GetBytes(this.channel1NextPreviousValue); + Array.Reverse((Array) bytes20); + outStream.Write(bytes20, 0, bytes20.Length); + byte[] bytes21 = BitConverter.GetBytes(this.channel1LoopPredictiveScale); + Array.Reverse((Array) bytes21); + outStream.Write(bytes21, 0, bytes21.Length); + byte[] bytes22 = BitConverter.GetBytes(this.channel1LoopPreviousValue); + Array.Reverse((Array) bytes22); + outStream.Write(bytes22, 0, bytes22.Length); + byte[] bytes23 = BitConverter.GetBytes(this.channel1LoopNextPreviousValue); + Array.Reverse((Array) bytes23); + outStream.Write(bytes23, 0, bytes23.Length); + byte[] bytes24 = BitConverter.GetBytes(this.channel1LoopPadding); + Array.Reverse((Array) bytes24); + outStream.Write(bytes24, 0, bytes24.Length); + foreach (int num in this.coefficients2) + { + byte[] bytes16 = BitConverter.GetBytes(num); + Array.Reverse((Array) bytes16); + outStream.Write(bytes16, 2, bytes16.Length - 2); + } + byte[] bytes25 = BitConverter.GetBytes(this.channel2Gain); + Array.Reverse((Array) bytes25); + outStream.Write(bytes25, 0, bytes25.Length); + byte[] bytes26 = BitConverter.GetBytes(this.channel2PredictiveScale); + Array.Reverse((Array) bytes26); + outStream.Write(bytes26, 0, bytes26.Length); + byte[] bytes27 = BitConverter.GetBytes(this.channel2PreviousValue); + Array.Reverse((Array) bytes27); + outStream.Write(bytes27, 0, bytes27.Length); + byte[] bytes28 = BitConverter.GetBytes(this.channel2NextPreviousValue); + Array.Reverse((Array) bytes28); + outStream.Write(bytes28, 0, bytes28.Length); + byte[] bytes29 = BitConverter.GetBytes(this.channel2LoopPredictiveScale); + Array.Reverse((Array) bytes29); + outStream.Write(bytes29, 0, bytes29.Length); + byte[] bytes30 = BitConverter.GetBytes(this.channel2LoopPreviousValue); + Array.Reverse((Array) bytes30); + outStream.Write(bytes30, 0, bytes30.Length); + byte[] bytes31 = BitConverter.GetBytes(this.channel2LoopNextPreviousValue); + Array.Reverse((Array) bytes31); + outStream.Write(bytes31, 0, bytes31.Length); + byte[] bytes32 = BitConverter.GetBytes(this.channel2LoopPadding); + Array.Reverse((Array) bytes32); + outStream.Write(bytes32, 0, bytes32.Length); + } + else + { + if (this.channelCount != (byte) 1) + return; + foreach (int num in this.coefficients1) + { + byte[] bytes12 = BitConverter.GetBytes(num); + Array.Reverse((Array) bytes12); + outStream.Write(bytes12, 2, bytes12.Length - 2); + } + byte[] bytes13 = BitConverter.GetBytes(this.channel1Gain); + Array.Reverse((Array) bytes13); + outStream.Write(bytes13, 0, bytes13.Length); + byte[] bytes14 = BitConverter.GetBytes(this.channel1PredictiveScale); + Array.Reverse((Array) bytes14); + outStream.Write(bytes14, 0, bytes14.Length); + byte[] bytes15 = BitConverter.GetBytes(this.channel1PreviousValue); + Array.Reverse((Array) bytes15); + outStream.Write(bytes15, 0, bytes15.Length); + byte[] bytes16 = BitConverter.GetBytes(this.channel1NextPreviousValue); + Array.Reverse((Array) bytes16); + outStream.Write(bytes16, 0, bytes16.Length); + byte[] bytes17 = BitConverter.GetBytes(this.channel1LoopPredictiveScale); + Array.Reverse((Array) bytes17); + outStream.Write(bytes17, 0, bytes17.Length); + byte[] bytes18 = BitConverter.GetBytes(this.channel1LoopPreviousValue); + Array.Reverse((Array) bytes18); + outStream.Write(bytes18, 0, bytes18.Length); + byte[] bytes19 = BitConverter.GetBytes(this.channel1LoopNextPreviousValue); + Array.Reverse((Array) bytes19); + outStream.Write(bytes19, 0, bytes19.Length); + byte[] bytes20 = BitConverter.GetBytes(this.channel1LoopPadding); + Array.Reverse((Array) bytes20); + outStream.Write(bytes20, 0, bytes20.Length); + } + } + + public void Read(Stream input) + { + BinaryReader binaryReader = new BinaryReader(input); + this.size = Shared.CompareByteArrays(this.magic, binaryReader.ReadBytes(4)) ? Shared.Swap(binaryReader.ReadUInt32()) : throw new Exception("This is not a valid BNS audfo file!"); + this.codec = binaryReader.ReadByte(); + this.hasLoop = binaryReader.ReadByte(); + this.channelCount = binaryReader.ReadByte(); + this.zero = binaryReader.ReadByte(); + this.sampleRate = Shared.Swap(binaryReader.ReadUInt16()); + this.pad0 = Shared.Swap(binaryReader.ReadUInt16()); + this.loopStart = Shared.Swap(binaryReader.ReadUInt32()); + this.loopEnd = Shared.Swap(binaryReader.ReadUInt32()); + this.offsetToChannelStart = Shared.Swap(binaryReader.ReadUInt32()); + this.pad1 = Shared.Swap(binaryReader.ReadUInt32()); + this.channel1StartOffset = Shared.Swap(binaryReader.ReadUInt32()); + this.channel2StartOffset = Shared.Swap(binaryReader.ReadUInt32()); + this.channel1Start = Shared.Swap(binaryReader.ReadUInt32()); + this.coefficients1Offset = Shared.Swap(binaryReader.ReadUInt32()); + if (this.channelCount == (byte) 2) + { + this.pad2 = Shared.Swap(binaryReader.ReadUInt32()); + this.channel2Start = Shared.Swap(binaryReader.ReadUInt32()); + this.coefficients2Offset = Shared.Swap(binaryReader.ReadUInt32()); + this.pad3 = Shared.Swap(binaryReader.ReadUInt32()); + for (int index = 0; index < 16; ++index) + this.coefficients1[index] = (int) (short) Shared.Swap(binaryReader.ReadUInt16()); + this.channel1Gain = Shared.Swap(binaryReader.ReadUInt16()); + this.channel1PredictiveScale = Shared.Swap(binaryReader.ReadUInt16()); + this.channel1PreviousValue = Shared.Swap(binaryReader.ReadUInt16()); + this.channel1NextPreviousValue = Shared.Swap(binaryReader.ReadUInt16()); + this.channel1LoopPredictiveScale = Shared.Swap(binaryReader.ReadUInt16()); + this.channel1LoopPreviousValue = Shared.Swap(binaryReader.ReadUInt16()); + this.channel1LoopNextPreviousValue = Shared.Swap(binaryReader.ReadUInt16()); + this.channel1LoopPadding = Shared.Swap(binaryReader.ReadUInt16()); + for (int index = 0; index < 16; ++index) + this.coefficients2[index] = (int) (short) Shared.Swap(binaryReader.ReadUInt16()); + this.channel2Gain = Shared.Swap(binaryReader.ReadUInt16()); + this.channel2PredictiveScale = Shared.Swap(binaryReader.ReadUInt16()); + this.channel2PreviousValue = Shared.Swap(binaryReader.ReadUInt16()); + this.channel2NextPreviousValue = Shared.Swap(binaryReader.ReadUInt16()); + this.channel2LoopPredictiveScale = Shared.Swap(binaryReader.ReadUInt16()); + this.channel2LoopPreviousValue = Shared.Swap(binaryReader.ReadUInt16()); + this.channel2LoopNextPreviousValue = Shared.Swap(binaryReader.ReadUInt16()); + this.channel2LoopPadding = Shared.Swap(binaryReader.ReadUInt16()); + } + else + { + if (this.channelCount != (byte) 1) + return; + for (int index = 0; index < 16; ++index) + this.coefficients1[index] = (int) (short) Shared.Swap(binaryReader.ReadUInt16()); + this.channel1Gain = Shared.Swap(binaryReader.ReadUInt16()); + this.channel1PredictiveScale = Shared.Swap(binaryReader.ReadUInt16()); + this.channel1PreviousValue = Shared.Swap(binaryReader.ReadUInt16()); + this.channel1NextPreviousValue = Shared.Swap(binaryReader.ReadUInt16()); + this.channel1LoopPredictiveScale = Shared.Swap(binaryReader.ReadUInt16()); + this.channel1LoopPreviousValue = Shared.Swap(binaryReader.ReadUInt16()); + this.channel1LoopNextPreviousValue = Shared.Swap(binaryReader.ReadUInt16()); + this.channel1LoopPadding = Shared.Swap(binaryReader.ReadUInt16()); + } + } + } +} diff --git a/Brlan.cs b/Brlan.cs new file mode 100644 index 0000000..7b0a37d --- /dev/null +++ b/Brlan.cs @@ -0,0 +1,66 @@ +// Decompiled with JetBrains decompiler +// Type: libWiiSharp.Brlan +// Assembly: libWiiSharp, Version=0.4.0.0, Culture=neutral, PublicKeyToken=null +// MVID: FBF36F3D-B5D6-481F-B5F5-1BD3C19E13B2 +// Assembly location: C:\Users\theso\Downloads\NCPatcher\pack\libWiiSharp.dll + +using System; +using System.Collections.Generic; +using System.IO; + +namespace libWiiSharp +{ + public class Brlan + { + public static string[] GetBrlanTpls(string pathTobrlan) => Brlan.getBrlanTpls(File.ReadAllBytes(pathTobrlan)); + + public static string[] GetBrlanTpls(byte[] brlanFile) => Brlan.getBrlanTpls(brlanFile); + + public static string[] GetBrlanTpls(WAD wad, bool banner) + { + if (!wad.HasBanner) + return new string[0]; + string str = nameof (banner); + if (!banner) + str = "icon"; + for (int index1 = 0; index1 < wad.BannerApp.Nodes.Count; ++index1) + { + if (wad.BannerApp.StringTable[index1].ToLower() == str + ".bin") + { + U8 u8 = U8.Load(wad.BannerApp.Data[index1]); + string[] a = new string[0]; + for (int index2 = 0; index2 < u8.Nodes.Count; ++index2) + { + if (u8.StringTable[index2].ToLower() == str + "_start.brlan" || u8.StringTable[index2].ToLower() == str + "_loop.brlan" || u8.StringTable[index2].ToLower() == str + ".brlan") + a = Shared.MergeStringArrays(a, Brlan.getBrlanTpls(u8.Data[index2])); + } + return a; + } + } + return new string[0]; + } + + private static string[] getBrlanTpls(byte[] brlanFile) + { + List stringList = new List(); + int numOfTpls = Brlan.getNumOfTpls(brlanFile); + int index1 = 36 + numOfTpls * 4; + for (int index2 = 0; index2 < numOfTpls; ++index2) + { + string empty = string.Empty; + while (brlanFile[index1] != (byte) 0) + empty += Convert.ToChar(brlanFile[index1++]).ToString(); + stringList.Add(empty); + ++index1; + } + for (int index2 = stringList.Count - 1; index2 >= 0; --index2) + { + if (!stringList[index2].ToLower().EndsWith(".tpl")) + stringList.RemoveAt(index2); + } + return stringList.ToArray(); + } + + private static int getNumOfTpls(byte[] brlanFile) => (int) Shared.Swap(BitConverter.ToUInt16(brlanFile, 28)); + } +} diff --git a/Brlyt.cs b/Brlyt.cs new file mode 100644 index 0000000..5bd937f --- /dev/null +++ b/Brlyt.cs @@ -0,0 +1,66 @@ +// Decompiled with JetBrains decompiler +// Type: libWiiSharp.Brlyt +// Assembly: libWiiSharp, Version=0.4.0.0, Culture=neutral, PublicKeyToken=null +// MVID: FBF36F3D-B5D6-481F-B5F5-1BD3C19E13B2 +// Assembly location: C:\Users\theso\Downloads\NCPatcher\pack\libWiiSharp.dll + +using System; +using System.Collections.Generic; +using System.IO; + +namespace libWiiSharp +{ + public class Brlyt + { + public static string[] GetBrlytTpls(string pathToBrlyt) => Brlyt.getBrlytTpls(File.ReadAllBytes(pathToBrlyt)); + + public static string[] GetBrlytTpls(byte[] brlytFile) => Brlyt.getBrlytTpls(brlytFile); + + public static string[] GetBrlytTpls(WAD wad, bool banner) + { + if (!wad.HasBanner) + return new string[0]; + string str = nameof (banner); + if (!banner) + str = "icon"; + for (int index1 = 0; index1 < wad.BannerApp.Nodes.Count; ++index1) + { + if (wad.BannerApp.StringTable[index1].ToLower() == str + ".bin") + { + U8 u8 = U8.Load(wad.BannerApp.Data[index1]); + string[] a = new string[0]; + for (int index2 = 0; index2 < u8.Nodes.Count; ++index2) + { + if (u8.StringTable[index2].ToLower() == str + ".brlyt") + a = Shared.MergeStringArrays(a, Brlyt.getBrlytTpls(u8.Data[index2])); + } + return a; + } + } + return new string[0]; + } + + private static string[] getBrlytTpls(byte[] brlytFile) + { + List stringList = new List(); + int numOfTpls = Brlyt.getNumOfTpls(brlytFile); + int index1 = 48 + numOfTpls * 8; + for (int index2 = 0; index2 < numOfTpls; ++index2) + { + string empty = string.Empty; + while (brlytFile[index1] != (byte) 0) + empty += Convert.ToChar(brlytFile[index1++]).ToString(); + stringList.Add(empty); + ++index1; + } + for (int index2 = stringList.Count - 1; index2 >= 0; --index2) + { + if (!stringList[index2].ToLower().EndsWith(".tpl")) + stringList.RemoveAt(index2); + } + return stringList.ToArray(); + } + + private static int getNumOfTpls(byte[] brlytFile) => (int) Shared.Swap(BitConverter.ToUInt16(brlytFile, 44)); + } +} diff --git a/CertificateChain.cs b/CertificateChain.cs new file mode 100644 index 0000000..2ba228a --- /dev/null +++ b/CertificateChain.cs @@ -0,0 +1,438 @@ +// Decompiled with JetBrains decompiler +// Type: libWiiSharp.CertificateChain +// Assembly: libWiiSharp, Version=0.4.0.0, Culture=neutral, PublicKeyToken=null +// MVID: FBF36F3D-B5D6-481F-B5F5-1BD3C19E13B2 +// Assembly location: C:\Users\theso\Downloads\NCPatcher\pack\libWiiSharp.dll + +using System; +using System.IO; +using System.Security.Cryptography; + +namespace libWiiSharp +{ + public class CertificateChain : IDisposable + { + private const string certCaHash = "5B7D3EE28706AD8DA2CBD5A6B75C15D0F9B6F318"; + private const string certCpHash = "6824D6DA4C25184F0D6DAF6EDB9C0FC57522A41C"; + private const string certXsHash = "09787045037121477824BC6A3E5E076156573F8A"; + private SHA1 sha = SHA1.Create(); + private bool[] certsComplete = new bool[3]; + private byte[] certCa = new byte[1024]; + private byte[] certCp = new byte[768]; + private byte[] certXs = new byte[768]; + private bool isDisposed; + + public bool CertsComplete => this.certsComplete[0] && this.certsComplete[1] && this.certsComplete[2]; + + public event EventHandler Debug; + + ~CertificateChain() => 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.certsComplete = (bool[]) null; + this.certCa = (byte[]) null; + this.certCp = (byte[]) null; + this.certXs = (byte[]) null; + } + this.isDisposed = true; + } + + public static CertificateChain Load(string pathToCert) => CertificateChain.Load(File.ReadAllBytes(pathToCert)); + + public static CertificateChain Load(byte[] certFile) + { + CertificateChain certificateChain = new CertificateChain(); + MemoryStream memoryStream = new MemoryStream(certFile); + try + { + certificateChain.parseCert((Stream) memoryStream); + } + catch + { + memoryStream.Dispose(); + throw; + } + memoryStream.Dispose(); + return certificateChain; + } + + public static CertificateChain Load(Stream cert) + { + CertificateChain certificateChain = new CertificateChain(); + certificateChain.parseCert(cert); + return certificateChain; + } + + public static CertificateChain FromTikTmd(string pathToTik, string pathToTmd) => CertificateChain.FromTikTmd(File.ReadAllBytes(pathToTik), File.ReadAllBytes(pathToTmd)); + + public static CertificateChain FromTikTmd(byte[] tikFile, byte[] tmdFile) + { + CertificateChain certificateChain = new CertificateChain(); + MemoryStream memoryStream1 = new MemoryStream(tikFile); + try + { + certificateChain.grabFromTik((Stream) memoryStream1); + } + catch + { + memoryStream1.Dispose(); + throw; + } + MemoryStream memoryStream2 = new MemoryStream(tmdFile); + try + { + certificateChain.grabFromTmd((Stream) memoryStream2); + } + catch + { + memoryStream2.Dispose(); + throw; + } + memoryStream2.Dispose(); + return certificateChain.CertsComplete ? certificateChain : throw new Exception("Couldn't locate all certs!"); + } + + public static CertificateChain FromTikTmd(Stream tik, Stream tmd) + { + CertificateChain certificateChain = new CertificateChain(); + certificateChain.grabFromTik(tik); + certificateChain.grabFromTmd(tmd); + return certificateChain; + } + + public void LoadFile(string pathToCert) => this.LoadFile(File.ReadAllBytes(pathToCert)); + + public void LoadFile(byte[] certFile) + { + MemoryStream memoryStream = new MemoryStream(certFile); + try + { + this.parseCert((Stream) memoryStream); + } + catch + { + memoryStream.Dispose(); + throw; + } + memoryStream.Dispose(); + } + + public void LoadFile(Stream cert) => this.parseCert(cert); + + public void LoadFromTikTmd(string pathToTik, string pathToTmd) => this.LoadFromTikTmd(File.ReadAllBytes(pathToTik), File.ReadAllBytes(pathToTmd)); + + public void LoadFromTikTmd(byte[] tikFile, byte[] tmdFile) + { + MemoryStream memoryStream1 = new MemoryStream(tikFile); + try + { + this.grabFromTik((Stream) memoryStream1); + } + catch + { + memoryStream1.Dispose(); + throw; + } + MemoryStream memoryStream2 = new MemoryStream(tmdFile); + try + { + this.grabFromTmd((Stream) memoryStream2); + } + catch + { + memoryStream2.Dispose(); + throw; + } + memoryStream2.Dispose(); + if (!this.CertsComplete) + throw new Exception("Couldn't locate all certs!"); + } + + public void LoadFromTikTmd(Stream tik, Stream tmd) + { + this.grabFromTik(tik); + this.grabFromTmd(tmd); + } + + public void Save(string savePath) + { + if (File.Exists(savePath)) + File.Delete(savePath); + using (FileStream fileStream = new FileStream(savePath, FileMode.Create)) + this.writeToStream((Stream) fileStream); + } + + public MemoryStream ToMemoryStream() + { + MemoryStream memoryStream = new MemoryStream(); + try + { + this.writeToStream((Stream) memoryStream); + return memoryStream; + } + catch + { + memoryStream.Dispose(); + throw; + } + } + + public byte[] ToByteArray() + { + MemoryStream memoryStream = new MemoryStream(); + try + { + this.writeToStream((Stream) memoryStream); + } + catch + { + memoryStream.Dispose(); + throw; + } + byte[] array = memoryStream.ToArray(); + memoryStream.Dispose(); + return array; + } + + private void writeToStream(Stream writeStream) + { + this.fireDebug("Writing Certificate Chain..."); + if (!this.CertsComplete) + { + this.fireDebug(" Certificate Chain incomplete..."); + throw new Exception("At least one certificate is missing!"); + } + writeStream.Seek(0L, SeekOrigin.Begin); + object[] objArray1 = new object[1]; + long position = writeStream.Position; + objArray1[0] = (object) position.ToString("x8"); + this.fireDebug(" Writing Certificate CA... (Offset: 0x{0})", objArray1); + writeStream.Write(this.certCa, 0, this.certCa.Length); + object[] objArray2 = new object[1]; + position = writeStream.Position; + objArray2[0] = (object) position.ToString("x8"); + this.fireDebug(" Writing Certificate CP... (Offset: 0x{0})", objArray2); + writeStream.Write(this.certCp, 0, this.certCp.Length); + object[] objArray3 = new object[1]; + position = writeStream.Position; + objArray3[0] = (object) position.ToString("x8"); + this.fireDebug(" Writing Certificate XS... (Offset: 0x{0})", objArray3); + writeStream.Write(this.certXs, 0, this.certXs.Length); + this.fireDebug("Writing Certificate Chain Finished..."); + } + + private void parseCert(Stream certFile) + { + this.fireDebug("Parsing Certificate Chain..."); + int num = 0; + for (int index = 0; index < 3; ++index) + { + this.fireDebug(" Scanning at Offset 0x{0}:", (object) num.ToString("x8")); + try + { + certFile.Seek((long) num, SeekOrigin.Begin); + byte[] array = new byte[1024]; + certFile.Read(array, 0, array.Length); + this.fireDebug(" Checking for Certificate CA..."); + if (this.isCertCa(array) && !this.certsComplete[1]) + { + this.fireDebug(" Certificate CA detected..."); + this.certCa = array; + this.certsComplete[1] = true; + num += 1024; + continue; + } + this.fireDebug(" Checking for Certificate CP..."); + if (this.isCertCp(array) && !this.certsComplete[2]) + { + this.fireDebug(" Certificate CP detected..."); + Array.Resize(ref array, 768); + this.certCp = array; + this.certsComplete[2] = true; + num += 768; + continue; + } + this.fireDebug(" Checking for Certificate XS..."); + if (this.isCertXs(array)) + { + if (!this.certsComplete[0]) + { + this.fireDebug(" Certificate XS detected..."); + Array.Resize(ref array, 768); + this.certXs = array; + this.certsComplete[0] = true; + num += 768; + continue; + } + } + } + catch (Exception ex) + { + this.fireDebug("Error: {0}", (object) ex.Message); + } + num += 768; + } + if (!this.CertsComplete) + { + this.fireDebug(" Couldn't locate all Certificates..."); + throw new Exception("Couldn't locate all certs!"); + } + this.fireDebug("Parsing Certificate Chain Finished..."); + } + + private void grabFromTik(Stream tik) + { + this.fireDebug("Scanning Ticket for Certificates..."); + int num = 676; + for (int index = 0; index < 3; ++index) + { + this.fireDebug(" Scanning at Offset 0x{0}:", (object) num.ToString("x8")); + try + { + tik.Seek((long) num, SeekOrigin.Begin); + byte[] array = new byte[1024]; + tik.Read(array, 0, array.Length); + this.fireDebug(" Checking for Certificate CA..."); + if (this.isCertCa(array) && !this.certsComplete[1]) + { + this.fireDebug(" Certificate CA detected..."); + this.certCa = array; + this.certsComplete[1] = true; + num += 1024; + continue; + } + this.fireDebug(" Checking for Certificate CP..."); + if (this.isCertCp(array) && !this.certsComplete[2]) + { + this.fireDebug(" Certificate CP detected..."); + Array.Resize(ref array, 768); + this.certCp = array; + this.certsComplete[2] = true; + num += 768; + continue; + } + this.fireDebug(" Checking for Certificate XS..."); + if (this.isCertXs(array)) + { + if (!this.certsComplete[0]) + { + this.fireDebug(" Certificate XS detected..."); + Array.Resize(ref array, 768); + this.certXs = array; + this.certsComplete[0] = true; + num += 768; + continue; + } + } + } + catch + { + } + num += 768; + } + this.fireDebug("Scanning Ticket for Certificates Finished..."); + } + + private void grabFromTmd(Stream tmd) + { + this.fireDebug("Scanning TMD for Certificates..."); + byte[] buffer = new byte[2]; + tmd.Seek(478L, SeekOrigin.Begin); + tmd.Read(buffer, 0, 2); + int num = 484 + (int) Shared.Swap(BitConverter.ToUInt16(buffer, 0)) * 36; + for (int index = 0; index < 3; ++index) + { + this.fireDebug(" Scanning at Offset 0x{0}:", (object) num.ToString("x8")); + try + { + tmd.Seek((long) num, SeekOrigin.Begin); + byte[] array = new byte[1024]; + tmd.Read(array, 0, array.Length); + this.fireDebug(" Checking for Certificate CA..."); + if (this.isCertCa(array) && !this.certsComplete[1]) + { + this.fireDebug(" Certificate CA detected..."); + this.certCa = array; + this.certsComplete[1] = true; + num += 1024; + continue; + } + this.fireDebug(" Checking for Certificate CP..."); + if (this.isCertCp(array) && !this.certsComplete[2]) + { + this.fireDebug(" Certificate CP detected..."); + Array.Resize(ref array, 768); + this.certCp = array; + this.certsComplete[2] = true; + num += 768; + continue; + } + this.fireDebug(" Checking for Certificate XS..."); + if (this.isCertXs(array)) + { + if (!this.certsComplete[0]) + { + this.fireDebug(" Certificate XS detected..."); + Array.Resize(ref array, 768); + this.certXs = array; + this.certsComplete[0] = true; + num += 768; + continue; + } + } + } + catch + { + } + num += 768; + } + this.fireDebug("Scanning TMD for Certificates Finished..."); + } + + private bool isCertXs(byte[] part) + { + if (part.Length < 768) + return false; + if (part.Length > 768) + Array.Resize(ref part, 768); + return part[388] == (byte) 88 && part[389] == (byte) 83 && Shared.CompareByteArrays(this.sha.ComputeHash(part), Shared.HexStringToByteArray("09787045037121477824BC6A3E5E076156573F8A")); + } + + private bool isCertCa(byte[] part) + { + if (part.Length < 1024) + return false; + if (part.Length > 1024) + Array.Resize(ref part, 1024); + return part[644] == (byte) 67 && part[645] == (byte) 65 && Shared.CompareByteArrays(this.sha.ComputeHash(part), Shared.HexStringToByteArray("5B7D3EE28706AD8DA2CBD5A6B75C15D0F9B6F318")); + } + + private bool isCertCp(byte[] part) + { + if (part.Length < 768) + return false; + if (part.Length > 768) + Array.Resize(ref part, 768); + return part[388] == (byte) 67 && part[389] == (byte) 80 && Shared.CompareByteArrays(this.sha.ComputeHash(part), Shared.HexStringToByteArray("6824D6DA4C25184F0D6DAF6EDB9C0FC57522A41C")); + } + + 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))); + } + } +} diff --git a/Changelog_libWiiSharp.txt b/Changelog_libWiiSharp.txt new file mode 100644 index 0000000..e87ea87 --- /dev/null +++ b/Changelog_libWiiSharp.txt @@ -0,0 +1,34 @@ +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + libWiiSharp + by Leathl + Modified by TheShadowEevee +https://github.com/TheShadowEevee/libWiiSharp +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + + + +Changelog: +-=-=-=-=-=- + +Version 0.4 (TheShadowEevee's takeover) + - Checks for an existing CETK now, which helps for some files that needed one but couldn't get it through other means. + +Version 0.3 + - Added support to change the common key index of the ticket in the WAD class + - Made the sorting of wad/tmd contents optional (off by default!) + +Version 0.21 + - Fixed smpl region not read in Wave class + +Version 0.2 + - Added support to pass streams + - Speed up in TPL conversion + - Further changes in TPL class (e.g. changed saving to be compatible with official TPL files) + - Fixed IA8 TPL code (from/to) + - Fixed CI14X2 TPL code (from) + - Added TPL conversion to CI4 and CI8 + - Added BNS to Wave conversion + - Wave class completely rewritten + +Version 0.1 + - Initial Release \ No newline at end of file diff --git a/ColorIndexConverter.cs b/ColorIndexConverter.cs new file mode 100644 index 0000000..414ddec --- /dev/null +++ b/ColorIndexConverter.cs @@ -0,0 +1,279 @@ +// Decompiled with JetBrains decompiler +// Type: libWiiSharp.ColorIndexConverter +// Assembly: libWiiSharp, Version=0.4.0.0, Culture=neutral, PublicKeyToken=null +// MVID: FBF36F3D-B5D6-481F-B5F5-1BD3C19E13B2 +// Assembly location: C:\Users\theso\Downloads\NCPatcher\pack\libWiiSharp.dll + +using System; +using System.Collections.Generic; + +namespace libWiiSharp +{ + internal class ColorIndexConverter + { + private uint[] rgbaPalette; + private byte[] tplPalette; + private uint[] rgbaData; + private byte[] tplData; + private TPL_TextureFormat tplFormat; + private TPL_PaletteFormat paletteFormat; + private int width; + private int height; + + public byte[] Palette => this.tplPalette; + + public byte[] Data => this.tplData; + + public ColorIndexConverter( + uint[] rgbaData, + int width, + int height, + TPL_TextureFormat tplFormat, + TPL_PaletteFormat paletteFormat) + { + if (tplFormat != TPL_TextureFormat.CI4 && tplFormat != TPL_TextureFormat.CI8) + throw new Exception("Texture format must be either CI4 or CI8"); + if (paletteFormat != TPL_PaletteFormat.IA8 && paletteFormat != TPL_PaletteFormat.RGB565 && paletteFormat != TPL_PaletteFormat.RGB5A3) + throw new Exception("Palette format must be either IA8, RGB565 or RGB5A3!"); + this.rgbaData = rgbaData; + this.width = width; + this.height = height; + this.tplFormat = tplFormat; + this.paletteFormat = paletteFormat; + this.buildPalette(); + if (tplFormat != TPL_TextureFormat.CI4) + { + if (tplFormat == TPL_TextureFormat.CI8) + this.toCI8(); + else + this.toCI14X2(); + } + else + this.toCI4(); + } + + private void toCI4() + { + byte[] numArray = new byte[Shared.AddPadding(this.width, 8) * Shared.AddPadding(this.height, 8) / 2]; + int num = 0; + for (int index1 = 0; index1 < this.height; index1 += 8) + { + for (int index2 = 0; index2 < this.width; index2 += 8) + { + for (int index3 = index1; index3 < index1 + 8; ++index3) + { + for (int index4 = index2; index4 < index2 + 8; index4 += 2) + { + uint colorIndex1 = this.getColorIndex(index3 >= this.height || index4 >= this.width ? 0U : this.rgbaData[index3 * this.width + index4]); + uint colorIndex2 = this.getColorIndex(index3 >= this.height || index4 >= this.width ? 0U : (index3 * this.width + index4 + 1 < this.rgbaData.Length ? this.rgbaData[index3 * this.width + index4 + 1] : 0U)); + numArray[num++] = (byte) ((uint) (byte) colorIndex1 << 4 | (uint) (byte) colorIndex2); + } + } + } + } + this.tplData = numArray; + } + + private void toCI8() + { + byte[] numArray = new byte[Shared.AddPadding(this.width, 8) * Shared.AddPadding(this.height, 4)]; + int num1 = 0; + for (int index1 = 0; index1 < this.height; index1 += 4) + { + for (int index2 = 0; index2 < this.width; index2 += 8) + { + for (int index3 = index1; index3 < index1 + 4; ++index3) + { + for (int index4 = index2; index4 < index2 + 8; ++index4) + { + uint num2 = index3 >= this.height || index4 >= this.width ? 0U : this.rgbaData[index3 * this.width + index4]; + numArray[num1++] = (byte) this.getColorIndex(num2); + } + } + } + } + this.tplData = numArray; + } + + private void toCI14X2() + { + byte[] numArray1 = new byte[Shared.AddPadding(this.width, 4) * Shared.AddPadding(this.height, 4) * 2]; + int num1 = 0; + for (int index1 = 0; index1 < this.height; index1 += 4) + { + for (int index2 = 0; index2 < this.width; index2 += 4) + { + for (int index3 = index1; index3 < index1 + 4; ++index3) + { + for (int index4 = index2; index4 < index2 + 4; ++index4) + { + byte[] bytes = BitConverter.GetBytes((ushort) this.getColorIndex(index3 >= this.height || index4 >= this.width ? 0U : this.rgbaData[index3 * this.width + index4])); + byte[] numArray2 = numArray1; + int index5 = num1; + int num2 = index5 + 1; + int num3 = (int) bytes[1]; + numArray2[index5] = (byte) num3; + byte[] numArray3 = numArray1; + int index6 = num2; + num1 = index6 + 1; + int num4 = (int) bytes[0]; + numArray3[index6] = (byte) num4; + } + } + } + } + this.tplData = numArray1; + } + + private void buildPalette() + { + int num1 = 256; + if (this.tplFormat == TPL_TextureFormat.CI4) + num1 = 16; + else if (this.tplFormat == TPL_TextureFormat.CI14X2) + num1 = 16384; + List uintList = new List(); + List ushortList = new List(); + uintList.Add(0U); + ushortList.Add((ushort) 0); + for (int index = 1; index < this.rgbaData.Length && uintList.Count != num1; ++index) + { + if ((long) (this.rgbaData[index] >> 24 & (uint) byte.MaxValue) >= (this.tplFormat == TPL_TextureFormat.CI14X2 ? 1L : 25L)) + { + ushort num2 = Shared.Swap(this.convertToPaletteValue((int) this.rgbaData[index])); + if (!uintList.Contains(this.rgbaData[index]) && !ushortList.Contains(num2)) + { + uintList.Add(this.rgbaData[index]); + ushortList.Add(num2); + } + } + } + while (uintList.Count % 16 != 0) + { + uintList.Add(uint.MaxValue); + ushortList.Add(ushort.MaxValue); + } + this.tplPalette = Shared.UShortArrayToByteArray(ushortList.ToArray()); + this.rgbaPalette = uintList.ToArray(); + } + + private ushort convertToPaletteValue(int rgba) + { + int num1 = 0; + int num2; + if (this.paletteFormat == TPL_PaletteFormat.IA8) + { + int num3 = ((rgba & (int) byte.MaxValue) + (rgba >> 8 & (int) byte.MaxValue) + (rgba >> 16 & (int) byte.MaxValue)) / 3 & (int) byte.MaxValue; + num2 = (int) (ushort) ((rgba >> 24 & (int) byte.MaxValue) << 8 | num3); + } + else if (this.paletteFormat == TPL_PaletteFormat.RGB565) + { + num2 = (int) (ushort) ((rgba >> 16 & (int) byte.MaxValue) >> 3 << 11 | (rgba >> 8 & (int) byte.MaxValue) >> 2 << 5 | (rgba & (int) byte.MaxValue) >> 3); + } + else + { + int num3 = rgba >> 16 & (int) byte.MaxValue; + int num4 = rgba >> 8 & (int) byte.MaxValue; + int num5 = rgba & (int) byte.MaxValue; + int num6 = rgba >> 24 & (int) byte.MaxValue; + if (num6 <= 218) + { + int num7 = num1 & -32769; + int num8 = num3 * 15 / (int) byte.MaxValue & 15; + int num9 = num4 * 15 / (int) byte.MaxValue & 15; + int num10 = num5 * 15 / (int) byte.MaxValue & 15; + int num11 = num6 * 7 / (int) byte.MaxValue & 7; + num2 = num7 | num11 << 12 | num10 | num9 << 4 | num8 << 8; + } + else + { + int num7 = num1 | 32768; + int num8 = num3 * 31 / (int) byte.MaxValue & 31; + int num9 = num4 * 31 / (int) byte.MaxValue & 31; + int num10 = num5 * 31 / (int) byte.MaxValue & 31; + num2 = num7 | num10 | num9 << 5 | num8 << 10; + } + } + return (ushort) num2; + } + + private uint getColorIndex(uint value) + { + uint num1 = (uint) int.MaxValue; + uint num2 = 0; + if ((long) (value >> 24 & (uint) byte.MaxValue) < (this.tplFormat == TPL_TextureFormat.CI14X2 ? 1L : 25L)) + return 0; + ushort paletteValue1 = this.convertToPaletteValue((int) value); + for (int index = 0; index < this.rgbaPalette.Length; ++index) + { + ushort paletteValue2 = this.convertToPaletteValue((int) this.rgbaPalette[index]); + if ((int) paletteValue1 == (int) paletteValue2) + return (uint) index; + uint distance = this.getDistance(paletteValue1, paletteValue2); + if (distance < num1) + { + num1 = distance; + num2 = (uint) index; + } + } + return num2; + } + + private uint getDistance(ushort color, ushort paletteColor) + { + int rgbaValue1 = (int) this.convertToRgbaValue(color); + uint rgbaValue2 = this.convertToRgbaValue(paletteColor); + uint val1_1 = (uint) rgbaValue1 >> 24 & (uint) byte.MaxValue; + uint val1_2 = (uint) rgbaValue1 >> 16 & (uint) byte.MaxValue; + uint val1_3 = (uint) rgbaValue1 >> 8 & (uint) byte.MaxValue; + uint val1_4 = (uint) (rgbaValue1 & (int) byte.MaxValue); + uint val2_1 = rgbaValue2 >> 24 & (uint) byte.MaxValue; + uint val2_2 = rgbaValue2 >> 16 & (uint) byte.MaxValue; + uint val2_3 = rgbaValue2 >> 8 & (uint) byte.MaxValue; + uint val2_4 = rgbaValue2 & (uint) byte.MaxValue; + int num1 = (int) Math.Max(val1_1, val2_1) - (int) Math.Min(val1_1, val2_1); + uint num2 = Math.Max(val1_2, val2_2) - Math.Min(val1_2, val2_2); + uint num3 = Math.Max(val1_3, val2_3) - Math.Min(val1_3, val2_3); + uint num4 = Math.Max(val1_4, val2_4) - Math.Min(val1_4, val2_4); + int num5 = (int) num2; + return (uint) (num1 + num5) + num3 + num4; + } + + private uint convertToRgbaValue(ushort pixel) + { + if (this.paletteFormat == TPL_PaletteFormat.IA8) + { + int num1 = (int) pixel >> 8; + int num2 = (int) pixel & (int) byte.MaxValue; + return (uint) (num1 | num1 << 8 | num1 << 16 | num2 << 24); + } + if (this.paletteFormat == TPL_PaletteFormat.RGB565) + { + int num1 = ((int) pixel >> 11 & 31) << 3 & (int) byte.MaxValue; + int num2 = ((int) pixel >> 5 & 63) << 2 & (int) byte.MaxValue; + int num3 = ((int) pixel & 31) << 3 & (int) byte.MaxValue; + int maxValue = (int) byte.MaxValue; + return (uint) (num3 | num2 << 8 | num1 << 16 | maxValue << 24); + } + int num4; + int num5; + int num6; + int num7; + if (((int) pixel & 32768) != 0) + { + num4 = ((int) pixel >> 10 & 31) * (int) byte.MaxValue / 31; + num5 = ((int) pixel >> 5 & 31) * (int) byte.MaxValue / 31; + num6 = ((int) pixel & 31) * (int) byte.MaxValue / 31; + num7 = (int) byte.MaxValue; + } + else + { + num7 = ((int) pixel >> 12 & 7) * (int) byte.MaxValue / 7; + num4 = ((int) pixel >> 8 & 15) * (int) byte.MaxValue / 15; + num5 = ((int) pixel >> 4 & 15) * (int) byte.MaxValue / 15; + num6 = ((int) pixel & 15) * (int) byte.MaxValue / 15; + } + return (uint) (num6 | num5 << 8 | num4 << 16 | num7 << 24); + } + } +} diff --git a/CommonKey.cs b/CommonKey.cs new file mode 100644 index 0000000..ad04749 --- /dev/null +++ b/CommonKey.cs @@ -0,0 +1,18 @@ +// Decompiled with JetBrains decompiler +// Type: libWiiSharp.CommonKey +// Assembly: libWiiSharp, Version=0.4.0.0, Culture=neutral, PublicKeyToken=null +// MVID: FBF36F3D-B5D6-481F-B5F5-1BD3C19E13B2 +// Assembly location: C:\Users\theso\Downloads\NCPatcher\pack\libWiiSharp.dll + +namespace libWiiSharp +{ + public class CommonKey + { + private static string standardKey = "ebe42a225e8593e448d9c5457381aaf7"; + private static string koreanKey = "63b82bb4f4614e2e13f2fefbba4c9b7e"; + + public static byte[] GetStandardKey() => Shared.HexStringToByteArray(CommonKey.standardKey); + + public static byte[] GetKoreanKey() => Shared.HexStringToByteArray(CommonKey.koreanKey); + } +} diff --git a/CommonKeyType.cs b/CommonKeyType.cs new file mode 100644 index 0000000..b5d75c2 --- /dev/null +++ b/CommonKeyType.cs @@ -0,0 +1,14 @@ +// Decompiled with JetBrains decompiler +// Type: libWiiSharp.CommonKeyType +// Assembly: libWiiSharp, Version=0.4.0.0, Culture=neutral, PublicKeyToken=null +// MVID: FBF36F3D-B5D6-481F-B5F5-1BD3C19E13B2 +// Assembly location: C:\Users\theso\Downloads\NCPatcher\pack\libWiiSharp.dll + +namespace libWiiSharp +{ + public enum CommonKeyType : byte + { + Standard, + Korean, + } +} diff --git a/ContentIndices.cs b/ContentIndices.cs new file mode 100644 index 0000000..57d3075 --- /dev/null +++ b/ContentIndices.cs @@ -0,0 +1,33 @@ +// Decompiled with JetBrains decompiler +// Type: libWiiSharp.ContentIndices +// Assembly: libWiiSharp, Version=0.4.0.0, Culture=neutral, PublicKeyToken=null +// MVID: FBF36F3D-B5D6-481F-B5F5-1BD3C19E13B2 +// Assembly location: C:\Users\theso\Downloads\NCPatcher\pack\libWiiSharp.dll + +using System; + +namespace libWiiSharp +{ + public struct ContentIndices : IComparable + { + private int index; + private int contentIndex; + + public int Index => this.index; + + public int ContentIndex => this.contentIndex; + + public ContentIndices(int index, int contentIndex) + { + this.index = index; + this.contentIndex = contentIndex; + } + + public int CompareTo(object obj) + { + if (obj is ContentIndices contentIndices) + return this.contentIndex.CompareTo(contentIndices.contentIndex); + throw new ArgumentException(); + } + } +} diff --git a/ContentType.cs b/ContentType.cs new file mode 100644 index 0000000..ff0fd4f --- /dev/null +++ b/ContentType.cs @@ -0,0 +1,15 @@ +// Decompiled with JetBrains decompiler +// Type: libWiiSharp.ContentType +// Assembly: libWiiSharp, Version=0.4.0.0, Culture=neutral, PublicKeyToken=null +// MVID: FBF36F3D-B5D6-481F-B5F5-1BD3C19E13B2 +// Assembly location: C:\Users\theso\Downloads\NCPatcher\pack\libWiiSharp.dll + +namespace libWiiSharp +{ + public enum ContentType : ushort + { + Normal = 1, + DLC = 16385, // 0x4001 + Shared = 32769, // 0x8001 + } +} diff --git a/HbcTransmitter.cs b/HbcTransmitter.cs new file mode 100644 index 0000000..3460f9f --- /dev/null +++ b/HbcTransmitter.cs @@ -0,0 +1,301 @@ +// Decompiled with JetBrains decompiler +// Type: libWiiSharp.HbcTransmitter +// Assembly: libWiiSharp, Version=0.4.0.0, Culture=neutral, PublicKeyToken=null +// MVID: FBF36F3D-B5D6-481F-B5F5-1BD3C19E13B2 +// Assembly location: C:\Users\theso\Downloads\NCPatcher\pack\libWiiSharp.dll + +using System; +using System.ComponentModel; +using System.IO; +using System.Net.Sockets; + +namespace libWiiSharp +{ + public class HbcTransmitter : IDisposable + { + private int blocksize = 4096; + private int wiiloadMayor; + private int wiiloadMinor = 5; + private bool compress; + private string ipAddress; + private int port = 4299; + private string lastErrorMessage = string.Empty; + private Protocol protocol; + private TcpClient tcpClient; + private NetworkStream nwStream; + private string lastError = string.Empty; + private int transmittedLength; + private int compressionRatio; + private bool isDisposed; + + public int Blocksize + { + get => this.blocksize; + set => this.blocksize = value; + } + + public int WiiloadVersionMayor + { + get => this.wiiloadMayor; + set => this.wiiloadMayor = value; + } + + public int WiiloadVersionMinor + { + get => this.wiiloadMinor; + set => this.wiiloadMinor = value; + } + + public bool Compress + { + get => this.compress; + set + { + if (this.protocol == Protocol.HAXX) + return; + this.compress = value; + } + } + + public string IpAddress + { + get => this.ipAddress; + set => this.ipAddress = value; + } + + public int Port + { + get => this.port; + set => this.port = value; + } + + public int TransmittedLength => this.transmittedLength; + + public int CompressionRatio => this.compressionRatio; + + public string LastError => this.lastError; + + public event EventHandler Progress; + + public event EventHandler Debug; + + public HbcTransmitter(Protocol protocol, string ipAddress) + { + this.protocol = protocol; + this.ipAddress = ipAddress; + this.wiiloadMinor = protocol == Protocol.HAXX ? 4 : 5; + this.compress = protocol == Protocol.JODI; + } + + ~HbcTransmitter() => this.Dispose(false); + + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize((object) this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing && !this.isDisposed) + { + this.ipAddress = (string) null; + this.lastErrorMessage = (string) null; + this.lastError = (string) null; + if (this.nwStream != null) + { + this.nwStream.Close(); + this.nwStream = (NetworkStream) null; + } + if (this.tcpClient != null) + { + this.tcpClient.Close(); + this.tcpClient = (TcpClient) null; + } + } + this.isDisposed = true; + } + + public bool TransmitFile(string pathToFile) => this.transmit(Path.GetFileName(pathToFile), File.ReadAllBytes(pathToFile)); + + public bool TransmitFile(string fileName, byte[] fileData) => this.transmit(fileName, fileData); + + private bool transmit(string fileName, byte[] fileData) + { + this.fireDebug("Transmitting {0} to {1}:{2}...", (object) fileName, (object) this.ipAddress, (object) this.port); + if (!Environment.OSVersion.ToString().ToLower().Contains("windows")) + this.compress = false; + if (fileName.ToLower().EndsWith(".zip")) + this.compress = false; + this.tcpClient = new TcpClient(); + byte[] buffer1 = new byte[4]; + this.fireDebug(" Connecting..."); + try + { + this.tcpClient.Connect(this.ipAddress, 4299); + } + catch (Exception ex) + { + this.fireDebug(" -> Connection Failed:\n" + ex.Message); + this.lastError = "Connection Failed:\n" + ex.Message; + this.tcpClient.Close(); + return false; + } + this.nwStream = this.tcpClient.GetStream(); + this.fireDebug(" Sending Magic..."); + buffer1[0] = (byte) 72; + buffer1[1] = (byte) 65; + buffer1[2] = (byte) 88; + buffer1[3] = (byte) 88; + try + { + this.nwStream.Write(buffer1, 0, 4); + } + catch (Exception ex) + { + this.fireDebug(" -> Error sending Magic:\n" + ex.Message); + this.lastError = "Error sending Magic:\n" + ex.Message; + this.nwStream.Close(); + this.tcpClient.Close(); + return false; + } + this.fireDebug(" Sending Version Info..."); + buffer1[0] = (byte) this.wiiloadMayor; + buffer1[1] = (byte) this.wiiloadMinor; + buffer1[2] = (byte) (fileName.Length + 2 >> 8 & (int) byte.MaxValue); + buffer1[3] = (byte) (fileName.Length + 2 & (int) byte.MaxValue); + try + { + this.nwStream.Write(buffer1, 0, 4); + } + catch (Exception ex) + { + this.fireDebug(" -> Error sending Version Info:\n" + ex.Message); + this.lastError = "Error sending Version Info:\n" + ex.Message; + this.nwStream.Close(); + this.tcpClient.Close(); + return false; + } + byte[] buffer2; + if (this.compress) + { + this.fireDebug(" Compressing File..."); + try + { + buffer2 = zlibWrapper.Compress(fileData); + } + catch + { + this.fireDebug(" -> Compression failed, continuing without compression..."); + this.compress = false; + buffer2 = fileData; + fileData = new byte[0]; + } + } + else + { + buffer2 = fileData; + fileData = new byte[0]; + } + this.fireDebug(" Sending Filesize..."); + buffer1[0] = (byte) (buffer2.Length >> 24 & (int) byte.MaxValue); + buffer1[1] = (byte) (buffer2.Length >> 16 & (int) byte.MaxValue); + buffer1[2] = (byte) (buffer2.Length >> 8 & (int) byte.MaxValue); + buffer1[3] = (byte) (buffer2.Length & (int) byte.MaxValue); + try + { + this.nwStream.Write(buffer1, 0, 4); + } + catch (Exception ex) + { + this.fireDebug(" -> Error sending Filesize:\n" + ex.Message); + this.lastError = "Error sending Filesize:\n" + ex.Message; + this.nwStream.Close(); + this.tcpClient.Close(); + return false; + } + if (this.protocol != Protocol.HAXX) + { + buffer1[0] = (byte) (fileData.Length >> 24 & (int) byte.MaxValue); + buffer1[1] = (byte) (fileData.Length >> 16 & (int) byte.MaxValue); + buffer1[2] = (byte) (fileData.Length >> 8 & (int) byte.MaxValue); + buffer1[3] = (byte) (fileData.Length & (int) byte.MaxValue); + try + { + this.nwStream.Write(buffer1, 0, 4); + } + catch (Exception ex) + { + this.fireDebug(" -> Error sending Filesize:\n" + ex.Message); + this.lastError = "Error sending Filesize:\n" + ex.Message; + this.nwStream.Close(); + this.tcpClient.Close(); + return false; + } + } + this.fireDebug(" Sending File..."); + int offset = 0; + int num1 = 0; + int num2 = buffer2.Length / this.Blocksize; + int num3 = buffer2.Length % this.Blocksize; + try + { + do + { + this.fireProgress(++num1 * 100 / num2); + this.nwStream.Write(buffer2, offset, this.Blocksize); + offset += this.Blocksize; + } + while (num1 < num2); + if (num3 > 0) + this.nwStream.Write(buffer2, offset, buffer2.Length - offset); + } + catch (Exception ex) + { + this.fireDebug(" -> Error sending File:\n" + ex.Message); + this.lastError = "Error sending File:\n" + ex.Message; + this.nwStream.Close(); + this.tcpClient.Close(); + return false; + } + this.fireDebug(" Sending Arguments..."); + byte[] buffer3 = new byte[fileName.Length + 2]; + for (int index = 0; index < fileName.Length; ++index) + buffer3[index] = (byte) fileName.ToCharArray()[index]; + try + { + this.nwStream.Write(buffer3, 0, buffer3.Length); + } + catch (Exception ex) + { + this.fireDebug(" -> Error sending Arguments:\n" + ex.Message); + this.lastError = "Error sending Arguments:\n" + ex.Message; + this.nwStream.Close(); + this.tcpClient.Close(); + return false; + } + this.nwStream.Close(); + this.tcpClient.Close(); + this.transmittedLength = buffer2.Length; + this.compressionRatio = !this.compress || fileData.Length == 0 ? 0 : buffer2.Length * 100 / fileData.Length; + this.fireDebug("Transmitting {0} to {1}:{2} Finished...", (object) fileName, (object) this.ipAddress, (object) this.port); + return true; + } + + 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 fireProgress(int progressPercentage) + { + EventHandler progress = this.Progress; + if (progress == null) + return; + progress(new object(), new ProgressChangedEventArgs(progressPercentage, (object) string.Empty)); + } + } +} diff --git a/Headers.cs b/Headers.cs new file mode 100644 index 0000000..8018362 --- /dev/null +++ b/Headers.cs @@ -0,0 +1,590 @@ +// Decompiled with JetBrains decompiler +// Type: libWiiSharp.Headers +// Assembly: libWiiSharp, Version=0.4.0.0, Culture=neutral, PublicKeyToken=null +// MVID: FBF36F3D-B5D6-481F-B5F5-1BD3C19E13B2 +// Assembly location: C:\Users\theso\Downloads\NCPatcher\pack\libWiiSharp.dll + +using System; +using System.IO; +using System.Security.Cryptography; + +namespace libWiiSharp +{ + public class Headers + { + private static uint imd5Magic = 1229800501; + private static uint imetMagic = 1229800788; + + public static Headers.HeaderType DetectHeader(string pathToFile) => Headers.DetectHeader(File.ReadAllBytes(pathToFile)); + + public static Headers.HeaderType DetectHeader(byte[] file) + { + if (file.Length > 68 && (int) Shared.Swap(BitConverter.ToUInt32(file, 64)) == (int) Headers.imetMagic) + return Headers.HeaderType.ShortIMET; + if (file.Length > 132 && (int) Shared.Swap(BitConverter.ToUInt32(file, 128)) == (int) Headers.imetMagic) + return Headers.HeaderType.IMET; + return file.Length > 4 && (int) Shared.Swap(BitConverter.ToUInt32(file, 0)) == (int) Headers.imd5Magic ? Headers.HeaderType.IMD5 : Headers.HeaderType.None; + } + + public static Headers.HeaderType DetectHeader(Stream file) + { + byte[] buffer = new byte[4]; + if (file.Length > 68L) + { + file.Seek(64L, SeekOrigin.Begin); + file.Read(buffer, 0, buffer.Length); + if ((int) Shared.Swap(BitConverter.ToUInt32(buffer, 0)) == (int) Headers.imetMagic) + return Headers.HeaderType.ShortIMET; + } + if (file.Length > 132L) + { + file.Seek(128L, SeekOrigin.Begin); + file.Read(buffer, 0, buffer.Length); + if ((int) Shared.Swap(BitConverter.ToUInt32(buffer, 0)) == (int) Headers.imetMagic) + return Headers.HeaderType.IMET; + } + if (file.Length > 4L) + { + file.Seek(0L, SeekOrigin.Begin); + file.Read(buffer, 0, buffer.Length); + if ((int) Shared.Swap(BitConverter.ToUInt32(buffer, 0)) == (int) Headers.imd5Magic) + return Headers.HeaderType.IMD5; + } + return Headers.HeaderType.None; + } + + public enum HeaderType + { + None = 0, + IMD5 = 32, // 0x00000020 + ShortIMET = 1536, // 0x00000600 + IMET = 1600, // 0x00000640 + } + + public class IMET + { + private bool hashesMatch = true; + private bool isShortImet; + private byte[] additionalPadding = new byte[64]; + private byte[] padding = new byte[64]; + private uint imetMagic = 1229800788; + private uint sizeOfHeader = 1536; + private uint unknown = 3; + private uint iconSize; + private uint bannerSize; + private uint soundSize; + private uint flags; + private byte[] japaneseTitle = new byte[84]; + private byte[] englishTitle = new byte[84]; + private byte[] germanTitle = new byte[84]; + private byte[] frenchTitle = new byte[84]; + private byte[] spanishTitle = new byte[84]; + private byte[] italianTitle = new byte[84]; + private byte[] dutchTitle = new byte[84]; + private byte[] unknownTitle1 = new byte[84]; + private byte[] unknownTitle2 = new byte[84]; + private byte[] koreanTitle = new byte[84]; + private byte[] padding2 = new byte[588]; + private byte[] hash = new byte[16]; + + public bool IsShortIMET + { + get => this.isShortImet; + set => this.isShortImet = value; + } + + public uint IconSize + { + get => this.iconSize; + set => this.iconSize = value; + } + + public uint BannerSize + { + get => this.bannerSize; + set => this.bannerSize = value; + } + + public uint SoundSize + { + get => this.soundSize; + set => this.soundSize = value; + } + + public string JapaneseTitle + { + get => this.returnTitleAsString(this.japaneseTitle); + set => this.setTitleFromString(value, 0); + } + + public string EnglishTitle + { + get => this.returnTitleAsString(this.englishTitle); + set => this.setTitleFromString(value, 1); + } + + public string GermanTitle + { + get => this.returnTitleAsString(this.germanTitle); + set => this.setTitleFromString(value, 2); + } + + public string FrenchTitle + { + get => this.returnTitleAsString(this.frenchTitle); + set => this.setTitleFromString(value, 3); + } + + public string SpanishTitle + { + get => this.returnTitleAsString(this.spanishTitle); + set => this.setTitleFromString(value, 4); + } + + public string ItalianTitle + { + get => this.returnTitleAsString(this.italianTitle); + set => this.setTitleFromString(value, 5); + } + + public string DutchTitle + { + get => this.returnTitleAsString(this.dutchTitle); + set => this.setTitleFromString(value, 6); + } + + public string KoreanTitle + { + get => this.returnTitleAsString(this.koreanTitle); + set => this.setTitleFromString(value, 7); + } + + public string[] AllTitles => new string[8] + { + this.JapaneseTitle, + this.EnglishTitle, + this.GermanTitle, + this.FrenchTitle, + this.SpanishTitle, + this.ItalianTitle, + this.DutchTitle, + this.KoreanTitle + }; + + public bool HashesMatch => this.hashesMatch; + + public static Headers.IMET Load(string pathToFile) => Headers.IMET.Load(File.ReadAllBytes(pathToFile)); + + public static Headers.IMET Load(byte[] fileOrHeader) + { + Headers.HeaderType headerType = Headers.DetectHeader(fileOrHeader); + switch (headerType) + { + case Headers.HeaderType.ShortIMET: + case Headers.HeaderType.IMET: + Headers.IMET imet = new Headers.IMET(); + if (headerType == Headers.HeaderType.ShortIMET) + imet.isShortImet = true; + MemoryStream memoryStream = new MemoryStream(fileOrHeader); + try + { + imet.parseHeader((Stream) memoryStream); + } + catch + { + memoryStream.Dispose(); + throw; + } + memoryStream.Dispose(); + return imet; + default: + throw new Exception("No IMET Header found!"); + } + } + + public static Headers.IMET Load(Stream fileOrHeader) + { + Headers.HeaderType headerType = Headers.DetectHeader(fileOrHeader); + switch (headerType) + { + case Headers.HeaderType.ShortIMET: + case Headers.HeaderType.IMET: + Headers.IMET imet = new Headers.IMET(); + if (headerType == Headers.HeaderType.ShortIMET) + imet.isShortImet = true; + imet.parseHeader(fileOrHeader); + return imet; + default: + throw new Exception("No IMET Header found!"); + } + } + + public static Headers.IMET Create( + bool isShortImet, + int iconSize, + int bannerSize, + int soundSize, + params string[] titles) + { + Headers.IMET imet = new Headers.IMET(); + imet.isShortImet = isShortImet; + for (int titleIndex = 0; titleIndex < titles.Length; ++titleIndex) + imet.setTitleFromString(titles[titleIndex], titleIndex); + for (int length = titles.Length; length < 8; ++length) + imet.setTitleFromString(titles.Length > 1 ? titles[1] : titles[0], length); + imet.iconSize = (uint) iconSize; + imet.bannerSize = (uint) bannerSize; + imet.soundSize = (uint) soundSize; + return imet; + } + + public static void RemoveHeader(string pathToFile) + { + byte[] bytes = Headers.IMET.RemoveHeader(File.ReadAllBytes(pathToFile)); + File.Delete(pathToFile); + File.WriteAllBytes(pathToFile, bytes); + } + + public static byte[] RemoveHeader(byte[] file) + { + Headers.HeaderType headerType = Headers.DetectHeader(file); + switch (headerType) + { + case Headers.HeaderType.ShortIMET: + case Headers.HeaderType.IMET: + byte[] numArray = new byte[(int) (file.Length - headerType)]; + Array.Copy((Array) file, (int) headerType, (Array) numArray, 0, numArray.Length); + return numArray; + default: + throw new Exception("No IMET Header found!"); + } + } + + public void SetAllTitles(string newTitle) + { + for (int titleIndex = 0; titleIndex < 10; ++titleIndex) + this.setTitleFromString(newTitle, titleIndex); + } + + public MemoryStream ToMemoryStream() + { + MemoryStream memoryStream = new MemoryStream(); + try + { + this.writeToStream((Stream) memoryStream); + return memoryStream; + } + catch + { + memoryStream.Dispose(); + throw; + } + } + + public byte[] ToByteArray() => this.ToMemoryStream().ToArray(); + + public void Write(Stream writeStream) => this.writeToStream(writeStream); + + public void ChangeTitles(params string[] newTitles) + { + for (int titleIndex = 0; titleIndex < newTitles.Length; ++titleIndex) + this.setTitleFromString(newTitles[titleIndex], titleIndex); + for (int length = newTitles.Length; length < 8; ++length) + this.setTitleFromString(newTitles.Length > 1 ? newTitles[1] : newTitles[0], length); + } + + public string[] GetTitles() => new string[8] + { + this.JapaneseTitle, + this.EnglishTitle, + this.GermanTitle, + this.FrenchTitle, + this.SpanishTitle, + this.ItalianTitle, + this.DutchTitle, + this.KoreanTitle + }; + + private void writeToStream(Stream writeStream) + { + writeStream.Seek(0L, SeekOrigin.Begin); + if (!this.isShortImet) + writeStream.Write(this.additionalPadding, 0, this.additionalPadding.Length); + writeStream.Write(this.padding, 0, this.padding.Length); + writeStream.Write(BitConverter.GetBytes(Shared.Swap(this.imetMagic)), 0, 4); + writeStream.Write(BitConverter.GetBytes(Shared.Swap(this.sizeOfHeader)), 0, 4); + writeStream.Write(BitConverter.GetBytes(Shared.Swap(this.unknown)), 0, 4); + writeStream.Write(BitConverter.GetBytes(Shared.Swap(this.iconSize)), 0, 4); + writeStream.Write(BitConverter.GetBytes(Shared.Swap(this.bannerSize)), 0, 4); + writeStream.Write(BitConverter.GetBytes(Shared.Swap(this.soundSize)), 0, 4); + writeStream.Write(BitConverter.GetBytes(Shared.Swap(this.flags)), 0, 4); + writeStream.Write(this.japaneseTitle, 0, this.japaneseTitle.Length); + writeStream.Write(this.englishTitle, 0, this.englishTitle.Length); + writeStream.Write(this.germanTitle, 0, this.germanTitle.Length); + writeStream.Write(this.frenchTitle, 0, this.frenchTitle.Length); + writeStream.Write(this.spanishTitle, 0, this.spanishTitle.Length); + writeStream.Write(this.italianTitle, 0, this.italianTitle.Length); + writeStream.Write(this.dutchTitle, 0, this.dutchTitle.Length); + writeStream.Write(this.unknownTitle1, 0, this.unknownTitle1.Length); + writeStream.Write(this.unknownTitle2, 0, this.unknownTitle2.Length); + writeStream.Write(this.koreanTitle, 0, this.koreanTitle.Length); + writeStream.Write(this.padding2, 0, this.padding2.Length); + int position = (int) writeStream.Position; + this.hash = new byte[16]; + writeStream.Write(this.hash, 0, this.hash.Length); + byte[] numArray = new byte[writeStream.Position]; + writeStream.Seek(0L, SeekOrigin.Begin); + writeStream.Read(numArray, 0, numArray.Length); + this.computeHash(numArray, !this.isShortImet ? 64 : 0); + writeStream.Seek((long) position, SeekOrigin.Begin); + writeStream.Write(this.hash, 0, this.hash.Length); + } + + private void computeHash(byte[] headerBytes, int hashPos) + { + MD5 md5 = MD5.Create(); + this.hash = md5.ComputeHash(headerBytes, hashPos, 1536); + md5.Clear(); + } + + private void parseHeader(Stream headerStream) + { + headerStream.Seek(0L, SeekOrigin.Begin); + byte[] buffer1 = new byte[4]; + if (!this.isShortImet) + headerStream.Read(this.additionalPadding, 0, this.additionalPadding.Length); + headerStream.Read(this.padding, 0, this.padding.Length); + headerStream.Read(buffer1, 0, 4); + if ((int) Shared.Swap(BitConverter.ToUInt32(buffer1, 0)) != (int) this.imetMagic) + throw new Exception("Invalid Magic!"); + headerStream.Read(buffer1, 0, 4); + if ((int) Shared.Swap(BitConverter.ToUInt32(buffer1, 0)) != (int) this.sizeOfHeader) + throw new Exception("Invalid Header Size!"); + headerStream.Read(buffer1, 0, 4); + this.unknown = Shared.Swap(BitConverter.ToUInt32(buffer1, 0)); + headerStream.Read(buffer1, 0, 4); + this.iconSize = Shared.Swap(BitConverter.ToUInt32(buffer1, 0)); + headerStream.Read(buffer1, 0, 4); + this.bannerSize = Shared.Swap(BitConverter.ToUInt32(buffer1, 0)); + headerStream.Read(buffer1, 0, 4); + this.soundSize = Shared.Swap(BitConverter.ToUInt32(buffer1, 0)); + headerStream.Read(buffer1, 0, 4); + this.flags = Shared.Swap(BitConverter.ToUInt32(buffer1, 0)); + headerStream.Read(this.japaneseTitle, 0, this.japaneseTitle.Length); + headerStream.Read(this.englishTitle, 0, this.englishTitle.Length); + headerStream.Read(this.germanTitle, 0, this.germanTitle.Length); + headerStream.Read(this.frenchTitle, 0, this.frenchTitle.Length); + headerStream.Read(this.spanishTitle, 0, this.spanishTitle.Length); + headerStream.Read(this.italianTitle, 0, this.italianTitle.Length); + headerStream.Read(this.dutchTitle, 0, this.dutchTitle.Length); + headerStream.Read(this.unknownTitle1, 0, this.unknownTitle1.Length); + headerStream.Read(this.unknownTitle2, 0, this.unknownTitle2.Length); + headerStream.Read(this.koreanTitle, 0, this.koreanTitle.Length); + headerStream.Read(this.padding2, 0, this.padding2.Length); + headerStream.Read(this.hash, 0, this.hash.Length); + headerStream.Seek(-16L, SeekOrigin.Current); + headerStream.Write(new byte[16], 0, 16); + byte[] buffer2 = new byte[headerStream.Length]; + headerStream.Seek(0L, SeekOrigin.Begin); + headerStream.Read(buffer2, 0, buffer2.Length); + MD5 md5 = MD5.Create(); + byte[] hash = md5.ComputeHash(buffer2, !this.isShortImet ? 64 : 0, 1536); + md5.Clear(); + this.hashesMatch = Shared.CompareByteArrays(hash, this.hash); + } + + private string returnTitleAsString(byte[] title) + { + string empty = string.Empty; + for (int index = 0; index < 84; index += 2) + { + char ch = BitConverter.ToChar(new byte[2] + { + title[index + 1], + title[index] + }, 0); + if (ch != char.MinValue) + empty += ch.ToString(); + } + return empty; + } + + private void setTitleFromString(string title, int titleIndex) + { + byte[] numArray = new byte[84]; + for (int index = 0; index < title.Length; ++index) + { + byte[] bytes = BitConverter.GetBytes(title[index]); + numArray[index * 2 + 1] = bytes[0]; + numArray[index * 2] = bytes[1]; + } + switch (titleIndex) + { + case 0: + this.japaneseTitle = numArray; + break; + case 1: + this.englishTitle = numArray; + break; + case 2: + this.germanTitle = numArray; + break; + case 3: + this.frenchTitle = numArray; + break; + case 4: + this.spanishTitle = numArray; + break; + case 5: + this.italianTitle = numArray; + break; + case 6: + this.dutchTitle = numArray; + break; + case 7: + this.koreanTitle = numArray; + break; + } + } + } + + public class IMD5 + { + private uint imd5Magic = 1229800501; + private uint fileSize; + private byte[] padding = new byte[8]; + private byte[] hash = new byte[16]; + + public uint FileSize => this.fileSize; + + public byte[] Hash => this.hash; + + private IMD5() + { + } + + public static Headers.IMD5 Load(string pathToFile) => Headers.IMD5.Load(File.ReadAllBytes(pathToFile)); + + public static Headers.IMD5 Load(byte[] fileOrHeader) + { + if (Headers.DetectHeader(fileOrHeader) != Headers.HeaderType.IMD5) + throw new Exception("No IMD5 Header found!"); + Headers.IMD5 imD5 = new Headers.IMD5(); + MemoryStream memoryStream = new MemoryStream(fileOrHeader); + try + { + imD5.parseHeader((Stream) memoryStream); + } + catch + { + memoryStream.Dispose(); + throw; + } + memoryStream.Dispose(); + return imD5; + } + + public static Headers.IMD5 Load(Stream fileOrHeader) + { + if (Headers.DetectHeader(fileOrHeader) != Headers.HeaderType.IMD5) + throw new Exception("No IMD5 Header found!"); + Headers.IMD5 imD5 = new Headers.IMD5(); + imD5.parseHeader(fileOrHeader); + return imD5; + } + + public static Headers.IMD5 Create(byte[] file) + { + Headers.IMD5 imD5 = new Headers.IMD5(); + imD5.fileSize = (uint) file.Length; + imD5.computeHash(file); + return imD5; + } + + public static void AddHeader(string pathToFile) + { + byte[] buffer = Headers.IMD5.AddHeader(File.ReadAllBytes(pathToFile)); + File.Delete(pathToFile); + using (FileStream fileStream = new FileStream(pathToFile, FileMode.Create)) + fileStream.Write(buffer, 0, buffer.Length); + } + + public static byte[] AddHeader(byte[] file) + { + Headers.IMD5 imD5 = Headers.IMD5.Create(file); + MemoryStream memoryStream1 = new MemoryStream(); + MemoryStream memoryStream2 = memoryStream1; + imD5.writeToStream((Stream) memoryStream2); + memoryStream1.Write(file, 0, file.Length); + byte[] array = memoryStream1.ToArray(); + memoryStream1.Dispose(); + return array; + } + + public static void RemoveHeader(string pathToFile) + { + byte[] buffer = Headers.IMD5.RemoveHeader(File.ReadAllBytes(pathToFile)); + File.Delete(pathToFile); + using (FileStream fileStream = new FileStream(pathToFile, FileMode.Create)) + fileStream.Write(buffer, 0, buffer.Length); + } + + public static byte[] RemoveHeader(byte[] file) + { + MemoryStream memoryStream = new MemoryStream(); + memoryStream.Write(file, 32, file.Length - 32); + byte[] array = memoryStream.ToArray(); + memoryStream.Dispose(); + return array; + } + + public MemoryStream ToMemoryStream() + { + MemoryStream memoryStream = new MemoryStream(); + try + { + this.writeToStream((Stream) memoryStream); + return memoryStream; + } + catch + { + memoryStream.Dispose(); + throw; + } + } + + public byte[] ToByteArray() => this.ToMemoryStream().ToArray(); + + public void Write(Stream writeStream) => this.writeToStream(writeStream); + + private void writeToStream(Stream writeStream) + { + writeStream.Seek(0L, SeekOrigin.Begin); + writeStream.Write(BitConverter.GetBytes(Shared.Swap(this.imd5Magic)), 0, 4); + writeStream.Write(BitConverter.GetBytes(Shared.Swap(this.fileSize)), 0, 4); + writeStream.Write(this.padding, 0, this.padding.Length); + writeStream.Write(this.hash, 0, this.hash.Length); + } + + private void computeHash(byte[] bytesToHash) + { + MD5 md5 = MD5.Create(); + this.hash = md5.ComputeHash(bytesToHash); + md5.Clear(); + } + + private void parseHeader(Stream headerStream) + { + headerStream.Seek(0L, SeekOrigin.Begin); + byte[] buffer = new byte[4]; + headerStream.Read(buffer, 0, 4); + if ((int) Shared.Swap(BitConverter.ToUInt32(buffer, 0)) != (int) this.imd5Magic) + throw new Exception("Invalid Magic!"); + headerStream.Read(buffer, 0, 4); + this.fileSize = Shared.Swap(BitConverter.ToUInt32(buffer, 0)); + headerStream.Read(this.padding, 0, this.padding.Length); + headerStream.Read(this.hash, 0, this.hash.Length); + } + } + } +} diff --git a/HexView.cs b/HexView.cs new file mode 100644 index 0000000..232cbb1 --- /dev/null +++ b/HexView.cs @@ -0,0 +1,260 @@ +// Decompiled with JetBrains decompiler +// Type: libWiiSharp.HexView +// Assembly: libWiiSharp, Version=0.4.0.0, Culture=neutral, PublicKeyToken=null +// MVID: FBF36F3D-B5D6-481F-B5F5-1BD3C19E13B2 +// Assembly location: C:\Users\theso\Downloads\NCPatcher\pack\libWiiSharp.dll + +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Globalization; +using System.Windows.Forms; + +namespace libWiiSharp +{ + public static class HexView + { + private static string savedValue; + + public static void DumpToListView(byte[] data, ListView listView) => HexView.dumpToListView(data, listView); + + public static void DumpToDataGridView(byte[] data, DataGridView dataGridView) => HexView.dumpToDataGridView(data, dataGridView); + + public static byte[] DumpFromDataGridView(DataGridView dataGridView) => HexView.dumpFromDataGridView(dataGridView); + + public static void DumpToRichTextBox(byte[] data, RichTextBox richTextBox) + { + richTextBox.Clear(); + richTextBox.Font = new Font("Courier New", 9f); + richTextBox.ReadOnly = true; + richTextBox.Text = HexView.DumpAsString(data); + } + + public static void DumpToTextBox(byte[] data, TextBox textBox) + { + textBox.Multiline = true; + textBox.Font = new Font("Courier New", 9f); + textBox.ReadOnly = true; + textBox.Text = HexView.DumpAsString(data).Replace("\n", "\r\n"); + } + + public static string[] DumpAsStringArray(byte[] data) => HexView.dumpAsStringArray(data); + + public static string DumpAsString(byte[] data) => string.Join("\n", HexView.dumpAsStringArray(data)); + + public static void DataGridView_CellEndEdit(object sender, DataGridViewCellEventArgs e) + { + try + { + DataGridView dataGridView = sender as DataGridView; + if (dataGridView.Columns[e.ColumnIndex].HeaderText.ToLower() == "dump") + { + string str = (string) dataGridView.Rows[e.RowIndex].Cells[17].Value; + if (!(str != HexView.savedValue)) + return; + if (str.Length != 16) + throw new Exception(); + for (int index = 0; index < 16; ++index) + { + if ((int) HexView.toAscii(byte.Parse((string) dataGridView.Rows[e.RowIndex].Cells[index + 1].Value, NumberStyles.HexNumber)) != (int) str[index]) + dataGridView.Rows[e.RowIndex].Cells[index + 1].Value = (object) HexView.fromAscii(str[index]).ToString("x2"); + } + } + else + { + if (((string) dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value).Length == 1) + dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = (object) ("0" + dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value?.ToString()); + int startIndex = int.Parse(dataGridView.Columns[e.ColumnIndex].HeaderText, NumberStyles.HexNumber); + string str = ((string) dataGridView.Rows[e.RowIndex].Cells[17].Value).Remove(startIndex, 1).Insert(startIndex, HexView.toAscii(byte.Parse((string) dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value, NumberStyles.HexNumber)).ToString()); + dataGridView.Rows[e.RowIndex].Cells[17].Value = (object) str; + if (((string) dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value).Length <= 2) + return; + dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = (object) ((string) dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value).Remove(0, ((string) dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value).Length - 2); + } + } + catch + { + ((DataGridView) sender).Rows[e.RowIndex].Cells[e.ColumnIndex].Value = (object) HexView.savedValue; + } + } + + public static void DataGridView_CellBeginEdit(object sender, DataGridViewCellCancelEventArgs e) => HexView.savedValue = (string) ((DataGridView) sender).Rows[e.RowIndex].Cells[e.ColumnIndex].Value; + + private static string[] dumpAsStringArray(byte[] data) + { + List stringList = new List(); + string empty = string.Empty; + int num; + char ascii; + for (num = 0; (double) (data.Length - num) / 16.0 >= 1.0; num += 16) + { + string str1 = string.Empty + num.ToString("x8") + " "; + string str2 = string.Empty; + for (int index = 0; index < 16; ++index) + { + str1 = str1 + data[num + index].ToString("x2") + " "; + string str3 = str2; + ascii = HexView.toAscii(data[num + index]); + string str4 = ascii.ToString(); + str2 = str3 + str4; + } + string str5 = str1 + " " + str2; + stringList.Add(str5); + } + if (data.Length > num) + { + string str1 = string.Empty + num.ToString("x8") + " "; + string str2 = string.Empty; + for (int index = 0; index < 16; ++index) + { + if (index < data.Length - num) + { + str1 = str1 + data[num + index].ToString("x2") + " "; + string str3 = str2; + ascii = HexView.toAscii(data[num + index]); + string str4 = ascii.ToString(); + str2 = str3 + str4; + } + else + str1 += " "; + } + string str5 = str1 + " " + str2; + stringList.Add(str5); + } + return stringList.ToArray(); + } + + private static byte[] dumpFromDataGridView(DataGridView dataGridView) + { + try + { + List byteList = new List(); + for (int index1 = 0; !string.IsNullOrEmpty((string) dataGridView.Rows[index1].Cells[1].Value); ++index1) + { + for (int index2 = 0; index2 < 16; ++index2) + { + if (!string.IsNullOrEmpty((string) dataGridView.Rows[index1].Cells[index2 + 1].Value)) + byteList.Add(byte.Parse((string) dataGridView.Rows[index1].Cells[index2 + 1].Value, NumberStyles.HexNumber)); + } + if (index1 == dataGridView.Rows.Count - 1) + break; + } + return byteList.ToArray(); + } + catch + { + throw new Exception("An error occured. The DataGridView might have the wrong format!"); + } + } + + private static void dumpToDataGridView(byte[] data, DataGridView dataGridView) + { + dataGridView.Columns.Clear(); + dataGridView.Rows.Clear(); + dataGridView.Font = new Font("Courier New", 9f); + dataGridView.Columns.Add(new DataGridViewColumn() + { + HeaderText = "Offset", + Width = 80, + CellTemplate = (DataGridViewCell) new DataGridViewTextBoxCell() + }); + for (int index = 0; index < 16; ++index) + dataGridView.Columns.Add(new DataGridViewColumn() + { + HeaderText = index.ToString("x1"), + Width = 30, + CellTemplate = (DataGridViewCell) new DataGridViewTextBoxCell() + }); + dataGridView.Columns.Add(new DataGridViewColumn() + { + HeaderText = "Dump", + Width = 125, + CellTemplate = (DataGridViewCell) new DataGridViewTextBoxCell() + }); + int num; + for (num = 0; (double) (data.Length - num) / 16.0 >= 1.0; num += 16) + { + DataGridViewRow dataGridViewRow = new DataGridViewRow(); + int index1 = dataGridViewRow.Cells.Add((DataGridViewCell) new DataGridViewTextBoxCell()); + dataGridViewRow.Cells[index1].Value = (object) num.ToString("x8"); + dataGridViewRow.Cells[index1].ReadOnly = true; + string empty = string.Empty; + for (int index2 = 0; index2 < 16; ++index2) + { + int index3 = dataGridViewRow.Cells.Add((DataGridViewCell) new DataGridViewTextBoxCell()); + dataGridViewRow.Cells[index3].Value = (object) data[num + index2].ToString("x2"); + empty += HexView.toAscii(data[num + index2]).ToString(); + } + int index4 = dataGridViewRow.Cells.Add((DataGridViewCell) new DataGridViewTextBoxCell()); + dataGridViewRow.Cells[index4].Value = (object) empty; + dataGridView.Rows.Add(dataGridViewRow); + } + if (data.Length <= num) + return; + DataGridViewRow dataGridViewRow1 = new DataGridViewRow(); + int index5 = dataGridViewRow1.Cells.Add((DataGridViewCell) new DataGridViewTextBoxCell()); + dataGridViewRow1.Cells[index5].Value = (object) num.ToString("x8"); + dataGridViewRow1.Cells[index5].ReadOnly = true; + string empty1 = string.Empty; + for (int index1 = 0; index1 < 16; ++index1) + { + if (index1 < data.Length - num) + { + int index2 = dataGridViewRow1.Cells.Add((DataGridViewCell) new DataGridViewTextBoxCell()); + dataGridViewRow1.Cells[index2].Value = (object) data[num + index1].ToString("x2"); + empty1 += HexView.toAscii(data[num + index1]).ToString(); + } + else + dataGridViewRow1.Cells.Add((DataGridViewCell) new DataGridViewTextBoxCell()); + } + int index6 = dataGridViewRow1.Cells.Add((DataGridViewCell) new DataGridViewTextBoxCell()); + dataGridViewRow1.Cells[index6].Value = (object) empty1; + dataGridView.Rows.Add(dataGridViewRow1); + } + + private static void dumpToListView(byte[] data, ListView listView) + { + listView.Columns.Clear(); + listView.Items.Clear(); + listView.View = View.Details; + listView.Font = new Font("Courier New", 9f); + listView.Columns.Add("Offset", "Offset", 80, HorizontalAlignment.Left, string.Empty); + for (int index = 0; index < 16; ++index) + listView.Columns.Add(index.ToString("x1"), index.ToString("x1"), 30, HorizontalAlignment.Left, string.Empty); + listView.Columns.Add("Dump", "Dump", 125, HorizontalAlignment.Left, string.Empty); + int num; + for (num = 0; (double) (data.Length - num) / 16.0 >= 1.0; num += 16) + { + ListViewItem listViewItem = new ListViewItem(num.ToString("x8")); + string empty = string.Empty; + for (int index = 0; index < 16; ++index) + { + listViewItem.SubItems.Add(data[num + index].ToString("x2")); + empty += HexView.toAscii(data[num + index]).ToString(); + } + listViewItem.SubItems.Add(empty); + listView.Items.Add(listViewItem); + } + if (data.Length <= num) + return; + ListViewItem listViewItem1 = new ListViewItem(num.ToString("x8")); + string empty1 = string.Empty; + for (int index = 0; index < 16; ++index) + { + if (index < data.Length - num) + { + listViewItem1.SubItems.Add(data[num + index].ToString("x2")); + empty1 += HexView.toAscii(data[num + index]).ToString(); + } + else + listViewItem1.SubItems.Add(string.Empty); + } + listViewItem1.SubItems.Add(empty1); + listView.Items.Add(listViewItem1); + } + + private static char toAscii(byte value) => value >= (byte) 32 && value <= (byte) 126 ? (char) value : '.'; + + private static byte fromAscii(char value) => (byte) value; + } +} diff --git a/IosPatcher.cs b/IosPatcher.cs new file mode 100644 index 0000000..82dd0e3 --- /dev/null +++ b/IosPatcher.cs @@ -0,0 +1,286 @@ +// Decompiled with JetBrains decompiler +// Type: libWiiSharp.IosPatcher +// Assembly: libWiiSharp, Version=0.4.0.0, Culture=neutral, PublicKeyToken=null +// MVID: FBF36F3D-B5D6-481F-B5F5-1BD3C19E13B2 +// Assembly location: C:\Users\theso\Downloads\NCPatcher\pack\libWiiSharp.dll + +using System; +using System.ComponentModel; +using System.Text; + +namespace libWiiSharp +{ + public class IosPatcher + { + private WAD wadFile; + private int esIndex = -1; + + public event EventHandler Progress; + + public event EventHandler Debug; + + public void LoadIOS(ref WAD iosWad) + { + this.wadFile = iosWad; + this.getEsIndex(); + } + + public int PatchFakeSigning() => this.esIndex < 0 ? -1 : this.patchFakeSigning(ref this.wadFile.Contents[this.esIndex]); + + public int PatchEsIdentify() => this.esIndex < 0 ? -1 : this.patchEsIdentify(ref this.wadFile.Contents[this.esIndex]); + + public int PatchNandPermissions() => this.esIndex < 0 ? -1 : this.patchNandPermissions(ref this.wadFile.Contents[this.esIndex]); + + public int PatchVP() => this.esIndex < 0 ? -1 : this.patchVP(ref this.wadFile.Contents[this.esIndex]); + + public int PatchAll() => this.esIndex < 0 ? -1 : this.patchAll(ref this.wadFile.Contents[this.esIndex]); + + public int PatchFakeSigning(ref byte[] esModule) => this.patchFakeSigning(ref esModule); + + public int PatchEsIdentify(ref byte[] esModule) => this.patchEsIdentify(ref esModule); + + public int PatchNandPermissions(ref byte[] esModule) => this.patchNandPermissions(ref esModule); + + public int PatchVP(ref byte[] esModule) => this.patchVP(ref esModule); + + public int PatchAll(ref byte[] esModule) => this.patchAll(ref esModule); + + private int patchFakeSigning(ref byte[] esModule) + { + this.fireDebug("Patching Fakesigning..."); + int num = 0; + byte[] second1 = new byte[4] + { + (byte) 32, + (byte) 7, + (byte) 35, + (byte) 162 + }; + byte[] second2 = new byte[4] + { + (byte) 32, + (byte) 7, + (byte) 75, + (byte) 11 + }; + for (int firstIndex = 0; firstIndex < esModule.Length - 4; ++firstIndex) + { + this.fireProgress((firstIndex + 1) * 100 / esModule.Length); + if (Shared.CompareByteArrays(esModule, firstIndex, second1, 0, 4) || Shared.CompareByteArrays(esModule, firstIndex, second2, 0, 4)) + { + this.fireDebug(" Patching at Offset: 0x{0}", (object) firstIndex.ToString("x8").ToUpper()); + esModule[firstIndex + 1] = (byte) 0; + firstIndex += 4; + ++num; + } + } + this.fireDebug("Patching Fakesigning Finished... (Patches applied: {0})", (object) num); + return num; + } + + private int patchEsIdentify(ref byte[] esModule) + { + this.fireDebug("Patching ES_Identify..."); + int num = 0; + byte[] second = new byte[4] + { + (byte) 40, + (byte) 3, + (byte) 209, + (byte) 35 + }; + for (int firstIndex = 0; firstIndex < esModule.Length - 4; ++firstIndex) + { + this.fireProgress((firstIndex + 1) * 100 / esModule.Length); + if (Shared.CompareByteArrays(esModule, firstIndex, second, 0, 4)) + { + this.fireDebug(" Patching at Offset: 0x{0}", (object) firstIndex.ToString("x8").ToUpper()); + esModule[firstIndex + 2] = (byte) 0; + esModule[firstIndex + 3] = (byte) 0; + firstIndex += 4; + ++num; + } + } + this.fireDebug("Patching ES_Identify Finished... (Patches applied: {0})", (object) num); + return num; + } + + private int patchNandPermissions(ref byte[] esModule) + { + this.fireDebug("Patching NAND Permissions..."); + int num = 0; + byte[] second = new byte[6] + { + (byte) 66, + (byte) 139, + (byte) 208, + (byte) 1, + (byte) 37, + (byte) 102 + }; + for (int firstIndex = 0; firstIndex < esModule.Length - 6; ++firstIndex) + { + this.fireProgress((firstIndex + 1) * 100 / esModule.Length); + if (Shared.CompareByteArrays(esModule, firstIndex, second, 0, 6)) + { + this.fireDebug(" Patching at Offset: 0x{0}", (object) firstIndex.ToString("x8").ToUpper()); + esModule[firstIndex + 2] = (byte) 224; + firstIndex += 6; + ++num; + } + } + this.fireDebug("Patching NAND Permissions Finished... (Patches applied: {0})", (object) num); + return num; + } + + private int patchVP(ref byte[] esModule) + { + this.fireDebug("Patching VP..."); + int num = 0; + byte[] second = new byte[4] + { + (byte) 210, + (byte) 1, + (byte) 78, + (byte) 86 + }; + for (int firstIndex = 0; firstIndex < esModule.Length - 4; ++firstIndex) + { + this.fireProgress((firstIndex + 1) * 100 / esModule.Length); + if (Shared.CompareByteArrays(esModule, firstIndex, second, 0, 4)) + { + this.fireDebug(" Patching for VP at Offset: 0x{0}", (object) firstIndex.ToString("x8").ToUpper()); + esModule[firstIndex] = (byte) 224; + firstIndex += 4; + ++num; + } + } + this.fireDebug("Patching VP Finished... (Patches applied: {0})", (object) num); + return num; + } + + private int patchAll(ref byte[] esModule) + { + this.fireDebug("Patching Fakesigning, ES_Identify, NAND Permissions and VP ..."); + int num = 0; + byte[] second1 = new byte[4] + { + (byte) 32, + (byte) 7, + (byte) 35, + (byte) 162 + }; + byte[] second2 = new byte[4] + { + (byte) 32, + (byte) 7, + (byte) 75, + (byte) 11 + }; + byte[] second3 = new byte[4] + { + (byte) 40, + (byte) 3, + (byte) 209, + (byte) 35 + }; + byte[] second4 = new byte[6] + { + (byte) 66, + (byte) 139, + (byte) 208, + (byte) 1, + (byte) 37, + (byte) 102 + }; + byte[] second5 = new byte[4] + { + (byte) 210, + (byte) 1, + (byte) 78, + (byte) 86 + }; + for (int firstIndex = 0; firstIndex < esModule.Length - 6; ++firstIndex) + { + this.fireProgress((firstIndex + 1) * 100 / esModule.Length); + if (Shared.CompareByteArrays(esModule, firstIndex, second1, 0, 4) || Shared.CompareByteArrays(esModule, firstIndex, second2, 0, 4)) + { + this.fireDebug(" Patching Fakesigning at Offset: 0x{0}", (object) firstIndex.ToString("x8").ToUpper()); + esModule[firstIndex + 1] = (byte) 0; + firstIndex += 4; + ++num; + } + else if (Shared.CompareByteArrays(esModule, firstIndex, second3, 0, 4)) + { + this.fireDebug(" Patching ES_Identify at Offset: 0x{0}", (object) firstIndex.ToString("x8").ToUpper()); + esModule[firstIndex + 2] = (byte) 0; + esModule[firstIndex + 3] = (byte) 0; + firstIndex += 4; + ++num; + } + else if (Shared.CompareByteArrays(esModule, firstIndex, second4, 0, 6)) + { + this.fireDebug(" Patching NAND Permissions at Offset: 0x{0}", (object) firstIndex.ToString("x8").ToUpper()); + esModule[firstIndex + 2] = (byte) 224; + firstIndex += 6; + ++num; + } + else if (Shared.CompareByteArrays(esModule, firstIndex, second5, 0, 4)) + { + this.fireDebug(" Patching VP at Offset: 0x{0}", (object) firstIndex.ToString("x8").ToUpper()); + esModule[firstIndex] = (byte) 224; + firstIndex += 4; + ++num; + } + } + this.fireDebug("Patching Fakesigning, ES_Identify, NAND Permissions and VP Finished... (Patches applied: {0})", (object) num); + return num; + } + + private void getEsIndex() + { + this.fireDebug("Scanning for ES Module..."); + string str = "$IOSVersion:"; + for (int index1 = this.wadFile.NumOfContents - 1; index1 >= 0; --index1) + { + this.fireDebug(" Scanning Content #{0} of {1}...", (object) (index1 + 1), (object) this.wadFile.NumOfContents); + this.fireProgress((index1 + 1) * 100 / this.wadFile.NumOfContents); + for (int index2 = 0; index2 < this.wadFile.Contents[index1].Length - 64; ++index2) + { + if (Encoding.ASCII.GetString(this.wadFile.Contents[index1], index2, 12) == str) + { + int index3 = index2 + 12; + while (this.wadFile.Contents[index1][index3] == (byte) 32) + ++index3; + if (Encoding.ASCII.GetString(this.wadFile.Contents[index1], index3, 3) == "ES:") + { + this.fireDebug(" -> ES Module found!"); + this.fireDebug("Scanning for ES Module Finished..."); + this.esIndex = index1; + this.fireProgress(100); + return; + } + } + } + } + this.fireDebug("/!\\/!\\/!\\ ES Module wasn't found! /!\\/!\\/!\\"); + throw new Exception("ES module wasn't found!"); + } + + 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 fireProgress(int progressPercentage) + { + EventHandler progress = this.Progress; + if (progress == null) + return; + progress(new object(), new ProgressChangedEventArgs(progressPercentage, (object) string.Empty)); + } + } +} diff --git a/License_libWiiSharp.txt b/License_libWiiSharp.txt new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/License_libWiiSharp.txt @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program 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. + + This program 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 . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/LowerTitleID.cs b/LowerTitleID.cs new file mode 100644 index 0000000..bd9b486 --- /dev/null +++ b/LowerTitleID.cs @@ -0,0 +1,18 @@ +// Decompiled with JetBrains decompiler +// Type: libWiiSharp.LowerTitleID +// Assembly: libWiiSharp, Version=0.4.0.0, Culture=neutral, PublicKeyToken=null +// MVID: FBF36F3D-B5D6-481F-B5F5-1BD3C19E13B2 +// Assembly location: C:\Users\theso\Downloads\NCPatcher\pack\libWiiSharp.dll + +namespace libWiiSharp +{ + public enum LowerTitleID : uint + { + SystemTitles = 1, + Channel = 65537, // 0x00010001 + SystemChannels = 65538, // 0x00010002 + GameChannel = 65540, // 0x00010004 + DLC = 65541, // 0x00010005 + HiddenChannels = 65544, // 0x00010008 + } +} diff --git a/Lz77.cs b/Lz77.cs new file mode 100644 index 0000000..0fd79c3 --- /dev/null +++ b/Lz77.cs @@ -0,0 +1,360 @@ +// Decompiled with JetBrains decompiler +// Type: libWiiSharp.Lz77 +// Assembly: libWiiSharp, Version=0.4.0.0, Culture=neutral, PublicKeyToken=null +// MVID: FBF36F3D-B5D6-481F-B5F5-1BD3C19E13B2 +// Assembly location: C:\Users\theso\Downloads\NCPatcher\pack\libWiiSharp.dll + +using System; +using System.IO; + +namespace libWiiSharp +{ + public class Lz77 + { + private const int N = 4096; + private const int F = 18; + private const int threshold = 2; + private static uint lz77Magic = 1280980791; + private int[] leftSon = new int[4097]; + private int[] rightSon = new int[4353]; + private int[] dad = new int[4097]; + private ushort[] textBuffer = new ushort[4113]; + private int matchPosition; + private int matchLength; + + public static uint Lz77Magic => Lz77.lz77Magic; + + public static bool IsLz77Compressed(string file) => Lz77.IsLz77Compressed(File.ReadAllBytes(file)); + + public static bool IsLz77Compressed(byte[] file) + { + Headers.HeaderType headerType = Headers.DetectHeader(file); + return (int) Shared.Swap(BitConverter.ToUInt32(file, (int) headerType)) == (int) Lz77.lz77Magic; + } + + public static bool IsLz77Compressed(Stream file) + { + Headers.HeaderType headerType = Headers.DetectHeader(file); + byte[] buffer = new byte[4]; + file.Seek((long) headerType, SeekOrigin.Begin); + file.Read(buffer, 0, buffer.Length); + return (int) Shared.Swap(BitConverter.ToUInt32(buffer, 0)) == (int) Lz77.lz77Magic; + } + + public void Compress(string inFile, string outFile) + { + Stream stream = (Stream) null; + using (FileStream fileStream = new FileStream(inFile, FileMode.Open)) + stream = this.compress((Stream) fileStream); + byte[] buffer = new byte[stream.Length]; + stream.Read(buffer, 0, buffer.Length); + if (File.Exists(outFile)) + File.Delete(outFile); + using (FileStream fileStream = new FileStream(outFile, FileMode.Create)) + fileStream.Write(buffer, 0, buffer.Length); + } + + public byte[] Compress(byte[] file) => ((MemoryStream) this.compress((Stream) new MemoryStream(file))).ToArray(); + + public Stream Compress(Stream file) => this.compress(file); + + public void Decompress(string inFile, string outFile) + { + Stream stream = (Stream) null; + using (FileStream fileStream = new FileStream(inFile, FileMode.Open)) + stream = this.decompress((Stream) fileStream); + byte[] buffer = new byte[stream.Length]; + stream.Read(buffer, 0, buffer.Length); + if (File.Exists(outFile)) + File.Delete(outFile); + using (FileStream fileStream = new FileStream(outFile, FileMode.Create)) + fileStream.Write(buffer, 0, buffer.Length); + } + + public byte[] Decompress(byte[] file) => ((MemoryStream) this.decompress((Stream) new MemoryStream(file))).ToArray(); + + public Stream Decompress(Stream file) => this.decompress(file); + + private Stream decompress(Stream inFile) + { + if (!Lz77.IsLz77Compressed(inFile)) + return inFile; + inFile.Seek(0L, SeekOrigin.Begin); + uint num1 = 0; + Headers.HeaderType headerType = Headers.DetectHeader(inFile); + byte[] buffer = new byte[8]; + inFile.Seek((long) headerType, SeekOrigin.Begin); + inFile.Read(buffer, 0, 8); + if ((int) Shared.Swap(BitConverter.ToUInt32(buffer, 0)) != (int) Lz77.lz77Magic) + { + inFile.Dispose(); + throw new Exception("Invaild Magic!"); + } + if (buffer[4] != (byte) 16) + { + inFile.Dispose(); + throw new Exception("Unsupported Compression Type!"); + } + uint num2 = BitConverter.ToUInt32(buffer, 4) >> 8; + for (int index = 0; index < 4078; ++index) + this.textBuffer[index] = (ushort) 223; + int num3 = 4078; + uint num4 = 7; + int num5 = 7; + MemoryStream memoryStream = new MemoryStream(); +label_10: + while (true) + { + num4 <<= 1; + ++num5; + if (num5 == 8) + { + int num6; + if ((num6 = inFile.ReadByte()) != -1) + { + num4 = (uint) num6; + num5 = 0; + } + else + goto label_24; + } + if (((int) num4 & 128) == 0) + { + int num6; + if ((long) (num6 = inFile.ReadByte()) != inFile.Length - 1L) + { + if (num1 < num2) + memoryStream.WriteByte((byte) num6); + ushort[] textBuffer = this.textBuffer; + int index = num3; + int num7 = index + 1; + int num8 = (int) (byte) num6; + textBuffer[index] = (ushort) num8; + num3 = num7 & 4095; + ++num1; + } + else + goto label_24; + } + else + break; + } + int num9; + int num10; + if ((num9 = inFile.ReadByte()) != -1 && (num10 = inFile.ReadByte()) != -1) + { + int num6 = num10 | num9 << 8 & 3840; + int num7 = (num9 >> 4 & 15) + 2; + for (int index1 = 0; index1 <= num7; ++index1) + { + int num8 = (int) this.textBuffer[num3 - num6 - 1 & 4095]; + if (num1 < num2) + memoryStream.WriteByte((byte) num8); + ushort[] textBuffer = this.textBuffer; + int index2 = num3; + int num11 = index2 + 1; + int num12 = (int) (byte) num8; + textBuffer[index2] = (ushort) num12; + num3 = num11 & 4095; + ++num1; + } + goto label_10; + } +label_24: + return (Stream) memoryStream; + } + + private Stream compress(Stream inFile) + { + if (Lz77.IsLz77Compressed(inFile)) + return inFile; + inFile.Seek(0L, SeekOrigin.Begin); + int num1 = 0; + int[] numArray1 = new int[17]; + uint num2 = (uint) (((int) Convert.ToUInt32(inFile.Length) << 8) + 16); + MemoryStream memoryStream = new MemoryStream(); + memoryStream.Write(BitConverter.GetBytes(Shared.Swap(Lz77.lz77Magic)), 0, 4); + memoryStream.Write(BitConverter.GetBytes(num2), 0, 4); + this.InitTree(); + numArray1[0] = 0; + int num3 = 1; + int num4 = 128; + int p = 0; + int r = 4078; + for (int index = p; index < r; ++index) + this.textBuffer[index] = ushort.MaxValue; + int num5; + int num6; + for (num5 = 0; num5 < 18 && (num6 = inFile.ReadByte()) != -1; ++num5) + this.textBuffer[r + num5] = (ushort) num6; + if (num5 == 0) + return inFile; + for (int index = 1; index <= 18; ++index) + this.InsertNode(r - index); + this.InsertNode(r); + do + { + if (this.matchLength > num5) + this.matchLength = num5; + if (this.matchLength <= 2) + { + this.matchLength = 1; + numArray1[num3++] = (int) this.textBuffer[r]; + } + else + { + numArray1[0] |= num4; + int[] numArray2 = numArray1; + int index1 = num3; + int num7 = index1 + 1; + int num8 = (int) (ushort) (r - this.matchPosition - 1 >> 8 & 15) | this.matchLength - 3 << 4; + numArray2[index1] = num8; + int[] numArray3 = numArray1; + int index2 = num7; + num3 = index2 + 1; + int num9 = (int) (ushort) (r - this.matchPosition - 1 & (int) byte.MaxValue); + numArray3[index2] = num9; + } + if ((num4 >>= 1) == 0) + { + for (int index = 0; index < num3; ++index) + memoryStream.WriteByte((byte) numArray1[index]); + num1 += num3; + numArray1[0] = 0; + num3 = 1; + num4 = 128; + } + int matchLength = this.matchLength; + int num10; + int num11; + for (num10 = 0; num10 < matchLength && (num11 = inFile.ReadByte()) != -1; ++num10) + { + this.DeleteNode(p); + this.textBuffer[p] = (ushort) num11; + if (p < 17) + this.textBuffer[p + 4096] = (ushort) num11; + p = p + 1 & 4095; + r = r + 1 & 4095; + this.InsertNode(r); + } + while (num10++ < matchLength) + { + this.DeleteNode(p); + p = p + 1 & 4095; + r = r + 1 & 4095; + if (--num5 != 0) + this.InsertNode(r); + } + } + while (num5 > 0); + if (num3 > 1) + { + for (int index = 0; index < num3; ++index) + memoryStream.WriteByte((byte) numArray1[index]); + num1 += num3; + } + if (num1 % 4 != 0) + { + for (int index = 0; index < 4 - num1 % 4; ++index) + memoryStream.WriteByte((byte) 0); + } + return (Stream) memoryStream; + } + + private void InitTree() + { + for (int index = 4097; index <= 4352; ++index) + this.rightSon[index] = 4096; + for (int index = 0; index < 4096; ++index) + this.dad[index] = 4096; + } + + private void InsertNode(int r) + { + int num1 = 1; + int index = 4097 + (this.textBuffer[r] != ushort.MaxValue ? (int) this.textBuffer[r] : 0); + this.rightSon[r] = this.leftSon[r] = 4096; + this.matchLength = 0; + int num2; + do + { + do + { + if (num1 >= 0) + { + if (this.rightSon[index] == 4096) + { + this.rightSon[index] = r; + this.dad[r] = index; + return; + } + index = this.rightSon[index]; + } + else + { + if (this.leftSon[index] == 4096) + { + this.leftSon[index] = r; + this.dad[r] = index; + return; + } + index = this.leftSon[index]; + } + num2 = 1; + while (num2 < 18 && (num1 = (int) this.textBuffer[r + num2] - (int) this.textBuffer[index + num2]) == 0) + ++num2; + } + while (num2 <= this.matchLength); + this.matchPosition = index; + } + while ((this.matchLength = num2) < 18); + this.dad[r] = this.dad[index]; + this.leftSon[r] = this.leftSon[index]; + this.rightSon[r] = this.rightSon[index]; + this.dad[this.leftSon[index]] = r; + this.dad[this.rightSon[index]] = r; + if (this.rightSon[this.dad[index]] == index) + this.rightSon[this.dad[index]] = r; + else + this.leftSon[this.dad[index]] = r; + this.dad[index] = 4096; + } + + private void DeleteNode(int p) + { + if (this.dad[p] == 4096) + return; + int index; + if (this.rightSon[p] == 4096) + index = this.leftSon[p]; + else if (this.leftSon[p] == 4096) + { + index = this.rightSon[p]; + } + else + { + index = this.leftSon[p]; + if (this.rightSon[index] != 4096) + { + do + { + index = this.rightSon[index]; + } + while (this.rightSon[index] != 4096); + this.rightSon[this.dad[index]] = this.leftSon[index]; + this.dad[this.leftSon[index]] = this.dad[index]; + this.leftSon[index] = this.leftSon[p]; + this.dad[this.leftSon[p]] = index; + } + this.rightSon[index] = this.rightSon[p]; + this.dad[this.rightSon[p]] = index; + } + this.dad[index] = this.dad[p]; + if (this.rightSon[this.dad[p]] == p) + this.rightSon[this.dad[p]] = index; + else + this.leftSon[this.dad[p]] = index; + this.dad[p] = 4096; + } + } +} diff --git a/MessageEventArgs.cs b/MessageEventArgs.cs new file mode 100644 index 0000000..cc07d39 --- /dev/null +++ b/MessageEventArgs.cs @@ -0,0 +1,19 @@ +// Decompiled with JetBrains decompiler +// Type: libWiiSharp.MessageEventArgs +// Assembly: libWiiSharp, Version=0.4.0.0, Culture=neutral, PublicKeyToken=null +// MVID: FBF36F3D-B5D6-481F-B5F5-1BD3C19E13B2 +// Assembly location: C:\Users\theso\Downloads\NCPatcher\pack\libWiiSharp.dll + +using System; + +namespace libWiiSharp +{ + public class MessageEventArgs : EventArgs + { + private string message; + + public string Message => this.message; + + public MessageEventArgs(string message) => this.message = message; + } +} diff --git a/NusClient.cs b/NusClient.cs new file mode 100644 index 0000000..45c0603 --- /dev/null +++ b/NusClient.cs @@ -0,0 +1,436 @@ +// Decompiled with JetBrains decompiler +// Type: libWiiSharp.NusClient +// Assembly: libWiiSharp, Version=0.4.0.0, Culture=neutral, PublicKeyToken=null +// MVID: FBF36F3D-B5D6-481F-B5F5-1BD3C19E13B2 +// Assembly location: C:\Users\theso\Downloads\NCPatcher\pack\libWiiSharp.dll + +using System; +using System.ComponentModel; +using System.Globalization; +using System.IO; +using System.Net; +using System.Security.Cryptography; + +namespace libWiiSharp +{ + public class NusClient : IDisposable + { + private const string nusUrl = "http://nus.cdn.shop.wii.com/ccs/download/"; + private WebClient wcNus = new WebClient(); + private bool useLocalFiles; + private bool continueWithoutTicket; + private bool isDisposed; + + public bool UseLocalFiles + { + get => this.useLocalFiles; + set => this.useLocalFiles = value; + } + + public bool ContinueWithoutTicket + { + get => this.continueWithoutTicket; + set => this.continueWithoutTicket = value; + } + + public event EventHandler Progress; + + public event EventHandler Debug; + + ~NusClient() => this.Dispose(false); + + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize((object) this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing && !this.isDisposed) + this.wcNus.Dispose(); + this.isDisposed = true; + } + + public void DownloadTitle( + string titleId, + string titleVersion, + string outputDir, + params StoreType[] storeTypes) + { + if (titleId.Length != 16) + throw new Exception("Title ID must be 16 characters long!"); + this.downloadTitle(titleId, titleVersion, outputDir, storeTypes); + } + + public TMD DownloadTMD(string titleId, string titleVersion) => titleId.Length == 16 ? this.downloadTmd(titleId, titleVersion) : throw new Exception("Title ID must be 16 characters long!"); + + public Ticket DownloadTicket(string titleId) => titleId.Length == 16 ? this.downloadTicket(titleId) : throw new Exception("Title ID must be 16 characters long!"); + + public byte[] DownloadSingleContent(string titleId, string titleVersion, string contentId) + { + if (titleId.Length != 16) + throw new Exception("Title ID must be 16 characters long!"); + return this.downloadSingleContent(titleId, titleVersion, contentId); + } + + 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 (System.IO.File.Exists(savePath)) + System.IO.File.Delete(savePath); + byte[] bytes = this.downloadSingleContent(titleId, titleVersion, contentId); + System.IO.File.WriteAllBytes(savePath, bytes); + } + + private byte[] downloadSingleContent(string titleId, string titleVersion, string contentId) + { + uint num = uint.Parse(contentId, NumberStyles.HexNumber); + contentId = num.ToString("x8"); + this.fireDebug("Downloading Content (Content ID: {0}) of Title {1} v{2}...", (object) contentId, (object) titleId, string.IsNullOrEmpty(titleVersion) ? (object) "[Latest]" : (object) titleVersion); + this.fireDebug(" Checking for Internet connection..."); + if (!this.CheckInet()) + { + this.fireDebug(" Connection not found..."); + throw new Exception("You're not connected to the internet!"); + } + this.fireProgress(0); + string str1 = "tmd" + (string.IsNullOrEmpty(titleVersion) ? string.Empty : "." + titleVersion); + string str2 = string.Format("{0}{1}/", (object) "http://nus.cdn.shop.wii.com/ccs/download/", (object) titleId); + string empty = string.Empty; + int contentIndex = 0; + this.fireDebug(" Downloading TMD..."); + byte[] tmdFile = this.wcNus.DownloadData(str2 + str1); + this.fireDebug(" Parsing TMD..."); + TMD tmd = TMD.Load(tmdFile); + this.fireProgress(20); + this.fireDebug(" Looking for Content ID {0} in TMD...", (object) contentId); + bool flag = false; + for (int index = 0; index < tmd.Contents.Length; ++index) + { + if ((int) tmd.Contents[index].ContentID == (int) num) + { + this.fireDebug(" Content ID {0} found in TMD...", (object) contentId); + flag = true; + empty = tmd.Contents[index].ContentID.ToString("x8"); + contentIndex = index; + break; + } + } + if (!flag) + { + this.fireDebug(" Content ID {0} wasn't found in TMD...", (object) contentId); + throw new Exception("Content ID wasn't found in the TMD!"); + } + if (!File.Exists("cetk")) + { + fireDebug(" Downloading Ticket..."); + byte[] tikArray = wcNus.DownloadData(str2 + "cetk"); + Console.WriteLine("Downloading"); + } + Console.WriteLine("Continuing"); + this.fireDebug("Parsing Ticket..."); + Ticket tik = Ticket.Load("cetk"); + this.fireProgress(40); + this.fireDebug(" Downloading Content... ({0} bytes)", (object) tmd.Contents[contentIndex].Size); + byte[] content = this.wcNus.DownloadData(str2 + empty); + this.fireProgress(80); + this.fireDebug(" Decrypting Content..."); + byte[] array = this.decryptContent(content, contentIndex, tik, tmd); + Array.Resize(ref array, (int) tmd.Contents[contentIndex].Size); + if (!Shared.CompareByteArrays(SHA1.Create().ComputeHash(array), tmd.Contents[contentIndex].Hash)) + { + this.fireDebug("/!\\ /!\\ /!\\ Hashes do not match /!\\ /!\\ /!\\"); + throw new Exception("Hashes do not match!"); + } + this.fireProgress(100); + this.fireDebug("Downloading Content (Content ID: {0}) of Title {1} v{2} Finished...", (object) contentId, (object) titleId, string.IsNullOrEmpty(titleVersion) ? (object) "[Latest]" : (object) titleVersion); + return array; + } + + private Ticket downloadTicket(string titleId) + { + if (!CheckInet()) + throw new Exception("You're not connected to the internet!"); + + string titleUrl = string.Format("{0}{1}/", nusUrl, titleId); + byte[] tikArray = wcNus.DownloadData(titleUrl + "cetk"); + + return Ticket.Load(tikArray); + } + + private TMD downloadTmd(string titleId, string titleVersion) + { + if (!this.CheckInet()) + throw new Exception("You're not connected to the internet!"); + return TMD.Load(this.wcNus.DownloadData(string.Format("{0}{1}/", (object) "http://nus.cdn.shop.wii.com/ccs/download/", (object) titleId) + ("tmd" + (string.IsNullOrEmpty(titleVersion) ? string.Empty : "." + titleVersion)))); + } + + private void downloadTitle( + string titleId, + string titleVersion, + string outputDir, + StoreType[] storeTypes) + { + this.fireDebug("Downloading Title {0} v{1}...", (object) titleId, string.IsNullOrEmpty(titleVersion) ? (object) "[Latest]" : (object) titleVersion); + if (storeTypes.Length < 1) + { + this.fireDebug(" No store types were defined..."); + throw new Exception("You must at least define one store type!"); + } + string str1 = string.Format("{0}{1}/", (object) "http://nus.cdn.shop.wii.com/ccs/download/", (object) titleId); + bool flag1 = false; + bool flag2 = false; + bool flag3 = false; + this.fireProgress(0); + for (int index = 0; index < storeTypes.Length; ++index) + { + switch (storeTypes[index]) + { + case StoreType.EncryptedContent: + this.fireDebug(" -> Storing Encrypted Content..."); + flag1 = true; + break; + case StoreType.DecryptedContent: + this.fireDebug(" -> Storing Decrypted Content..."); + flag2 = true; + break; + case StoreType.WAD: + this.fireDebug(" -> Storing WAD..."); + flag3 = true; + break; + case StoreType.All: + this.fireDebug(" -> Storing Decrypted Content..."); + this.fireDebug(" -> Storing Encrypted Content..."); + this.fireDebug(" -> Storing WAD..."); + flag2 = true; + flag1 = true; + flag3 = true; + break; + } + } + this.fireDebug(" Checking for Internet connection..."); + if (!this.CheckInet()) + { + this.fireDebug(" Connection not found..."); + throw new Exception("You're not connected to the internet!"); + } + if ((int) outputDir[outputDir.Length - 1] != (int) Path.DirectorySeparatorChar) + outputDir += Path.DirectorySeparatorChar.ToString(); + if (!Directory.Exists(outputDir)) + Directory.CreateDirectory(outputDir); + string str2 = "tmd" + (string.IsNullOrEmpty(titleVersion) ? string.Empty : "." + titleVersion); + this.fireDebug(" Downloading TMD..."); + try + { + this.wcNus.DownloadFile(str1 + str2, outputDir + str2); + } + catch (Exception ex) + { + this.fireDebug(" Downloading TMD Failed..."); + throw new Exception("Downloading TMD Failed:\n" + ex.Message); + } + + if (!File.Exists(outputDir + "cetk")) + { + //Download cetk + fireDebug(" Downloading Ticket..."); + try + { + wcNus.DownloadFile(string.Format("{0}{1}/", nusUrl, titleId) + "cetk", outputDir + "cetk"); + } + catch (Exception ex) + { + if (!continueWithoutTicket || !flag1) + { + fireDebug(" Downloading Ticket Failed..."); + throw new Exception("CETK Doesn't Exist and Downloading Ticket Failed:\n" + ex.Message); + } + + flag2 = false; + flag3 = false; + } + } + + + this.fireProgress(10); + this.fireDebug(" Parsing TMD..."); + TMD tmd = TMD.Load(outputDir + str2); + if (string.IsNullOrEmpty(titleVersion)) + this.fireDebug(" -> Title Version: {0}", (object) tmd.TitleVersion); + this.fireDebug(" -> {0} Contents", (object) tmd.NumOfContents); + this.fireDebug(" Parsing Ticket..."); + Ticket tik = Ticket.Load(outputDir + "cetk"); + string[] strArray1 = new string[(int) tmd.NumOfContents]; + uint contentId; + for (int index1 = 0; index1 < (int) tmd.NumOfContents; ++index1) + { + this.fireDebug(" Downloading Content #{0} of {1}... ({2} bytes)", (object) (index1 + 1), (object) tmd.NumOfContents, (object) tmd.Contents[index1].Size); + this.fireProgress((index1 + 1) * 60 / (int) tmd.NumOfContents + 10); + if (this.useLocalFiles) + { + string str3 = outputDir; + contentId = tmd.Contents[index1].ContentID; + string str4 = contentId.ToString("x8"); + if (System.IO.File.Exists(str3 + str4)) + { + this.fireDebug(" Using Local File, Skipping..."); + continue; + } + } + try + { + WebClient wcNus = this.wcNus; + string str3 = str1; + contentId = tmd.Contents[index1].ContentID; + string str4 = contentId.ToString("x8"); + string address = str3 + str4; + string str5 = outputDir; + contentId = tmd.Contents[index1].ContentID; + string str6 = contentId.ToString("x8"); + string fileName = str5 + str6; + wcNus.DownloadFile(address, fileName); + string[] strArray2 = strArray1; + int index2 = index1; + contentId = tmd.Contents[index1].ContentID; + string str7 = contentId.ToString("x8"); + strArray2[index2] = str7; + } + catch (Exception ex) + { + this.fireDebug(" Downloading Content #{0} of {1} failed...", (object) (index1 + 1), (object) tmd.NumOfContents); + throw new Exception("Downloading Content Failed:\n" + ex.Message); + } + } + if (flag2 | flag3) + { + SHA1 shA1 = SHA1.Create(); + for (int contentIndex = 0; contentIndex < (int) tmd.NumOfContents; ++contentIndex) + { + this.fireDebug(" Decrypting Content #{0} of {1}...", (object) (contentIndex + 1), (object) tmd.NumOfContents); + this.fireProgress((contentIndex + 1) * 20 / (int) tmd.NumOfContents + 75); + string str3 = outputDir; + contentId = tmd.Contents[contentIndex].ContentID; + string str4 = contentId.ToString("x8"); + byte[] array = this.decryptContent(System.IO.File.ReadAllBytes(str3 + str4), contentIndex, tik, tmd); + Array.Resize(ref array, (int) tmd.Contents[contentIndex].Size); + if (!Shared.CompareByteArrays(shA1.ComputeHash(array), tmd.Contents[contentIndex].Hash)) + { + this.fireDebug("/!\\ /!\\ /!\\ Hashes do not match /!\\ /!\\ /!\\"); + throw new Exception(string.Format("Content #{0}: Hashes do not match!", (object) contentIndex)); + } + string str5 = outputDir; + contentId = tmd.Contents[contentIndex].ContentID; + string str6 = contentId.ToString("x8"); + System.IO.File.WriteAllBytes(str5 + str6 + ".app", array); + } + shA1.Clear(); + } + if (flag3) + { + this.fireDebug(" Building Certificate Chain..."); + CertificateChain cert = CertificateChain.FromTikTmd(outputDir + "cetk", outputDir + str2); + byte[][] contents = new byte[(int) tmd.NumOfContents][]; + for (int index1 = 0; index1 < (int) tmd.NumOfContents; ++index1) + { + byte[][] numArray1 = contents; + int index2 = index1; + string str3 = outputDir; + contentId = tmd.Contents[index1].ContentID; + string str4 = contentId.ToString("x8"); + byte[] numArray2 = System.IO.File.ReadAllBytes(str3 + str4 + ".app"); + numArray1[index2] = numArray2; + } + this.fireDebug(" Creating WAD..."); + WAD.Create(cert, tik, tmd, contents).Save(outputDir + tmd.TitleID.ToString("x16") + "v" + tmd.TitleVersion.ToString() + ".wad"); + } + if (!flag1) + { + this.fireDebug(" Deleting Encrypted Contents..."); + for (int index = 0; index < strArray1.Length; ++index) + { + if (System.IO.File.Exists(outputDir + strArray1[index])) + System.IO.File.Delete(outputDir + strArray1[index]); + } + } + if (flag3 && !flag2) + { + this.fireDebug(" Deleting Decrypted Contents..."); + for (int index = 0; index < strArray1.Length; ++index) + { + if (System.IO.File.Exists(outputDir + strArray1[index] + ".app")) + System.IO.File.Delete(outputDir + strArray1[index] + ".app"); + } + } + if (!flag2 && !flag1) + { + this.fireDebug(" Deleting TMD and Ticket..."); + System.IO.File.Delete(outputDir + str2); + System.IO.File.Delete(outputDir + "cetk"); + } + this.fireDebug("Downloading Title {0} v{1} Finished...", (object) titleId, string.IsNullOrEmpty(titleVersion) ? (object) "[Latest]" : (object) titleVersion); + this.fireProgress(100); + } + + 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[] numArray = new byte[16]; + byte[] bytes = BitConverter.GetBytes(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[content.Length]; + cryptoStream.Read(buffer, 0, buffer.Length); + cryptoStream.Dispose(); + memoryStream.Dispose(); + return buffer; + } + + private bool CheckInet() + { + try + { + Dns.GetHostEntry("www.google.com"); + return true; + } + catch + { + return false; + } + } + + 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 fireProgress(int progressPercentage) + { + EventHandler progress = this.Progress; + if (progress == null) + return; + progress(new object(), new ProgressChangedEventArgs(progressPercentage, (object) string.Empty)); + } + } +} diff --git a/Protocol.cs b/Protocol.cs new file mode 100644 index 0000000..a695c31 --- /dev/null +++ b/Protocol.cs @@ -0,0 +1,15 @@ +// Decompiled with JetBrains decompiler +// Type: libWiiSharp.Protocol +// Assembly: libWiiSharp, Version=0.4.0.0, Culture=neutral, PublicKeyToken=null +// MVID: FBF36F3D-B5D6-481F-B5F5-1BD3C19E13B2 +// Assembly location: C:\Users\theso\Downloads\NCPatcher\pack\libWiiSharp.dll + +namespace libWiiSharp +{ + public enum Protocol + { + HAXX, + JODI, + Custom, + } +} diff --git a/Readme_libWiiSharp.txt b/Readme_libWiiSharp.txt new file mode 100644 index 0000000..f6f4e0b --- /dev/null +++ b/Readme_libWiiSharp.txt @@ -0,0 +1,45 @@ +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + libWiiSharp + by Leathl + Modified by TheShadowEevee +https://github.com/TheShadowEevee/libWiiSharp +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + + + +Info: +-=-=- + +libWiiSharp is a Wii related .NET (2.0+) library. +It can be used with any .NET language to easily develop Wii related applications. + +All public functions are documented and most should be pretty self-explanatory. +There are also some example applications that use libWiiSharp included. + +If you want to contribute, you can do so at the github repo. + + + +Thanks: +-=-=-=- + + - Xuzz, SquidMan, megazig, Matt_P, Omega and The Lemon Man for Wii.py + - megazig for his bns conversion code (bns.py) + - SquidMan for Zetsubou + - Arikado and Lunatik for Dop-Mii + - Andre Perrot for gbalzss + + + +License: +-=-=-=-=- + +libWiiSharp is released under the terms of the GNU General Public License v3. +See "License_libWiiSharp.txt" for more information. + + + +Changelog: +-=-=-=-=-=- + +See "Changelog_libWiiSharp.txt" \ No newline at end of file diff --git a/Region.cs b/Region.cs new file mode 100644 index 0000000..d98312a --- /dev/null +++ b/Region.cs @@ -0,0 +1,16 @@ +// Decompiled with JetBrains decompiler +// Type: libWiiSharp.Region +// Assembly: libWiiSharp, Version=0.4.0.0, Culture=neutral, PublicKeyToken=null +// MVID: FBF36F3D-B5D6-481F-B5F5-1BD3C19E13B2 +// Assembly location: C:\Users\theso\Downloads\NCPatcher\pack\libWiiSharp.dll + +namespace libWiiSharp +{ + public enum Region : ushort + { + Japan, + USA, + Europe, + Free, + } +} diff --git a/Shared.cs b/Shared.cs new file mode 100644 index 0000000..7b36820 --- /dev/null +++ b/Shared.cs @@ -0,0 +1,148 @@ +// Decompiled with JetBrains decompiler +// Type: libWiiSharp.Shared +// Assembly: libWiiSharp, Version=0.4.0.0, Culture=neutral, PublicKeyToken=null +// MVID: FBF36F3D-B5D6-481F-B5F5-1BD3C19E13B2 +// Assembly location: C:\Users\theso\Downloads\NCPatcher\pack\libWiiSharp.dll + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Net; + +namespace libWiiSharp +{ + public static class Shared + { + public static string[] MergeStringArrays(string[] a, string[] b) + { + List stringList = new List((IEnumerable) a); + foreach (string str in b) + { + if (!stringList.Contains(str)) + stringList.Add(str); + } + stringList.Sort(); + return stringList.ToArray(); + } + + public static bool CompareByteArrays( + byte[] first, + int firstIndex, + byte[] second, + int secondIndex, + int length) + { + if (first.Length < length || second.Length < length) + return false; + for (int index = 0; index < length; ++index) + { + if ((int) first[firstIndex + index] != (int) second[secondIndex + index]) + return false; + } + return true; + } + + public static bool CompareByteArrays(byte[] first, byte[] second) + { + if (first.Length != second.Length) + return false; + for (int index = 0; index < first.Length; ++index) + { + if ((int) first[index] != (int) second[index]) + return false; + } + return true; + } + + public static string ByteArrayToString(byte[] byteArray, char separator = ' ') + { + string str = string.Empty; + foreach (byte num in byteArray) + str = str + num.ToString("x2").ToUpper() + separator.ToString(); + return str.Remove(str.Length - 1); + } + + public static byte[] HexStringToByteArray(string hexString) + { + byte[] numArray = new byte[hexString.Length / 2]; + for (int index = 0; index < hexString.Length / 2; ++index) + numArray[index] = byte.Parse(hexString.Substring(index * 2, 2), NumberStyles.HexNumber); + return numArray; + } + + public static int CountCharsInString(string theString, char theChar) + { + int num1 = 0; + foreach (int num2 in theString) + { + if (num2 == (int) theChar) + ++num1; + } + return num1; + } + + public static long AddPadding(long value) => Shared.AddPadding(value, 64); + + public static long AddPadding(long value, int padding) + { + if (value % (long) padding != 0L) + value += (long) padding - value % (long) padding; + return value; + } + + public static int AddPadding(int value) => Shared.AddPadding(value, 64); + + public static int AddPadding(int value, int padding) + { + if (value % padding != 0) + value += padding - value % padding; + return value; + } + + public static ushort Swap(ushort value) => (ushort) IPAddress.HostToNetworkOrder((short) value); + + public static uint Swap(uint value) => (uint) IPAddress.HostToNetworkOrder((int) value); + + public static ulong Swap(ulong value) => (ulong) IPAddress.HostToNetworkOrder((long) value); + + public static byte[] UShortArrayToByteArray(ushort[] array) + { + List byteList = new List(); + foreach (ushort num in array) + { + byte[] bytes = BitConverter.GetBytes(num); + byteList.AddRange((IEnumerable) bytes); + } + return byteList.ToArray(); + } + + public static byte[] UIntArrayToByteArray(uint[] array) + { + List byteList = new List(); + foreach (uint num in array) + { + byte[] bytes = BitConverter.GetBytes(num); + byteList.AddRange((IEnumerable) bytes); + } + return byteList.ToArray(); + } + + public static uint[] ByteArrayToUIntArray(byte[] array) + { + uint[] numArray = new uint[array.Length / 4]; + int num = 0; + for (int startIndex = 0; startIndex < array.Length; startIndex += 4) + numArray[num++] = BitConverter.ToUInt32(array, startIndex); + return numArray; + } + + public static ushort[] ByteArrayToUShortArray(byte[] array) + { + ushort[] numArray = new ushort[array.Length / 2]; + int num = 0; + for (int startIndex = 0; startIndex < array.Length; startIndex += 2) + numArray[num++] = BitConverter.ToUInt16(array, startIndex); + return numArray; + } + } +} diff --git a/StoreType.cs b/StoreType.cs new file mode 100644 index 0000000..fe6ee13 --- /dev/null +++ b/StoreType.cs @@ -0,0 +1,16 @@ +// Decompiled with JetBrains decompiler +// Type: libWiiSharp.StoreType +// Assembly: libWiiSharp, Version=0.4.0.0, Culture=neutral, PublicKeyToken=null +// MVID: FBF36F3D-B5D6-481F-B5F5-1BD3C19E13B2 +// Assembly location: C:\Users\theso\Downloads\NCPatcher\pack\libWiiSharp.dll + +namespace libWiiSharp +{ + public enum StoreType + { + EncryptedContent, + DecryptedContent, + WAD, + All, + } +} diff --git a/TMD.cs b/TMD.cs new file mode 100644 index 0000000..8023f54 --- /dev/null +++ b/TMD.cs @@ -0,0 +1,521 @@ +// Decompiled with JetBrains decompiler +// Type: libWiiSharp.TMD +// Assembly: libWiiSharp, Version=0.4.0.0, Culture=neutral, PublicKeyToken=null +// MVID: FBF36F3D-B5D6-481F-B5F5-1BD3C19E13B2 +// Assembly location: C:\Users\theso\Downloads\NCPatcher\pack\libWiiSharp.dll + +using System; +using System.Collections.Generic; +using System.IO; +using System.Security.Cryptography; + +namespace libWiiSharp +{ + public class TMD : IDisposable + { + private bool fakeSign; + private bool sortContents; + private uint signatureExponent = 65537; + private byte[] signature = new byte[256]; + private byte[] padding = new byte[60]; + private byte[] issuer = new byte[64]; + private byte version; + private byte caCrlVersion; + private byte signerCrlVersion; + private byte paddingByte; + private ulong startupIos; + private ulong titleId; + private uint titleType; + private ushort groupId; + private ushort padding2; + private ushort region; + private byte[] reserved = new byte[58]; + private uint accessRights; + private ushort titleVersion; + private ushort numOfContents; + private ushort bootIndex; + private ushort padding3; + private List contents; + private bool isDisposed; + + public Region Region + { + get => (Region) this.region; + set => this.region = (ushort) value; + } + + public ulong StartupIOS + { + get => this.startupIos; + set => this.startupIos = value; + } + + public ulong TitleID + { + get => this.titleId; + set => this.titleId = value; + } + + public ushort TitleVersion + { + get => this.titleVersion; + set => this.titleVersion = value; + } + + public ushort NumOfContents => this.numOfContents; + + public ushort BootIndex + { + get => this.bootIndex; + set + { + if ((int) value > (int) this.numOfContents) + return; + this.bootIndex = value; + } + } + + public TMD_Content[] Contents + { + get => this.contents.ToArray(); + set + { + this.contents = new List((IEnumerable) value); + this.numOfContents = (ushort) value.Length; + } + } + + public bool FakeSign + { + get => this.fakeSign; + set => this.fakeSign = value; + } + + public bool SortContents + { + get => this.sortContents; + set => this.sortContents = true; + } + + public event EventHandler Debug; + + ~TMD() => this.Dispose(false); + + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize((object) this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing && !this.isDisposed) + { + this.signature = (byte[]) null; + this.padding = (byte[]) null; + this.issuer = (byte[]) null; + this.reserved = (byte[]) null; + this.contents.Clear(); + this.contents = (List) null; + } + this.isDisposed = true; + } + + public static TMD Load(string pathToTmd) => TMD.Load(File.ReadAllBytes(pathToTmd)); + + public static TMD Load(byte[] tmdFile) + { + TMD tmd = new TMD(); + MemoryStream memoryStream = new MemoryStream(tmdFile); + try + { + tmd.parseTmd((Stream) memoryStream); + } + catch + { + memoryStream.Dispose(); + throw; + } + memoryStream.Dispose(); + return tmd; + } + + public static TMD Load(Stream tmd) + { + TMD tmd1 = new TMD(); + tmd1.parseTmd(tmd); + return tmd1; + } + + public void LoadFile(string pathToTmd) => this.LoadFile(File.ReadAllBytes(pathToTmd)); + + public void LoadFile(byte[] tmdFile) + { + MemoryStream memoryStream = new MemoryStream(tmdFile); + try + { + this.parseTmd((Stream) memoryStream); + } + catch + { + memoryStream.Dispose(); + throw; + } + memoryStream.Dispose(); + } + + public void LoadFile(Stream tmd) => this.parseTmd(tmd); + + public void Save(string savePath) => this.Save(savePath, false); + + public void Save(string savePath, bool fakeSign) + { + if (fakeSign) + this.fakeSign = true; + if (File.Exists(savePath)) + File.Delete(savePath); + using (FileStream fileStream = new FileStream(savePath, FileMode.Create)) + this.writeToStream((Stream) fileStream); + } + + public MemoryStream ToMemoryStream() => this.ToMemoryStream(false); + + public MemoryStream ToMemoryStream(bool fakeSign) + { + if (fakeSign) + this.fakeSign = true; + MemoryStream memoryStream = new MemoryStream(); + try + { + this.writeToStream((Stream) memoryStream); + return memoryStream; + } + catch + { + memoryStream.Dispose(); + throw; + } + } + + public byte[] ToByteArray() => this.ToByteArray(false); + + public byte[] ToByteArray(bool fakeSign) + { + if (fakeSign) + this.fakeSign = true; + MemoryStream memoryStream = new MemoryStream(); + try + { + this.writeToStream((Stream) memoryStream); + } + catch + { + memoryStream.Dispose(); + throw; + } + byte[] array = memoryStream.ToArray(); + memoryStream.Dispose(); + return array; + } + + public void UpdateContents(string contentDir) + { + bool flag = true; + char directorySeparatorChar; + for (int index = 0; index < this.contents.Count; ++index) + { + string str1 = contentDir; + directorySeparatorChar = Path.DirectorySeparatorChar; + string str2 = directorySeparatorChar.ToString(); + string str3 = this.contents[index].ContentID.ToString("x8"); + if (!File.Exists(str1 + str2 + str3 + ".app")) + { + flag = false; + break; + } + } + if (!flag) + { + for (int index = 0; index < this.contents.Count; ++index) + { + string str1 = contentDir; + directorySeparatorChar = Path.DirectorySeparatorChar; + string str2 = directorySeparatorChar.ToString(); + string str3 = this.contents[index].ContentID.ToString("x8"); + if (!File.Exists(str1 + str2 + str3 + ".app")) + throw new Exception("Couldn't find all content files!"); + } + } + byte[][] conts = new byte[this.contents.Count][]; + for (int index = 0; index < this.contents.Count; ++index) + { + string str1 = contentDir; + directorySeparatorChar = Path.DirectorySeparatorChar; + string str2 = directorySeparatorChar.ToString(); + string str3 = flag ? this.contents[index].ContentID.ToString("x8") : this.contents[index].Index.ToString("x8"); + string path = str1 + str2 + str3 + ".app"; + conts[index] = File.ReadAllBytes(path); + } + this.updateContents(conts); + } + + public void UpdateContents(byte[][] contents) => this.updateContents(contents); + + public string GetUpperTitleID() + { + byte[] bytes = BitConverter.GetBytes(Shared.Swap((uint) this.titleId)); + return new string(new char[4] + { + (char) bytes[0], + (char) bytes[1], + (char) bytes[2], + (char) bytes[3] + }); + } + + public string GetNandBlocks() => this.calculateNandBlocks(); + + public void AddContent(TMD_Content content) + { + this.contents.Add(content); + this.numOfContents = (ushort) this.contents.Count; + } + + public void RemoveContent(int contentIndex) + { + for (int index = 0; index < (int) this.numOfContents; ++index) + { + if ((int) this.contents[index].Index == contentIndex) + { + this.contents.RemoveAt(index); + break; + } + } + this.numOfContents = (ushort) this.contents.Count; + } + + public void RemoveContentByID(int contentId) + { + for (int index = 0; index < (int) this.numOfContents; ++index) + { + if ((long) this.contents[index].ContentID == (long) contentId) + { + this.contents.RemoveAt(index); + break; + } + } + this.numOfContents = (ushort) this.contents.Count; + } + + public ContentIndices[] GetSortedContentList() + { + List contentIndicesList = new List(); + for (int index = 0; index < this.contents.Count; ++index) + contentIndicesList.Add(new ContentIndices(index, (int) this.contents[index].Index)); + if (this.sortContents) + contentIndicesList.Sort(); + return contentIndicesList.ToArray(); + } + + private void writeToStream(Stream writeStream) + { + this.fireDebug("Writing TMD..."); + if (this.fakeSign) + { + this.fireDebug(" Clearing Signature..."); + this.signature = new byte[256]; + } + MemoryStream memoryStream = new MemoryStream(); + memoryStream.Seek(0L, SeekOrigin.Begin); + this.fireDebug(" Writing Signature Exponent... (Offset: 0x{0})", (object) memoryStream.Position.ToString("x8").ToUpper()); + memoryStream.Write(BitConverter.GetBytes(Shared.Swap(this.signatureExponent)), 0, 4); + this.fireDebug(" Writing Signature... (Offset: 0x{0})", (object) memoryStream.Position.ToString("x8").ToUpper()); + memoryStream.Write(this.signature, 0, this.signature.Length); + this.fireDebug(" Writing Padding... (Offset: 0x{0})", (object) memoryStream.Position.ToString("x8").ToUpper()); + memoryStream.Write(this.padding, 0, this.padding.Length); + this.fireDebug(" Writing Issuer... (Offset: 0x{0})", (object) memoryStream.Position.ToString("x8").ToUpper()); + memoryStream.Write(this.issuer, 0, this.issuer.Length); + this.fireDebug(" Writing Version... (Offset: 0x{0})", (object) memoryStream.Position.ToString("x8").ToUpper()); + memoryStream.WriteByte(this.version); + this.fireDebug(" Writing CA Crl Version... (Offset: 0x{0})", (object) memoryStream.Position.ToString("x8").ToUpper()); + memoryStream.WriteByte(this.caCrlVersion); + this.fireDebug(" Writing Signer Crl Version... (Offset: 0x{0})", (object) memoryStream.Position.ToString("x8").ToUpper()); + memoryStream.WriteByte(this.signerCrlVersion); + this.fireDebug(" Writing Padding Byte... (Offset: 0x{0})", (object) memoryStream.Position.ToString("x8").ToUpper()); + memoryStream.WriteByte(this.paddingByte); + this.fireDebug(" Writing Startup IOS... (Offset: 0x{0})", (object) memoryStream.Position.ToString("x8").ToUpper()); + memoryStream.Write(BitConverter.GetBytes(Shared.Swap(this.startupIos)), 0, 8); + this.fireDebug(" Writing Title ID... (Offset: 0x{0})", (object) memoryStream.Position.ToString("x8").ToUpper()); + memoryStream.Write(BitConverter.GetBytes(Shared.Swap(this.titleId)), 0, 8); + this.fireDebug(" Writing Title Type... (Offset: 0x{0})", (object) memoryStream.Position.ToString("x8").ToUpper()); + memoryStream.Write(BitConverter.GetBytes(Shared.Swap(this.titleType)), 0, 4); + this.fireDebug(" Writing Group ID... (Offset: 0x{0})", (object) memoryStream.Position.ToString("x8").ToUpper()); + memoryStream.Write(BitConverter.GetBytes(Shared.Swap(this.groupId)), 0, 2); + this.fireDebug(" Writing Padding2... (Offset: 0x{0})", (object) memoryStream.Position.ToString("x8").ToUpper()); + memoryStream.Write(BitConverter.GetBytes(Shared.Swap(this.padding2)), 0, 2); + this.fireDebug(" Writing Region... (Offset: 0x{0})", (object) memoryStream.Position.ToString("x8").ToUpper()); + memoryStream.Write(BitConverter.GetBytes(Shared.Swap(this.region)), 0, 2); + this.fireDebug(" Writing Reserved... (Offset: 0x{0})", (object) memoryStream.Position.ToString("x8").ToUpper()); + memoryStream.Write(this.reserved, 0, this.reserved.Length); + this.fireDebug(" Writing Access Rights... (Offset: 0x{0})", (object) memoryStream.Position.ToString("x8").ToUpper()); + memoryStream.Write(BitConverter.GetBytes(Shared.Swap(this.accessRights)), 0, 4); + this.fireDebug(" Writing Title Version... (Offset: 0x{0})", (object) memoryStream.Position.ToString("x8").ToUpper()); + memoryStream.Write(BitConverter.GetBytes(Shared.Swap(this.titleVersion)), 0, 2); + this.fireDebug(" Writing NumOfContents... (Offset: 0x{0})", (object) memoryStream.Position.ToString("x8").ToUpper()); + memoryStream.Write(BitConverter.GetBytes(Shared.Swap(this.numOfContents)), 0, 2); + this.fireDebug(" Writing Boot Index... (Offset: 0x{0})", (object) memoryStream.Position.ToString("x8").ToUpper()); + memoryStream.Write(BitConverter.GetBytes(Shared.Swap(this.bootIndex)), 0, 2); + this.fireDebug(" Writing Padding3... (Offset: 0x{0})", (object) memoryStream.Position.ToString("x8").ToUpper()); + memoryStream.Write(BitConverter.GetBytes(Shared.Swap(this.padding3)), 0, 2); + List contentIndicesList = new List(); + for (int index = 0; index < this.contents.Count; ++index) + contentIndicesList.Add(new ContentIndices(index, (int) this.contents[index].Index)); + if (this.sortContents) + contentIndicesList.Sort(); + for (int index = 0; index < contentIndicesList.Count; ++index) + { + this.fireDebug(" Writing Content #{1} of {2}... (Offset: 0x{0})", (object) memoryStream.Position.ToString("x8").ToUpper().ToUpper(), (object) (index + 1), (object) this.numOfContents); + memoryStream.Write(BitConverter.GetBytes(Shared.Swap(this.contents[contentIndicesList[index].Index].ContentID)), 0, 4); + memoryStream.Write(BitConverter.GetBytes(Shared.Swap(this.contents[contentIndicesList[index].Index].Index)), 0, 2); + memoryStream.Write(BitConverter.GetBytes(Shared.Swap((ushort) this.contents[contentIndicesList[index].Index].Type)), 0, 2); + memoryStream.Write(BitConverter.GetBytes(Shared.Swap(this.contents[contentIndicesList[index].Index].Size)), 0, 8); + memoryStream.Write(this.contents[contentIndicesList[index].Index].Hash, 0, this.contents[contentIndicesList[index].Index].Hash.Length); + } + byte[] array = memoryStream.ToArray(); + memoryStream.Dispose(); + if (this.fakeSign) + { + this.fireDebug(" Fakesigning TMD..."); + byte[] numArray = new byte[20]; + SHA1 shA1 = SHA1.Create(); + for (ushort index = 0; index < ushort.MaxValue; ++index) + { + byte[] bytes = BitConverter.GetBytes(index); + array[482] = bytes[1]; + array[483] = bytes[0]; + if (shA1.ComputeHash(array)[0] == (byte) 0) + { + this.fireDebug(" -> Signed ({0})", (object) index); + break; + } + if (index == (ushort) 65534) + { + this.fireDebug(" -> Signing Failed..."); + throw new Exception("Fakesigning failed..."); + } + } + shA1.Clear(); + } + writeStream.Seek(0L, SeekOrigin.Begin); + writeStream.Write(array, 0, array.Length); + this.fireDebug("Writing TMD Finished..."); + } + + private void updateContents(byte[][] conts) + { + SHA1 shA1 = SHA1.Create(); + for (int index = 0; index < this.contents.Count; ++index) + { + this.contents[index].Size = (ulong) conts[index].Length; + this.contents[index].Hash = shA1.ComputeHash(conts[index]); + } + shA1.Clear(); + } + + private void parseTmd(Stream tmdFile) + { + this.fireDebug("Pasing TMD..."); + tmdFile.Seek(0L, SeekOrigin.Begin); + byte[] buffer = new byte[8]; + this.fireDebug(" Reading Signature Exponent... (Offset: 0x{0})", (object) tmdFile.Position.ToString("x8").ToUpper()); + tmdFile.Read(buffer, 0, 4); + this.signatureExponent = Shared.Swap(BitConverter.ToUInt32(buffer, 0)); + this.fireDebug(" Reading Signature... (Offset: 0x{0})", (object) tmdFile.Position.ToString("x8").ToUpper()); + tmdFile.Read(this.signature, 0, this.signature.Length); + this.fireDebug(" Reading Padding... (Offset: 0x{0})", (object) tmdFile.Position.ToString("x8").ToUpper()); + tmdFile.Read(this.padding, 0, this.padding.Length); + this.fireDebug(" Reading Issuer... (Offset: 0x{0})", (object) tmdFile.Position.ToString("x8").ToUpper()); + tmdFile.Read(this.issuer, 0, this.issuer.Length); + this.fireDebug(" Reading Version... (Offset: 0x{0})", (object) tmdFile.Position.ToString("x8").ToUpper()); + this.fireDebug(" Reading CA Crl Version... (Offset: 0x{0})", (object) tmdFile.Position.ToString("x8").ToUpper()); + this.fireDebug(" Reading Signer Crl Version... (Offset: 0x{0})", (object) tmdFile.Position.ToString("x8").ToUpper()); + this.fireDebug(" Reading Padding Byte... (Offset: 0x{0})", (object) tmdFile.Position.ToString("x8").ToUpper()); + tmdFile.Read(buffer, 0, 4); + this.version = buffer[0]; + this.caCrlVersion = buffer[1]; + this.signerCrlVersion = buffer[2]; + this.paddingByte = buffer[3]; + this.fireDebug(" Reading Startup IOS... (Offset: 0x{0})", (object) tmdFile.Position.ToString("x8").ToUpper()); + tmdFile.Read(buffer, 0, 8); + this.startupIos = Shared.Swap(BitConverter.ToUInt64(buffer, 0)); + this.fireDebug(" Reading Title ID... (Offset: 0x{0})", (object) tmdFile.Position.ToString("x8").ToUpper()); + tmdFile.Read(buffer, 0, 8); + this.titleId = Shared.Swap(BitConverter.ToUInt64(buffer, 0)); + this.fireDebug(" Reading Title Type... (Offset: 0x{0})", (object) tmdFile.Position.ToString("x8").ToUpper()); + tmdFile.Read(buffer, 0, 4); + this.titleType = Shared.Swap(BitConverter.ToUInt32(buffer, 0)); + this.fireDebug(" Reading Group ID... (Offset: 0x{0})", (object) tmdFile.Position.ToString("x8").ToUpper()); + tmdFile.Read(buffer, 0, 2); + this.groupId = Shared.Swap(BitConverter.ToUInt16(buffer, 0)); + this.fireDebug(" Reading Padding2... (Offset: 0x{0})", (object) tmdFile.Position.ToString("x8").ToUpper()); + tmdFile.Read(buffer, 0, 2); + this.padding2 = Shared.Swap(BitConverter.ToUInt16(buffer, 0)); + this.fireDebug(" Reading Region... (Offset: 0x{0})", (object) tmdFile.Position.ToString("x8").ToUpper()); + tmdFile.Read(buffer, 0, 2); + this.region = Shared.Swap(BitConverter.ToUInt16(buffer, 0)); + this.fireDebug(" Reading Reserved... (Offset: 0x{0})", (object) tmdFile.Position.ToString("x8").ToUpper()); + tmdFile.Read(this.reserved, 0, this.reserved.Length); + this.fireDebug(" Reading Access Rights... (Offset: 0x{0})", (object) tmdFile.Position.ToString("x8").ToUpper()); + tmdFile.Read(buffer, 0, 4); + this.accessRights = Shared.Swap(BitConverter.ToUInt32(buffer, 0)); + this.fireDebug(" Reading Title Version... (Offset: 0x{0})", (object) tmdFile.Position.ToString("x8").ToUpper()); + this.fireDebug(" Reading NumOfContents... (Offset: 0x{0})", (object) tmdFile.Position.ToString("x8").ToUpper()); + this.fireDebug(" Reading Boot Index... (Offset: 0x{0})", (object) tmdFile.Position.ToString("x8").ToUpper()); + this.fireDebug(" Reading Padding3... (Offset: 0x{0})", (object) tmdFile.Position.ToString("x8").ToUpper()); + tmdFile.Read(buffer, 0, 8); + this.titleVersion = Shared.Swap(BitConverter.ToUInt16(buffer, 0)); + this.numOfContents = Shared.Swap(BitConverter.ToUInt16(buffer, 2)); + this.bootIndex = Shared.Swap(BitConverter.ToUInt16(buffer, 4)); + this.padding3 = Shared.Swap(BitConverter.ToUInt16(buffer, 6)); + this.contents = new List(); + for (int index = 0; index < (int) this.numOfContents; ++index) + { + this.fireDebug(" Reading Content #{0} of {1}... (Offset: 0x{2})", (object) (index + 1), (object) this.numOfContents, (object) tmdFile.Position.ToString("x8").ToUpper().ToUpper()); + TMD_Content tmdContent = new TMD_Content(); + tmdContent.Hash = new byte[20]; + tmdFile.Read(buffer, 0, 8); + tmdContent.ContentID = Shared.Swap(BitConverter.ToUInt32(buffer, 0)); + tmdContent.Index = Shared.Swap(BitConverter.ToUInt16(buffer, 4)); + tmdContent.Type = (ContentType) Shared.Swap(BitConverter.ToUInt16(buffer, 6)); + tmdFile.Read(buffer, 0, 8); + tmdContent.Size = Shared.Swap(BitConverter.ToUInt64(buffer, 0)); + tmdFile.Read(tmdContent.Hash, 0, tmdContent.Hash.Length); + this.contents.Add(tmdContent); + } + this.fireDebug("Pasing TMD Finished..."); + } + + private string calculateNandBlocks() + { + int num1 = 0; + int num2 = 0; + for (int index = 0; index < (int) this.numOfContents; ++index) + { + num2 += (int) this.contents[index].Size; + if (this.contents[index].Type == ContentType.Normal) + num1 += (int) this.contents[index].Size; + } + int num3 = (int) Math.Ceiling((double) num1 / 131072.0); + int num4 = (int) Math.Ceiling((double) num2 / 131072.0); + return num3 == num4 ? num4.ToString() : string.Format("{0} - {1}", (object) num3, (object) num4); + } + + 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))); + } + } +} diff --git a/TMD_Content.cs b/TMD_Content.cs new file mode 100644 index 0000000..68c9ee2 --- /dev/null +++ b/TMD_Content.cs @@ -0,0 +1,47 @@ +// Decompiled with JetBrains decompiler +// Type: libWiiSharp.TMD_Content +// Assembly: libWiiSharp, Version=0.4.0.0, Culture=neutral, PublicKeyToken=null +// MVID: FBF36F3D-B5D6-481F-B5F5-1BD3C19E13B2 +// Assembly location: C:\Users\theso\Downloads\NCPatcher\pack\libWiiSharp.dll + +namespace libWiiSharp +{ + public class TMD_Content + { + private uint contentId; + private ushort index; + private ushort type; + private ulong size; + private byte[] hash = new byte[20]; + + public uint ContentID + { + get => this.contentId; + set => this.contentId = value; + } + + public ushort Index + { + get => this.index; + set => this.index = value; + } + + public ContentType Type + { + get => (ContentType) this.type; + set => this.type = (ushort) value; + } + + public ulong Size + { + get => this.size; + set => this.size = value; + } + + public byte[] Hash + { + get => this.hash; + set => this.hash = value; + } + } +} diff --git a/TPL.cs b/TPL.cs new file mode 100644 index 0000000..9bec3e4 --- /dev/null +++ b/TPL.cs @@ -0,0 +1,1382 @@ +// Decompiled with JetBrains decompiler +// Type: libWiiSharp.TPL +// Assembly: libWiiSharp, Version=0.4.0.0, Culture=neutral, PublicKeyToken=null +// MVID: FBF36F3D-B5D6-481F-B5F5-1BD3C19E13B2 +// Assembly location: C:\Users\theso\Downloads\NCPatcher\pack\libWiiSharp.dll + +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using System.Runtime.InteropServices; + +namespace libWiiSharp +{ + public class TPL : IDisposable + { + private TPL_Header tplHeader = new TPL_Header(); + private List tplTextureEntries = new List(); + private List tplTextureHeaders = new List(); + private List tplPaletteHeaders = new List(); + private List textureData = new List(); + private List paletteData = new List(); + private bool isDisposed; + + public int NumOfTextures => (int) this.tplHeader.NumOfTextures; + + public event EventHandler Debug; + + ~TPL() => this.Dispose(false); + + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize((object) this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing && !this.isDisposed) + { + this.tplHeader = (TPL_Header) null; + this.tplTextureEntries.Clear(); + this.tplTextureEntries = (List) null; + this.tplTextureHeaders.Clear(); + this.tplTextureHeaders = (List) null; + this.tplPaletteHeaders.Clear(); + this.tplPaletteHeaders = (List) null; + this.textureData.Clear(); + this.textureData = (List) null; + this.paletteData.Clear(); + this.paletteData = (List) null; + } + this.isDisposed = true; + } + + public static TPL Load(string pathToTpl) + { + TPL tpl = new TPL(); + MemoryStream memoryStream = new MemoryStream(File.ReadAllBytes(pathToTpl)); + try + { + tpl.parseTpl((Stream) memoryStream); + } + catch + { + memoryStream.Dispose(); + throw; + } + memoryStream.Dispose(); + return tpl; + } + + public static TPL Load(byte[] tplFile) + { + TPL tpl = new TPL(); + MemoryStream memoryStream = new MemoryStream(tplFile); + try + { + tpl.parseTpl((Stream) memoryStream); + } + catch + { + memoryStream.Dispose(); + throw; + } + memoryStream.Dispose(); + return tpl; + } + + public static TPL Load(Stream tpl) + { + TPL tpl1 = new TPL(); + tpl1.parseTpl(tpl); + return tpl1; + } + + public static TPL FromImage( + string pathToImage, + TPL_TextureFormat tplFormat, + TPL_PaletteFormat paletteFormat = TPL_PaletteFormat.RGB5A3) + { + return TPL.FromImages(new string[1]{ pathToImage }, new TPL_TextureFormat[1] + { + tplFormat + }, new TPL_PaletteFormat[1]{ paletteFormat }); + } + + public static TPL FromImage( + Image img, + TPL_TextureFormat tplFormat, + TPL_PaletteFormat paletteFormat = TPL_PaletteFormat.RGB5A3) + { + return TPL.FromImages(new Image[1]{ img }, new TPL_TextureFormat[1] + { + tplFormat + }, new TPL_PaletteFormat[1]{ paletteFormat }); + } + + public static TPL FromImages( + string[] imagePaths, + TPL_TextureFormat[] tplFormats, + TPL_PaletteFormat[] paletteFormats) + { + if (tplFormats.Length < imagePaths.Length) + throw new Exception("You must specify a format for each image!"); + List imageList = new List(); + foreach (string imagePath in imagePaths) + imageList.Add(Image.FromFile(imagePath)); + TPL tpl = new TPL(); + tpl.createFromImages(imageList.ToArray(), tplFormats, paletteFormats); + return tpl; + } + + public static TPL FromImages( + Image[] images, + TPL_TextureFormat[] tplFormats, + TPL_PaletteFormat[] paletteFormats) + { + if (tplFormats.Length < images.Length) + throw new Exception("You must specify a format for each image!"); + TPL tpl = new TPL(); + tpl.createFromImages(images, tplFormats, paletteFormats); + return tpl; + } + + public void LoadFile(string pathToTpl) + { + MemoryStream memoryStream = new MemoryStream(File.ReadAllBytes(pathToTpl)); + try + { + this.parseTpl((Stream) memoryStream); + } + catch + { + memoryStream.Dispose(); + throw; + } + memoryStream.Dispose(); + } + + public void LoadFile(byte[] tplFile) + { + MemoryStream memoryStream = new MemoryStream(tplFile); + try + { + this.parseTpl((Stream) memoryStream); + } + catch + { + memoryStream.Dispose(); + throw; + } + memoryStream.Dispose(); + } + + public void LoadFile(Stream tpl) => this.parseTpl(tpl); + + public void CreateFromImage( + string pathToImage, + TPL_TextureFormat tplFormat, + TPL_PaletteFormat paletteFormat = TPL_PaletteFormat.RGB5A3) + { + this.CreateFromImages(new string[1]{ pathToImage }, new TPL_TextureFormat[1] + { + tplFormat + }, new TPL_PaletteFormat[1]{ paletteFormat }); + } + + public void CreateFromImage( + Image img, + TPL_TextureFormat tplFormat, + TPL_PaletteFormat paletteFormat = TPL_PaletteFormat.RGB5A3) + { + this.CreateFromImages(new Image[1]{ img }, new TPL_TextureFormat[1] + { + tplFormat + }, new TPL_PaletteFormat[1]{ paletteFormat }); + } + + public void CreateFromImages( + string[] imagePaths, + TPL_TextureFormat[] tplFormats, + TPL_PaletteFormat[] paletteFormats) + { + if (tplFormats.Length < imagePaths.Length) + throw new Exception("You must specify a format for each image!"); + List imageList = new List(); + foreach (string imagePath in imagePaths) + imageList.Add(Image.FromFile(imagePath)); + this.createFromImages(imageList.ToArray(), tplFormats, paletteFormats); + } + + public void CreateFromImages( + Image[] images, + TPL_TextureFormat[] tplFormats, + TPL_PaletteFormat[] paletteFormats) + { + if (tplFormats.Length < images.Length) + throw new Exception("You must specify a format for each image!"); + this.createFromImages(images, tplFormats, paletteFormats); + } + + public void Save(string savePath) + { + if (File.Exists(savePath)) + File.Delete(savePath); + FileStream fileStream = new FileStream(savePath, FileMode.Create); + try + { + this.writeToStream((Stream) fileStream); + } + catch + { + fileStream.Dispose(); + throw; + } + fileStream.Dispose(); + } + + public MemoryStream ToMemoryStream() + { + MemoryStream memoryStream = new MemoryStream(); + try + { + this.writeToStream((Stream) memoryStream); + return memoryStream; + } + catch + { + memoryStream.Dispose(); + throw; + } + } + + public byte[] ToByteArray() => this.ToMemoryStream().ToArray(); + + public Image ExtractTexture() => this.ExtractTexture(0); + + public Image ExtractTexture(int index) + { + byte[] data; + switch (this.tplTextureHeaders[index].TextureFormat) + { + case 0: + data = this.fromI4(this.textureData[index], (int) this.tplTextureHeaders[index].TextureWidth, (int) this.tplTextureHeaders[index].TextureHeight); + break; + case 1: + data = this.fromI8(this.textureData[index], (int) this.tplTextureHeaders[index].TextureWidth, (int) this.tplTextureHeaders[index].TextureHeight); + break; + case 2: + data = this.fromIA4(this.textureData[index], (int) this.tplTextureHeaders[index].TextureWidth, (int) this.tplTextureHeaders[index].TextureHeight); + break; + case 3: + data = this.fromIA8(this.textureData[index], (int) this.tplTextureHeaders[index].TextureWidth, (int) this.tplTextureHeaders[index].TextureHeight); + break; + case 4: + data = this.fromRGB565(this.textureData[index], (int) this.tplTextureHeaders[index].TextureWidth, (int) this.tplTextureHeaders[index].TextureHeight); + break; + case 5: + data = this.fromRGB5A3(this.textureData[index], (int) this.tplTextureHeaders[index].TextureWidth, (int) this.tplTextureHeaders[index].TextureHeight); + break; + case 6: + data = this.fromRGBA8(this.textureData[index], (int) this.tplTextureHeaders[index].TextureWidth, (int) this.tplTextureHeaders[index].TextureHeight); + break; + case 8: + data = this.fromCI4(this.textureData[index], this.paletteToRgba(index), (int) this.tplTextureHeaders[index].TextureWidth, (int) this.tplTextureHeaders[index].TextureHeight); + break; + case 9: + data = this.fromCI8(this.textureData[index], this.paletteToRgba(index), (int) this.tplTextureHeaders[index].TextureWidth, (int) this.tplTextureHeaders[index].TextureHeight); + break; + case 10: + data = this.fromCI14X2(this.textureData[index], this.paletteToRgba(index), (int) this.tplTextureHeaders[index].TextureWidth, (int) this.tplTextureHeaders[index].TextureHeight); + break; + case 14: + data = this.fromCMP(this.textureData[index], (int) this.tplTextureHeaders[index].TextureWidth, (int) this.tplTextureHeaders[index].TextureHeight); + break; + default: + throw new FormatException("Unsupported Texture Format!"); + } + return (Image) this.rgbaToImage(data, (int) this.tplTextureHeaders[index].TextureWidth, (int) this.tplTextureHeaders[index].TextureHeight); + } + + public void ExtractTexture(string savePath) => this.ExtractTexture(0, savePath); + + public void ExtractTexture(int index, string savePath) + { + if (File.Exists(savePath)) + File.Delete(savePath); + Image texture = this.ExtractTexture(index); + switch (Path.GetExtension(savePath).ToLower()) + { + case ".tif": + case ".tiff": + texture.Save(savePath, ImageFormat.Tiff); + break; + case ".bmp": + texture.Save(savePath, ImageFormat.Bmp); + break; + case ".gif": + texture.Save(savePath, ImageFormat.Gif); + break; + case ".jpg": + case ".jpeg": + texture.Save(savePath, ImageFormat.Jpeg); + break; + default: + texture.Save(savePath, ImageFormat.Png); + break; + } + } + + public Image[] ExtractAllTextures() + { + List imageList = new List(); + for (int index = 0; (long) index < (long) this.tplHeader.NumOfTextures; ++index) + imageList.Add(this.ExtractTexture(index)); + return imageList.ToArray(); + } + + public void ExtractAllTextures(string saveDir) + { + if (Directory.Exists(saveDir)) + Directory.CreateDirectory(saveDir); + for (int index = 0; (long) index < (long) this.tplHeader.NumOfTextures; ++index) + this.ExtractTexture(index, saveDir + Path.DirectorySeparatorChar.ToString() + "Texture_" + index.ToString("x2") + ".png"); + } + + public void AddTexture( + string imagePath, + TPL_TextureFormat tplFormat, + TPL_PaletteFormat paletteFormat = TPL_PaletteFormat.RGB5A3) + { + this.AddTexture(Image.FromFile(imagePath), tplFormat, paletteFormat); + } + + public void AddTexture(Image img, TPL_TextureFormat tplFormat, TPL_PaletteFormat paletteFormat = TPL_PaletteFormat.RGB5A3) + { + TPL_TextureEntry tplTextureEntry = new TPL_TextureEntry(); + TPL_TextureHeader tplTextureHeader = new TPL_TextureHeader(); + TPL_PaletteHeader tplPaletteHeader = new TPL_PaletteHeader(); + byte[] numArray1 = this.imageToTpl(img, tplFormat); + byte[] numArray2 = new byte[0]; + tplTextureHeader.TextureHeight = (ushort) img.Height; + tplTextureHeader.TextureWidth = (ushort) img.Width; + tplTextureHeader.TextureFormat = (uint) tplFormat; + if (tplFormat == TPL_TextureFormat.CI4 || tplFormat == TPL_TextureFormat.CI8 || tplFormat == TPL_TextureFormat.CI14X2) + { + ColorIndexConverter colorIndexConverter = new ColorIndexConverter(this.imageToRgba(img), img.Width, img.Height, tplFormat, paletteFormat); + numArray1 = colorIndexConverter.Data; + numArray2 = colorIndexConverter.Palette; + tplPaletteHeader.NumberOfItems = (ushort) (numArray2.Length / 2); + tplPaletteHeader.PaletteFormat = (uint) paletteFormat; + } + this.tplTextureEntries.Add(tplTextureEntry); + this.tplTextureHeaders.Add(tplTextureHeader); + this.tplPaletteHeaders.Add(tplPaletteHeader); + this.textureData.Add(numArray1); + this.paletteData.Add(numArray2); + ++this.tplHeader.NumOfTextures; + } + + public void RemoveTexture(int index) + { + if ((long) this.tplHeader.NumOfTextures <= (long) index) + return; + this.tplTextureEntries.RemoveAt(index); + this.tplTextureHeaders.RemoveAt(index); + this.tplPaletteHeaders.RemoveAt(index); + this.textureData.RemoveAt(index); + this.paletteData.RemoveAt(index); + --this.tplHeader.NumOfTextures; + } + + public TPL_TextureFormat GetTextureFormat(int index) => (TPL_TextureFormat) this.tplTextureHeaders[index].TextureFormat; + + public TPL_PaletteFormat GetPaletteFormat(int index) => (TPL_PaletteFormat) this.tplPaletteHeaders[index].PaletteFormat; + + public Size GetTextureSize(int index) => new Size((int) this.tplTextureHeaders[index].TextureWidth, (int) this.tplTextureHeaders[index].TextureHeight); + + private void writeToStream(Stream writeStream) + { + this.fireDebug("Writing TPL..."); + writeStream.Seek(0L, SeekOrigin.Begin); + this.fireDebug(" Writing TPL Header... (Offset: 0x{0})", (object) writeStream.Position); + this.tplHeader.Write(writeStream); + int position1 = (int) writeStream.Position; + writeStream.Seek((long) (this.tplHeader.NumOfTextures * 8U), SeekOrigin.Current); + int num = 0; + for (int index = 0; (long) index < (long) this.tplHeader.NumOfTextures; ++index) + { + if (this.tplTextureHeaders[index].TextureFormat == 8U || this.tplTextureHeaders[index].TextureFormat == 9U || this.tplTextureHeaders[index].TextureFormat == 10U) + ++num; + } + int position2 = (int) writeStream.Position; + writeStream.Seek((long) (num * 12), SeekOrigin.Current); + for (int index = 0; (long) index < (long) this.tplHeader.NumOfTextures; ++index) + { + if (this.tplTextureHeaders[index].TextureFormat == 8U || this.tplTextureHeaders[index].TextureFormat == 9U || this.tplTextureHeaders[index].TextureFormat == 10U) + { + this.fireDebug(" Writing Palette of Texture #{1}... (Offset: 0x{0})", (object) writeStream.Position, (object) (index + 1)); + writeStream.Seek(Shared.AddPadding(writeStream.Position, 32), SeekOrigin.Begin); + this.tplPaletteHeaders[index].PaletteDataOffset = (uint) writeStream.Position; + writeStream.Write(this.paletteData[index], 0, this.paletteData[index].Length); + } + } + int position3 = (int) writeStream.Position; + writeStream.Seek((long) (this.tplHeader.NumOfTextures * 36U), SeekOrigin.Current); + for (int index = 0; (long) index < (long) this.tplHeader.NumOfTextures; ++index) + { + this.fireDebug(" Writing Texture #{1} of {2}... (Offset: 0x{0})", (object) writeStream.Position, (object) (index + 1), (object) this.tplHeader.NumOfTextures); + writeStream.Seek((long) Shared.AddPadding((int) writeStream.Position, 32), SeekOrigin.Begin); + this.tplTextureHeaders[index].TextureDataOffset = (uint) writeStream.Position; + writeStream.Write(this.textureData[index], 0, this.textureData[index].Length); + } + while (writeStream.Position % 32L != 0L) + writeStream.WriteByte((byte) 0); + writeStream.Seek((long) position2, SeekOrigin.Begin); + for (int index = 0; (long) index < (long) this.tplHeader.NumOfTextures; ++index) + { + if (this.tplTextureHeaders[index].TextureFormat == 8U || this.tplTextureHeaders[index].TextureFormat == 9U || this.tplTextureHeaders[index].TextureFormat == 10U) + { + this.fireDebug(" Writing Palette Header of Texture #{1}... (Offset: 0x{0})", (object) writeStream.Position, (object) (index + 1)); + this.tplTextureEntries[index].PaletteHeaderOffset = (uint) writeStream.Position; + this.tplPaletteHeaders[index].Write(writeStream); + } + } + writeStream.Seek((long) position3, SeekOrigin.Begin); + for (int index = 0; (long) index < (long) this.tplHeader.NumOfTextures; ++index) + { + this.fireDebug(" Writing Texture Header #{1} of {2}... (Offset: 0x{0})", (object) writeStream.Position, (object) (index + 1), (object) this.tplHeader.NumOfTextures); + this.tplTextureEntries[index].TextureHeaderOffset = (uint) writeStream.Position; + this.tplTextureHeaders[index].Write(writeStream); + } + writeStream.Seek((long) position1, SeekOrigin.Begin); + for (int index = 0; (long) index < (long) this.tplHeader.NumOfTextures; ++index) + { + this.fireDebug(" Writing Texture Entry #{1} of {2}... (Offset: 0x{0})", (object) writeStream.Position, (object) (index + 1), (object) this.tplHeader.NumOfTextures); + this.tplTextureEntries[index].Write(writeStream); + } + this.fireDebug("Writing TPL Finished..."); + } + + private void parseTpl(Stream tplFile) + { + this.fireDebug("Parsing TPL..."); + this.tplHeader = new TPL_Header(); + this.tplTextureEntries = new List(); + this.tplTextureHeaders = new List(); + this.tplPaletteHeaders = new List(); + this.textureData = new List(); + this.paletteData = new List(); + tplFile.Seek(0L, SeekOrigin.Begin); + byte[] buffer1 = new byte[4]; + this.fireDebug(" Reading TPL Header: Magic... (Offset: 0x{0})", (object) tplFile.Position); + tplFile.Read(buffer1, 0, 4); + if ((int) Shared.Swap(BitConverter.ToUInt32(buffer1, 0)) != (int) this.tplHeader.TplMagic) + { + this.fireDebug(" -> Invalid Magic: 0x{0}", (object) Shared.Swap(BitConverter.ToUInt32(buffer1, 0))); + throw new Exception("TPL Header: Invalid Magic!"); + } + this.fireDebug(" Reading TPL Header: NumOfTextures... (Offset: 0x{0})", (object) tplFile.Position); + tplFile.Read(buffer1, 0, 4); + this.tplHeader.NumOfTextures = Shared.Swap(BitConverter.ToUInt32(buffer1, 0)); + this.fireDebug(" Reading TPL Header: Headersize... (Offset: 0x{0})", (object) tplFile.Position); + tplFile.Read(buffer1, 0, 4); + if ((int) Shared.Swap(BitConverter.ToUInt32(buffer1, 0)) != (int) this.tplHeader.HeaderSize) + { + this.fireDebug(" -> Invalid Headersize: 0x{0}", (object) Shared.Swap(BitConverter.ToUInt32(buffer1, 0))); + throw new Exception("TPL Header: Invalid Headersize!"); + } + for (int index = 0; (long) index < (long) this.tplHeader.NumOfTextures; ++index) + { + this.fireDebug(" Reading Texture Entry #{1} of {2}... (Offset: 0x{0})", (object) tplFile.Position, (object) (index + 1), (object) this.tplHeader.NumOfTextures); + TPL_TextureEntry tplTextureEntry = new TPL_TextureEntry(); + tplFile.Read(buffer1, 0, 4); + tplTextureEntry.TextureHeaderOffset = Shared.Swap(BitConverter.ToUInt32(buffer1, 0)); + tplFile.Read(buffer1, 0, 4); + tplTextureEntry.PaletteHeaderOffset = Shared.Swap(BitConverter.ToUInt32(buffer1, 0)); + this.tplTextureEntries.Add(tplTextureEntry); + } + for (int index = 0; (long) index < (long) this.tplHeader.NumOfTextures; ++index) + { + this.fireDebug(" Reading Texture Header #{1} of {2}... (Offset: 0x{0})", (object) tplFile.Position, (object) (index + 1), (object) this.tplHeader.NumOfTextures); + TPL_TextureHeader tplTextureHeader = new TPL_TextureHeader(); + TPL_PaletteHeader tplPaletteHeader = new TPL_PaletteHeader(); + tplFile.Seek((long) this.tplTextureEntries[index].TextureHeaderOffset, SeekOrigin.Begin); + tplFile.Read(buffer1, 0, 4); + tplTextureHeader.TextureHeight = Shared.Swap(BitConverter.ToUInt16(buffer1, 0)); + tplTextureHeader.TextureWidth = Shared.Swap(BitConverter.ToUInt16(buffer1, 2)); + tplFile.Read(buffer1, 0, 4); + tplTextureHeader.TextureFormat = Shared.Swap(BitConverter.ToUInt32(buffer1, 0)); + tplFile.Read(buffer1, 0, 4); + tplTextureHeader.TextureDataOffset = Shared.Swap(BitConverter.ToUInt32(buffer1, 0)); + tplFile.Read(buffer1, 0, 4); + tplTextureHeader.WrapS = Shared.Swap(BitConverter.ToUInt32(buffer1, 0)); + tplFile.Read(buffer1, 0, 4); + tplTextureHeader.WrapT = Shared.Swap(BitConverter.ToUInt32(buffer1, 0)); + tplFile.Read(buffer1, 0, 4); + tplTextureHeader.MinFilter = Shared.Swap(BitConverter.ToUInt32(buffer1, 0)); + tplFile.Read(buffer1, 0, 4); + tplTextureHeader.MagFilter = Shared.Swap(BitConverter.ToUInt32(buffer1, 0)); + tplFile.Read(buffer1, 0, 4); + tplTextureHeader.LodBias = Shared.Swap(BitConverter.ToUInt32(buffer1, 0)); + tplFile.Read(buffer1, 0, 4); + tplTextureHeader.EdgeLod = buffer1[0]; + tplTextureHeader.MinLod = buffer1[1]; + tplTextureHeader.MaxLod = buffer1[2]; + tplTextureHeader.Unpacked = buffer1[3]; + if (this.tplTextureEntries[index].PaletteHeaderOffset != 0U) + { + this.fireDebug(" Reading Palette Header #{1} of {2}... (Offset: 0x{0})", (object) tplFile.Position, (object) (index + 1), (object) this.tplHeader.NumOfTextures); + tplFile.Seek((long) this.tplTextureEntries[index].PaletteHeaderOffset, SeekOrigin.Begin); + tplFile.Read(buffer1, 0, 4); + tplPaletteHeader.NumberOfItems = Shared.Swap(BitConverter.ToUInt16(buffer1, 0)); + tplPaletteHeader.Unpacked = buffer1[2]; + tplPaletteHeader.Pad = buffer1[3]; + tplFile.Read(buffer1, 0, 4); + tplPaletteHeader.PaletteFormat = Shared.Swap(BitConverter.ToUInt32(buffer1, 0)); + tplFile.Read(buffer1, 0, 4); + tplPaletteHeader.PaletteDataOffset = Shared.Swap(BitConverter.ToUInt32(buffer1, 0)); + } + tplFile.Seek((long) tplTextureHeader.TextureDataOffset, SeekOrigin.Begin); + byte[] buffer2 = new byte[this.textureByteSize((TPL_TextureFormat) tplTextureHeader.TextureFormat, (int) tplTextureHeader.TextureWidth, (int) tplTextureHeader.TextureHeight)]; + byte[] buffer3 = new byte[(int) tplPaletteHeader.NumberOfItems * 2]; + this.fireDebug(" Reading Texture #{1} of {2}... (Offset: 0x{0})", (object) tplFile.Position, (object) (index + 1), (object) this.tplHeader.NumOfTextures); + tplFile.Read(buffer2, 0, buffer2.Length); + if (this.tplTextureEntries[index].PaletteHeaderOffset != 0U) + { + this.fireDebug(" Reading Palette #{1} of {2}... (Offset: 0x{0})", (object) tplFile.Position, (object) (index + 1), (object) this.tplHeader.NumOfTextures); + tplFile.Seek((long) tplPaletteHeader.PaletteDataOffset, SeekOrigin.Begin); + tplFile.Read(buffer3, 0, buffer3.Length); + } + else + buffer3 = new byte[0]; + this.tplTextureHeaders.Add(tplTextureHeader); + this.tplPaletteHeaders.Add(tplPaletteHeader); + this.textureData.Add(buffer2); + this.paletteData.Add(buffer3); + } + } + + private int textureByteSize(TPL_TextureFormat tplFormat, int width, int height) + { + switch (tplFormat) + { + case TPL_TextureFormat.I4: + return Shared.AddPadding(width, 8) * Shared.AddPadding(height, 8) / 2; + case TPL_TextureFormat.I8: + case TPL_TextureFormat.IA4: + return Shared.AddPadding(width, 8) * Shared.AddPadding(height, 4); + case TPL_TextureFormat.IA8: + case TPL_TextureFormat.RGB565: + case TPL_TextureFormat.RGB5A3: + return Shared.AddPadding(width, 4) * Shared.AddPadding(height, 4) * 2; + case TPL_TextureFormat.RGBA8: + return Shared.AddPadding(width, 4) * Shared.AddPadding(height, 4) * 4; + case TPL_TextureFormat.CI4: + return Shared.AddPadding(width, 8) * Shared.AddPadding(height, 8) / 2; + case TPL_TextureFormat.CI8: + return Shared.AddPadding(width, 8) * Shared.AddPadding(height, 4); + case TPL_TextureFormat.CI14X2: + return Shared.AddPadding(width, 4) * Shared.AddPadding(height, 4) * 2; + case TPL_TextureFormat.CMP: + return width * height; + default: + throw new FormatException("Unsupported Texture Format!"); + } + } + + private void createFromImages( + Image[] images, + TPL_TextureFormat[] tplFormats, + TPL_PaletteFormat[] paletteFormats) + { + this.tplHeader = new TPL_Header(); + this.tplTextureEntries = new List(); + this.tplTextureHeaders = new List(); + this.tplPaletteHeaders = new List(); + this.textureData = new List(); + this.paletteData = new List(); + this.tplHeader.NumOfTextures = (uint) images.Length; + for (int index = 0; index < images.Length; ++index) + { + Image image = images[index]; + TPL_TextureEntry tplTextureEntry = new TPL_TextureEntry(); + TPL_TextureHeader tplTextureHeader = new TPL_TextureHeader(); + TPL_PaletteHeader tplPaletteHeader = new TPL_PaletteHeader(); + byte[] numArray1 = this.imageToTpl(image, tplFormats[index]); + byte[] numArray2 = new byte[0]; + tplTextureHeader.TextureHeight = (ushort) image.Height; + tplTextureHeader.TextureWidth = (ushort) image.Width; + tplTextureHeader.TextureFormat = (uint) tplFormats[index]; + if (tplFormats[index] == TPL_TextureFormat.CI4 || tplFormats[index] == TPL_TextureFormat.CI8 || tplFormats[index] == TPL_TextureFormat.CI14X2) + { + ColorIndexConverter colorIndexConverter = new ColorIndexConverter(this.imageToRgba(image), image.Width, image.Height, tplFormats[index], paletteFormats[index]); + numArray1 = colorIndexConverter.Data; + numArray2 = colorIndexConverter.Palette; + tplPaletteHeader.NumberOfItems = (ushort) (numArray2.Length / 2); + tplPaletteHeader.PaletteFormat = (uint) paletteFormats[index]; + } + this.tplTextureEntries.Add(tplTextureEntry); + this.tplTextureHeaders.Add(tplTextureHeader); + this.tplPaletteHeaders.Add(tplPaletteHeader); + this.textureData.Add(numArray1); + this.paletteData.Add(numArray2); + } + } + + private byte[] imageToTpl(Image img, TPL_TextureFormat tplFormat) + { + switch (tplFormat) + { + case TPL_TextureFormat.I4: + return this.toI4((Bitmap) img); + case TPL_TextureFormat.I8: + return this.toI8((Bitmap) img); + case TPL_TextureFormat.IA4: + return this.toIA4((Bitmap) img); + case TPL_TextureFormat.IA8: + return this.toIA8((Bitmap) img); + case TPL_TextureFormat.RGB565: + return this.toRGB565((Bitmap) img); + case TPL_TextureFormat.RGB5A3: + return this.toRGB5A3((Bitmap) img); + case TPL_TextureFormat.RGBA8: + return this.toRGBA8((Bitmap) img); + case TPL_TextureFormat.CI4: + case TPL_TextureFormat.CI8: + case TPL_TextureFormat.CI14X2: + return new byte[0]; + default: + throw new FormatException("Format not supported!\nCurrently, images can only be converted to the following formats:\nI4, I8, IA4, IA8, RGB565, RGB5A3, RGBA8, CI4, CI8 , CI14X2."); + } + } + + private uint[] imageToRgba(Image img) + { + Bitmap bitmap = (Bitmap) img; + BitmapData bitmapdata = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); + byte[] numArray = new byte[bitmapdata.Height * Math.Abs(bitmapdata.Stride)]; + Marshal.Copy(bitmapdata.Scan0, numArray, 0, numArray.Length); + bitmap.UnlockBits(bitmapdata); + return Shared.ByteArrayToUIntArray(numArray); + } + + private Bitmap rgbaToImage(byte[] data, int width, int height) + { + if (width == 0) + width = 1; + if (height == 0) + height = 1; + Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb); + try + { + BitmapData bitmapdata = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.WriteOnly, bitmap.PixelFormat); + Marshal.Copy(data, 0, bitmapdata.Scan0, data.Length); + bitmap.UnlockBits(bitmapdata); + return bitmap; + } + catch + { + bitmap.Dispose(); + throw; + } + } + + private uint[] paletteToRgba(int index) + { + TPL_PaletteFormat paletteFormat = (TPL_PaletteFormat) this.tplPaletteHeaders[index].PaletteFormat; + int numberOfItems = (int) this.tplPaletteHeaders[index].NumberOfItems; + uint[] numArray = new uint[numberOfItems]; + for (int index1 = 0; index1 < numberOfItems; ++index1) + { + if (index1 < numberOfItems) + { + ushort uint16 = BitConverter.ToUInt16(new byte[2] + { + this.paletteData[index][index1 * 2 + 1], + this.paletteData[index][index1 * 2] + }, 0); + int num1; + int num2; + int num3; + int num4; + switch (paletteFormat) + { + case TPL_PaletteFormat.IA8: + num1 = (int) uint16 & (int) byte.MaxValue; + num2 = num1; + num3 = num1; + num4 = (int) uint16 >> 8; + break; + case TPL_PaletteFormat.RGB565: + num2 = ((int) uint16 >> 11 & 31) << 3 & (int) byte.MaxValue; + num3 = ((int) uint16 >> 5 & 63) << 2 & (int) byte.MaxValue; + num1 = ((int) uint16 & 31) << 3 & (int) byte.MaxValue; + num4 = (int) byte.MaxValue; + break; + default: + if (((int) uint16 & 32768) != 0) + { + num4 = (int) byte.MaxValue; + num2 = ((int) uint16 >> 10 & 31) * (int) byte.MaxValue / 31; + num3 = ((int) uint16 >> 5 & 31) * (int) byte.MaxValue / 31; + num1 = ((int) uint16 & 31) * (int) byte.MaxValue / 31; + break; + } + num4 = ((int) uint16 >> 12 & 7) * (int) byte.MaxValue / 7; + num2 = ((int) uint16 >> 8 & 15) * (int) byte.MaxValue / 15; + num3 = ((int) uint16 >> 4 & 15) * (int) byte.MaxValue / 15; + num1 = ((int) uint16 & 15) * (int) byte.MaxValue / 15; + break; + } + numArray[index1] = (uint) (num1 | num3 << 8 | num2 << 16 | num4 << 24); + } + } + return numArray; + } + + private int avg(int w0, int w1, int c0, int c1) + { + int num1 = c0 >> 11; + int num2 = c1 >> 11; + int num3 = (w0 * num1 + w1 * num2) / (w0 + w1) << 11 & (int) ushort.MaxValue; + int num4 = c0 >> 5 & 63; + int num5 = c1 >> 5 & 63; + int num6 = (w0 * num4 + w1 * num5) / (w0 + w1) << 5 & (int) ushort.MaxValue; + int num7 = num3 | num6; + int num8 = c0 & 31; + int num9 = c1 & 31; + int num10 = (w0 * num8 + w1 * num9) / (w0 + w1); + return num7 | num10; + } + + private byte[] fromRGBA8(byte[] tpl, int width, int height) + { + uint[] array = new uint[width * height]; + int num1 = 0; + for (int index1 = 0; index1 < height; index1 += 4) + { + for (int index2 = 0; index2 < width; index2 += 4) + { + for (int index3 = 0; index3 < 2; ++index3) + { + for (int index4 = index1; index4 < index1 + 4; ++index4) + { + for (int index5 = index2; index5 < index2 + 4; ++index5) + { + ushort num2 = Shared.Swap(BitConverter.ToUInt16(tpl, num1++ * 2)); + if (index5 < width && index4 < height) + { + if (index3 == 0) + { + int num3 = (int) num2 >> 8 & (int) byte.MaxValue; + int num4 = (int) num2 & (int) byte.MaxValue; + array[index5 + index4 * width] |= (uint) (num4 << 16 | num3 << 24); + } + else + { + int num3 = (int) num2 >> 8 & (int) byte.MaxValue; + int num4 = (int) num2 & (int) byte.MaxValue; + array[index5 + index4 * width] |= (uint) (num3 << 8 | num4); + } + } + } + } + } + } + } + return Shared.UIntArrayToByteArray(array); + } + + private byte[] toRGBA8(Bitmap img) + { + uint[] rgba = this.imageToRgba((Image) img); + int width = img.Width; + int height = img.Height; + int index1 = 0; + int num1 = 0; + byte[] numArray1 = new byte[Shared.AddPadding(width, 4) * Shared.AddPadding(height, 4) * 4]; + uint[] numArray2 = new uint[32]; + uint[] numArray3 = new uint[32]; + uint[] numArray4 = new uint[32]; + uint[] numArray5 = new uint[32]; + for (int index2 = 0; index2 < height; index2 += 4) + { + for (int index3 = 0; index3 < width; index3 += 4) + { + for (int index4 = index2; index4 < index2 + 4; ++index4) + { + for (int index5 = index3; index5 < index3 + 4; ++index5) + { + uint num2 = index4 >= height || index5 >= width ? 0U : rgba[index5 + index4 * width]; + numArray2[index1] = num2 >> 16 & (uint) byte.MaxValue; + numArray3[index1] = num2 >> 8 & (uint) byte.MaxValue; + numArray4[index1] = num2 & (uint) byte.MaxValue; + numArray5[index1] = num2 >> 24 & (uint) byte.MaxValue; + ++index1; + } + } + if (index1 == 16) + { + for (int index4 = 0; index4 < 16; ++index4) + { + byte[] numArray6 = numArray1; + int index5 = num1; + int num2 = index5 + 1; + int num3 = (int) (byte) numArray5[index4]; + numArray6[index5] = (byte) num3; + byte[] numArray7 = numArray1; + int index6 = num2; + num1 = index6 + 1; + int num4 = (int) (byte) numArray2[index4]; + numArray7[index6] = (byte) num4; + } + for (int index4 = 0; index4 < 16; ++index4) + { + byte[] numArray6 = numArray1; + int index5 = num1; + int num2 = index5 + 1; + int num3 = (int) (byte) numArray3[index4]; + numArray6[index5] = (byte) num3; + byte[] numArray7 = numArray1; + int index6 = num2; + num1 = index6 + 1; + int num4 = (int) (byte) numArray4[index4]; + numArray7[index6] = (byte) num4; + } + index1 = 0; + } + } + } + return numArray1; + } + + private byte[] fromRGB5A3(byte[] tpl, int width, int height) + { + uint[] array = new uint[width * height]; + int num1 = 0; + for (int index1 = 0; index1 < height; index1 += 4) + { + for (int index2 = 0; index2 < width; index2 += 4) + { + for (int index3 = index1; index3 < index1 + 4; ++index3) + { + for (int index4 = index2; index4 < index2 + 4; ++index4) + { + ushort num2 = Shared.Swap(BitConverter.ToUInt16(tpl, num1++ * 2)); + if (index3 < height && index4 < width) + { + int num3; + int num4; + int num5; + int num6; + if (((int) num2 & 32768) != 0) + { + num3 = ((int) num2 >> 10 & 31) * (int) byte.MaxValue / 31; + num4 = ((int) num2 >> 5 & 31) * (int) byte.MaxValue / 31; + num5 = ((int) num2 & 31) * (int) byte.MaxValue / 31; + num6 = (int) byte.MaxValue; + } + else + { + num6 = ((int) num2 >> 12 & 7) * (int) byte.MaxValue / 7; + num3 = ((int) num2 >> 8 & 15) * (int) byte.MaxValue / 15; + num4 = ((int) num2 >> 4 & 15) * (int) byte.MaxValue / 15; + num5 = ((int) num2 & 15) * (int) byte.MaxValue / 15; + } + array[index3 * width + index4] = (uint) (num5 | num4 << 8 | num3 << 16 | num6 << 24); + } + } + } + } + } + return Shared.UIntArrayToByteArray(array); + } + + private byte[] toRGB5A3(Bitmap img) + { + uint[] rgba = this.imageToRgba((Image) img); + int width = img.Width; + int height = img.Height; + int num1 = -1; + byte[] numArray = new byte[Shared.AddPadding(width, 4) * Shared.AddPadding(height, 4) * 2]; + for (int index1 = 0; index1 < height; index1 += 4) + { + for (int index2 = 0; index2 < width; index2 += 4) + { + for (int index3 = index1; index3 < index1 + 4; ++index3) + { + for (int index4 = index2; index4 < index2 + 4; ++index4) + { + int num2; + if (index3 >= height || index4 >= width) + { + num2 = 0; + } + else + { + int num3 = (int) rgba[index4 + index3 * width]; + int num4 = 0; + int num5 = num3 >> 16 & (int) byte.MaxValue; + int num6 = num3 >> 8 & (int) byte.MaxValue; + int num7 = num3 & (int) byte.MaxValue; + int num8 = num3 >> 24 & (int) byte.MaxValue; + if (num8 <= 218) + { + int num9 = num4 & -32769; + int num10 = num5 * 15 / (int) byte.MaxValue & 15; + int num11 = num6 * 15 / (int) byte.MaxValue & 15; + int num12 = num7 * 15 / (int) byte.MaxValue & 15; + int num13 = num8 * 7 / (int) byte.MaxValue & 7; + num2 = num9 | (num13 << 12 | num10 << 8 | num11 << 4 | num12); + } + else + num2 = num4 | 32768 | ((num5 * 31 / (int) byte.MaxValue & 31) << 10 | (num6 * 31 / (int) byte.MaxValue & 31) << 5 | num7 * 31 / (int) byte.MaxValue & 31); + } + int num14; + numArray[num14 = num1 + 1] = (byte) (num2 >> 8); + numArray[num1 = num14 + 1] = (byte) (num2 & (int) byte.MaxValue); + } + } + } + } + return numArray; + } + + private byte[] fromRGB565(byte[] tpl, int width, int height) + { + uint[] array = new uint[width * height]; + int num1 = 0; + for (int index1 = 0; index1 < height; index1 += 4) + { + for (int index2 = 0; index2 < width; index2 += 4) + { + for (int index3 = index1; index3 < index1 + 4; ++index3) + { + for (int index4 = index2; index4 < index2 + 4; ++index4) + { + ushort num2 = Shared.Swap(BitConverter.ToUInt16(tpl, num1++ * 2)); + if (index3 < height && index4 < width) + { + int num3 = ((int) num2 >> 11 & 31) << 3 & (int) byte.MaxValue; + int num4 = ((int) num2 >> 5 & 63) << 2 & (int) byte.MaxValue; + int num5 = ((int) num2 & 31) << 3 & (int) byte.MaxValue; + array[index3 * width + index4] = (uint) (num5 | num4 << 8 | num3 << 16 | -16777216); + } + } + } + } + } + return Shared.UIntArrayToByteArray(array); + } + + private byte[] toRGB565(Bitmap img) + { + uint[] rgba = this.imageToRgba((Image) img); + int width = img.Width; + int height = img.Height; + int num1 = -1; + byte[] numArray = new byte[Shared.AddPadding(width, 4) * Shared.AddPadding(height, 4) * 2]; + for (int index1 = 0; index1 < height; index1 += 4) + { + for (int index2 = 0; index2 < width; index2 += 4) + { + for (int index3 = index1; index3 < index1 + 4; ++index3) + { + for (int index4 = index2; index4 < index2 + 4; ++index4) + { + ushort num2; + if (index3 >= height || index4 >= width) + { + num2 = (ushort) 0; + } + else + { + int num3 = (int) rgba[index4 + index3 * width]; + num2 = (ushort) ((uint) ((int) (((uint) num3 >> 16 & (uint) byte.MaxValue) >> 3) << 11 | (int) (((uint) num3 >> 8 & (uint) byte.MaxValue) >> 2) << 5) | (uint) (num3 & (int) byte.MaxValue) >> 3); + } + int num4; + numArray[num4 = num1 + 1] = (byte) ((uint) num2 >> 8); + numArray[num1 = num4 + 1] = (byte) ((uint) num2 & (uint) byte.MaxValue); + } + } + } + } + return numArray; + } + + private byte[] fromI4(byte[] tpl, int width, int height) + { + uint[] array = new uint[width * height]; + int num1 = 0; + for (int index1 = 0; index1 < height; index1 += 8) + { + for (int index2 = 0; index2 < width; index2 += 8) + { + for (int index3 = index1; index3 < index1 + 8; ++index3) + { + for (int index4 = index2; index4 < index2 + 8; index4 += 2) + { + int num2 = (int) tpl[num1++]; + if (index3 < height && index4 < width) + { + int num3 = (num2 >> 4) * (int) byte.MaxValue / 15; + array[index3 * width + index4] = (uint) (num3 | num3 << 8 | num3 << 16 | -16777216); + int num4 = (num2 & 15) * (int) byte.MaxValue / 15; + if (index3 * width + index4 + 1 < array.Length) + array[index3 * width + index4 + 1] = (uint) (num4 | num4 << 8 | num4 << 16 | -16777216); + } + } + } + } + } + return Shared.UIntArrayToByteArray(array); + } + + private byte[] toI4(Bitmap img) + { + uint[] rgba = this.imageToRgba((Image) img); + int width = img.Width; + int height = img.Height; + int num1 = 0; + byte[] numArray = new byte[Shared.AddPadding(width, 8) * Shared.AddPadding(height, 8) / 2]; + for (int index1 = 0; index1 < height; index1 += 8) + { + for (int index2 = 0; index2 < width; index2 += 8) + { + for (int index3 = index1; index3 < index1 + 8; ++index3) + { + for (int index4 = index2; index4 < index2 + 8; index4 += 2) + { + byte num2; + if (index4 >= width || index3 >= height) + { + num2 = (byte) 0; + } + else + { + int num3 = (int) rgba[index4 + index3 * width]; + int num4 = (int) (((uint) (num3 & (int) byte.MaxValue) + ((uint) num3 >> 8 & (uint) byte.MaxValue) + ((uint) num3 >> 16 & (uint) byte.MaxValue)) / 3U) & (int) byte.MaxValue; + int num5 = index4 + index3 * width + 1 < rgba.Length ? (int) rgba[index4 + index3 * width + 1] : 0; + uint num6 = ((uint) (num5 & (int) byte.MaxValue) + ((uint) num5 >> 8 & (uint) byte.MaxValue) + ((uint) num5 >> 16 & (uint) byte.MaxValue)) / 3U & (uint) byte.MaxValue; + num2 = (byte) ((int) ((uint) (num4 * 15) / (uint) byte.MaxValue) << 4 | (int) (num6 * 15U / (uint) byte.MaxValue) & 15); + } + numArray[num1++] = num2; + } + } + } + } + return numArray; + } + + private byte[] fromI8(byte[] tpl, int width, int height) + { + uint[] array = new uint[width * height]; + int num1 = 0; + for (int index1 = 0; index1 < height; index1 += 4) + { + for (int index2 = 0; index2 < width; index2 += 8) + { + for (int index3 = index1; index3 < index1 + 4; ++index3) + { + for (int index4 = index2; index4 < index2 + 8; ++index4) + { + int num2 = (int) tpl[num1++]; + if (index3 < height && index4 < width) + array[index3 * width + index4] = (uint) (num2 | num2 << 8 | num2 << 16 | -16777216); + } + } + } + } + return Shared.UIntArrayToByteArray(array); + } + + private byte[] toI8(Bitmap img) + { + uint[] rgba = this.imageToRgba((Image) img); + int width = img.Width; + int height = img.Height; + int num1 = 0; + byte[] numArray = new byte[Shared.AddPadding(width, 8) * Shared.AddPadding(height, 4)]; + for (int index1 = 0; index1 < height; index1 += 4) + { + for (int index2 = 0; index2 < width; index2 += 8) + { + for (int index3 = index1; index3 < index1 + 4; ++index3) + { + for (int index4 = index2; index4 < index2 + 8; ++index4) + { + byte num2; + if (index4 >= width || index3 >= height) + { + num2 = (byte) 0; + } + else + { + int num3 = (int) rgba[index4 + index3 * width]; + num2 = (byte) (((uint) (num3 & (int) byte.MaxValue) + ((uint) num3 >> 8 & (uint) byte.MaxValue) + ((uint) num3 >> 16 & (uint) byte.MaxValue)) / 3U & (uint) byte.MaxValue); + } + numArray[num1++] = num2; + } + } + } + } + return numArray; + } + + private byte[] fromIA4(byte[] tpl, int width, int height) + { + uint[] array = new uint[width * height]; + int num1 = 0; + for (int index1 = 0; index1 < height; index1 += 4) + { + for (int index2 = 0; index2 < width; index2 += 8) + { + for (int index3 = index1; index3 < index1 + 4; ++index3) + { + for (int index4 = index2; index4 < index2 + 8; ++index4) + { + int num2 = (int) tpl[num1++]; + if (index3 < height && index4 < width) + { + int num3 = (num2 & 15) * (int) byte.MaxValue / 15 & (int) byte.MaxValue; + int num4 = (num2 >> 4) * (int) byte.MaxValue / 15 & (int) byte.MaxValue; + array[index3 * width + index4] = (uint) (num3 | num3 << 8 | num3 << 16 | num4 << 24); + } + } + } + } + } + return Shared.UIntArrayToByteArray(array); + } + + private byte[] toIA4(Bitmap img) + { + uint[] rgba = this.imageToRgba((Image) img); + int width = img.Width; + int height = img.Height; + int num1 = 0; + byte[] numArray = new byte[Shared.AddPadding(width, 8) * Shared.AddPadding(height, 4)]; + for (int index1 = 0; index1 < height; index1 += 4) + { + for (int index2 = 0; index2 < width; index2 += 8) + { + for (int index3 = index1; index3 < index1 + 4; ++index3) + { + for (int index4 = index2; index4 < index2 + 8; ++index4) + { + byte num2; + if (index4 >= width || index3 >= height) + { + num2 = (byte) 0; + } + else + { + uint num3 = rgba[index4 + index3 * width]; + num2 = (byte) ((int) ((uint) (((int) (((num3 & (uint) byte.MaxValue) + (num3 >> 8 & (uint) byte.MaxValue) + (num3 >> 16 & (uint) byte.MaxValue)) / 3U) & (int) byte.MaxValue) * 15) / (uint) byte.MaxValue) & 15 | (int) ((num3 >> 24 & (uint) byte.MaxValue) * 15U / (uint) byte.MaxValue) << 4); + } + numArray[num1++] = num2; + } + } + } + } + return numArray; + } + + private byte[] fromIA8(byte[] tpl, int width, int height) + { + uint[] array = new uint[width * height]; + int num1 = 0; + for (int index1 = 0; index1 < height; index1 += 4) + { + for (int index2 = 0; index2 < width; index2 += 4) + { + for (int index3 = index1; index3 < index1 + 4; ++index3) + { + for (int index4 = index2; index4 < index2 + 4; ++index4) + { + int num2 = (int) Shared.Swap(BitConverter.ToUInt16(tpl, num1++ * 2)); + if (index3 < height && index4 < width) + { + uint num3 = (uint) (num2 >> 8); + uint num4 = (uint) (num2 & (int) byte.MaxValue); + array[index3 * width + index4] = (uint) ((int) num4 | (int) num4 << 8 | (int) num4 << 16 | (int) num3 << 24); + } + } + } + } + } + return Shared.UIntArrayToByteArray(array); + } + + private byte[] toIA8(Bitmap img) + { + uint[] rgba = this.imageToRgba((Image) img); + int width = img.Width; + int height = img.Height; + int num1 = 0; + byte[] numArray1 = new byte[Shared.AddPadding(width, 4) * Shared.AddPadding(height, 4) * 2]; + for (int index1 = 0; index1 < height; index1 += 4) + { + for (int index2 = 0; index2 < width; index2 += 4) + { + for (int index3 = index1; index3 < index1 + 4; ++index3) + { + for (int index4 = index2; index4 < index2 + 4; ++index4) + { + ushort num2; + if (index4 >= width || index3 >= height) + { + num2 = (ushort) 0; + } + else + { + int num3 = (int) rgba[index4 + index3 * width]; + num2 = (ushort) ((uint) (((int) ((uint) num3 >> 24) & (int) byte.MaxValue) << 8) | ((uint) (num3 & (int) byte.MaxValue) + ((uint) num3 >> 8 & (uint) byte.MaxValue) + ((uint) num3 >> 16 & (uint) byte.MaxValue)) / 3U & (uint) byte.MaxValue); + } + Array.Reverse((Array) BitConverter.GetBytes(num2)); + byte[] numArray2 = numArray1; + int index5 = num1; + int num4 = index5 + 1; + int num5 = (int) (byte) ((uint) num2 >> 8); + numArray2[index5] = (byte) num5; + byte[] numArray3 = numArray1; + int index6 = num4; + num1 = index6 + 1; + int num6 = (int) (byte) ((uint) num2 & (uint) byte.MaxValue); + numArray3[index6] = (byte) num6; + } + } + } + } + return numArray1; + } + + private byte[] fromCI4(byte[] tpl, uint[] paletteData, int width, int height) + { + uint[] array = new uint[width * height]; + int num1 = 0; + for (int index1 = 0; index1 < height; index1 += 8) + { + for (int index2 = 0; index2 < width; index2 += 8) + { + for (int index3 = index1; index3 < index1 + 8; ++index3) + { + for (int index4 = index2; index4 < index2 + 8; index4 += 2) + { + byte num2 = tpl[num1++]; + if (index3 < height && index4 < width) + { + array[index3 * width + index4] = paletteData[(int) num2 >> 4]; + if (index3 * width + index4 + 1 < array.Length) + array[index3 * width + index4 + 1] = paletteData[(int) num2 & 15]; + } + } + } + } + } + return Shared.UIntArrayToByteArray(array); + } + + private byte[] fromCI8(byte[] tpl, uint[] paletteData, int width, int height) + { + uint[] array = new uint[width * height]; + int num1 = 0; + for (int index1 = 0; index1 < height; index1 += 4) + { + for (int index2 = 0; index2 < width; index2 += 8) + { + for (int index3 = index1; index3 < index1 + 4; ++index3) + { + for (int index4 = index2; index4 < index2 + 8; ++index4) + { + ushort num2 = (ushort) tpl[num1++]; + if (index3 < height && index4 < width) + array[index3 * width + index4] = paletteData[(int) num2]; + } + } + } + } + return Shared.UIntArrayToByteArray(array); + } + + private byte[] fromCI14X2(byte[] tpl, uint[] paletteData, int width, int height) + { + uint[] array = new uint[width * height]; + int num1 = 0; + for (int index1 = 0; index1 < height; index1 += 4) + { + for (int index2 = 0; index2 < width; index2 += 4) + { + for (int index3 = index1; index3 < index1 + 4; ++index3) + { + for (int index4 = index2; index4 < index2 + 4; ++index4) + { + ushort num2 = Shared.Swap(BitConverter.ToUInt16(tpl, num1++ * 2)); + if (index3 < height && index4 < width) + array[index3 * width + index4] = paletteData[(int) num2 & 16383]; + } + } + } + } + return Shared.UIntArrayToByteArray(array); + } + + private byte[] fromCMP(byte[] tpl, int width, int height) + { + uint[] array = new uint[width * height]; + ushort[] numArray1 = new ushort[4]; + int[] numArray2 = new int[4]; + int index1 = 0; + for (int index2 = 0; index2 < height; ++index2) + { + for (int index3 = 0; index3 < width; ++index3) + { + int num1 = Shared.AddPadding(width, 8); + int num2 = index3 & 3; + int num3 = index3 >> 2 & 1; + int num4 = index3 >> 3; + int num5 = index2 & 3; + int num6 = index2 >> 2 & 1; + int num7 = index2 >> 3; + int startIndex = 8 * num3 + 16 * num6 + 32 * num4 + 4 * num1 * num7; + numArray1[0] = Shared.Swap(BitConverter.ToUInt16(tpl, startIndex)); + numArray1[1] = Shared.Swap(BitConverter.ToUInt16(tpl, startIndex + 2)); + if ((int) numArray1[0] > (int) numArray1[1]) + { + numArray1[2] = (ushort) this.avg(2, 1, (int) numArray1[0], (int) numArray1[1]); + numArray1[3] = (ushort) this.avg(1, 2, (int) numArray1[0], (int) numArray1[1]); + } + else + { + numArray1[2] = (ushort) this.avg(1, 1, (int) numArray1[0], (int) numArray1[1]); + numArray1[3] = (ushort) 0; + } + uint num8 = Shared.Swap(BitConverter.ToUInt32(tpl, startIndex + 4)); + int num9 = num2 + 4 * num5; + int num10 = (int) numArray1[(int) (num8 >> 30 - 2 * num9) & 3]; + numArray2[0] = num10 >> 8 & 248; + numArray2[1] = num10 >> 3 & 248; + numArray2[2] = num10 << 3 & 248; + numArray2[3] = (int) byte.MaxValue; + if (((int) (num8 >> 30 - 2 * num9) & 3) == 3 && (int) numArray1[0] <= (int) numArray1[1]) + numArray2[3] = 0; + array[index1] = (uint) (numArray2[0] << 16 | numArray2[1] << 8 | numArray2[2] | numArray2[3] << 24); + ++index1; + } + } + return Shared.UIntArrayToByteArray(array); + } + + 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))); + } + } +} diff --git a/TPL_Header.cs b/TPL_Header.cs new file mode 100644 index 0000000..8a857b6 --- /dev/null +++ b/TPL_Header.cs @@ -0,0 +1,35 @@ +// Decompiled with JetBrains decompiler +// Type: libWiiSharp.TPL_Header +// Assembly: libWiiSharp, Version=0.4.0.0, Culture=neutral, PublicKeyToken=null +// MVID: FBF36F3D-B5D6-481F-B5F5-1BD3C19E13B2 +// Assembly location: C:\Users\theso\Downloads\NCPatcher\pack\libWiiSharp.dll + +using System; +using System.IO; + +namespace libWiiSharp +{ + public class TPL_Header + { + private uint tplMagic = 2142000; + private uint numOfTextures; + private uint headerSize = 12; + + public uint TplMagic => this.tplMagic; + + public uint NumOfTextures + { + get => this.numOfTextures; + set => this.numOfTextures = value; + } + + public uint HeaderSize => this.headerSize; + + public void Write(Stream writeStream) + { + writeStream.Write(BitConverter.GetBytes(Shared.Swap(this.tplMagic)), 0, 4); + writeStream.Write(BitConverter.GetBytes(Shared.Swap(this.numOfTextures)), 0, 4); + writeStream.Write(BitConverter.GetBytes(Shared.Swap(this.headerSize)), 0, 4); + } + } +} diff --git a/TPL_PaletteFormat.cs b/TPL_PaletteFormat.cs new file mode 100644 index 0000000..ca1e4f7 --- /dev/null +++ b/TPL_PaletteFormat.cs @@ -0,0 +1,16 @@ +// Decompiled with JetBrains decompiler +// Type: libWiiSharp.TPL_PaletteFormat +// Assembly: libWiiSharp, Version=0.4.0.0, Culture=neutral, PublicKeyToken=null +// MVID: FBF36F3D-B5D6-481F-B5F5-1BD3C19E13B2 +// Assembly location: C:\Users\theso\Downloads\NCPatcher\pack\libWiiSharp.dll + +namespace libWiiSharp +{ + public enum TPL_PaletteFormat + { + IA8 = 0, + RGB565 = 1, + RGB5A3 = 2, + None = 255, // 0x000000FF + } +} diff --git a/TPL_PaletteHeader.cs b/TPL_PaletteHeader.cs new file mode 100644 index 0000000..dcadc7d --- /dev/null +++ b/TPL_PaletteHeader.cs @@ -0,0 +1,59 @@ +// Decompiled with JetBrains decompiler +// Type: libWiiSharp.TPL_PaletteHeader +// Assembly: libWiiSharp, Version=0.4.0.0, Culture=neutral, PublicKeyToken=null +// MVID: FBF36F3D-B5D6-481F-B5F5-1BD3C19E13B2 +// Assembly location: C:\Users\theso\Downloads\NCPatcher\pack\libWiiSharp.dll + +using System; +using System.IO; + +namespace libWiiSharp +{ + public class TPL_PaletteHeader + { + private ushort numberOfItems; + private byte unpacked; + private byte pad; + private uint paletteFormat = (uint) byte.MaxValue; + private uint paletteDataOffset; + + public ushort NumberOfItems + { + get => this.numberOfItems; + set => this.numberOfItems = value; + } + + public byte Unpacked + { + get => this.unpacked; + set => this.unpacked = value; + } + + public byte Pad + { + get => this.pad; + set => this.pad = value; + } + + public uint PaletteFormat + { + get => this.paletteFormat; + set => this.paletteFormat = value; + } + + public uint PaletteDataOffset + { + get => this.paletteDataOffset; + set => this.paletteDataOffset = value; + } + + public void Write(Stream writeStream) + { + writeStream.Write(BitConverter.GetBytes(Shared.Swap(this.numberOfItems)), 0, 2); + writeStream.WriteByte(this.unpacked); + writeStream.WriteByte(this.pad); + writeStream.Write(BitConverter.GetBytes(Shared.Swap(this.paletteFormat)), 0, 4); + writeStream.Write(BitConverter.GetBytes(Shared.Swap(this.paletteDataOffset)), 0, 4); + } + } +} diff --git a/TPL_TextureEntry.cs b/TPL_TextureEntry.cs new file mode 100644 index 0000000..2fbd77b --- /dev/null +++ b/TPL_TextureEntry.cs @@ -0,0 +1,35 @@ +// Decompiled with JetBrains decompiler +// Type: libWiiSharp.TPL_TextureEntry +// Assembly: libWiiSharp, Version=0.4.0.0, Culture=neutral, PublicKeyToken=null +// MVID: FBF36F3D-B5D6-481F-B5F5-1BD3C19E13B2 +// Assembly location: C:\Users\theso\Downloads\NCPatcher\pack\libWiiSharp.dll + +using System; +using System.IO; + +namespace libWiiSharp +{ + public class TPL_TextureEntry + { + private uint textureHeaderOffset; + private uint paletteHeaderOffset; + + public uint TextureHeaderOffset + { + get => this.textureHeaderOffset; + set => this.textureHeaderOffset = value; + } + + public uint PaletteHeaderOffset + { + get => this.paletteHeaderOffset; + set => this.paletteHeaderOffset = value; + } + + public void Write(Stream writeStream) + { + writeStream.Write(BitConverter.GetBytes(Shared.Swap(this.textureHeaderOffset)), 0, 4); + writeStream.Write(BitConverter.GetBytes(Shared.Swap(this.paletteHeaderOffset)), 0, 4); + } + } +} diff --git a/TPL_TextureFormat.cs b/TPL_TextureFormat.cs new file mode 100644 index 0000000..fc77391 --- /dev/null +++ b/TPL_TextureFormat.cs @@ -0,0 +1,23 @@ +// Decompiled with JetBrains decompiler +// Type: libWiiSharp.TPL_TextureFormat +// Assembly: libWiiSharp, Version=0.4.0.0, Culture=neutral, PublicKeyToken=null +// MVID: FBF36F3D-B5D6-481F-B5F5-1BD3C19E13B2 +// Assembly location: C:\Users\theso\Downloads\NCPatcher\pack\libWiiSharp.dll + +namespace libWiiSharp +{ + public enum TPL_TextureFormat + { + I4 = 0, + I8 = 1, + IA4 = 2, + IA8 = 3, + RGB565 = 4, + RGB5A3 = 5, + RGBA8 = 6, + CI4 = 8, + CI8 = 9, + CI14X2 = 10, // 0x0000000A + CMP = 14, // 0x0000000E + } +} diff --git a/TPL_TextureHeader.cs b/TPL_TextureHeader.cs new file mode 100644 index 0000000..3665e2e --- /dev/null +++ b/TPL_TextureHeader.cs @@ -0,0 +1,123 @@ +// Decompiled with JetBrains decompiler +// Type: libWiiSharp.TPL_TextureHeader +// Assembly: libWiiSharp, Version=0.4.0.0, Culture=neutral, PublicKeyToken=null +// MVID: FBF36F3D-B5D6-481F-B5F5-1BD3C19E13B2 +// Assembly location: C:\Users\theso\Downloads\NCPatcher\pack\libWiiSharp.dll + +using System; +using System.IO; + +namespace libWiiSharp +{ + public class TPL_TextureHeader + { + private ushort textureHeight; + private ushort textureWidth; + private uint textureFormat; + private uint textureDataOffset; + private uint wrapS; + private uint wrapT; + private uint minFilter = 1; + private uint magFilter = 1; + private uint lodBias; + private byte edgeLod; + private byte minLod; + private byte maxLod; + private byte unpacked; + + public ushort TextureHeight + { + get => this.textureHeight; + set => this.textureHeight = value; + } + + public ushort TextureWidth + { + get => this.textureWidth; + set => this.textureWidth = value; + } + + public uint TextureFormat + { + get => this.textureFormat; + set => this.textureFormat = value; + } + + public uint TextureDataOffset + { + get => this.textureDataOffset; + set => this.textureDataOffset = value; + } + + public uint WrapS + { + get => this.wrapS; + set => this.wrapS = value; + } + + public uint WrapT + { + get => this.wrapT; + set => this.wrapT = value; + } + + public uint MinFilter + { + get => this.minFilter; + set => this.minFilter = value; + } + + public uint MagFilter + { + get => this.magFilter; + set => this.magFilter = value; + } + + public uint LodBias + { + get => this.lodBias; + set => this.lodBias = value; + } + + public byte EdgeLod + { + get => this.edgeLod; + set => this.edgeLod = value; + } + + public byte MinLod + { + get => this.minLod; + set => this.minLod = value; + } + + public byte MaxLod + { + get => this.maxLod; + set => this.maxLod = value; + } + + public byte Unpacked + { + get => this.unpacked; + set => this.unpacked = value; + } + + public void Write(Stream writeStream) + { + writeStream.Write(BitConverter.GetBytes(Shared.Swap(this.textureHeight)), 0, 2); + writeStream.Write(BitConverter.GetBytes(Shared.Swap(this.textureWidth)), 0, 2); + writeStream.Write(BitConverter.GetBytes(Shared.Swap(this.textureFormat)), 0, 4); + writeStream.Write(BitConverter.GetBytes(Shared.Swap(this.textureDataOffset)), 0, 4); + writeStream.Write(BitConverter.GetBytes(Shared.Swap(this.wrapS)), 0, 4); + writeStream.Write(BitConverter.GetBytes(Shared.Swap(this.wrapT)), 0, 4); + writeStream.Write(BitConverter.GetBytes(Shared.Swap(this.minFilter)), 0, 4); + writeStream.Write(BitConverter.GetBytes(Shared.Swap(this.magFilter)), 0, 4); + writeStream.Write(BitConverter.GetBytes(Shared.Swap(this.lodBias)), 0, 4); + writeStream.WriteByte(this.edgeLod); + writeStream.WriteByte(this.minLod); + writeStream.WriteByte(this.maxLod); + writeStream.WriteByte(this.unpacked); + } + } +} diff --git a/Ticket.cs b/Ticket.cs new file mode 100644 index 0000000..b982e67 --- /dev/null +++ b/Ticket.cs @@ -0,0 +1,485 @@ +// Decompiled with JetBrains decompiler +// Type: libWiiSharp.Ticket +// Assembly: libWiiSharp, Version=0.4.0.0, Culture=neutral, PublicKeyToken=null +// MVID: FBF36F3D-B5D6-481F-B5F5-1BD3C19E13B2 +// Assembly location: C:\Users\theso\Downloads\NCPatcher\pack\libWiiSharp.dll + +using System; +using System.IO; +using System.Security.Cryptography; + +namespace libWiiSharp +{ + public class Ticket : IDisposable + { + private byte newKeyIndex; + private byte[] decryptedTitleKey = new byte[16]; + private bool fakeSign; + private bool titleKeyChanged; + private byte[] newEncryptedTitleKey = new byte[0]; + private bool reDecrypt; + private uint signatureExponent = 65537; + private byte[] signature = new byte[256]; + private byte[] padding = new byte[60]; + private byte[] issuer = new byte[64]; + private byte[] unknown = new byte[63]; + private byte[] encryptedTitleKey = new byte[16]; + private byte unknown2; + private ulong ticketId; + private uint consoleId; + private ulong titleId; + private ushort unknown3 = ushort.MaxValue; + private ushort numOfDlc; + private ulong unknown4; + private byte padding2; + private byte commonKeyIndex; + private byte[] unknown5 = new byte[48]; + private byte[] unknown6 = new byte[32]; + private ushort padding3; + private uint enableTimeLimit; + private uint timeLimit; + private byte[] padding4 = new byte[88]; + private bool isDisposed; + + public byte[] TitleKey + { + get => this.decryptedTitleKey; + set + { + this.decryptedTitleKey = value; + this.titleKeyChanged = true; + this.reDecrypt = false; + } + } + + public CommonKeyType CommonKeyIndex + { + get => (CommonKeyType) this.newKeyIndex; + set => this.newKeyIndex = (byte) value; + } + + public ulong TicketID + { + get => this.ticketId; + set => this.ticketId = value; + } + + public uint ConsoleID + { + get => this.consoleId; + set => this.consoleId = value; + } + + public ulong TitleID + { + get => this.titleId; + set + { + this.titleId = value; + if (!this.reDecrypt) + return; + this.reDecryptTitleKey(); + } + } + + public ushort NumOfDLC + { + get => this.numOfDlc; + set => this.numOfDlc = value; + } + + public bool FakeSign + { + get => this.fakeSign; + set => this.fakeSign = value; + } + + public bool TitleKeyChanged => this.titleKeyChanged; + + public event EventHandler Debug; + + ~Ticket() => this.Dispose(false); + + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize((object) this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing && !this.isDisposed) + { + this.decryptedTitleKey = (byte[]) null; + this.newEncryptedTitleKey = (byte[]) null; + this.signature = (byte[]) null; + this.padding = (byte[]) null; + this.issuer = (byte[]) null; + this.unknown = (byte[]) null; + this.encryptedTitleKey = (byte[]) null; + this.unknown5 = (byte[]) null; + this.unknown6 = (byte[]) null; + this.padding4 = (byte[]) null; + } + this.isDisposed = true; + } + + public static Ticket Load(string pathToTicket) => Ticket.Load(File.ReadAllBytes(pathToTicket)); + + public static Ticket Load(byte[] ticket) + { + Ticket ticket1 = new Ticket(); + MemoryStream memoryStream = new MemoryStream(ticket); + try + { + ticket1.parseTicket((Stream) memoryStream); + } + catch + { + memoryStream.Dispose(); + throw; + } + memoryStream.Dispose(); + return ticket1; + } + + public static Ticket Load(Stream ticket) + { + Ticket ticket1 = new Ticket(); + ticket1.parseTicket(ticket); + return ticket1; + } + + public void LoadFile(string pathToTicket) => this.LoadFile(File.ReadAllBytes(pathToTicket)); + + public void LoadFile(byte[] ticket) + { + MemoryStream memoryStream = new MemoryStream(ticket); + try + { + this.parseTicket((Stream) memoryStream); + } + catch + { + memoryStream.Dispose(); + throw; + } + memoryStream.Dispose(); + } + + public void LoadFile(Stream ticket) => this.parseTicket(ticket); + + public void Save(string savePath) => this.Save(savePath, false); + + public void Save(string savePath, bool fakeSign) + { + if (fakeSign) + this.fakeSign = true; + if (File.Exists(savePath)) + File.Delete(savePath); + using (FileStream fileStream = new FileStream(savePath, FileMode.Create)) + this.writeToStream((Stream) fileStream); + } + + public MemoryStream ToMemoryStream() => this.ToMemoryStream(false); + + public MemoryStream ToMemoryStream(bool fakeSign) + { + if (fakeSign) + this.fakeSign = true; + MemoryStream memoryStream = new MemoryStream(); + try + { + this.writeToStream((Stream) memoryStream); + return memoryStream; + } + catch + { + memoryStream.Dispose(); + throw; + } + } + + public byte[] ToByteArray() => this.ToByteArray(false); + + public byte[] ToByteArray(bool fakeSign) + { + if (fakeSign) + this.fakeSign = true; + MemoryStream memoryStream = new MemoryStream(); + try + { + this.writeToStream((Stream) memoryStream); + } + catch + { + memoryStream.Dispose(); + throw; + } + byte[] array = memoryStream.ToArray(); + memoryStream.Dispose(); + return array; + } + + public void SetTitleKey(string newTitleKey) => this.SetTitleKey(newTitleKey.ToCharArray()); + + public void SetTitleKey(char[] newTitleKey) + { + if (newTitleKey.Length != 16) + throw new Exception("The title key must be 16 characters long!"); + for (int index = 0; index < 16; ++index) + this.encryptedTitleKey[index] = (byte) newTitleKey[index]; + this.decryptTitleKey(); + this.titleKeyChanged = true; + this.reDecrypt = true; + this.newEncryptedTitleKey = this.encryptedTitleKey; + } + + public void SetTitleKey(byte[] newTitleKey) + { + this.encryptedTitleKey = newTitleKey.Length == 16 ? newTitleKey : throw new Exception("The title key must be 16 characters long!"); + this.decryptTitleKey(); + this.titleKeyChanged = true; + this.reDecrypt = true; + this.newEncryptedTitleKey = newTitleKey; + } + + public string GetUpperTitleID() + { + byte[] bytes = BitConverter.GetBytes(Shared.Swap((uint) this.titleId)); + return new string(new char[4] + { + (char) bytes[0], + (char) bytes[1], + (char) bytes[2], + (char) bytes[3] + }); + } + + private void writeToStream(Stream writeStream) + { + this.fireDebug("Writing Ticket..."); + this.fireDebug(" Encrypting Title Key..."); + this.encryptTitleKey(); + this.fireDebug(" -> Decrypted Title Key: {0}", (object) Shared.ByteArrayToString(this.decryptedTitleKey)); + this.fireDebug(" -> Encrypted Title Key: {0}", (object) Shared.ByteArrayToString(this.encryptedTitleKey)); + if (this.fakeSign) + { + this.fireDebug(" Clearing Signature..."); + this.signature = new byte[256]; + } + MemoryStream memoryStream = new MemoryStream(); + memoryStream.Seek(0L, SeekOrigin.Begin); + this.fireDebug(" Writing Signature Exponent... (Offset: 0x{0})", (object) memoryStream.Position.ToString("x8").ToUpper()); + memoryStream.Write(BitConverter.GetBytes(Shared.Swap(this.signatureExponent)), 0, 4); + this.fireDebug(" Writing Signature... (Offset: 0x{0})", (object) memoryStream.Position.ToString("x8").ToUpper()); + memoryStream.Write(this.signature, 0, this.signature.Length); + this.fireDebug(" Writing Padding... (Offset: 0x{0})", (object) memoryStream.Position.ToString("x8").ToUpper()); + memoryStream.Write(this.padding, 0, this.padding.Length); + this.fireDebug(" Writing Issuer... (Offset: 0x{0})", (object) memoryStream.Position.ToString("x8").ToUpper()); + memoryStream.Write(this.issuer, 0, this.issuer.Length); + this.fireDebug(" Writing Unknown... (Offset: 0x{0})", (object) memoryStream.Position.ToString("x8").ToUpper()); + memoryStream.Write(this.unknown, 0, this.unknown.Length); + this.fireDebug(" Writing Title Key... (Offset: 0x{0})", (object) memoryStream.Position.ToString("x8").ToUpper()); + memoryStream.Write(this.encryptedTitleKey, 0, this.encryptedTitleKey.Length); + this.fireDebug(" Writing Unknown2... (Offset: 0x{0})", (object) memoryStream.Position.ToString("x8").ToUpper()); + memoryStream.WriteByte(this.unknown2); + this.fireDebug(" Writing Ticket ID... (Offset: 0x{0})", (object) memoryStream.Position.ToString("x8").ToUpper()); + memoryStream.Write(BitConverter.GetBytes(Shared.Swap(this.ticketId)), 0, 8); + this.fireDebug(" Writing Console ID... (Offset: 0x{0})", (object) memoryStream.Position.ToString("x8").ToUpper()); + memoryStream.Write(BitConverter.GetBytes(Shared.Swap(this.consoleId)), 0, 4); + this.fireDebug(" Writing Title ID... (Offset: 0x{0})", (object) memoryStream.Position.ToString("x8").ToUpper()); + memoryStream.Write(BitConverter.GetBytes(Shared.Swap(this.titleId)), 0, 8); + this.fireDebug(" Writing Unknwon3... (Offset: 0x{0})", (object) memoryStream.Position.ToString("x8").ToUpper()); + memoryStream.Write(BitConverter.GetBytes(Shared.Swap(this.unknown3)), 0, 2); + this.fireDebug(" Writing NumOfDLC... (Offset: 0x{0})", (object) memoryStream.Position.ToString("x8").ToUpper()); + memoryStream.Write(BitConverter.GetBytes(Shared.Swap(this.numOfDlc)), 0, 2); + this.fireDebug(" Writing Unknwon4... (Offset: 0x{0})", (object) memoryStream.Position.ToString("x8").ToUpper()); + memoryStream.Write(BitConverter.GetBytes(Shared.Swap(this.unknown4)), 0, 8); + this.fireDebug(" Writing Padding2... (Offset: 0x{0})", (object) memoryStream.Position.ToString("x8").ToUpper()); + memoryStream.WriteByte(this.padding2); + this.fireDebug(" Writing Common Key Index... (Offset: 0x{0})", (object) memoryStream.Position.ToString("x8").ToUpper()); + memoryStream.WriteByte(this.commonKeyIndex); + object[] objArray1 = new object[1]; + long position = memoryStream.Position; + objArray1[0] = (object) position.ToString("x8").ToUpper(); + this.fireDebug(" Writing Unknown5... (Offset: 0x{0})", objArray1); + memoryStream.Write(this.unknown5, 0, this.unknown5.Length); + object[] objArray2 = new object[1]; + position = memoryStream.Position; + objArray2[0] = (object) position.ToString("x8").ToUpper(); + this.fireDebug(" Writing Unknown6... (Offset: 0x{0})", objArray2); + memoryStream.Write(this.unknown6, 0, this.unknown6.Length); + object[] objArray3 = new object[1]; + position = memoryStream.Position; + objArray3[0] = (object) position.ToString("x8").ToUpper(); + this.fireDebug(" Writing Padding3... (Offset: 0x{0})", objArray3); + memoryStream.Write(BitConverter.GetBytes(Shared.Swap(this.padding3)), 0, 2); + object[] objArray4 = new object[1]; + position = memoryStream.Position; + objArray4[0] = (object) position.ToString("x8").ToUpper(); + this.fireDebug(" Writing Enable Time Limit... (Offset: 0x{0})", objArray4); + memoryStream.Write(BitConverter.GetBytes(Shared.Swap(this.enableTimeLimit)), 0, 4); + object[] objArray5 = new object[1]; + position = memoryStream.Position; + objArray5[0] = (object) position.ToString("x8").ToUpper(); + this.fireDebug(" Writing Time Limit... (Offset: 0x{0})", objArray5); + memoryStream.Write(BitConverter.GetBytes(Shared.Swap(this.timeLimit)), 0, 4); + object[] objArray6 = new object[1]; + position = memoryStream.Position; + objArray6[0] = (object) position.ToString("x8").ToUpper(); + this.fireDebug(" Writing Padding4... (Offset: 0x{0})", objArray6); + memoryStream.Write(this.padding4, 0, this.padding4.Length); + byte[] array = memoryStream.ToArray(); + memoryStream.Dispose(); + if (this.fakeSign) + { + this.fireDebug(" Fakesigning Ticket..."); + byte[] numArray = new byte[20]; + SHA1 shA1 = SHA1.Create(); + for (ushort index = 0; index < ushort.MaxValue; ++index) + { + byte[] bytes = BitConverter.GetBytes(index); + array[498] = bytes[1]; + array[499] = bytes[0]; + if (shA1.ComputeHash(array)[0] == (byte) 0) + { + this.fireDebug(" -> Signed ({0})", (object) index); + break; + } + if (index == (ushort) 65534) + { + this.fireDebug(" -> Signing Failed..."); + throw new Exception("Fakesigning failed..."); + } + } + shA1.Clear(); + } + writeStream.Seek(0L, SeekOrigin.Begin); + writeStream.Write(array, 0, array.Length); + this.fireDebug("Writing Ticket Finished..."); + } + + private void parseTicket(Stream ticketFile) + { + this.fireDebug("Parsing Ticket..."); + ticketFile.Seek(0L, SeekOrigin.Begin); + byte[] buffer = new byte[8]; + this.fireDebug(" Reading Signature Exponent... (Offset: 0x{0})", (object) ticketFile.Position.ToString("x8").ToUpper()); + ticketFile.Read(buffer, 0, 4); + this.signatureExponent = Shared.Swap(BitConverter.ToUInt32(buffer, 0)); + this.fireDebug(" Reading Signature... (Offset: 0x{0})", (object) ticketFile.Position.ToString("x8").ToUpper()); + ticketFile.Read(this.signature, 0, this.signature.Length); + this.fireDebug(" Reading Padding... (Offset: 0x{0})", (object) ticketFile.Position.ToString("x8").ToUpper()); + ticketFile.Read(this.padding, 0, this.padding.Length); + this.fireDebug(" Reading Issuer... (Offset: 0x{0})", (object) ticketFile.Position.ToString("x8").ToUpper()); + ticketFile.Read(this.issuer, 0, this.issuer.Length); + this.fireDebug(" Reading Unknown... (Offset: 0x{0})", (object) ticketFile.Position.ToString("x8").ToUpper()); + ticketFile.Read(this.unknown, 0, this.unknown.Length); + this.fireDebug(" Reading Title Key... (Offset: 0x{0})", (object) ticketFile.Position.ToString("x8").ToUpper()); + ticketFile.Read(this.encryptedTitleKey, 0, this.encryptedTitleKey.Length); + this.fireDebug(" Reading Unknown2... (Offset: 0x{0})", (object) ticketFile.Position.ToString("x8").ToUpper()); + this.unknown2 = (byte) ticketFile.ReadByte(); + this.fireDebug(" Reading Ticket ID.. (Offset: 0x{0})", (object) ticketFile.Position.ToString("x8").ToUpper()); + ticketFile.Read(buffer, 0, 8); + this.ticketId = Shared.Swap(BitConverter.ToUInt64(buffer, 0)); + this.fireDebug(" Reading Console ID... (Offset: 0x{0})", (object) ticketFile.Position.ToString("x8").ToUpper()); + ticketFile.Read(buffer, 0, 4); + this.consoleId = Shared.Swap(BitConverter.ToUInt32(buffer, 0)); + this.fireDebug(" Reading Title ID... (Offset: 0x{0})", (object) ticketFile.Position.ToString("x8").ToUpper()); + ticketFile.Read(buffer, 0, 8); + this.titleId = Shared.Swap(BitConverter.ToUInt64(buffer, 0)); + this.fireDebug(" Reading Unknown3... (Offset: 0x{0})", (object) ticketFile.Position.ToString("x8").ToUpper()); + this.fireDebug(" Reading NumOfDLC... (Offset: 0x{0})", (object) ticketFile.Position.ToString("x8").ToUpper()); + ticketFile.Read(buffer, 0, 4); + this.unknown3 = Shared.Swap(BitConverter.ToUInt16(buffer, 0)); + this.numOfDlc = Shared.Swap(BitConverter.ToUInt16(buffer, 2)); + this.fireDebug(" Reading Unknown4... (Offset: 0x{0})", (object) ticketFile.Position.ToString("x8").ToUpper()); + ticketFile.Read(buffer, 0, 8); + this.unknown4 = Shared.Swap(BitConverter.ToUInt64(buffer, 0)); + this.fireDebug(" Reading Padding2... (Offset: 0x{0})", (object) ticketFile.Position.ToString("x8").ToUpper()); + this.padding2 = (byte) ticketFile.ReadByte(); + this.fireDebug(" Reading Common Key Index... (Offset: 0x{0})", (object) ticketFile.Position.ToString("x8").ToUpper()); + this.commonKeyIndex = (byte) ticketFile.ReadByte(); + this.newKeyIndex = this.commonKeyIndex; + this.fireDebug(" Reading Unknown5... (Offset: 0x{0})", (object) ticketFile.Position.ToString("x8").ToUpper()); + ticketFile.Read(this.unknown5, 0, this.unknown5.Length); + this.fireDebug(" Reading Unknown6... (Offset: 0x{0})", (object) ticketFile.Position.ToString("x8").ToUpper()); + ticketFile.Read(this.unknown6, 0, this.unknown6.Length); + this.fireDebug(" Reading Padding3... (Offset: 0x{0})", (object) ticketFile.Position.ToString("x8").ToUpper()); + ticketFile.Read(buffer, 0, 2); + this.padding3 = Shared.Swap(BitConverter.ToUInt16(buffer, 0)); + this.fireDebug(" Reading Enable Time Limit... (Offset: 0x{0})", (object) ticketFile.Position.ToString("x8").ToUpper()); + this.fireDebug(" Reading Time Limit... (Offset: 0x{0})", (object) ticketFile.Position.ToString("x8").ToUpper()); + ticketFile.Read(buffer, 0, 8); + this.enableTimeLimit = Shared.Swap(BitConverter.ToUInt32(buffer, 0)); + this.timeLimit = Shared.Swap(BitConverter.ToUInt32(buffer, 4)); + this.fireDebug(" Reading Padding4... (Offset: 0x{0})", (object) ticketFile.Position.ToString("x8").ToUpper()); + ticketFile.Read(this.padding4, 0, this.padding4.Length); + this.fireDebug(" Decrypting Title Key..."); + this.decryptTitleKey(); + this.fireDebug(" -> Encrypted Title Key: {0}", (object) Shared.ByteArrayToString(this.encryptedTitleKey)); + this.fireDebug(" -> Decrypted Title Key: {0}", (object) Shared.ByteArrayToString(this.decryptedTitleKey)); + this.fireDebug("Parsing Ticket Finished..."); + } + + private void decryptTitleKey() + { + byte[] numArray = this.commonKeyIndex == (byte) 1 ? CommonKey.GetKoreanKey() : CommonKey.GetStandardKey(); + byte[] bytes = BitConverter.GetBytes(Shared.Swap(this.titleId)); + Array.Resize(ref bytes, 16); + RijndaelManaged rijndaelManaged = new RijndaelManaged(); + rijndaelManaged.Mode = CipherMode.CBC; + rijndaelManaged.Padding = PaddingMode.None; + rijndaelManaged.KeySize = 128; + rijndaelManaged.BlockSize = 128; + rijndaelManaged.Key = numArray; + rijndaelManaged.IV = bytes; + ICryptoTransform decryptor = rijndaelManaged.CreateDecryptor(); + MemoryStream memoryStream = new MemoryStream(this.encryptedTitleKey); + CryptoStream cryptoStream = new CryptoStream((Stream) memoryStream, decryptor, CryptoStreamMode.Read); + cryptoStream.Read(this.decryptedTitleKey, 0, this.decryptedTitleKey.Length); + cryptoStream.Dispose(); + memoryStream.Dispose(); + decryptor.Dispose(); + rijndaelManaged.Clear(); + } + + private void encryptTitleKey() + { + this.commonKeyIndex = this.newKeyIndex; + byte[] numArray = this.commonKeyIndex == (byte) 1 ? CommonKey.GetKoreanKey() : CommonKey.GetStandardKey(); + byte[] bytes = BitConverter.GetBytes(Shared.Swap(this.titleId)); + Array.Resize(ref bytes, 16); + RijndaelManaged rijndaelManaged = new RijndaelManaged(); + rijndaelManaged.Mode = CipherMode.CBC; + rijndaelManaged.Padding = PaddingMode.None; + rijndaelManaged.KeySize = 128; + rijndaelManaged.BlockSize = 128; + rijndaelManaged.Key = numArray; + rijndaelManaged.IV = bytes; + ICryptoTransform encryptor = rijndaelManaged.CreateEncryptor(); + MemoryStream memoryStream = new MemoryStream(this.decryptedTitleKey); + CryptoStream cryptoStream = new CryptoStream((Stream) memoryStream, encryptor, CryptoStreamMode.Read); + cryptoStream.Read(this.encryptedTitleKey, 0, this.encryptedTitleKey.Length); + cryptoStream.Dispose(); + memoryStream.Dispose(); + encryptor.Dispose(); + rijndaelManaged.Clear(); + } + + private void reDecryptTitleKey() + { + this.encryptedTitleKey = this.newEncryptedTitleKey; + this.decryptTitleKey(); + } + + 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))); + } + } +} diff --git a/U8.cs b/U8.cs new file mode 100644 index 0000000..23ef256 --- /dev/null +++ b/U8.cs @@ -0,0 +1,785 @@ +// Decompiled with JetBrains decompiler +// Type: libWiiSharp.U8 +// Assembly: libWiiSharp, Version=0.4.0.0, Culture=neutral, PublicKeyToken=null +// MVID: FBF36F3D-B5D6-481F-B5F5-1BD3C19E13B2 +// Assembly location: C:\Users\theso\Downloads\NCPatcher\pack\libWiiSharp.dll + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.IO; +using System.Security.Cryptography; +using System.Text; + +namespace libWiiSharp +{ + public class U8 : IDisposable + { + private const int dataPadding = 32; + private Headers.HeaderType headerType; + private object header; + private U8_Header u8Header = new U8_Header(); + private U8_Node rootNode = new U8_Node(); + private List u8Nodes = new List(); + private List stringTable = new List(); + private List data = new List(); + private int iconSize = -1; + private int bannerSize = -1; + private int soundSize = -1; + private bool lz77; + private bool isDisposed; + + public Headers.HeaderType HeaderType => this.headerType; + + public object Header => this.header; + + public U8_Node RootNode => this.rootNode; + + public List Nodes => this.u8Nodes; + + public string[] StringTable => this.stringTable.ToArray(); + + public byte[][] Data => this.data.ToArray(); + + public int NumOfNodes => (int) this.rootNode.SizeOfData - 1; + + public int IconSize => this.iconSize; + + public int BannerSize => this.bannerSize; + + public int SoundSize => this.soundSize; + + public bool Lz77Compress + { + get => this.lz77; + set => this.lz77 = value; + } + + public event EventHandler Progress; + + public event EventHandler Warning; + + public event EventHandler Debug; + + public U8() => this.rootNode.Type = U8_NodeType.Directory; + + ~U8() => this.Dispose(false); + + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize((object) this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing && !this.isDisposed) + { + this.header = (object) null; + this.u8Header = (U8_Header) null; + this.rootNode = (U8_Node) null; + this.u8Nodes.Clear(); + this.u8Nodes = (List) null; + this.stringTable.Clear(); + this.stringTable = (List) null; + this.data.Clear(); + this.data = (List) null; + } + this.isDisposed = true; + } + + public static bool IsU8(string pathToFile) => U8.IsU8(File.ReadAllBytes(pathToFile)); + + public static bool IsU8(byte[] file) + { + if (Lz77.IsLz77Compressed(file)) + { + byte[] file1 = new byte[file.Length > 2000 ? 2000 : file.Length]; + for (int index = 0; index < file1.Length; ++index) + file1[index] = file[index]; + return U8.IsU8(new Lz77().Decompress(file1)); + } + Headers.HeaderType headerType = Headers.DetectHeader(file); + return Shared.Swap(BitConverter.ToUInt32(file, (int) headerType)) == 1437218861U; + } + + public static U8 Load(string pathToU8) => U8.Load(File.ReadAllBytes(pathToU8)); + + public static U8 Load(byte[] u8File) + { + U8 u8 = new U8(); + MemoryStream memoryStream = new MemoryStream(u8File); + try + { + u8.parseU8((Stream) memoryStream); + } + catch + { + memoryStream.Dispose(); + throw; + } + memoryStream.Dispose(); + return u8; + } + + public static U8 Load(Stream u8File) + { + U8 u8 = new U8(); + u8.parseU8(u8File); + return u8; + } + + public static U8 FromDirectory(string pathToDirectory) + { + U8 u8 = new U8(); + u8.createFromDir(pathToDirectory); + return u8; + } + + public void LoadFile(string pathToU8) => this.LoadFile(File.ReadAllBytes(pathToU8)); + + public void LoadFile(byte[] u8File) + { + MemoryStream memoryStream = new MemoryStream(u8File); + try + { + this.parseU8((Stream) memoryStream); + } + catch + { + memoryStream.Dispose(); + throw; + } + memoryStream.Dispose(); + } + + public void LoadFile(Stream u8File) => this.parseU8(u8File); + + public void CreateFromDirectory(string pathToDirectory) => this.createFromDir(pathToDirectory); + + public void Save(string savePath) + { + if (File.Exists(savePath)) + File.Delete(savePath); + using (FileStream fileStream = new FileStream(savePath, FileMode.Create)) + this.writeToStream((Stream) fileStream); + } + + public MemoryStream ToMemoryStream() + { + MemoryStream memoryStream = new MemoryStream(); + try + { + this.writeToStream((Stream) memoryStream); + return memoryStream; + } + catch + { + memoryStream.Dispose(); + throw; + } + } + + public byte[] ToByteArray() => this.ToMemoryStream().ToArray(); + + public void Unpack(string saveDir) => this.unpackToDir(saveDir); + + public void Extract(string saveDir) => this.unpackToDir(saveDir); + + public void AddHeaderImet(bool shortImet, params string[] titles) + { + if (this.iconSize == -1) + throw new Exception("icon.bin wasn't found!"); + if (this.bannerSize == -1) + throw new Exception("banner.bin wasn't found!"); + if (this.soundSize == -1) + throw new Exception("sound.bin wasn't found!"); + this.header = (object) Headers.IMET.Create(shortImet, this.iconSize, this.bannerSize, this.soundSize, titles); + this.headerType = shortImet ? Headers.HeaderType.ShortIMET : Headers.HeaderType.IMET; + } + + public void AddHeaderImd5() => this.headerType = Headers.HeaderType.IMD5; + + public void ReplaceFile(int fileIndex, string pathToNewFile, bool changeFileName = false) + { + if (this.u8Nodes[fileIndex].Type == U8_NodeType.Directory) + throw new Exception("You can't replace a directory with a file!"); + this.data[fileIndex] = File.ReadAllBytes(pathToNewFile); + if (changeFileName) + this.stringTable[fileIndex] = Path.GetFileName(pathToNewFile); + if (this.stringTable[fileIndex].ToLower() == "icon.bin") + this.iconSize = this.getRealSize(File.ReadAllBytes(pathToNewFile)); + else if (this.stringTable[fileIndex].ToLower() == "banner.bin") + { + this.bannerSize = this.getRealSize(File.ReadAllBytes(pathToNewFile)); + } + else + { + if (!(this.stringTable[fileIndex].ToLower() == "sound.bin")) + return; + this.soundSize = this.getRealSize(File.ReadAllBytes(pathToNewFile)); + } + } + + public void ReplaceFile(int fileIndex, byte[] newData) + { + if (this.u8Nodes[fileIndex].Type == U8_NodeType.Directory) + throw new Exception("You can't replace a directory with a file!"); + this.data[fileIndex] = newData; + if (this.stringTable[fileIndex].ToLower() == "icon.bin") + this.iconSize = this.getRealSize(newData); + else if (this.stringTable[fileIndex].ToLower() == "banner.bin") + { + this.bannerSize = this.getRealSize(newData); + } + else + { + if (!(this.stringTable[fileIndex].ToLower() == "sound.bin")) + return; + this.soundSize = this.getRealSize(newData); + } + } + + public int GetNodeIndex(string fileOrDirName) + { + for (int index = 0; index < this.u8Nodes.Count; ++index) + { + if (this.stringTable[index].ToLower() == fileOrDirName.ToLower()) + return index; + } + return -1; + } + + public void RenameNode(int index, string newName) => this.stringTable[index] = newName; + + public void RenameNode(string oldName, string newName) => this.stringTable[this.GetNodeIndex(oldName)] = newName; + + public void AddDirectory(string path) => this.addEntry(path, new byte[0]); + + public void AddFile(string path, byte[] data) => this.addEntry(path, data); + + public void RemoveDirectory(string path) => this.removeEntry(path); + + public void RemoveFile(string path) => this.removeEntry(path); + + private void writeToStream(Stream writeStream) + { + this.fireDebug("Writing U8 File..."); + this.fireDebug(" Updating Rootnode..."); + this.rootNode.SizeOfData = (uint) (this.u8Nodes.Count + 1); + MemoryStream memoryStream = new MemoryStream(); + memoryStream.Seek((long) this.u8Header.OffsetToRootNode + (long) ((this.u8Nodes.Count + 1) * 12), SeekOrigin.Begin); + this.fireDebug(" Writing String Table... (Offset: 0x{0})", (object) memoryStream.Position.ToString("x8").ToUpper()); + memoryStream.WriteByte((byte) 0); + int num = (int) memoryStream.Position - 1; + long position; + for (int index = 0; index < this.u8Nodes.Count; ++index) + { + object[] objArray = new object[4]; + position = memoryStream.Position; + objArray[0] = (object) position.ToString("x8").ToUpper(); + objArray[1] = (object) (index + 1); + objArray[2] = (object) this.u8Nodes.Count; + objArray[3] = (object) this.stringTable[index]; + this.fireDebug(" -> Entry #{1} of {2}: \"{3}\"... (Offset: 0x{0})", objArray); + this.u8Nodes[index].OffsetToName = (ushort) ((ulong) memoryStream.Position - (ulong) num); + byte[] bytes = Encoding.ASCII.GetBytes(this.stringTable[index]); + memoryStream.Write(bytes, 0, bytes.Length); + memoryStream.WriteByte((byte) 0); + } + this.u8Header.HeaderSize = (uint) ((ulong) memoryStream.Position - (ulong) this.u8Header.OffsetToRootNode); + this.u8Header.OffsetToData = 0U; + for (int index = 0; index < this.u8Nodes.Count; ++index) + { + this.fireProgress((index + 1) * 100 / this.u8Nodes.Count); + if (this.u8Nodes[index].Type == U8_NodeType.File) + { + memoryStream.Seek((long) Shared.AddPadding((int) memoryStream.Position, 32), SeekOrigin.Begin); + object[] objArray = new object[3]; + position = memoryStream.Position; + objArray[0] = (object) position.ToString("x8").ToUpper(); + objArray[1] = (object) (index + 1); + objArray[2] = (object) this.u8Nodes.Count; + this.fireDebug(" Writing Data #{1} of {2}... (Offset: 0x{0})", objArray); + if (this.u8Header.OffsetToData == 0U) + this.u8Header.OffsetToData = (uint) memoryStream.Position; + this.u8Nodes[index].OffsetToData = (uint) memoryStream.Position; + this.u8Nodes[index].SizeOfData = (uint) this.data[index].Length; + memoryStream.Write(this.data[index], 0, this.data[index].Length); + } + else + this.fireDebug(" Node #{0} of {1} is a Directory...", (object) (index + 1), (object) this.u8Nodes.Count); + } + while (memoryStream.Position % 16L != 0L) + memoryStream.WriteByte((byte) 0); + memoryStream.Seek(0L, SeekOrigin.Begin); + object[] objArray1 = new object[1]; + position = memoryStream.Position; + objArray1[0] = (object) position.ToString("x8").ToUpper(); + this.fireDebug(" Writing Header... (Offset: 0x{0})", objArray1); + this.u8Header.Write((Stream) memoryStream); + object[] objArray2 = new object[1]; + position = memoryStream.Position; + objArray2[0] = (object) position.ToString("x8").ToUpper(); + this.fireDebug(" Writing Rootnode... (Offset: 0x{0})", objArray2); + this.rootNode.Write((Stream) memoryStream); + for (int index = 0; index < this.u8Nodes.Count; ++index) + { + object[] objArray3 = new object[3]; + position = memoryStream.Position; + objArray3[0] = (object) position.ToString("x8").ToUpper(); + objArray3[1] = (object) (index + 1); + objArray3[2] = (object) this.u8Nodes.Count; + this.fireDebug(" Writing Node Entry #{1} of {2}... (Offset: 0x{0})", objArray3); + this.u8Nodes[index].Write((Stream) memoryStream); + } + byte[] numArray = memoryStream.ToArray(); + memoryStream.Dispose(); + if (this.lz77) + { + this.fireDebug(" Lz77 Compressing U8 File..."); + numArray = new Lz77().Compress(numArray); + } + if (this.headerType == Headers.HeaderType.IMD5) + { + this.fireDebug(" Adding IMD5 Header..."); + writeStream.Seek(0L, SeekOrigin.Begin); + Headers.IMD5.Create(numArray).Write(writeStream); + } + else if (this.headerType == Headers.HeaderType.IMET || this.headerType == Headers.HeaderType.ShortIMET) + { + this.fireDebug(" Adding IMET Header..."); + ((Headers.IMET) this.header).IconSize = (uint) this.iconSize; + ((Headers.IMET) this.header).BannerSize = (uint) this.bannerSize; + ((Headers.IMET) this.header).SoundSize = (uint) this.soundSize; + writeStream.Seek(0L, SeekOrigin.Begin); + ((Headers.IMET) this.header).Write(writeStream); + } + writeStream.Write(numArray, 0, numArray.Length); + this.fireDebug("Writing U8 File Finished..."); + } + + private void unpackToDir(string saveDir) + { + this.fireDebug("Unpacking U8 File to: {0}", (object) saveDir); + if (!Directory.Exists(saveDir)) + Directory.CreateDirectory(saveDir); + string[] strArray = new string[this.u8Nodes.Count]; + strArray[0] = saveDir; + int[] numArray = new int[this.u8Nodes.Count]; + int index1 = 0; + for (int index2 = 0; index2 < this.u8Nodes.Count; ++index2) + { + this.fireDebug(" Unpacking Entry #{0} of {1}", (object) (index2 + 1), (object) this.u8Nodes.Count); + this.fireProgress((index2 + 1) * 100 / this.u8Nodes.Count); + if (this.u8Nodes[index2].Type == U8_NodeType.Directory) + { + this.fireDebug(" -> Directory: \"{0}\"", (object) this.stringTable[index2]); + if ((int) strArray[index1][strArray[index1].Length - 1] != (int) Path.DirectorySeparatorChar) + { + // ISSUE: explicit reference operation + strArray[index1] += Path.DirectorySeparatorChar.ToString(); + } + Directory.CreateDirectory(strArray[index1] + this.stringTable[index2]); + strArray[index1 + 1] = strArray[index1] + this.stringTable[index2]; + ++index1; + numArray[index1] = (int) this.u8Nodes[index2].SizeOfData; + } + else + { + this.fireDebug(" -> File: \"{0}\"", (object) this.stringTable[index2]); + this.fireDebug(" -> Size: {0} bytes", (object) this.data[index2].Length); + using (FileStream fileStream = new FileStream(strArray[index1] + Path.DirectorySeparatorChar.ToString() + this.stringTable[index2], FileMode.Create)) + fileStream.Write(this.data[index2], 0, this.data[index2].Length); + } + while (index1 > 0 && numArray[index1] == index2 + 2) + --index1; + } + this.fireDebug("Unpacking U8 File Finished"); + } + + private void parseU8(Stream u8File) + { + this.fireDebug("Pasing U8 File..."); + this.u8Header = new U8_Header(); + this.rootNode = new U8_Node(); + this.u8Nodes = new List(); + this.stringTable = new List(); + this.data = new List(); + this.fireDebug(" Detecting Header..."); + this.headerType = Headers.DetectHeader(u8File); + Headers.HeaderType headerType = this.headerType; + this.fireDebug(" -> {0}", (object) this.headerType.ToString()); + if (this.headerType == Headers.HeaderType.IMD5) + { + this.fireDebug(" Reading IMD5 Header..."); + this.header = (object) Headers.IMD5.Load(u8File); + byte[] buffer = new byte[u8File.Length]; + u8File.Read(buffer, 0, buffer.Length); + MD5 md5 = MD5.Create(); + byte[] hash1 = md5.ComputeHash(buffer, (int) this.headerType, (int) ((int) u8File.Length - this.headerType)); + md5.Clear(); + byte[] hash2 = ((Headers.IMD5) this.header).Hash; + if (!Shared.CompareByteArrays(hash1, hash2)) + { + this.fireDebug("/!\\ /!\\ /!\\ Hashes do not match /!\\ /!\\ /!\\"); + this.fireWarning("Hashes of IMD5 header and file do not match! The content might be corrupted!"); + } + } + else if (this.headerType == Headers.HeaderType.IMET || this.headerType == Headers.HeaderType.ShortIMET) + { + this.fireDebug(" Reading IMET Header..."); + this.header = (object) Headers.IMET.Load(u8File); + if (!((Headers.IMET) this.header).HashesMatch) + { + this.fireDebug("/!\\ /!\\ /!\\ Hashes do not match /!\\ /!\\ /!\\"); + this.fireWarning("The hash stored in the IMET header doesn't match the headers hash! The header and/or file might be corrupted!"); + } + } + this.fireDebug(" Checking for Lz77 Compression..."); + if (Lz77.IsLz77Compressed(u8File)) + { + this.fireDebug(" -> Lz77 Compression Found..."); + this.fireDebug(" Decompressing U8 Data..."); + Stream file = new Lz77().Decompress(u8File); + headerType = Headers.DetectHeader(file); + u8File = file; + this.lz77 = true; + } + u8File.Seek((long) headerType, SeekOrigin.Begin); + byte[] buffer1 = new byte[4]; + this.fireDebug(" Reading U8 Header: Magic... (Offset: 0x{0})", (object) u8File.Position.ToString("x8").ToUpper()); + u8File.Read(buffer1, 0, 4); + if ((int) Shared.Swap(BitConverter.ToUInt32(buffer1, 0)) != (int) this.u8Header.U8Magic) + { + this.fireDebug(" -> Invalid Magic!"); + throw new Exception("U8 Header: Invalid Magic!"); + } + this.fireDebug(" Reading U8 Header: Offset to Rootnode... (Offset: 0x{0})", (object) u8File.Position.ToString("x8").ToUpper()); + u8File.Read(buffer1, 0, 4); + if ((int) Shared.Swap(BitConverter.ToUInt32(buffer1, 0)) != (int) this.u8Header.OffsetToRootNode) + { + this.fireDebug(" -> Invalid Offset to Rootnode"); + throw new Exception("U8 Header: Invalid Offset to Rootnode!"); + } + this.fireDebug(" Reading U8 Header: Header Size... (Offset: 0x{0})", (object) u8File.Position.ToString("x8").ToUpper()); + u8File.Read(buffer1, 0, 4); + this.u8Header.HeaderSize = Shared.Swap(BitConverter.ToUInt32(buffer1, 0)); + this.fireDebug(" Reading U8 Header: Offset to Data... (Offset: 0x{0})", (object) u8File.Position.ToString("x8").ToUpper()); + u8File.Read(buffer1, 0, 4); + this.u8Header.OffsetToData = Shared.Swap(BitConverter.ToUInt32(buffer1, 0)); + u8File.Seek(16L, SeekOrigin.Current); + object[] objArray1 = new object[1]; + long position1 = u8File.Position; + objArray1[0] = (object) position1.ToString("x8").ToUpper(); + this.fireDebug(" Reading Rootnode... (Offset: 0x{0})", objArray1); + u8File.Read(buffer1, 0, 4); + this.rootNode.Type = (U8_NodeType) Shared.Swap(BitConverter.ToUInt16(buffer1, 0)); + this.rootNode.OffsetToName = Shared.Swap(BitConverter.ToUInt16(buffer1, 2)); + u8File.Read(buffer1, 0, 4); + this.rootNode.OffsetToData = Shared.Swap(BitConverter.ToUInt32(buffer1, 0)); + u8File.Read(buffer1, 0, 4); + this.rootNode.SizeOfData = Shared.Swap(BitConverter.ToUInt32(buffer1, 0)); + int num = (int) ((long) headerType + (long) this.u8Header.OffsetToRootNode + (long) (this.rootNode.SizeOfData * 12U)); + int position2 = (int) u8File.Position; + for (int index = 0; (long) index < (long) (this.rootNode.SizeOfData - 1U); ++index) + { + object[] objArray2 = new object[3]; + position1 = u8File.Position; + objArray2[0] = (object) position1.ToString("x8").ToUpper(); + objArray2[1] = (object) (index + 1); + objArray2[2] = (object) (uint) ((int) this.rootNode.SizeOfData - 1); + this.fireDebug(" Reading Node #{1} of {2}... (Offset: 0x{0})", objArray2); + this.fireProgress((int) ((long) ((index + 1) * 100) / (long) (this.rootNode.SizeOfData - 1U))); + U8_Node u8Node = new U8_Node(); + string empty = string.Empty; + byte[] numArray = new byte[0]; + u8File.Seek((long) position2, SeekOrigin.Begin); + object[] objArray3 = new object[1]; + position1 = u8File.Position; + objArray3[0] = (object) position1.ToString("x8").ToUpper(); + this.fireDebug(" -> Reading Node Entry... (Offset: 0x{0})", objArray3); + u8File.Read(buffer1, 0, 4); + u8Node.Type = (U8_NodeType) Shared.Swap(BitConverter.ToUInt16(buffer1, 0)); + u8Node.OffsetToName = Shared.Swap(BitConverter.ToUInt16(buffer1, 2)); + u8File.Read(buffer1, 0, 4); + u8Node.OffsetToData = Shared.Swap(BitConverter.ToUInt32(buffer1, 0)); + u8File.Read(buffer1, 0, 4); + u8Node.SizeOfData = Shared.Swap(BitConverter.ToUInt32(buffer1, 0)); + position2 = (int) u8File.Position; + this.fireDebug(" -> {0}", (object) u8Node.Type.ToString()); + u8File.Seek((long) (num + (int) u8Node.OffsetToName), SeekOrigin.Begin); + object[] objArray4 = new object[1]; + position1 = u8File.Position; + objArray4[0] = (object) position1.ToString("x8").ToUpper(); + this.fireDebug(" -> Reading Node Name... (Offset: 0x{0})", objArray4); + do + { + char ch = (char) u8File.ReadByte(); + if (ch != char.MinValue) + empty += ch.ToString(); + else + break; + } + while (empty.Length <= (int) byte.MaxValue); + this.fireDebug(" -> {0}", (object) empty); + if (u8Node.Type == U8_NodeType.File) + { + u8File.Seek((long) headerType + (long) u8Node.OffsetToData, SeekOrigin.Begin); + object[] objArray5 = new object[1]; + position1 = u8File.Position; + objArray5[0] = (object) position1.ToString("x8").ToUpper(); + this.fireDebug(" -> Reading Node Data (Offset: 0x{0})", objArray5); + numArray = new byte[(int) u8Node.SizeOfData]; + u8File.Read(numArray, 0, numArray.Length); + } + if (empty.ToLower() == "icon.bin") + this.iconSize = this.getRealSize(numArray); + else if (empty.ToLower() == "banner.bin") + this.bannerSize = this.getRealSize(numArray); + else if (empty.ToLower() == "sound.bin") + this.soundSize = this.getRealSize(numArray); + this.u8Nodes.Add(u8Node); + this.stringTable.Add(empty); + this.data.Add(numArray); + } + this.fireDebug("Pasing U8 File Finished..."); + } + + private void createFromDir(string path) + { + this.fireDebug("Creating U8 File from: {0}", (object) path); + if ((int) path[path.Length - 1] != (int) Path.DirectorySeparatorChar) + path += Path.DirectorySeparatorChar.ToString(); + this.fireDebug(" Collecting Content..."); + string[] dirContent = this.getDirContent(path, true); + int num1 = 1; + int num2 = 0; + this.fireDebug(" Creating U8 Header..."); + this.u8Header = new U8_Header(); + this.rootNode = new U8_Node(); + this.u8Nodes = new List(); + this.stringTable = new List(); + this.data = new List(); + this.fireDebug(" Creating Rootnode..."); + this.rootNode.Type = U8_NodeType.Directory; + this.rootNode.OffsetToName = (ushort) 0; + this.rootNode.OffsetToData = 0U; + this.rootNode.SizeOfData = (uint) (dirContent.Length + 1); + for (int index1 = 0; index1 < dirContent.Length; ++index1) + { + this.fireDebug(" Creating Node #{0} of {1}", (object) (index1 + 1), (object) dirContent.Length); + this.fireProgress((index1 + 1) * 100 / dirContent.Length); + U8_Node u8Node = new U8_Node(); + byte[] data = new byte[0]; + string theString = dirContent[index1].Remove(0, path.Length - 1); + if (Directory.Exists(dirContent[index1])) + { + this.fireDebug(" -> Directory"); + u8Node.Type = U8_NodeType.Directory; + u8Node.OffsetToData = (uint) Shared.CountCharsInString(theString, Path.DirectorySeparatorChar); + int num3 = this.u8Nodes.Count + 2; + for (int index2 = 0; index2 < dirContent.Length; ++index2) + { + if (dirContent[index2].Contains(dirContent[index1] + "\\")) + ++num3; + } + u8Node.SizeOfData = (uint) num3; + } + else + { + this.fireDebug(" -> File"); + this.fireDebug(" -> Reading File Data..."); + data = File.ReadAllBytes(dirContent[index1]); + u8Node.Type = U8_NodeType.File; + u8Node.OffsetToData = (uint) num2; + u8Node.SizeOfData = (uint) data.Length; + num2 += Shared.AddPadding(num2 + data.Length, 32); + } + u8Node.OffsetToName = (ushort) num1; + num1 += Path.GetFileName(dirContent[index1]).Length + 1; + this.fireDebug(" -> Reading Name..."); + string fileName = Path.GetFileName(dirContent[index1]); + if (fileName.ToLower() == "icon.bin") + this.iconSize = this.getRealSize(data); + else if (fileName.ToLower() == "banner.bin") + this.bannerSize = this.getRealSize(data); + else if (fileName.ToLower() == "sound.bin") + this.soundSize = this.getRealSize(data); + this.u8Nodes.Add(u8Node); + this.stringTable.Add(fileName); + this.data.Add(data); + } + this.fireDebug(" Updating U8 Header..."); + this.u8Header.HeaderSize = (uint) ((this.u8Nodes.Count + 1) * 12 + num1); + this.u8Header.OffsetToData = (uint) Shared.AddPadding((int) this.u8Header.OffsetToRootNode + (int) this.u8Header.HeaderSize, 32); + this.fireDebug(" Calculating Data Offsets..."); + for (int index = 0; index < this.u8Nodes.Count; ++index) + { + this.fireDebug(" -> Node #{0} of {1}...", (object) (index + 1), (object) this.u8Nodes.Count); + int offsetToData = (int) this.u8Nodes[index].OffsetToData; + this.u8Nodes[index].OffsetToData = (uint) ((ulong) this.u8Header.OffsetToData + (ulong) offsetToData); + } + this.fireDebug("Creating U8 File Finished..."); + } + + private string[] getDirContent(string dir, bool root) + { + string[] files = Directory.GetFiles(dir); + string[] directories = Directory.GetDirectories(dir); + string str1 = ""; + if (!root) + str1 = str1 + dir + "\n"; + for (int index = 0; index < files.Length; ++index) + str1 = str1 + files[index] + "\n"; + foreach (string dir1 in directories) + { + foreach (string str2 in this.getDirContent(dir1, false)) + str1 = str1 + str2 + "\n"; + } + return str1.Split(new char[1]{ '\n' }, StringSplitOptions.RemoveEmptyEntries); + } + + private int getRealSize(byte[] data) + { + if (data[0] != (byte) 73 || data[1] != (byte) 77 || (data[2] != (byte) 68 || data[3] != (byte) 53)) + return data.Length; + return data[32] == (byte) 76 && data[33] == (byte) 90 && (data[34] == (byte) 55 && data[35] == (byte) 55) ? BitConverter.ToInt32(data, 36) >> 8 : data.Length - 32; + } + + private void addEntry(string nodePath, byte[] fileData) + { + if (nodePath.StartsWith("/")) + nodePath = nodePath.Remove(0, 1); + string[] strArray = nodePath.Split('/'); + int index1 = -1; + int num1 = this.u8Nodes.Count > 0 ? this.u8Nodes.Count - 1 : 0; + int num2 = 0; + List intList = new List(); + for (int index2 = 0; index2 < strArray.Length - 1; ++index2) + { + for (int index3 = num2; index3 <= num1; ++index3) + { + if (!(this.stringTable[index3].ToLower() == strArray[index2].ToLower())) + { + if (index3 == num1 - 1) + throw new Exception("Path wasn't found!"); + } + else + { + if (index2 == strArray.Length - 2) + index1 = index3; + num1 = (int) this.u8Nodes[index3].SizeOfData - 1; + num2 = index3 + 1; + intList.Add(index3); + break; + } + } + } + int num3 = index1 > -1 ? (int) this.u8Nodes[index1].SizeOfData - 2 : (this.rootNode.SizeOfData > 1U ? (int) this.rootNode.SizeOfData - 2 : -1); + U8_Node u8Node = new U8_Node(); + u8Node.Type = fileData.Length == 0 ? U8_NodeType.Directory : U8_NodeType.File; + u8Node.SizeOfData = fileData.Length == 0 ? (uint) (num3 + 2) : (uint) fileData.Length; + u8Node.OffsetToData = fileData.Length == 0 ? (uint) Shared.CountCharsInString(nodePath, '/') : 0U; + this.stringTable.Insert(num3 + 1, strArray[strArray.Length - 1]); + this.u8Nodes.Insert(num3 + 1, u8Node); + this.data.Insert(num3 + 1, fileData); + ++this.rootNode.SizeOfData; + foreach (int index2 in intList) + { + if (this.u8Nodes[index2].Type == U8_NodeType.Directory) + ++this.u8Nodes[index2].SizeOfData; + } + for (int index2 = num3 + 1; index2 < this.u8Nodes.Count; ++index2) + { + if (this.u8Nodes[index2].Type == U8_NodeType.Directory) + ++this.u8Nodes[index2].SizeOfData; + } + } + + private void removeEntry(string nodePath) + { + if (nodePath.StartsWith("/")) + nodePath = nodePath.Remove(0, 1); + string[] strArray = nodePath.Split('/'); + int index1 = -1; + int num1 = this.u8Nodes.Count - 1; + int num2 = 0; + List intList = new List(); + for (int index2 = 0; index2 < strArray.Length; ++index2) + { + for (int index3 = num2; index3 < num1; ++index3) + { + if (!(this.stringTable[index3].ToLower() == strArray[index2].ToLower())) + { + if (index3 == num1 - 1) + throw new Exception("Path wasn't found!"); + } + else + { + if (index2 == strArray.Length - 1) + index1 = index3; + else + intList.Add(index3); + num1 = (int) this.u8Nodes[index3].SizeOfData - 1; + num2 = index3 + 1; + break; + } + } + } + int num3 = 0; + if (this.u8Nodes[index1].Type == U8_NodeType.Directory) + { + for (int index2 = (int) this.u8Nodes[index1].SizeOfData - 2; index2 >= index1; --index2) + { + this.stringTable.RemoveAt(index2); + this.u8Nodes.RemoveAt(index2); + this.data.RemoveAt(index2); + ++num3; + } + } + else + { + this.stringTable.RemoveAt(index1); + this.u8Nodes.RemoveAt(index1); + this.data.RemoveAt(index1); + ++num3; + } + this.rootNode.SizeOfData -= (uint) num3; + foreach (int index2 in intList) + { + if (this.u8Nodes[index2].Type == U8_NodeType.Directory) + this.u8Nodes[index2].SizeOfData -= (uint) num3; + } + for (int index2 = index1 + 1; index2 < this.u8Nodes.Count; ++index2) + { + if (this.u8Nodes[index2].Type == U8_NodeType.Directory) + this.u8Nodes[index2].SizeOfData -= (uint) num3; + } + } + + private void fireWarning(string warningMessage) + { + EventHandler warning = this.Warning; + if (warning == null) + return; + warning(new object(), new MessageEventArgs(warningMessage)); + } + + 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 fireProgress(int progressPercentage) + { + EventHandler progress = this.Progress; + if (progress == null) + return; + progress(new object(), new ProgressChangedEventArgs(progressPercentage, (object) string.Empty)); + } + } +} diff --git a/U8_Header.cs b/U8_Header.cs new file mode 100644 index 0000000..5c484c7 --- /dev/null +++ b/U8_Header.cs @@ -0,0 +1,47 @@ +// Decompiled with JetBrains decompiler +// Type: libWiiSharp.U8_Header +// Assembly: libWiiSharp, Version=0.4.0.0, Culture=neutral, PublicKeyToken=null +// MVID: FBF36F3D-B5D6-481F-B5F5-1BD3C19E13B2 +// Assembly location: C:\Users\theso\Downloads\NCPatcher\pack\libWiiSharp.dll + +using System; +using System.IO; + +namespace libWiiSharp +{ + public class U8_Header + { + private uint u8Magic = 1437218861; + private uint offsetToRootNode = 32; + private uint headerSize; + private uint offsetToData; + private byte[] padding = new byte[16]; + + public uint U8Magic => this.u8Magic; + + public uint OffsetToRootNode => this.offsetToRootNode; + + public uint HeaderSize + { + get => this.headerSize; + set => this.headerSize = value; + } + + public uint OffsetToData + { + get => this.offsetToData; + set => this.offsetToData = value; + } + + public byte[] Padding => this.padding; + + public void Write(Stream writeStream) + { + writeStream.Write(BitConverter.GetBytes(Shared.Swap(this.u8Magic)), 0, 4); + writeStream.Write(BitConverter.GetBytes(Shared.Swap(this.offsetToRootNode)), 0, 4); + writeStream.Write(BitConverter.GetBytes(Shared.Swap(this.headerSize)), 0, 4); + writeStream.Write(BitConverter.GetBytes(Shared.Swap(this.offsetToData)), 0, 4); + writeStream.Write(this.padding, 0, 16); + } + } +} diff --git a/U8_Node.cs b/U8_Node.cs new file mode 100644 index 0000000..618542c --- /dev/null +++ b/U8_Node.cs @@ -0,0 +1,51 @@ +// Decompiled with JetBrains decompiler +// Type: libWiiSharp.U8_Node +// Assembly: libWiiSharp, Version=0.4.0.0, Culture=neutral, PublicKeyToken=null +// MVID: FBF36F3D-B5D6-481F-B5F5-1BD3C19E13B2 +// Assembly location: C:\Users\theso\Downloads\NCPatcher\pack\libWiiSharp.dll + +using System; +using System.IO; + +namespace libWiiSharp +{ + public class U8_Node + { + private ushort type; + private ushort offsetToName; + private uint offsetToData; + private uint sizeOfData; + + public U8_NodeType Type + { + get => (U8_NodeType) this.type; + set => this.type = (ushort) value; + } + + public ushort OffsetToName + { + get => this.offsetToName; + set => this.offsetToName = value; + } + + public uint OffsetToData + { + get => this.offsetToData; + set => this.offsetToData = value; + } + + public uint SizeOfData + { + get => this.sizeOfData; + set => this.sizeOfData = value; + } + + public void Write(Stream writeStream) + { + writeStream.Write(BitConverter.GetBytes(Shared.Swap(this.type)), 0, 2); + writeStream.Write(BitConverter.GetBytes(Shared.Swap(this.offsetToName)), 0, 2); + writeStream.Write(BitConverter.GetBytes(Shared.Swap(this.offsetToData)), 0, 4); + writeStream.Write(BitConverter.GetBytes(Shared.Swap(this.sizeOfData)), 0, 4); + } + } +} diff --git a/U8_NodeType.cs b/U8_NodeType.cs new file mode 100644 index 0000000..131ca4d --- /dev/null +++ b/U8_NodeType.cs @@ -0,0 +1,14 @@ +// Decompiled with JetBrains decompiler +// Type: libWiiSharp.U8_NodeType +// Assembly: libWiiSharp, Version=0.4.0.0, Culture=neutral, PublicKeyToken=null +// MVID: FBF36F3D-B5D6-481F-B5F5-1BD3C19E13B2 +// Assembly location: C:\Users\theso\Downloads\NCPatcher\pack\libWiiSharp.dll + +namespace libWiiSharp +{ + public enum U8_NodeType : ushort + { + File = 0, + Directory = 256, // 0x0100 + } +} diff --git a/WAD.cs b/WAD.cs new file mode 100644 index 0000000..f1019fa --- /dev/null +++ b/WAD.cs @@ -0,0 +1,1002 @@ +// Decompiled with JetBrains decompiler +// Type: libWiiSharp.WAD +// Assembly: libWiiSharp, Version=0.4.0.0, Culture=neutral, PublicKeyToken=null +// MVID: FBF36F3D-B5D6-481F-B5F5-1BD3C19E13B2 +// Assembly location: C:\Users\theso\Downloads\NCPatcher\pack\libWiiSharp.dll + +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 CommonKeyType CommonKeyType + { + get => this.tik.CommonKeyIndex; + set => this.tik.CommonKeyIndex = value; + } + + public bool SortContents + { + get => this.tmd.SortContents; + set => this.tmd.SortContents = value; + } + + public event EventHandler Progress; + + public event EventHandler Warning; + + public event EventHandler Debug; + + 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 memoryStream = new MemoryStream(wadFile); + try + { + wad.parseWad((Stream) memoryStream); + } + catch + { + memoryStream.Dispose(); + throw; + } + memoryStream.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 + Path.DirectorySeparatorChar.ToString() + 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 + Path.DirectorySeparatorChar.ToString() + 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 + Path.DirectorySeparatorChar.ToString() + (flag ? tmd.Contents[index].ContentID.ToString("x8") : 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 + Path.DirectorySeparatorChar.ToString() + 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 + Path.DirectorySeparatorChar.ToString() + 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 + Path.DirectorySeparatorChar.ToString() + (flag ? tmd.Contents[index].ContentID.ToString("x8") : 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) + { + CertificateChain cert1 = CertificateChain.Load(cert); + Ticket ticket = Ticket.Load(tik); + TMD tmd1 = TMD.Load(tmd); + Ticket tik1 = ticket; + TMD tmd2 = tmd1; + byte[][] contents1 = contents; + return WAD.Create(cert1, tik1, tmd2, contents1); + } + + 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; + return wad; + } + catch + { + wad.hasBanner = false; + return wad; + } + } + } + return wad; + } + + public void LoadFile(string pathToWad) => this.LoadFile(File.ReadAllBytes(pathToWad)); + + public void LoadFile(byte[] wadFile) + { + MemoryStream memoryStream = new MemoryStream(wadFile); + try + { + this.parseWad((Stream) memoryStream); + } + catch + { + memoryStream.Dispose(); + throw; + } + memoryStream.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 + Path.DirectorySeparatorChar.ToString() + 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 + Path.DirectorySeparatorChar.ToString() + 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 + Path.DirectorySeparatorChar.ToString() + (flag ? tmd.Contents[index].ContentID.ToString("x8") : 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 + Path.DirectorySeparatorChar.ToString() + 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 + Path.DirectorySeparatorChar.ToString() + 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 + Path.DirectorySeparatorChar.ToString() + (flag ? tmd.Contents[index].ContentID.ToString("x8") : 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 fileStream = new FileStream(savePath, FileMode.Create)) + this.writeToStream((Stream) fileStream); + } + + public MemoryStream ToMemoryStream() + { + MemoryStream memoryStream = new MemoryStream(); + try + { + this.writeToStream((Stream) memoryStream); + return memoryStream; + } + catch + { + memoryStream.Dispose(); + throw; + } + } + + public byte[] ToByteArray() + { + MemoryStream memoryStream = new MemoryStream(); + try + { + this.writeToStream((Stream) memoryStream); + } + catch + { + memoryStream.Dispose(); + throw; + } + byte[] array = memoryStream.ToArray(); + memoryStream.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[] file = new byte[this.bannerApp.Data[index].Length - 32]; + Array.Copy((Array) this.bannerApp.Data[index], 32, (Array) file, 0, file.Length); + byte[] numArray = Headers.IMD5.AddHeader(new Lz77().Compress(file)); + this.bannerApp.Data[index] = numArray; + this.bannerApp.Nodes[index].SizeOfData = (uint) numArray.Length; + } + else if (Lz77.IsLz77Compressed(this.bannerApp.Data[index]) && this.lz77DecompressBannerAndIcon) + { + this.fireDebug(" Decompressing {0}...", (object) this.bannerApp.StringTable[index]); + byte[] file = new byte[this.bannerApp.Data[index].Length - 32]; + Array.Copy((Array) this.bannerApp.Data[index], 32, (Array) file, 0, file.Length); + byte[] numArray = Headers.IMD5.AddHeader(new Lz77().Decompress(file)); + this.bannerApp.Data[index] = numArray; + this.bannerApp.Nodes[index].SizeOfData = (uint) numArray.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); + ContentIndices[] sortedContentList = this.tmd.GetSortedContentList(); + for (int index = 0; index < sortedContentList.Length; ++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[sortedContentList[index].Index].ContentID.ToString("x8")); + this.fireDebug(" -> Index: 0x{0}", (object) this.tmd.Contents[sortedContentList[index].Index].Index.ToString("x4")); + this.fireDebug(" -> Type: 0x{0} ({1})", (object) ((ushort) this.tmd.Contents[sortedContentList[index].Index].Type).ToString("x4"), (object) this.tmd.Contents[sortedContentList[index].Index].Type.ToString()); + this.fireDebug(" -> Size: {0} bytes", (object) this.tmd.Contents[sortedContentList[index].Index].Size); + this.fireDebug(" -> Hash: {0}", (object) Shared.ByteArrayToString(this.tmd.Contents[sortedContentList[index].Index].Hash)); + byte[] buffer = this.encryptContent(this.contents[sortedContentList[index].Index], sortedContentList[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 str1 = this.tik.TitleID.ToString("x16"); + this.fireDebug(" Saving Certificate Chain: {0}.cert", (object) str1); + CertificateChain cert = this.cert; + string str2 = unpackDir; + char directorySeparatorChar = Path.DirectorySeparatorChar; + string str3 = directorySeparatorChar.ToString(); + string str4 = str1; + string savePath1 = str2 + str3 + str4 + ".cert"; + cert.Save(savePath1); + this.fireDebug(" Saving Ticket: {0}.tik", (object) str1); + Ticket tik = this.tik; + string str5 = unpackDir; + directorySeparatorChar = Path.DirectorySeparatorChar; + string str6 = directorySeparatorChar.ToString(); + string str7 = str1; + string savePath2 = str5 + str6 + str7 + ".tik"; + tik.Save(savePath2); + this.fireDebug(" Saving TMD: {0}.tmd", (object) str1); + TMD tmd = this.tmd; + string str8 = unpackDir; + directorySeparatorChar = Path.DirectorySeparatorChar; + string str9 = directorySeparatorChar.ToString(); + string str10 = str1; + string savePath3 = str8 + str9 + str10 + ".tmd"; + tmd.Save(savePath3); + 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")); + object[] objArray = new object[2]; + ushort num = (ushort) this.tmd.Contents[index].Type; + objArray[0] = (object) num.ToString("x4"); + objArray[1] = (object) this.tmd.Contents[index].Type.ToString(); + this.fireDebug(" -> Type: 0x{0} ({1})", objArray); + this.fireDebug(" -> Size: {0} bytes", (object) this.tmd.Contents[index].Size); + this.fireDebug(" -> Hash: {0}", (object) Shared.ByteArrayToString(this.tmd.Contents[index].Hash)); + string str11 = unpackDir; + directorySeparatorChar = Path.DirectorySeparatorChar; + string str12 = directorySeparatorChar.ToString(); + string str13; + if (!nameContentId) + { + num = this.tmd.Contents[index].Index; + str13 = num.ToString("x8"); + } + else + str13 = this.tmd.Contents[index].ContentID.ToString("x8"); + using (FileStream fileStream = new FileStream(str11 + str12 + str13 + ".app", FileMode.Create)) + fileStream.Write(this.contents[index], 0, this.contents[index].Length); + } + this.fireDebug(" Saving Footer: {0}.footer", (object) str1); + string str14 = unpackDir; + directorySeparatorChar = Path.DirectorySeparatorChar; + string str15 = directorySeparatorChar.ToString(); + string str16 = str1; + using (FileStream fileStream = new FileStream(str14 + str15 + str16 + ".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[(int) 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[(int) 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[(int) 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!"); + long position; + for (int contentIndex = 0; contentIndex < (int) this.tmd.NumOfContents; ++contentIndex) + { + this.fireProgress((contentIndex + 1) * 100 / (int) this.tmd.NumOfContents); + object[] objArray1 = new object[3] + { + (object) (contentIndex + 1), + (object) this.tmd.NumOfContents, + null + }; + position = wadFile.Position; + objArray1[2] = (object) position.ToString("x8").ToUpper(); + this.fireDebug(" Reading Content #{0} of {1}... (Offset: 0x{2})", objArray1); + this.fireDebug(" -> Content ID: 0x{0}", (object) this.tmd.Contents[contentIndex].ContentID.ToString("x8")); + object[] objArray2 = new object[1]; + ushort num = this.tmd.Contents[contentIndex].Index; + objArray2[0] = (object) num.ToString("x4"); + this.fireDebug(" -> Index: 0x{0}", objArray2); + object[] objArray3 = new object[2]; + num = (ushort) this.tmd.Contents[contentIndex].Type; + objArray3[0] = (object) num.ToString("x4"); + objArray3[1] = (object) this.tmd.Contents[contentIndex].Type.ToString(); + this.fireDebug(" -> Type: 0x{0} ({1})", objArray3); + 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[] numArray4 = new byte[Shared.AddPadding((int) this.tmd.Contents[contentIndex].Size, 16)]; + wadFile.Read(numArray4, 0, numArray4.Length); + byte[] array = this.decryptContent(numArray4, 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 /!\\ /!\\ /!\\"); + // ISSUE: variable of a boxed type + int local = contentIndex + 1; + string str1 = this.tmd.Contents[contentIndex].ContentID.ToString("x8"); + num = this.tmd.Contents[contentIndex].Index; + string str2 = num.ToString("x4"); + this.fireWarning(string.Format("Content #{0} (Content ID: 0x{1}; Index: 0x{2}): Hashes do not match! The content might be corrupted!", (object) local, (object) str1, (object) str2)); + } + 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) + { + object[] objArray = new object[1]; + position = wadFile.Position; + objArray[0] = (object) position.ToString("x8").ToUpper(); + this.fireDebug(" Reading Footer... (Offset: 0x{0})", objArray); + this.footer = new byte[(int) 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 num = 0; + ref int local = ref num; + if (!int.TryParse(s, out local)) + return; + this.creationTimeUTC = this.creationTimeUTC.AddSeconds((double) num); + } + + 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); + } +} diff --git a/WAD_Header.cs b/WAD_Header.cs new file mode 100644 index 0000000..5579902 --- /dev/null +++ b/WAD_Header.cs @@ -0,0 +1,68 @@ +// Decompiled with JetBrains decompiler +// Type: libWiiSharp.WAD_Header +// Assembly: libWiiSharp, Version=0.4.0.0, Culture=neutral, PublicKeyToken=null +// MVID: FBF36F3D-B5D6-481F-B5F5-1BD3C19E13B2 +// Assembly location: C:\Users\theso\Downloads\NCPatcher\pack\libWiiSharp.dll + +using System; +using System.IO; + +namespace libWiiSharp +{ + public class WAD_Header + { + private uint headerSize = 32; + private uint wadType = 1232273408; + private uint certSize = 2560; + private uint reserved; + private uint tikSize = 676; + private uint tmdSize; + private uint contentSize; + private uint footerSize; + + public uint HeaderSize => this.headerSize; + + public uint WadType + { + get => this.wadType; + set => this.wadType = value; + } + + public uint CertSize => this.certSize; + + public uint Reserved => this.reserved; + + public uint TicketSize => this.tikSize; + + public uint TmdSize + { + get => this.tmdSize; + set => this.tmdSize = value; + } + + public uint ContentSize + { + get => this.contentSize; + set => this.contentSize = value; + } + + public uint FooterSize + { + get => this.footerSize; + set => this.footerSize = value; + } + + public void Write(Stream writeStream) + { + writeStream.Seek(0L, SeekOrigin.Begin); + writeStream.Write(BitConverter.GetBytes(Shared.Swap(this.headerSize)), 0, 4); + writeStream.Write(BitConverter.GetBytes(Shared.Swap(this.wadType)), 0, 4); + writeStream.Write(BitConverter.GetBytes(Shared.Swap(this.certSize)), 0, 4); + writeStream.Write(BitConverter.GetBytes(Shared.Swap(this.reserved)), 0, 4); + writeStream.Write(BitConverter.GetBytes(Shared.Swap(this.tikSize)), 0, 4); + writeStream.Write(BitConverter.GetBytes(Shared.Swap(this.tmdSize)), 0, 4); + writeStream.Write(BitConverter.GetBytes(Shared.Swap(this.contentSize)), 0, 4); + writeStream.Write(BitConverter.GetBytes(Shared.Swap(this.footerSize)), 0, 4); + } + } +} diff --git a/Wave.cs b/Wave.cs new file mode 100644 index 0000000..de01fab --- /dev/null +++ b/Wave.cs @@ -0,0 +1,208 @@ +// Decompiled with JetBrains decompiler +// Type: libWiiSharp.Wave +// Assembly: libWiiSharp, Version=0.4.0.0, Culture=neutral, PublicKeyToken=null +// MVID: FBF36F3D-B5D6-481F-B5F5-1BD3C19E13B2 +// Assembly location: C:\Users\theso\Downloads\NCPatcher\pack\libWiiSharp.dll + +using System; +using System.IO; + +namespace libWiiSharp +{ + public class Wave : IDisposable + { + private WaveHeader header = new WaveHeader(); + private WaveFmtChunk fmt = new WaveFmtChunk(); + private WaveDataChunk data = new WaveDataChunk(); + private WaveSmplChunk smpl = new WaveSmplChunk(); + private bool hasSmpl; + private bool isDisposed; + + public int SampleRate => (int) this.fmt.SampleRate; + + public int BitDepth => (int) this.fmt.BitsPerSample; + + public int NumChannels => (int) this.fmt.NumChannels; + + public int NumLoops => !this.hasSmpl ? 0 : (int) this.smpl.NumLoops; + + public int LoopStart => this.NumLoops == 0 ? 0 : (int) this.smpl.Loops[0].LoopStart; + + public int NumSamples => (int) ((long) this.data.DataSize / (long) ((int) this.fmt.BitsPerSample / 8) / (long) this.fmt.NumChannels); + + public int DataFormat => (int) this.fmt.AudioFormat; + + public byte[] SampleData => this.data.Data; + + public int PlayLength => (int) ((long) (this.data.DataSize / (uint) this.fmt.NumChannels) / (long) ((int) this.fmt.BitsPerSample / 8) / (long) this.fmt.SampleRate); + + public Wave(string pathToFile) + { + using (FileStream fileStream = new FileStream(pathToFile, FileMode.Open)) + { + using (BinaryReader reader = new BinaryReader((Stream) fileStream)) + this.parseWave(reader); + } + } + + public Wave(Stream wave) => this.parseWave(new BinaryReader(wave)); + + public Wave(byte[] waveFile) + { + using (MemoryStream memoryStream = new MemoryStream(waveFile)) + { + using (BinaryReader reader = new BinaryReader((Stream) memoryStream)) + this.parseWave(reader); + } + } + + public Wave(int numChannels, int bitsPerSample, int sampleRate, byte[] samples) + { + this.fmt.SampleRate = (uint) sampleRate; + this.fmt.NumChannels = (ushort) numChannels; + this.fmt.BitsPerSample = (ushort) bitsPerSample; + this.data.Data = samples; + } + + ~Wave() => this.Dispose(false); + + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize((object) this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing && !this.isDisposed) + { + this.header = (WaveHeader) null; + this.fmt = (WaveFmtChunk) null; + this.data = (WaveDataChunk) null; + this.smpl = (WaveSmplChunk) null; + } + this.isDisposed = true; + } + + public void Write(Stream writeStream) => this.writeToStream(new BinaryWriter(writeStream)); + + public MemoryStream ToMemoryStream() + { + MemoryStream memoryStream = new MemoryStream(); + this.writeToStream(new BinaryWriter((Stream) memoryStream)); + return memoryStream; + } + + public byte[] ToByteArray() => this.ToMemoryStream().ToArray(); + + public void Save(string savePath) + { + using (FileStream fileStream = new FileStream(savePath, FileMode.Create)) + { + using (BinaryWriter writer = new BinaryWriter((Stream) fileStream)) + this.writeToStream(writer); + } + } + + public void AddLoop(int loopStartSample) + { + this.smpl.AddLoop(loopStartSample, this.NumSamples); + this.hasSmpl = true; + } + + public void RemoveLoop() => this.hasSmpl = false; + + public void TrimStart(int newStartSample) + { + int offset = (int) this.fmt.NumChannels * ((int) this.fmt.BitsPerSample / 8) * newStartSample; + MemoryStream memoryStream = new MemoryStream(); + memoryStream.Write(this.data.Data, offset, this.data.Data.Length - offset); + this.data.Data = memoryStream.ToArray(); + memoryStream.Dispose(); + } + + private void writeToStream(BinaryWriter writer) + { + this.header.FileSize = (uint) (4 + (int) this.fmt.FmtSize + 8 + (int) this.data.DataSize + 8 + (this.hasSmpl ? (int) this.smpl.SmplSize + 8 : 0)); + this.header.Write(writer); + this.fmt.Write(writer); + this.data.Write(writer); + if (!this.hasSmpl) + return; + this.smpl.Write(writer); + } + + private void parseWave(BinaryReader reader) + { + bool[] flagArray = new bool[3]; + while (reader.BaseStream.Position < reader.BaseStream.Length - 4L) + { + uint num1 = Shared.Swap(reader.ReadUInt32()); + uint num2 = reader.ReadUInt32(); + long offset = reader.BaseStream.Position + (long) num2; + switch (num1) + { + case 1380533830: + try + { + reader.BaseStream.Seek(-8L, SeekOrigin.Current); + this.header.Read(reader); + flagArray[0] = true; + break; + } + catch + { + reader.BaseStream.Seek(offset, SeekOrigin.Begin); + break; + } + case 1684108385: + try + { + reader.BaseStream.Seek(-8L, SeekOrigin.Current); + this.data.Read(reader); + flagArray[2] = true; + break; + } + catch + { + reader.BaseStream.Seek(offset, SeekOrigin.Begin); + break; + } + case 1718449184: + try + { + reader.BaseStream.Seek(-8L, SeekOrigin.Current); + this.fmt.Read(reader); + flagArray[1] = true; + break; + } + catch + { + reader.BaseStream.Seek(offset, SeekOrigin.Begin); + break; + } + case 1936552044: + try + { + reader.BaseStream.Seek(-8L, SeekOrigin.Current); + this.smpl.Read(reader); + this.hasSmpl = true; + break; + } + catch + { + reader.BaseStream.Seek(offset, SeekOrigin.Begin); + break; + } + default: + reader.BaseStream.Seek((long) num2, SeekOrigin.Current); + break; + } + if (flagArray[0] && flagArray[1] && (flagArray[2] && this.hasSmpl)) + break; + } + if (!flagArray[0] || !flagArray[1] || !flagArray[2]) + throw new Exception("Couldn't parse Wave file..."); + } + } +} diff --git a/WaveDataChunk.cs b/WaveDataChunk.cs new file mode 100644 index 0000000..7dd5a04 --- /dev/null +++ b/WaveDataChunk.cs @@ -0,0 +1,43 @@ +// Decompiled with JetBrains decompiler +// Type: libWiiSharp.WaveDataChunk +// Assembly: libWiiSharp, Version=0.4.0.0, Culture=neutral, PublicKeyToken=null +// MVID: FBF36F3D-B5D6-481F-B5F5-1BD3C19E13B2 +// Assembly location: C:\Users\theso\Downloads\NCPatcher\pack\libWiiSharp.dll + +using System; +using System.IO; + +namespace libWiiSharp +{ + internal class WaveDataChunk + { + private uint dataId = 1684108385; + private uint dataSize = 8; + private byte[] data; + + public uint DataSize => this.dataSize; + + public byte[] Data + { + get => this.data; + set + { + this.data = value; + this.dataSize = (uint) this.data.Length; + } + } + + public void Write(BinaryWriter writer) + { + writer.Write(Shared.Swap(this.dataId)); + writer.Write(this.dataSize); + writer.Write(this.data, 0, this.data.Length); + } + + public void Read(BinaryReader reader) + { + this.dataSize = (int) Shared.Swap(reader.ReadUInt32()) == (int) this.dataId ? reader.ReadUInt32() : throw new Exception("Wrong chunk ID!"); + this.data = reader.ReadBytes((int) this.dataSize); + } + } +} diff --git a/WaveFmtChunk.cs b/WaveFmtChunk.cs new file mode 100644 index 0000000..57bde38 --- /dev/null +++ b/WaveFmtChunk.cs @@ -0,0 +1,70 @@ +// Decompiled with JetBrains decompiler +// Type: libWiiSharp.WaveFmtChunk +// Assembly: libWiiSharp, Version=0.4.0.0, Culture=neutral, PublicKeyToken=null +// MVID: FBF36F3D-B5D6-481F-B5F5-1BD3C19E13B2 +// Assembly location: C:\Users\theso\Downloads\NCPatcher\pack\libWiiSharp.dll + +using System; +using System.IO; + +namespace libWiiSharp +{ + internal class WaveFmtChunk + { + private uint fmtId = 1718449184; + private uint fmtSize = 16; + private ushort audioFormat = 1; + private ushort numChannels = 2; + private uint sampleRate = 44100; + private uint byteRate; + private ushort blockAlign; + private ushort bitsPerSample = 16; + + public uint FmtSize => this.fmtSize; + + public ushort NumChannels + { + get => this.numChannels; + set => this.numChannels = value; + } + + public uint SampleRate + { + get => this.sampleRate; + set => this.sampleRate = value; + } + + public ushort BitsPerSample + { + get => this.bitsPerSample; + set => this.bitsPerSample = value; + } + + public uint AudioFormat => (uint) this.audioFormat; + + public void Write(BinaryWriter writer) + { + this.byteRate = this.sampleRate * (uint) this.numChannels * (uint) this.bitsPerSample / 8U; + this.blockAlign = (ushort) ((int) this.numChannels * (int) this.bitsPerSample / 8); + writer.Write(Shared.Swap(this.fmtId)); + writer.Write(this.fmtSize); + writer.Write(this.audioFormat); + writer.Write(this.numChannels); + writer.Write(this.sampleRate); + writer.Write(this.byteRate); + writer.Write(this.blockAlign); + writer.Write(this.bitsPerSample); + } + + public void Read(BinaryReader reader) + { + this.fmtSize = (int) Shared.Swap(reader.ReadUInt32()) == (int) this.fmtId ? reader.ReadUInt32() : throw new Exception("Wrong chunk ID!"); + this.audioFormat = reader.ReadUInt16(); + this.numChannels = reader.ReadUInt16(); + this.sampleRate = reader.ReadUInt32(); + this.byteRate = reader.ReadUInt32(); + this.blockAlign = reader.ReadUInt16(); + this.bitsPerSample = reader.ReadUInt16(); + } + } +} diff --git a/WaveHeader.cs b/WaveHeader.cs new file mode 100644 index 0000000..17442d6 --- /dev/null +++ b/WaveHeader.cs @@ -0,0 +1,38 @@ +// Decompiled with JetBrains decompiler +// Type: libWiiSharp.WaveHeader +// Assembly: libWiiSharp, Version=0.4.0.0, Culture=neutral, PublicKeyToken=null +// MVID: FBF36F3D-B5D6-481F-B5F5-1BD3C19E13B2 +// Assembly location: C:\Users\theso\Downloads\NCPatcher\pack\libWiiSharp.dll + +using System; +using System.IO; + +namespace libWiiSharp +{ + internal class WaveHeader + { + private uint headerId = 1380533830; + private uint fileSize = 12; + private uint format = 1463899717; + + public uint FileSize + { + get => this.fileSize; + set => this.fileSize = value; + } + + public void Write(BinaryWriter writer) + { + writer.Write(Shared.Swap(this.headerId)); + writer.Write(this.fileSize); + writer.Write(Shared.Swap(this.format)); + } + + public void Read(BinaryReader reader) + { + this.fileSize = (int) Shared.Swap(reader.ReadUInt32()) == (int) this.headerId ? reader.ReadUInt32() : throw new Exception("Not a valid RIFF Wave file!"); + if ((int) Shared.Swap(reader.ReadUInt32()) != (int) this.format) + throw new Exception("Not a valid RIFF Wave file!"); + } + } +} diff --git a/WaveSmplChunk.cs b/WaveSmplChunk.cs new file mode 100644 index 0000000..e5d5192 --- /dev/null +++ b/WaveSmplChunk.cs @@ -0,0 +1,88 @@ +// Decompiled with JetBrains decompiler +// Type: libWiiSharp.WaveSmplChunk +// Assembly: libWiiSharp, Version=0.4.0.0, Culture=neutral, PublicKeyToken=null +// MVID: FBF36F3D-B5D6-481F-B5F5-1BD3C19E13B2 +// Assembly location: C:\Users\theso\Downloads\NCPatcher\pack\libWiiSharp.dll + +using System; +using System.Collections.Generic; +using System.IO; + +namespace libWiiSharp +{ + internal class WaveSmplChunk + { + private uint smplId = 1936552044; + private uint smplSize = 36; + private uint manufacturer; + private uint product; + private uint samplePeriod; + private uint unityNote = 60; + private uint pitchFraction; + private uint smpteFormat; + private uint smpteOffset; + private uint numLoops; + private uint samplerData; + private List smplLoops = new List(); + + public uint SmplSize => this.smplSize; + + public uint NumLoops => this.numLoops; + + public WaveSmplLoop[] Loops => this.smplLoops.ToArray(); + + public void AddLoop(int loopStartSample, int loopEndSample) + { + this.RemoveAllLoops(); + ++this.numLoops; + this.smplLoops.Add(new WaveSmplLoop() + { + LoopStart = (uint) loopStartSample, + LoopEnd = (uint) loopEndSample + }); + } + + public void RemoveAllLoops() + { + this.smplLoops.Clear(); + this.numLoops = 0U; + } + + public void Write(BinaryWriter writer) + { + writer.Write(Shared.Swap(this.smplId)); + writer.Write(this.smplSize); + writer.Write(this.manufacturer); + writer.Write(this.product); + writer.Write(this.samplePeriod); + writer.Write(this.unityNote); + writer.Write(this.pitchFraction); + writer.Write(this.smpteFormat); + writer.Write(this.smpteOffset); + writer.Write(this.numLoops); + writer.Write(this.samplerData); + for (int index = 0; (long) index < (long) this.numLoops; ++index) + this.smplLoops[index].Write(writer); + } + + public void Read(BinaryReader reader) + { + this.smplSize = (int) Shared.Swap(reader.ReadUInt32()) == (int) this.smplId ? reader.ReadUInt32() : throw new Exception("Wrong chunk ID!"); + this.manufacturer = reader.ReadUInt32(); + this.product = reader.ReadUInt32(); + this.samplePeriod = reader.ReadUInt32(); + this.unityNote = reader.ReadUInt32(); + this.pitchFraction = reader.ReadUInt32(); + this.smpteFormat = reader.ReadUInt32(); + this.smpteOffset = reader.ReadUInt32(); + this.numLoops = reader.ReadUInt32(); + this.samplerData = reader.ReadUInt32(); + for (int index = 0; (long) index < (long) this.numLoops; ++index) + { + WaveSmplLoop waveSmplLoop = new WaveSmplLoop(); + waveSmplLoop.Read(reader); + this.smplLoops.Add(waveSmplLoop); + } + } + } +} diff --git a/WaveSmplLoop.cs b/WaveSmplLoop.cs new file mode 100644 index 0000000..0c297ed --- /dev/null +++ b/WaveSmplLoop.cs @@ -0,0 +1,52 @@ +// Decompiled with JetBrains decompiler +// Type: libWiiSharp.WaveSmplLoop +// Assembly: libWiiSharp, Version=0.4.0.0, Culture=neutral, PublicKeyToken=null +// MVID: FBF36F3D-B5D6-481F-B5F5-1BD3C19E13B2 +// Assembly location: C:\Users\theso\Downloads\NCPatcher\pack\libWiiSharp.dll + +using System.IO; + +namespace libWiiSharp +{ + internal class WaveSmplLoop + { + private uint cuePointId; + private uint type; + private uint start; + private uint end; + private uint fraction; + private uint playCount; + + public uint LoopStart + { + get => this.start; + set => this.start = value; + } + + public uint LoopEnd + { + get => this.end; + set => this.end = value; + } + + public void Write(BinaryWriter writer) + { + writer.Write(this.cuePointId); + writer.Write(this.type); + writer.Write(this.start); + writer.Write(this.end); + writer.Write(this.fraction); + writer.Write(this.playCount); + } + + public void Read(BinaryReader reader) + { + this.cuePointId = reader.ReadUInt32(); + this.type = reader.ReadUInt32(); + this.start = reader.ReadUInt32(); + this.end = reader.ReadUInt32(); + this.fraction = reader.ReadUInt32(); + this.playCount = reader.ReadUInt32(); + } + } +} diff --git a/libWiiSharp.csproj b/libWiiSharp.csproj new file mode 100644 index 0000000..57c0a2e --- /dev/null +++ b/libWiiSharp.csproj @@ -0,0 +1,97 @@ + + + + + latest + Debug + AnyCPU + {DA1DDE59-7F59-4EBA-91E9-B06729E8E131} + Library + libWiiSharp + v4.0 + + 0.4.0.0 + 512 + libWiiSharp + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + false + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + false + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/libWiiSharp.pdb b/libWiiSharp.pdb new file mode 100644 index 0000000..daee905 Binary files /dev/null and b/libWiiSharp.pdb differ diff --git a/libWiiSharp.sln b/libWiiSharp.sln new file mode 100644 index 0000000..d7e693f --- /dev/null +++ b/libWiiSharp.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "libWiiSharp", "libWiiSharp.csproj", "{DA1DDE59-7F59-4EBA-91E9-B06729E8E131}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {DA1DDE59-7F59-4EBA-91E9-B06729E8E131}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DA1DDE59-7F59-4EBA-91E9-B06729E8E131}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DA1DDE59-7F59-4EBA-91E9-B06729E8E131}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DA1DDE59-7F59-4EBA-91E9-B06729E8E131}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/zlibWrapper.cs b/zlibWrapper.cs new file mode 100644 index 0000000..2c61042 --- /dev/null +++ b/zlibWrapper.cs @@ -0,0 +1,46 @@ +// Decompiled with JetBrains decompiler +// Type: libWiiSharp.zlibWrapper +// Assembly: libWiiSharp, Version=0.4.0.0, Culture=neutral, PublicKeyToken=null +// MVID: FBF36F3D-B5D6-481F-B5F5-1BD3C19E13B2 +// Assembly location: C:\Users\theso\Downloads\NCPatcher\pack\libWiiSharp.dll + +using System; +using System.Runtime.InteropServices; + +namespace libWiiSharp +{ + internal class zlibWrapper + { + [DllImport("zlib1.dll")] + private static extern zlibWrapper.ZLibError compress2( + byte[] dest, + ref int destLength, + byte[] source, + int sourceLength, + int level); + + public static byte[] Compress(byte[] inFile) + { + byte[] array = new byte[inFile.Length + 64]; + int destLength = -1; + zlibWrapper.ZLibError zlibError = zlibWrapper.compress2(array, ref destLength, inFile, inFile.Length, 6); + if (zlibError != zlibWrapper.ZLibError.Z_OK || destLength <= -1 || destLength >= inFile.Length) + throw new Exception("An error occured while compressing! Code: " + zlibError.ToString()); + Array.Resize(ref array, destLength); + return array; + } + + public enum ZLibError + { + Z_VERSION_ERROR = -6, // 0xFFFFFFFA + Z_BUF_ERROR = -5, // 0xFFFFFFFB + Z_MEM_ERROR = -4, // 0xFFFFFFFC + Z_DATA_ERROR = -3, // 0xFFFFFFFD + Z_STREAM_ERROR = -2, // 0xFFFFFFFE + Z_ERRNO = -1, // 0xFFFFFFFF + Z_OK = 0, + Z_STREAM_END = 1, + Z_NEED_DICT = 2, + } + } +}