nusdownloader-fork/libWiiSharp/Headers.cs

590 lines
20 KiB
C#
Raw Normal View History

2022-03-23 17:04:52 +01:00
// Decompiled with JetBrains decompiler
// Type: libWiiSharp.Headers
// 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.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 headerStream = new MemoryStream(fileOrHeader);
try
{
imet.parseHeader((Stream) headerStream);
}
catch
{
headerStream.Dispose();
throw;
}
headerStream.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 sourceIndex = Headers.DetectHeader(file);
switch (sourceIndex)
{
case Headers.HeaderType.ShortIMET:
case Headers.HeaderType.IMET:
byte[] destinationArray = new byte[(int) (file.Length - sourceIndex)];
Array.Copy((Array) file, (int) sourceIndex, (Array) destinationArray, 0, destinationArray.Length);
return destinationArray;
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 writeStream = new MemoryStream();
try
{
this.writeToStream((Stream) writeStream);
}
catch
{
writeStream.Dispose();
throw;
}
return writeStream;
}
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 ? 0 : 64);
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 ? 0 : 64, 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 += (string) (object) ch;
}
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 headerStream = new MemoryStream(fileOrHeader);
try
{
imD5.parseHeader((Stream) headerStream);
}
catch
{
headerStream.Dispose();
throw;
}
headerStream.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 writeStream = new MemoryStream();
imD5.writeToStream((Stream) writeStream);
writeStream.Write(file, 0, file.Length);
byte[] array = writeStream.ToArray();
writeStream.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 writeStream = new MemoryStream();
try
{
this.writeToStream((Stream) writeStream);
}
catch
{
writeStream.Dispose();
throw;
}
return writeStream;
}
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);
}
}
}
}