mirror of
https://github.com/WiiDatabase/nusdownloader.git
synced 2024-11-24 03:09:17 +01:00
748 lines
28 KiB
C#
748 lines
28 KiB
C#
|
/* This file is part of libWiiSharp
|
|||
|
* Copyright (C) 2009 Leathl
|
|||
|
*
|
|||
|
* libWiiSharp 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.
|
|||
|
*
|
|||
|
* libWiiSharp 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 <http://www.gnu.org/licenses/>.
|
|||
|
*/
|
|||
|
|
|||
|
using System;
|
|||
|
using System.IO;
|
|||
|
using System.Security.Cryptography;
|
|||
|
|
|||
|
namespace libWiiSharp
|
|||
|
{
|
|||
|
public class Headers
|
|||
|
{
|
|||
|
private static uint imd5Magic = 0x494d4435;
|
|||
|
private static uint imetMagic = 0x494d4554;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Convert HeaderType to int to get it's Length.
|
|||
|
/// </summary>
|
|||
|
public enum HeaderType
|
|||
|
{
|
|||
|
None = 0,
|
|||
|
/// <summary>
|
|||
|
/// Used in opening.bnr
|
|||
|
/// </summary>
|
|||
|
ShortIMET = 1536,
|
|||
|
/// <summary>
|
|||
|
/// Used in 00000000.app
|
|||
|
/// </summary>
|
|||
|
IMET = 1600,
|
|||
|
/// <summary>
|
|||
|
/// Used in banner.bin / icon.bin
|
|||
|
/// </summary>
|
|||
|
IMD5 = 32,
|
|||
|
}
|
|||
|
|
|||
|
#region Public Functions
|
|||
|
/// <summary>
|
|||
|
/// Checks a file for Headers.
|
|||
|
/// </summary>
|
|||
|
/// <param name="pathToFile"></param>
|
|||
|
/// <returns></returns>
|
|||
|
public static HeaderType DetectHeader(string pathToFile)
|
|||
|
{
|
|||
|
return DetectHeader(File.ReadAllBytes(pathToFile));
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Checks the byte array for Headers.
|
|||
|
/// </summary>
|
|||
|
/// <param name="file"></param>
|
|||
|
/// <returns></returns>
|
|||
|
public static HeaderType DetectHeader(byte[] file)
|
|||
|
{
|
|||
|
if (file.Length > 68)
|
|||
|
if (Shared.Swap(BitConverter.ToUInt32(file, 64)) == imetMagic)
|
|||
|
return HeaderType.ShortIMET;
|
|||
|
if (file.Length > 132)
|
|||
|
if (Shared.Swap(BitConverter.ToUInt32(file, 128)) == imetMagic)
|
|||
|
return HeaderType.IMET;
|
|||
|
if (file.Length > 4)
|
|||
|
if (Shared.Swap(BitConverter.ToUInt32(file, 0)) == imd5Magic)
|
|||
|
return HeaderType.IMD5;
|
|||
|
|
|||
|
return HeaderType.None;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Checks the stream for Headers.
|
|||
|
/// </summary>
|
|||
|
/// <param name="file"></param>
|
|||
|
/// <returns></returns>
|
|||
|
public static HeaderType DetectHeader(Stream file)
|
|||
|
{
|
|||
|
byte[] tmp = new byte[4];
|
|||
|
|
|||
|
if (file.Length > 68)
|
|||
|
{
|
|||
|
file.Seek(64, SeekOrigin.Begin);
|
|||
|
file.Read(tmp, 0, tmp.Length);
|
|||
|
if (Shared.Swap(BitConverter.ToUInt32(tmp, 0)) == imetMagic)
|
|||
|
return HeaderType.ShortIMET;
|
|||
|
}
|
|||
|
if (file.Length > 132)
|
|||
|
{
|
|||
|
file.Seek(128, SeekOrigin.Begin);
|
|||
|
file.Read(tmp, 0, tmp.Length);
|
|||
|
if (Shared.Swap(BitConverter.ToUInt32(tmp, 0)) == imetMagic)
|
|||
|
return HeaderType.IMET;
|
|||
|
}
|
|||
|
if (file.Length > 4)
|
|||
|
{
|
|||
|
file.Seek(0, SeekOrigin.Begin);
|
|||
|
file.Read(tmp, 0, tmp.Length);
|
|||
|
if (Shared.Swap(BitConverter.ToUInt32(tmp, 0)) == imd5Magic)
|
|||
|
return HeaderType.IMD5;
|
|||
|
}
|
|||
|
|
|||
|
return HeaderType.None;
|
|||
|
}
|
|||
|
#endregion
|
|||
|
|
|||
|
public class IMET
|
|||
|
{
|
|||
|
private bool hashesMatch = true;
|
|||
|
private bool isShortImet = false;
|
|||
|
|
|||
|
private byte[] additionalPadding = new byte[64];
|
|||
|
private byte[] padding = new byte[64];
|
|||
|
private uint imetMagic = 0x494d4554;
|
|||
|
private uint sizeOfHeader = 0x00000600; //Without additionalPadding
|
|||
|
private uint unknown = 0x00000003;
|
|||
|
private uint iconSize;
|
|||
|
private uint bannerSize;
|
|||
|
private uint soundSize;
|
|||
|
private uint flags = 0x00000000;
|
|||
|
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];
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Short IMET has a padding of 64 bytes at the beginning while Long IMET has 128.
|
|||
|
/// </summary>
|
|||
|
public bool IsShortIMET { get { return isShortImet; } set { isShortImet = value; } }
|
|||
|
/// <summary>
|
|||
|
/// The size of uncompressed icon.bin
|
|||
|
/// </summary>
|
|||
|
public uint IconSize { get { return iconSize; } set { iconSize = value; } }
|
|||
|
/// <summary>
|
|||
|
/// The size of uncompressed banner.bin
|
|||
|
/// </summary>
|
|||
|
public uint BannerSize { get { return bannerSize; } set { bannerSize = value; } }
|
|||
|
/// <summary>
|
|||
|
/// The size of uncompressed sound.bin
|
|||
|
/// </summary>
|
|||
|
public uint SoundSize { get { return soundSize; } set { soundSize = value; } }
|
|||
|
/// <summary>
|
|||
|
/// The japanese Title.
|
|||
|
/// </summary>
|
|||
|
public string JapaneseTitle { get { return returnTitleAsString(japaneseTitle); } set { setTitleFromString(value, 0); } }
|
|||
|
/// <summary>
|
|||
|
/// The english Title.
|
|||
|
/// </summary>
|
|||
|
public string EnglishTitle { get { return returnTitleAsString(englishTitle); } set { setTitleFromString(value, 1); } }
|
|||
|
/// <summary>
|
|||
|
/// The german Title.
|
|||
|
/// </summary>
|
|||
|
public string GermanTitle { get { return returnTitleAsString(germanTitle); } set { setTitleFromString(value, 2); } }
|
|||
|
/// <summary>
|
|||
|
/// The french Title.
|
|||
|
/// </summary>
|
|||
|
public string FrenchTitle { get { return returnTitleAsString(frenchTitle); } set { setTitleFromString(value, 3); } }
|
|||
|
/// <summary>
|
|||
|
/// The spanish Title.
|
|||
|
/// </summary>
|
|||
|
public string SpanishTitle { get { return returnTitleAsString(spanishTitle); } set { setTitleFromString(value, 4); } }
|
|||
|
/// <summary>
|
|||
|
/// The italian Title.
|
|||
|
/// </summary>
|
|||
|
public string ItalianTitle { get { return returnTitleAsString(italianTitle); } set { setTitleFromString(value, 5); } }
|
|||
|
/// <summary>
|
|||
|
/// The dutch Title.
|
|||
|
/// </summary>
|
|||
|
public string DutchTitle { get { return returnTitleAsString(dutchTitle); } set { setTitleFromString(value, 6); } }
|
|||
|
/// <summary>
|
|||
|
/// The korean Title.
|
|||
|
/// </summary>
|
|||
|
public string KoreanTitle { get { return returnTitleAsString(koreanTitle); } set { setTitleFromString(value, 7); } }
|
|||
|
/// <summary>
|
|||
|
/// All Titles as a string array.
|
|||
|
/// </summary>
|
|||
|
public string[] AllTitles { get { return new string[] { JapaneseTitle, EnglishTitle, GermanTitle, FrenchTitle, SpanishTitle, ItalianTitle, DutchTitle, KoreanTitle }; } }
|
|||
|
/// <summary>
|
|||
|
/// When parsing an IMET header, this value will turn false if the hash stored in the header doesn't match the headers hash.
|
|||
|
/// </summary>
|
|||
|
public bool HashesMatch { get { return hashesMatch; } }
|
|||
|
|
|||
|
#region Public Functions
|
|||
|
/// <summary>
|
|||
|
/// Loads the IMET Header of a file.
|
|||
|
/// </summary>
|
|||
|
/// <param name="pathToFile"></param>
|
|||
|
/// <returns></returns>
|
|||
|
public static IMET Load(string pathToFile)
|
|||
|
{
|
|||
|
return Load(File.ReadAllBytes(pathToFile));
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Loads the IMET Header of a byte array.
|
|||
|
/// </summary>
|
|||
|
/// <param name="fileOrHeader"></param>
|
|||
|
/// <returns></returns>
|
|||
|
public static IMET Load(byte[] fileOrHeader)
|
|||
|
{
|
|||
|
HeaderType type = DetectHeader(fileOrHeader);
|
|||
|
if (type != HeaderType.IMET && type != HeaderType.ShortIMET)
|
|||
|
throw new Exception("No IMET Header found!");
|
|||
|
|
|||
|
IMET s = new IMET();
|
|||
|
if (type == HeaderType.ShortIMET) s.isShortImet = true;
|
|||
|
|
|||
|
MemoryStream ms = new MemoryStream(fileOrHeader);
|
|||
|
try { s.parseHeader(ms); }
|
|||
|
catch { ms.Dispose(); throw; }
|
|||
|
|
|||
|
ms.Dispose();
|
|||
|
return s;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Loads the IMET Header of a stream.
|
|||
|
/// </summary>
|
|||
|
/// <param name="fileOrHeader"></param>
|
|||
|
/// <returns></returns>
|
|||
|
public static IMET Load(Stream fileOrHeader)
|
|||
|
{
|
|||
|
HeaderType type = DetectHeader(fileOrHeader);
|
|||
|
if (type != HeaderType.IMET && type != HeaderType.ShortIMET)
|
|||
|
throw new Exception("No IMET Header found!");
|
|||
|
|
|||
|
IMET s = new IMET();
|
|||
|
if (type == HeaderType.ShortIMET) s.isShortImet = true;
|
|||
|
|
|||
|
s.parseHeader(fileOrHeader);
|
|||
|
return s;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Creates a new IMET Header.
|
|||
|
/// </summary>
|
|||
|
/// <param name="isShortImet"></param>
|
|||
|
/// <param name="iconSize"></param>
|
|||
|
/// <param name="bannerSize"></param>
|
|||
|
/// <param name="soundSize"></param>
|
|||
|
/// <param name="titles"></param>
|
|||
|
/// <returns></returns>
|
|||
|
public static IMET Create(bool isShortImet, int iconSize, int bannerSize, int soundSize, params string[] titles)
|
|||
|
{
|
|||
|
IMET s = new IMET();
|
|||
|
s.isShortImet = isShortImet;
|
|||
|
|
|||
|
for (int i = 0; i < titles.Length; i++)
|
|||
|
s.setTitleFromString(titles[i], i);
|
|||
|
|
|||
|
for (int i = titles.Length; i < 8; i++)
|
|||
|
s.setTitleFromString((titles.Length > 1) ? titles[1] : titles[0], i);
|
|||
|
|
|||
|
s.iconSize = (uint)iconSize;
|
|||
|
s.bannerSize = (uint)bannerSize;
|
|||
|
s.soundSize = (uint)soundSize;
|
|||
|
|
|||
|
return s;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Removes the IMET Header of a file.
|
|||
|
/// </summary>
|
|||
|
/// <param name="pathToFile"></param>
|
|||
|
public static void RemoveHeader(string pathToFile)
|
|||
|
{
|
|||
|
byte[] fileWithoutHeader = RemoveHeader(File.ReadAllBytes(pathToFile));
|
|||
|
File.Delete(pathToFile);
|
|||
|
|
|||
|
File.WriteAllBytes(pathToFile, fileWithoutHeader);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Removes the IMET Header of a byte array.
|
|||
|
/// </summary>
|
|||
|
/// <param name="file"></param>
|
|||
|
/// <returns></returns>
|
|||
|
public static byte[] RemoveHeader(byte[] file)
|
|||
|
{
|
|||
|
HeaderType type = DetectHeader(file);
|
|||
|
if (type != HeaderType.IMET && type != HeaderType.ShortIMET)
|
|||
|
throw new Exception("No IMET Header found!");
|
|||
|
|
|||
|
byte[] fileWithoutHeader = new byte[file.Length - (int)type];
|
|||
|
Array.Copy(file, (int)type, fileWithoutHeader, 0, fileWithoutHeader.Length);
|
|||
|
|
|||
|
return fileWithoutHeader;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Sets all title to the given string.
|
|||
|
/// </summary>
|
|||
|
/// <param name="newTitle"></param>
|
|||
|
public void SetAllTitles(string newTitle)
|
|||
|
{
|
|||
|
for (int i = 0; i < 10; i++)
|
|||
|
setTitleFromString(newTitle, i);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Returns the Header as a memory stream.
|
|||
|
/// </summary>
|
|||
|
/// <returns></returns>
|
|||
|
public MemoryStream ToMemoryStream()
|
|||
|
{
|
|||
|
MemoryStream ms = new MemoryStream();
|
|||
|
|
|||
|
try { writeToStream(ms); }
|
|||
|
catch { ms.Dispose(); throw; }
|
|||
|
|
|||
|
return ms;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Returns the Header as a byte array.
|
|||
|
/// </summary>
|
|||
|
/// <returns></returns>
|
|||
|
public byte[] ToByteArray()
|
|||
|
{
|
|||
|
return ToMemoryStream().ToArray();
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Writes the Header to the given stream.
|
|||
|
/// </summary>
|
|||
|
/// <param name="writeStream"></param>
|
|||
|
public void Write(Stream writeStream)
|
|||
|
{
|
|||
|
writeToStream(writeStream);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Changes the Titles.
|
|||
|
/// </summary>
|
|||
|
/// <param name="newTitles"></param>
|
|||
|
public void ChangeTitles(params string[] newTitles)
|
|||
|
{
|
|||
|
for (int i = 0; i < newTitles.Length; i++)
|
|||
|
setTitleFromString(newTitles[i], i);
|
|||
|
|
|||
|
for (int i = newTitles.Length; i < 8; i++)
|
|||
|
setTitleFromString((newTitles.Length > 1) ? newTitles[1] : newTitles[0], i);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Returns a string array with the Titles.
|
|||
|
/// </summary>
|
|||
|
/// <returns></returns>
|
|||
|
public string[] GetTitles()
|
|||
|
{
|
|||
|
return new string[] { JapaneseTitle, EnglishTitle, GermanTitle, FrenchTitle, SpanishTitle, ItalianTitle, DutchTitle, KoreanTitle };
|
|||
|
}
|
|||
|
#endregion
|
|||
|
|
|||
|
#region Private Functions
|
|||
|
private void writeToStream(Stream writeStream)
|
|||
|
{
|
|||
|
writeStream.Seek(0, SeekOrigin.Begin);
|
|||
|
|
|||
|
if (!isShortImet) writeStream.Write(additionalPadding, 0, additionalPadding.Length);
|
|||
|
|
|||
|
writeStream.Write(padding, 0, padding.Length);
|
|||
|
writeStream.Write(BitConverter.GetBytes(Shared.Swap(imetMagic)), 0, 4);
|
|||
|
writeStream.Write(BitConverter.GetBytes(Shared.Swap(sizeOfHeader)), 0, 4);
|
|||
|
writeStream.Write(BitConverter.GetBytes(Shared.Swap(unknown)), 0, 4);
|
|||
|
writeStream.Write(BitConverter.GetBytes(Shared.Swap(iconSize)), 0, 4);
|
|||
|
writeStream.Write(BitConverter.GetBytes(Shared.Swap(bannerSize)), 0, 4);
|
|||
|
writeStream.Write(BitConverter.GetBytes(Shared.Swap(soundSize)), 0, 4);
|
|||
|
writeStream.Write(BitConverter.GetBytes(Shared.Swap(flags)), 0, 4);
|
|||
|
|
|||
|
writeStream.Write(japaneseTitle, 0, japaneseTitle.Length);
|
|||
|
writeStream.Write(englishTitle, 0, englishTitle.Length);
|
|||
|
writeStream.Write(germanTitle, 0, germanTitle.Length);
|
|||
|
writeStream.Write(frenchTitle, 0, frenchTitle.Length);
|
|||
|
writeStream.Write(spanishTitle, 0, spanishTitle.Length);
|
|||
|
writeStream.Write(italianTitle, 0, italianTitle.Length);
|
|||
|
writeStream.Write(dutchTitle, 0, dutchTitle.Length);
|
|||
|
writeStream.Write(unknownTitle1, 0, unknownTitle1.Length);
|
|||
|
writeStream.Write(unknownTitle2, 0, unknownTitle2.Length);
|
|||
|
writeStream.Write(koreanTitle, 0, koreanTitle.Length);
|
|||
|
writeStream.Write(padding2, 0, padding2.Length);
|
|||
|
|
|||
|
int hashPos = (int)writeStream.Position;
|
|||
|
hash = new byte[16];
|
|||
|
writeStream.Write(hash, 0, hash.Length);
|
|||
|
|
|||
|
byte[] toHash = new byte[writeStream.Position];
|
|||
|
writeStream.Seek(0, SeekOrigin.Begin);
|
|||
|
writeStream.Read(toHash, 0, toHash.Length);
|
|||
|
|
|||
|
computeHash(toHash, isShortImet ? 0 : 0x40);
|
|||
|
|
|||
|
writeStream.Seek(hashPos, SeekOrigin.Begin);
|
|||
|
writeStream.Write(hash, 0, hash.Length);
|
|||
|
}
|
|||
|
|
|||
|
private void computeHash(byte[] headerBytes, int hashPos)
|
|||
|
{
|
|||
|
MD5 md5 = MD5.Create();
|
|||
|
hash = md5.ComputeHash(headerBytes, hashPos, 0x600);
|
|||
|
md5.Clear();
|
|||
|
}
|
|||
|
|
|||
|
private void parseHeader(Stream headerStream)
|
|||
|
{
|
|||
|
headerStream.Seek(0, SeekOrigin.Begin);
|
|||
|
byte[] tmp = new byte[4];
|
|||
|
|
|||
|
if (!isShortImet) headerStream.Read(additionalPadding, 0, additionalPadding.Length);
|
|||
|
headerStream.Read(padding, 0, padding.Length);
|
|||
|
|
|||
|
headerStream.Read(tmp, 0, 4);
|
|||
|
if (Shared.Swap(BitConverter.ToUInt32(tmp, 0)) != imetMagic)
|
|||
|
throw new Exception("Invalid Magic!");
|
|||
|
|
|||
|
headerStream.Read(tmp, 0, 4);
|
|||
|
if (Shared.Swap(BitConverter.ToUInt32(tmp, 0)) != sizeOfHeader)
|
|||
|
throw new Exception("Invalid Header Size!");
|
|||
|
|
|||
|
headerStream.Read(tmp, 0, 4);
|
|||
|
unknown = Shared.Swap(BitConverter.ToUInt32(tmp, 0));
|
|||
|
|
|||
|
headerStream.Read(tmp, 0, 4);
|
|||
|
iconSize = Shared.Swap(BitConverter.ToUInt32(tmp, 0));
|
|||
|
|
|||
|
headerStream.Read(tmp, 0, 4);
|
|||
|
bannerSize = Shared.Swap(BitConverter.ToUInt32(tmp, 0));
|
|||
|
|
|||
|
headerStream.Read(tmp, 0, 4);
|
|||
|
soundSize = Shared.Swap(BitConverter.ToUInt32(tmp, 0));
|
|||
|
|
|||
|
headerStream.Read(tmp, 0, 4);
|
|||
|
flags = Shared.Swap(BitConverter.ToUInt32(tmp, 0));
|
|||
|
|
|||
|
headerStream.Read(japaneseTitle, 0, japaneseTitle.Length);
|
|||
|
headerStream.Read(englishTitle, 0, englishTitle.Length);
|
|||
|
headerStream.Read(germanTitle, 0, germanTitle.Length);
|
|||
|
headerStream.Read(frenchTitle, 0, frenchTitle.Length);
|
|||
|
headerStream.Read(spanishTitle, 0, spanishTitle.Length);
|
|||
|
headerStream.Read(italianTitle, 0, italianTitle.Length);
|
|||
|
headerStream.Read(dutchTitle, 0, dutchTitle.Length);
|
|||
|
headerStream.Read(unknownTitle1, 0, unknownTitle1.Length);
|
|||
|
headerStream.Read(unknownTitle2, 0, unknownTitle2.Length);
|
|||
|
headerStream.Read(koreanTitle, 0, koreanTitle.Length);
|
|||
|
|
|||
|
headerStream.Read(padding2, 0, padding2.Length);
|
|||
|
headerStream.Read(hash, 0, hash.Length);
|
|||
|
|
|||
|
headerStream.Seek(-16, SeekOrigin.Current);
|
|||
|
headerStream.Write(new byte[16], 0, 16);
|
|||
|
|
|||
|
byte[] temp = new byte[headerStream.Length];
|
|||
|
headerStream.Seek(0, SeekOrigin.Begin);
|
|||
|
headerStream.Read(temp, 0, temp.Length);
|
|||
|
|
|||
|
MD5 m = MD5.Create();
|
|||
|
byte[] newHash = m.ComputeHash(temp, (isShortImet) ? 0 : 0x40, 0x600);
|
|||
|
m.Clear();
|
|||
|
|
|||
|
hashesMatch = Shared.CompareByteArrays(newHash, hash);
|
|||
|
}
|
|||
|
|
|||
|
private string returnTitleAsString(byte[] title)
|
|||
|
{
|
|||
|
string tempStr = string.Empty;
|
|||
|
|
|||
|
for (int i = 0; i < 84; i += 2)
|
|||
|
{
|
|||
|
char tempChar = BitConverter.ToChar(new byte[] { title[i + 1], title[i] }, 0);
|
|||
|
if (tempChar != 0x00) tempStr += tempChar;
|
|||
|
}
|
|||
|
|
|||
|
return tempStr;
|
|||
|
}
|
|||
|
|
|||
|
private void setTitleFromString(string title, int titleIndex)
|
|||
|
{
|
|||
|
byte[] tempArray = new byte[84];
|
|||
|
|
|||
|
for (int i = 0; i < title.Length; i++)
|
|||
|
{
|
|||
|
byte[] tempBytes = BitConverter.GetBytes(title[i]);
|
|||
|
tempArray[i * 2 + 1] = tempBytes[0];
|
|||
|
tempArray[i * 2] = tempBytes[1];
|
|||
|
}
|
|||
|
|
|||
|
switch (titleIndex)
|
|||
|
{
|
|||
|
case 0:
|
|||
|
japaneseTitle = tempArray;
|
|||
|
break;
|
|||
|
case 1:
|
|||
|
englishTitle = tempArray;
|
|||
|
break;
|
|||
|
case 2:
|
|||
|
germanTitle = tempArray;
|
|||
|
break;
|
|||
|
case 3:
|
|||
|
frenchTitle = tempArray;
|
|||
|
break;
|
|||
|
case 4:
|
|||
|
spanishTitle = tempArray;
|
|||
|
break;
|
|||
|
case 5:
|
|||
|
italianTitle = tempArray;
|
|||
|
break;
|
|||
|
case 6:
|
|||
|
dutchTitle = tempArray;
|
|||
|
break;
|
|||
|
case 7:
|
|||
|
koreanTitle = tempArray;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
#endregion
|
|||
|
}
|
|||
|
|
|||
|
public class IMD5
|
|||
|
{
|
|||
|
private uint imd5Magic = 0x494d4435;
|
|||
|
private uint fileSize;
|
|||
|
private byte[] padding = new byte[8];
|
|||
|
private byte[] hash = new byte[16];
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// The size of the file without the IMD5 Header.
|
|||
|
/// </summary>
|
|||
|
public uint FileSize { get { return fileSize; } }
|
|||
|
/// <summary>
|
|||
|
/// The hash of the file without the IMD5 Header.
|
|||
|
/// </summary>
|
|||
|
public byte[] Hash { get { return hash; } }
|
|||
|
|
|||
|
private IMD5() { }
|
|||
|
|
|||
|
#region Public Functions
|
|||
|
/// <summary>
|
|||
|
/// Loads the IMD5 Header of a file.
|
|||
|
/// </summary>
|
|||
|
/// <param name="pathToFile"></param>
|
|||
|
/// <returns></returns>
|
|||
|
public static IMD5 Load(string pathToFile)
|
|||
|
{
|
|||
|
return Load(File.ReadAllBytes(pathToFile));
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Loads the IMD5 Header of a byte array.
|
|||
|
/// </summary>
|
|||
|
/// <param name="fileOrHeader"></param>
|
|||
|
/// <returns></returns>
|
|||
|
public static IMD5 Load(byte[] fileOrHeader)
|
|||
|
{
|
|||
|
HeaderType type = DetectHeader(fileOrHeader);
|
|||
|
if (type != HeaderType.IMD5)
|
|||
|
throw new Exception("No IMD5 Header found!");
|
|||
|
|
|||
|
IMD5 h = new IMD5();
|
|||
|
MemoryStream ms = new MemoryStream(fileOrHeader);
|
|||
|
|
|||
|
try { h.parseHeader(ms); }
|
|||
|
catch { ms.Dispose(); throw; }
|
|||
|
|
|||
|
ms.Dispose();
|
|||
|
return h;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Loads the IMD5 Header of a stream.
|
|||
|
/// </summary>
|
|||
|
/// <param name="fileOrHeader"></param>
|
|||
|
/// <returns></returns>
|
|||
|
public static IMD5 Load(Stream fileOrHeader)
|
|||
|
{
|
|||
|
HeaderType type = DetectHeader(fileOrHeader);
|
|||
|
if (type != HeaderType.IMD5)
|
|||
|
throw new Exception("No IMD5 Header found!");
|
|||
|
|
|||
|
IMD5 h = new IMD5();
|
|||
|
h.parseHeader(fileOrHeader);
|
|||
|
return h;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Creates a new IMD5 Header.
|
|||
|
/// </summary>
|
|||
|
/// <param name="file"></param>
|
|||
|
/// <returns></returns>
|
|||
|
public static IMD5 Create(byte[] file)
|
|||
|
{
|
|||
|
IMD5 h = new IMD5();
|
|||
|
|
|||
|
h.fileSize = (uint)file.Length;
|
|||
|
h.computeHash(file);
|
|||
|
|
|||
|
return h;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Adds an IMD5 Header to a file.
|
|||
|
/// </summary>
|
|||
|
/// <param name="pathToFile"></param>
|
|||
|
public static void AddHeader(string pathToFile)
|
|||
|
{
|
|||
|
byte[] fileWithHeader = AddHeader(File.ReadAllBytes(pathToFile));
|
|||
|
File.Delete(pathToFile);
|
|||
|
|
|||
|
using (FileStream fs = new FileStream(pathToFile, FileMode.Create))
|
|||
|
fs.Write(fileWithHeader, 0, fileWithHeader.Length);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Adds an IMD5 Header to a byte array.
|
|||
|
/// </summary>
|
|||
|
/// <param name="file"></param>
|
|||
|
/// <returns></returns>
|
|||
|
public static byte[] AddHeader(byte[] file)
|
|||
|
{
|
|||
|
IMD5 h = IMD5.Create(file);
|
|||
|
|
|||
|
MemoryStream ms = new MemoryStream();
|
|||
|
h.writeToStream(ms);
|
|||
|
ms.Write(file, 0, file.Length);
|
|||
|
|
|||
|
byte[] res = ms.ToArray();
|
|||
|
ms.Dispose();
|
|||
|
return res;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Removes the IMD5 Header of a file.
|
|||
|
/// </summary>
|
|||
|
/// <param name="pathToFile"></param>
|
|||
|
public static void RemoveHeader(string pathToFile)
|
|||
|
{
|
|||
|
byte[] fileWithoutHeader = RemoveHeader(File.ReadAllBytes(pathToFile));
|
|||
|
File.Delete(pathToFile);
|
|||
|
|
|||
|
using (FileStream fs = new FileStream(pathToFile, FileMode.Create))
|
|||
|
fs.Write(fileWithoutHeader, 0, fileWithoutHeader.Length);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Removes the IMD5 Header of a byte array.
|
|||
|
/// </summary>
|
|||
|
/// <param name="file"></param>
|
|||
|
/// <returns></returns>
|
|||
|
public static byte[] RemoveHeader(byte[] file)
|
|||
|
{
|
|||
|
MemoryStream ms = new MemoryStream();
|
|||
|
ms.Write(file, 32, file.Length - 32);
|
|||
|
|
|||
|
byte[] ret = ms.ToArray();
|
|||
|
ms.Dispose();
|
|||
|
|
|||
|
return ret;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Returns the IMD5 Header as a memory stream.
|
|||
|
/// </summary>
|
|||
|
/// <returns></returns>
|
|||
|
public MemoryStream ToMemoryStream()
|
|||
|
{
|
|||
|
MemoryStream ms = new MemoryStream();
|
|||
|
|
|||
|
try { writeToStream(ms); }
|
|||
|
catch { ms.Dispose(); throw; }
|
|||
|
|
|||
|
return ms;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Returns the IMD5 Header as a byte array.
|
|||
|
/// </summary>
|
|||
|
/// <returns></returns>
|
|||
|
public byte[] ToByteArray()
|
|||
|
{
|
|||
|
return ToMemoryStream().ToArray();
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Writes the IMD5 Header to the given stream.
|
|||
|
/// </summary>
|
|||
|
/// <param name="writeStream"></param>
|
|||
|
public void Write(Stream writeStream)
|
|||
|
{
|
|||
|
writeToStream(writeStream);
|
|||
|
}
|
|||
|
#endregion
|
|||
|
|
|||
|
#region Private Functions
|
|||
|
private void writeToStream(Stream writeStream)
|
|||
|
{
|
|||
|
writeStream.Seek(0, SeekOrigin.Begin);
|
|||
|
writeStream.Write(BitConverter.GetBytes(Shared.Swap(imd5Magic)), 0, 4);
|
|||
|
writeStream.Write(BitConverter.GetBytes(Shared.Swap(fileSize)), 0, 4);
|
|||
|
writeStream.Write(padding, 0, padding.Length);
|
|||
|
writeStream.Write(hash, 0, hash.Length);
|
|||
|
}
|
|||
|
|
|||
|
private void computeHash(byte[] bytesToHash)
|
|||
|
{
|
|||
|
MD5 md5 = MD5.Create();
|
|||
|
hash = md5.ComputeHash(bytesToHash);
|
|||
|
md5.Clear();
|
|||
|
}
|
|||
|
|
|||
|
private void parseHeader(Stream headerStream)
|
|||
|
{
|
|||
|
headerStream.Seek(0, SeekOrigin.Begin);
|
|||
|
byte[] tmp = new byte[4];
|
|||
|
|
|||
|
headerStream.Read(tmp, 0, 4);
|
|||
|
if (Shared.Swap(BitConverter.ToUInt32(tmp, 0)) != imd5Magic)
|
|||
|
throw new Exception("Invalid Magic!");
|
|||
|
|
|||
|
headerStream.Read(tmp, 0, 4);
|
|||
|
fileSize = Shared.Swap(BitConverter.ToUInt32(tmp, 0));
|
|||
|
|
|||
|
headerStream.Read(padding, 0, padding.Length);
|
|||
|
headerStream.Read(hash, 0, hash.Length);
|
|||
|
}
|
|||
|
#endregion
|
|||
|
}
|
|||
|
}
|
|||
|
}
|