nusdownloader-fork/libWiiSharp/TMD.cs

491 lines
20 KiB
C#
Raw Normal View History

2022-03-23 17:04:52 +01:00
// Decompiled with JetBrains decompiler
// Type: libWiiSharp.TMD
// Assembly: NUS Downloader, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
// MVID: DDAF9FEC-76DE-4BD8-8A6D-D7CAD5827AC6
// Assembly location: C:\dotpeek\NUS Downloader.exe
using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
namespace libWiiSharp
{
public class TMD : IDisposable
{
private bool fakeSign;
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<TMD_Content> 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<TMD_Content>((IEnumerable<TMD_Content>) value);
this.numOfContents = (ushort) value.Length;
}
}
public bool FakeSign
{
get => this.fakeSign;
set => this.fakeSign = value;
}
~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<TMD_Content>) 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 tmdFile1 = new MemoryStream(tmdFile);
try
{
tmd.parseTmd((Stream) tmdFile1);
}
catch
{
tmdFile1.Dispose();
throw;
}
tmdFile1.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 tmdFile1 = new MemoryStream(tmdFile);
try
{
this.parseTmd((Stream) tmdFile1);
}
catch
{
tmdFile1.Dispose();
throw;
}
tmdFile1.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 writeStream = new FileStream(savePath, FileMode.Create))
this.writeToStream((Stream) writeStream);
}
public MemoryStream ToMemoryStream() => this.ToMemoryStream(false);
public MemoryStream ToMemoryStream(bool fakeSign)
{
if (fakeSign)
this.fakeSign = true;
MemoryStream writeStream = new MemoryStream();
try
{
this.writeToStream((Stream) writeStream);
}
catch
{
writeStream.Dispose();
throw;
}
return writeStream;
}
public byte[] ToByteArray() => this.ToByteArray(false);
public byte[] ToByteArray(bool fakeSign)
{
if (fakeSign)
this.fakeSign = true;
MemoryStream writeStream = new MemoryStream();
try
{
this.writeToStream((Stream) writeStream);
}
catch
{
writeStream.Dispose();
throw;
}
byte[] array = writeStream.ToArray();
writeStream.Dispose();
return array;
}
public void UpdateContents(string contentDir)
{
bool flag = true;
for (int index = 0; index < this.contents.Count; ++index)
{
if (!File.Exists(contentDir + (object) Path.DirectorySeparatorChar + this.contents[index].ContentID.ToString("x8") + ".app"))
{
flag = false;
break;
}
}
if (!flag)
{
for (int index = 0; index < this.contents.Count; ++index)
{
if (!File.Exists(contentDir + (object) Path.DirectorySeparatorChar + this.contents[index].ContentID.ToString("x8") + ".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 path = contentDir + (object) Path.DirectorySeparatorChar + (flag ? (object) this.contents[index].ContentID.ToString("x8") : (object) this.contents[index].Index.ToString("x8")) + ".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;
}
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<ContentIndices> contentIndicesList = new List<ContentIndices>();
for (int index = 0; index < this.contents.Count; ++index)
contentIndicesList.Add(new ContentIndices(index, (int) this.contents[index].Index));
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<TMD_Content>();
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);
}
public event EventHandler<MessageEventArgs> Debug;
private void fireDebug(string debugMessage, params object[] args)
{
EventHandler<MessageEventArgs> debug = this.Debug;
if (debug == null)
return;
debug(new object(), new MessageEventArgs(string.Format(debugMessage, args)));
}
}
}