2021-02-06 21:19:03 -06:00
|
|
|
|
/* This file is part of libWiiSharp
|
|
|
|
|
* Copyright (C) 2009 Leathl
|
2022-03-18 02:09:26 -05:00
|
|
|
|
* Copyright (C) 2020 - 2022 TheShadowEevee, Github Contributors
|
2021-02-06 21:19:03 -06:00
|
|
|
|
*
|
|
|
|
|
* 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/>.
|
|
|
|
|
*/
|
2020-12-28 22:28:44 -06:00
|
|
|
|
|
|
|
|
|
using System;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Security.Cryptography;
|
|
|
|
|
|
|
|
|
|
namespace libWiiSharp
|
|
|
|
|
{
|
2021-02-06 21:19:03 -06:00
|
|
|
|
public class Headers
|
2020-12-28 22:28:44 -06:00
|
|
|
|
{
|
2021-02-06 22:53:40 -06:00
|
|
|
|
private static readonly uint imd5Magic = 1229800501;
|
|
|
|
|
private static readonly uint imetMagic = 1229800788;
|
2020-12-28 22:28:44 -06:00
|
|
|
|
|
2021-02-06 21:19:03 -06:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Convert HeaderType to int to get it's Length.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public enum HeaderType
|
|
|
|
|
{
|
|
|
|
|
None = 0,
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Used in banner.bin / icon.bin
|
|
|
|
|
/// </summary>
|
|
|
|
|
IMD5 = 32,
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Used in opening.bnr
|
|
|
|
|
/// </summary>
|
|
|
|
|
ShortIMET = 1536,
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Used in 00000000.app
|
|
|
|
|
/// </summary>
|
|
|
|
|
IMET = 1600,
|
|
|
|
|
}
|
2020-12-28 22:28:44 -06:00
|
|
|
|
|
2021-02-06 21:19:03 -06:00
|
|
|
|
#region Public Functions
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Checks a file for Headers.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="pathToFile"></param>
|
|
|
|
|
/// <returns></returns>
|
2021-02-06 22:53:40 -06:00
|
|
|
|
public static Headers.HeaderType DetectHeader(string pathToFile)
|
|
|
|
|
{
|
2021-02-07 12:37:46 -06:00
|
|
|
|
return DetectHeader(File.ReadAllBytes(pathToFile));
|
2021-02-06 22:53:40 -06:00
|
|
|
|
}
|
2021-02-06 21:19:03 -06:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Checks the byte array for Headers.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="file"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public static Headers.HeaderType DetectHeader(byte[] file)
|
|
|
|
|
{
|
2021-02-07 12:37:46 -06:00
|
|
|
|
if (file.Length > 68 && (int)Shared.Swap(BitConverter.ToUInt32(file, 64)) == (int)imetMagic)
|
2021-02-06 22:53:40 -06:00
|
|
|
|
{
|
2021-02-07 12:37:46 -06:00
|
|
|
|
return HeaderType.ShortIMET;
|
2021-02-06 22:53:40 -06:00
|
|
|
|
}
|
|
|
|
|
|
2021-02-07 12:37:46 -06:00
|
|
|
|
if (file.Length > 132 && (int)Shared.Swap(BitConverter.ToUInt32(file, 128)) == (int)imetMagic)
|
2021-02-06 22:53:40 -06:00
|
|
|
|
{
|
2021-02-07 12:37:46 -06:00
|
|
|
|
return HeaderType.IMET;
|
2021-02-06 22:53:40 -06:00
|
|
|
|
}
|
|
|
|
|
|
2021-02-07 12:37:46 -06:00
|
|
|
|
return file.Length > 4 && (int)Shared.Swap(BitConverter.ToUInt32(file, 0)) == (int)imd5Magic ? HeaderType.IMD5 : HeaderType.None;
|
2021-02-06 21:19:03 -06:00
|
|
|
|
}
|
2020-12-28 22:28:44 -06:00
|
|
|
|
|
2021-02-06 21:19:03 -06:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Checks the stream for Headers.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="file"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public static Headers.HeaderType DetectHeader(Stream file)
|
2020-12-28 22:28:44 -06:00
|
|
|
|
{
|
2021-02-06 21:19:03 -06:00
|
|
|
|
byte[] buffer = new byte[4];
|
|
|
|
|
if (file.Length > 68L)
|
|
|
|
|
{
|
|
|
|
|
file.Seek(64L, SeekOrigin.Begin);
|
|
|
|
|
file.Read(buffer, 0, buffer.Length);
|
2021-02-07 12:37:46 -06:00
|
|
|
|
if ((int)Shared.Swap(BitConverter.ToUInt32(buffer, 0)) == (int)imetMagic)
|
2021-02-06 22:53:40 -06:00
|
|
|
|
{
|
2021-02-07 12:37:46 -06:00
|
|
|
|
return HeaderType.ShortIMET;
|
2021-02-06 22:53:40 -06:00
|
|
|
|
}
|
2021-02-06 21:19:03 -06:00
|
|
|
|
}
|
|
|
|
|
if (file.Length > 132L)
|
2020-12-28 22:28:44 -06:00
|
|
|
|
{
|
2021-02-06 21:19:03 -06:00
|
|
|
|
file.Seek(128L, SeekOrigin.Begin);
|
|
|
|
|
file.Read(buffer, 0, buffer.Length);
|
2021-02-07 12:37:46 -06:00
|
|
|
|
if ((int)Shared.Swap(BitConverter.ToUInt32(buffer, 0)) == (int)imetMagic)
|
2021-02-06 22:53:40 -06:00
|
|
|
|
{
|
2021-02-07 12:37:46 -06:00
|
|
|
|
return HeaderType.IMET;
|
2021-02-06 22:53:40 -06:00
|
|
|
|
}
|
2020-12-28 22:28:44 -06:00
|
|
|
|
}
|
2021-02-06 21:19:03 -06:00
|
|
|
|
if (file.Length > 4L)
|
2020-12-28 22:28:44 -06:00
|
|
|
|
{
|
2021-02-06 21:19:03 -06:00
|
|
|
|
file.Seek(0L, SeekOrigin.Begin);
|
|
|
|
|
file.Read(buffer, 0, buffer.Length);
|
2021-02-07 12:37:46 -06:00
|
|
|
|
if ((int)Shared.Swap(BitConverter.ToUInt32(buffer, 0)) == (int)imd5Magic)
|
2021-02-06 22:53:40 -06:00
|
|
|
|
{
|
2021-02-07 12:37:46 -06:00
|
|
|
|
return HeaderType.IMD5;
|
2021-02-06 22:53:40 -06:00
|
|
|
|
}
|
2020-12-28 22:28:44 -06:00
|
|
|
|
}
|
2021-02-07 12:37:46 -06:00
|
|
|
|
return HeaderType.None;
|
2020-12-28 22:28:44 -06:00
|
|
|
|
}
|
2021-02-06 21:19:03 -06:00
|
|
|
|
#endregion
|
2020-12-28 22:28:44 -06:00
|
|
|
|
|
2021-02-06 21:19:03 -06:00
|
|
|
|
public class IMET
|
2020-12-28 22:28:44 -06:00
|
|
|
|
{
|
2021-02-06 21:19:03 -06:00
|
|
|
|
private bool hashesMatch = true;
|
|
|
|
|
private bool isShortImet;
|
2021-02-06 22:53:40 -06:00
|
|
|
|
private readonly byte[] additionalPadding = new byte[64];
|
|
|
|
|
private readonly byte[] padding = new byte[64];
|
|
|
|
|
private readonly uint imetMagic = 1229800788;
|
|
|
|
|
private readonly uint sizeOfHeader = 1536;
|
2021-02-06 21:19:03 -06:00
|
|
|
|
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];
|
2021-02-06 22:53:40 -06:00
|
|
|
|
private readonly byte[] unknownTitle1 = new byte[84];
|
|
|
|
|
private readonly byte[] unknownTitle2 = new byte[84];
|
2021-02-06 21:19:03 -06:00
|
|
|
|
private byte[] koreanTitle = new byte[84];
|
2021-02-06 22:53:40 -06:00
|
|
|
|
private readonly byte[] padding2 = new byte[588];
|
2021-02-06 21:19:03 -06:00
|
|
|
|
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
|
|
|
|
|
{
|
2021-02-06 22:53:40 -06:00
|
|
|
|
get => isShortImet;
|
|
|
|
|
set => isShortImet = value;
|
2021-02-06 21:19:03 -06:00
|
|
|
|
}
|
2020-12-28 22:28:44 -06:00
|
|
|
|
|
2021-02-06 21:19:03 -06:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// The size of uncompressed icon.bin
|
|
|
|
|
/// </summary>
|
|
|
|
|
public uint IconSize
|
|
|
|
|
{
|
2021-02-06 22:53:40 -06:00
|
|
|
|
get => iconSize;
|
|
|
|
|
set => iconSize = value;
|
2021-02-06 21:19:03 -06:00
|
|
|
|
}
|
2020-12-28 22:28:44 -06:00
|
|
|
|
|
2021-02-06 21:19:03 -06:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// The size of uncompressed banner.bin
|
|
|
|
|
/// </summary>
|
|
|
|
|
public uint BannerSize
|
|
|
|
|
{
|
2021-02-06 22:53:40 -06:00
|
|
|
|
get => bannerSize;
|
|
|
|
|
set => bannerSize = value;
|
2021-02-06 21:19:03 -06:00
|
|
|
|
}
|
2020-12-28 22:28:44 -06:00
|
|
|
|
|
2021-02-06 21:19:03 -06:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// The size of uncompressed sound.bin
|
|
|
|
|
/// </summary>
|
|
|
|
|
public uint SoundSize
|
|
|
|
|
{
|
2021-02-06 22:53:40 -06:00
|
|
|
|
get => soundSize;
|
|
|
|
|
set => soundSize = value;
|
2021-02-06 21:19:03 -06:00
|
|
|
|
}
|
2020-12-28 22:28:44 -06:00
|
|
|
|
|
2021-02-06 21:19:03 -06:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// The Japanese Title.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public string JapaneseTitle
|
|
|
|
|
{
|
2021-02-06 22:53:40 -06:00
|
|
|
|
get => ReturnTitleAsString(japaneseTitle);
|
|
|
|
|
set => SetTitleFromString(value, 0);
|
2021-02-06 21:19:03 -06:00
|
|
|
|
}
|
2020-12-28 22:28:44 -06:00
|
|
|
|
|
2021-02-06 21:19:03 -06:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// The English Title.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public string EnglishTitle
|
|
|
|
|
{
|
2021-02-06 22:53:40 -06:00
|
|
|
|
get => ReturnTitleAsString(englishTitle);
|
|
|
|
|
set => SetTitleFromString(value, 1);
|
2021-02-06 21:19:03 -06:00
|
|
|
|
}
|
2020-12-28 22:28:44 -06:00
|
|
|
|
|
2021-02-06 21:19:03 -06:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// The German Title.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public string GermanTitle
|
|
|
|
|
{
|
2021-02-06 22:53:40 -06:00
|
|
|
|
get => ReturnTitleAsString(germanTitle);
|
|
|
|
|
set => SetTitleFromString(value, 2);
|
2021-02-06 21:19:03 -06:00
|
|
|
|
}
|
2020-12-28 22:28:44 -06:00
|
|
|
|
|
2021-02-06 21:19:03 -06:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// The French Title.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public string FrenchTitle
|
|
|
|
|
{
|
2021-02-06 22:53:40 -06:00
|
|
|
|
get => ReturnTitleAsString(frenchTitle);
|
|
|
|
|
set => SetTitleFromString(value, 3);
|
2021-02-06 21:19:03 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The Spanish Title.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public string SpanishTitle
|
|
|
|
|
{
|
2021-02-06 22:53:40 -06:00
|
|
|
|
get => ReturnTitleAsString(spanishTitle);
|
|
|
|
|
set => SetTitleFromString(value, 4);
|
2021-02-06 21:19:03 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The Italian Title.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public string ItalianTitle
|
|
|
|
|
{
|
2021-02-06 22:53:40 -06:00
|
|
|
|
get => ReturnTitleAsString(italianTitle);
|
|
|
|
|
set => SetTitleFromString(value, 5);
|
2021-02-06 21:19:03 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The Dutch Title.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public string DutchTitle
|
|
|
|
|
{
|
2021-02-06 22:53:40 -06:00
|
|
|
|
get => ReturnTitleAsString(dutchTitle);
|
|
|
|
|
set => SetTitleFromString(value, 6);
|
2021-02-06 21:19:03 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The Korean Title.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public string KoreanTitle
|
|
|
|
|
{
|
2021-02-06 22:53:40 -06:00
|
|
|
|
get => ReturnTitleAsString(koreanTitle);
|
|
|
|
|
set => SetTitleFromString(value, 7);
|
2021-02-06 21:19:03 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// All Titles as a string array.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public string[] AllTitles => new string[8]
|
|
|
|
|
{
|
2021-02-06 22:53:40 -06:00
|
|
|
|
JapaneseTitle,
|
|
|
|
|
EnglishTitle,
|
|
|
|
|
GermanTitle,
|
|
|
|
|
FrenchTitle,
|
|
|
|
|
SpanishTitle,
|
|
|
|
|
ItalianTitle,
|
|
|
|
|
DutchTitle,
|
|
|
|
|
KoreanTitle
|
2021-02-06 21:19:03 -06:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/// <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>
|
2021-02-06 22:53:40 -06:00
|
|
|
|
public bool HashesMatch => hashesMatch;
|
2021-02-06 21:19:03 -06:00
|
|
|
|
|
|
|
|
|
#region Public Functions
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Loads the IMET Header of a file.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="pathToFile"></param>
|
|
|
|
|
/// <returns></returns>
|
2021-02-06 22:53:40 -06:00
|
|
|
|
public static Headers.IMET Load(string pathToFile)
|
|
|
|
|
{
|
2021-02-07 12:37:46 -06:00
|
|
|
|
return Load(File.ReadAllBytes(pathToFile));
|
2021-02-06 22:53:40 -06:00
|
|
|
|
}
|
2021-02-06 21:19:03 -06:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Loads the IMET Header of a byte array.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="fileOrHeader"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public static Headers.IMET Load(byte[] fileOrHeader)
|
|
|
|
|
{
|
2021-02-07 12:37:46 -06:00
|
|
|
|
Headers.HeaderType headerType = DetectHeader(fileOrHeader);
|
2021-02-06 21:19:03 -06:00
|
|
|
|
switch (headerType)
|
|
|
|
|
{
|
2021-02-07 12:37:46 -06:00
|
|
|
|
case HeaderType.ShortIMET:
|
|
|
|
|
case HeaderType.IMET:
|
2021-02-06 21:19:03 -06:00
|
|
|
|
Headers.IMET imet = new Headers.IMET();
|
2021-02-07 12:37:46 -06:00
|
|
|
|
if (headerType == HeaderType.ShortIMET)
|
2021-02-06 22:53:40 -06:00
|
|
|
|
{
|
2021-02-06 21:19:03 -06:00
|
|
|
|
imet.isShortImet = true;
|
2021-02-06 22:53:40 -06:00
|
|
|
|
}
|
|
|
|
|
|
2021-02-06 21:19:03 -06:00
|
|
|
|
MemoryStream memoryStream = new MemoryStream(fileOrHeader);
|
|
|
|
|
try
|
|
|
|
|
{
|
2021-02-06 22:53:40 -06:00
|
|
|
|
imet.ParseHeader(memoryStream);
|
2021-02-06 21:19:03 -06:00
|
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{
|
|
|
|
|
memoryStream.Dispose();
|
|
|
|
|
throw;
|
|
|
|
|
}
|
|
|
|
|
memoryStream.Dispose();
|
|
|
|
|
return imet;
|
|
|
|
|
default:
|
|
|
|
|
throw new Exception("No IMET Header found!");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Loads the IMET Header of a stream.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="fileOrHeader"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public static Headers.IMET Load(Stream fileOrHeader)
|
|
|
|
|
{
|
2021-02-07 12:37:46 -06:00
|
|
|
|
Headers.HeaderType headerType = DetectHeader(fileOrHeader);
|
2021-02-06 21:19:03 -06:00
|
|
|
|
switch (headerType)
|
|
|
|
|
{
|
2021-02-07 12:37:46 -06:00
|
|
|
|
case HeaderType.ShortIMET:
|
|
|
|
|
case HeaderType.IMET:
|
2021-02-06 21:19:03 -06:00
|
|
|
|
Headers.IMET imet = new Headers.IMET();
|
2021-02-07 12:37:46 -06:00
|
|
|
|
if (headerType == HeaderType.ShortIMET)
|
2021-02-06 22:53:40 -06:00
|
|
|
|
{
|
2021-02-06 21:19:03 -06:00
|
|
|
|
imet.isShortImet = true;
|
2021-02-06 22:53:40 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
imet.ParseHeader(fileOrHeader);
|
2021-02-06 21:19:03 -06:00
|
|
|
|
return imet;
|
|
|
|
|
default:
|
|
|
|
|
throw new Exception("No IMET Header found!");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <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 Headers.IMET Create(
|
|
|
|
|
bool isShortImet,
|
|
|
|
|
int iconSize,
|
|
|
|
|
int bannerSize,
|
|
|
|
|
int soundSize,
|
|
|
|
|
params string[] titles)
|
|
|
|
|
{
|
2021-02-06 22:53:40 -06:00
|
|
|
|
Headers.IMET imet = new Headers.IMET
|
|
|
|
|
{
|
|
|
|
|
isShortImet = isShortImet
|
|
|
|
|
};
|
2021-02-06 21:19:03 -06:00
|
|
|
|
for (int titleIndex = 0; titleIndex < titles.Length; ++titleIndex)
|
2021-02-06 22:53:40 -06:00
|
|
|
|
{
|
|
|
|
|
imet.SetTitleFromString(titles[titleIndex], titleIndex);
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-06 21:19:03 -06:00
|
|
|
|
for (int length = titles.Length; length < 8; ++length)
|
2021-02-06 22:53:40 -06:00
|
|
|
|
{
|
|
|
|
|
imet.SetTitleFromString(titles.Length > 1 ? titles[1] : titles[0], length);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
imet.iconSize = (uint)iconSize;
|
|
|
|
|
imet.bannerSize = (uint)bannerSize;
|
|
|
|
|
imet.soundSize = (uint)soundSize;
|
2021-02-06 21:19:03 -06:00
|
|
|
|
return imet;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Removes the IMET Header of a file.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="pathToFile"></param>
|
|
|
|
|
public static void RemoveHeader(string pathToFile)
|
|
|
|
|
{
|
2021-02-07 12:37:46 -06:00
|
|
|
|
byte[] bytes = RemoveHeader(File.ReadAllBytes(pathToFile));
|
2021-02-06 21:19:03 -06:00
|
|
|
|
File.Delete(pathToFile);
|
|
|
|
|
File.WriteAllBytes(pathToFile, bytes);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Removes the IMET Header of a byte array.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="file"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public static byte[] RemoveHeader(byte[] file)
|
|
|
|
|
{
|
2021-02-07 12:37:46 -06:00
|
|
|
|
Headers.HeaderType headerType = DetectHeader(file);
|
2021-02-06 21:19:03 -06:00
|
|
|
|
switch (headerType)
|
|
|
|
|
{
|
2021-02-07 12:37:46 -06:00
|
|
|
|
case HeaderType.ShortIMET:
|
|
|
|
|
case HeaderType.IMET:
|
2021-02-06 22:53:40 -06:00
|
|
|
|
byte[] numArray = new byte[(int)(file.Length - headerType)];
|
|
|
|
|
Array.Copy(file, (int)headerType, numArray, 0, numArray.Length);
|
|
|
|
|
return numArray;
|
|
|
|
|
default:
|
|
|
|
|
throw new Exception("No IMET Header found!");
|
2021-02-06 21:19:03 -06:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Sets all title to the given string.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="newTitle"></param>
|
|
|
|
|
public void SetAllTitles(string newTitle)
|
|
|
|
|
{
|
|
|
|
|
for (int titleIndex = 0; titleIndex < 10; ++titleIndex)
|
2021-02-06 22:53:40 -06:00
|
|
|
|
{
|
|
|
|
|
SetTitleFromString(newTitle, titleIndex);
|
|
|
|
|
}
|
2021-02-06 21:19:03 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Returns the Header as a memory stream.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public MemoryStream ToMemoryStream()
|
|
|
|
|
{
|
|
|
|
|
MemoryStream memoryStream = new MemoryStream();
|
|
|
|
|
try
|
|
|
|
|
{
|
2021-02-06 22:53:40 -06:00
|
|
|
|
WriteToStream(memoryStream);
|
2021-02-06 21:19:03 -06:00
|
|
|
|
return memoryStream;
|
|
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{
|
|
|
|
|
memoryStream.Dispose();
|
|
|
|
|
throw;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Returns the Header as a byte array.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns></returns>
|
2021-02-06 22:53:40 -06:00
|
|
|
|
public byte[] ToByteArray()
|
|
|
|
|
{
|
|
|
|
|
return ToMemoryStream().ToArray();
|
|
|
|
|
}
|
2021-02-06 21:19:03 -06:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Writes the Header to the given stream.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="writeStream"></param>
|
2021-02-06 22:53:40 -06:00
|
|
|
|
public void Write(Stream writeStream)
|
|
|
|
|
{
|
|
|
|
|
WriteToStream(writeStream);
|
|
|
|
|
}
|
2021-02-06 21:19:03 -06:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Changes the Titles.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="newTitles"></param>
|
|
|
|
|
public void ChangeTitles(params string[] newTitles)
|
|
|
|
|
{
|
|
|
|
|
for (int titleIndex = 0; titleIndex < newTitles.Length; ++titleIndex)
|
2021-02-06 22:53:40 -06:00
|
|
|
|
{
|
|
|
|
|
SetTitleFromString(newTitles[titleIndex], titleIndex);
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-06 21:19:03 -06:00
|
|
|
|
for (int length = newTitles.Length; length < 8; ++length)
|
2021-02-06 22:53:40 -06:00
|
|
|
|
{
|
|
|
|
|
SetTitleFromString(newTitles.Length > 1 ? newTitles[1] : newTitles[0], length);
|
|
|
|
|
}
|
2021-02-06 21:19:03 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Returns a string array with the Titles.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns></returns>
|
2021-02-06 22:53:40 -06:00
|
|
|
|
public string[] GetTitles()
|
|
|
|
|
{
|
|
|
|
|
return new string[8]
|
|
|
|
|
{
|
|
|
|
|
JapaneseTitle,
|
|
|
|
|
EnglishTitle,
|
|
|
|
|
GermanTitle,
|
|
|
|
|
FrenchTitle,
|
|
|
|
|
SpanishTitle,
|
|
|
|
|
ItalianTitle,
|
|
|
|
|
DutchTitle,
|
|
|
|
|
KoreanTitle
|
|
|
|
|
};
|
|
|
|
|
}
|
2021-02-06 21:19:03 -06:00
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Private Functions
|
2021-02-06 22:53:40 -06:00
|
|
|
|
private void WriteToStream(Stream writeStream)
|
2021-02-06 21:19:03 -06:00
|
|
|
|
{
|
|
|
|
|
writeStream.Seek(0L, SeekOrigin.Begin);
|
2021-02-06 22:53:40 -06:00
|
|
|
|
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 position = (int)writeStream.Position;
|
|
|
|
|
hash = new byte[16];
|
|
|
|
|
writeStream.Write(hash, 0, hash.Length);
|
2021-02-06 21:19:03 -06:00
|
|
|
|
byte[] numArray = new byte[writeStream.Position];
|
|
|
|
|
writeStream.Seek(0L, SeekOrigin.Begin);
|
|
|
|
|
writeStream.Read(numArray, 0, numArray.Length);
|
2021-02-06 22:53:40 -06:00
|
|
|
|
ComputeHash(numArray, !isShortImet ? 64 : 0);
|
|
|
|
|
writeStream.Seek(position, SeekOrigin.Begin);
|
|
|
|
|
writeStream.Write(hash, 0, hash.Length);
|
2021-02-06 21:19:03 -06:00
|
|
|
|
}
|
|
|
|
|
|
2021-02-06 22:53:40 -06:00
|
|
|
|
private void ComputeHash(byte[] headerBytes, int hashPos)
|
2021-02-06 21:19:03 -06:00
|
|
|
|
{
|
|
|
|
|
MD5 md5 = MD5.Create();
|
2021-02-06 22:53:40 -06:00
|
|
|
|
hash = md5.ComputeHash(headerBytes, hashPos, 1536);
|
2021-02-06 21:19:03 -06:00
|
|
|
|
md5.Clear();
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-06 22:53:40 -06:00
|
|
|
|
private void ParseHeader(Stream headerStream)
|
2021-02-06 21:19:03 -06:00
|
|
|
|
{
|
|
|
|
|
headerStream.Seek(0L, SeekOrigin.Begin);
|
|
|
|
|
byte[] buffer1 = new byte[4];
|
2021-02-06 22:53:40 -06:00
|
|
|
|
if (!isShortImet)
|
|
|
|
|
{
|
|
|
|
|
headerStream.Read(additionalPadding, 0, additionalPadding.Length);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
headerStream.Read(padding, 0, padding.Length);
|
2021-02-06 21:19:03 -06:00
|
|
|
|
headerStream.Read(buffer1, 0, 4);
|
2021-02-06 22:53:40 -06:00
|
|
|
|
if ((int)Shared.Swap(BitConverter.ToUInt32(buffer1, 0)) != (int)imetMagic)
|
|
|
|
|
{
|
2021-02-06 21:19:03 -06:00
|
|
|
|
throw new Exception("Invalid Magic!");
|
2021-02-06 22:53:40 -06:00
|
|
|
|
}
|
|
|
|
|
|
2021-02-06 21:19:03 -06:00
|
|
|
|
headerStream.Read(buffer1, 0, 4);
|
2021-02-06 22:53:40 -06:00
|
|
|
|
if ((int)Shared.Swap(BitConverter.ToUInt32(buffer1, 0)) != (int)sizeOfHeader)
|
|
|
|
|
{
|
2021-02-06 21:19:03 -06:00
|
|
|
|
throw new Exception("Invalid Header Size!");
|
2021-02-06 22:53:40 -06:00
|
|
|
|
}
|
|
|
|
|
|
2021-02-06 21:19:03 -06:00
|
|
|
|
headerStream.Read(buffer1, 0, 4);
|
2021-02-06 22:53:40 -06:00
|
|
|
|
unknown = Shared.Swap(BitConverter.ToUInt32(buffer1, 0));
|
2021-02-06 21:19:03 -06:00
|
|
|
|
headerStream.Read(buffer1, 0, 4);
|
2021-02-06 22:53:40 -06:00
|
|
|
|
iconSize = Shared.Swap(BitConverter.ToUInt32(buffer1, 0));
|
2021-02-06 21:19:03 -06:00
|
|
|
|
headerStream.Read(buffer1, 0, 4);
|
2021-02-06 22:53:40 -06:00
|
|
|
|
bannerSize = Shared.Swap(BitConverter.ToUInt32(buffer1, 0));
|
2021-02-06 21:19:03 -06:00
|
|
|
|
headerStream.Read(buffer1, 0, 4);
|
2021-02-06 22:53:40 -06:00
|
|
|
|
soundSize = Shared.Swap(BitConverter.ToUInt32(buffer1, 0));
|
2021-02-06 21:19:03 -06:00
|
|
|
|
headerStream.Read(buffer1, 0, 4);
|
2021-02-06 22:53:40 -06:00
|
|
|
|
flags = Shared.Swap(BitConverter.ToUInt32(buffer1, 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);
|
2021-02-06 21:19:03 -06:00
|
|
|
|
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();
|
2021-02-06 22:53:40 -06:00
|
|
|
|
byte[] hash = md5.ComputeHash(buffer2, !isShortImet ? 64 : 0, 1536);
|
2021-02-06 21:19:03 -06:00
|
|
|
|
md5.Clear();
|
2021-02-06 22:53:40 -06:00
|
|
|
|
hashesMatch = Shared.CompareByteArrays(hash, this.hash);
|
2021-02-06 21:19:03 -06:00
|
|
|
|
}
|
|
|
|
|
|
2021-02-06 22:53:40 -06:00
|
|
|
|
private string ReturnTitleAsString(byte[] title)
|
2021-02-06 21:19:03 -06:00
|
|
|
|
{
|
|
|
|
|
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)
|
2021-02-06 22:53:40 -06:00
|
|
|
|
{
|
|
|
|
|
empty += ch.ToString();
|
|
|
|
|
}
|
2021-02-06 21:19:03 -06:00
|
|
|
|
}
|
|
|
|
|
return empty;
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-06 22:53:40 -06:00
|
|
|
|
private void SetTitleFromString(string title, int titleIndex)
|
2021-02-06 21:19:03 -06:00
|
|
|
|
{
|
|
|
|
|
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:
|
2021-02-06 22:53:40 -06:00
|
|
|
|
japaneseTitle = numArray;
|
2021-02-06 21:19:03 -06:00
|
|
|
|
break;
|
|
|
|
|
case 1:
|
2021-02-06 22:53:40 -06:00
|
|
|
|
englishTitle = numArray;
|
2021-02-06 21:19:03 -06:00
|
|
|
|
break;
|
|
|
|
|
case 2:
|
2021-02-06 22:53:40 -06:00
|
|
|
|
germanTitle = numArray;
|
2021-02-06 21:19:03 -06:00
|
|
|
|
break;
|
|
|
|
|
case 3:
|
2021-02-06 22:53:40 -06:00
|
|
|
|
frenchTitle = numArray;
|
2021-02-06 21:19:03 -06:00
|
|
|
|
break;
|
|
|
|
|
case 4:
|
2021-02-06 22:53:40 -06:00
|
|
|
|
spanishTitle = numArray;
|
2021-02-06 21:19:03 -06:00
|
|
|
|
break;
|
|
|
|
|
case 5:
|
2021-02-06 22:53:40 -06:00
|
|
|
|
italianTitle = numArray;
|
2021-02-06 21:19:03 -06:00
|
|
|
|
break;
|
|
|
|
|
case 6:
|
2021-02-06 22:53:40 -06:00
|
|
|
|
dutchTitle = numArray;
|
2021-02-06 21:19:03 -06:00
|
|
|
|
break;
|
|
|
|
|
case 7:
|
2021-02-06 22:53:40 -06:00
|
|
|
|
koreanTitle = numArray;
|
2021-02-06 21:19:03 -06:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
2020-12-28 22:28:44 -06:00
|
|
|
|
}
|
2021-02-06 21:19:03 -06:00
|
|
|
|
|
|
|
|
|
public class IMD5
|
2020-12-28 22:28:44 -06:00
|
|
|
|
{
|
2021-02-06 22:53:40 -06:00
|
|
|
|
private readonly uint imd5Magic = 1229800501;
|
2021-02-06 21:19:03 -06:00
|
|
|
|
private uint fileSize;
|
2021-02-06 22:53:40 -06:00
|
|
|
|
private readonly byte[] padding = new byte[8];
|
2021-02-06 21:19:03 -06:00
|
|
|
|
private byte[] hash = new byte[16];
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The size of the file without the IMD5 Header.
|
|
|
|
|
/// </summary>
|
2021-02-06 22:53:40 -06:00
|
|
|
|
public uint FileSize => fileSize;
|
2021-02-06 21:19:03 -06:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The hash of the file without the IMD5 Header.
|
|
|
|
|
/// </summary>
|
2021-02-06 22:53:40 -06:00
|
|
|
|
public byte[] Hash => hash;
|
2021-02-06 21:19:03 -06:00
|
|
|
|
|
2021-02-06 22:53:40 -06:00
|
|
|
|
private IMD5()
|
|
|
|
|
{
|
|
|
|
|
}
|
2021-02-06 21:19:03 -06:00
|
|
|
|
|
|
|
|
|
#region Public Functions
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Loads the IMD5 Header of a file.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="pathToFile"></param>
|
|
|
|
|
/// <returns></returns>
|
2021-02-06 22:53:40 -06:00
|
|
|
|
public static Headers.IMD5 Load(string pathToFile)
|
|
|
|
|
{
|
2021-02-07 12:37:46 -06:00
|
|
|
|
return Load(File.ReadAllBytes(pathToFile));
|
2021-02-06 22:53:40 -06:00
|
|
|
|
}
|
2021-02-06 21:19:03 -06:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Loads the IMD5 Header of a byte array.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="fileOrHeader"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public static Headers.IMD5 Load(byte[] fileOrHeader)
|
|
|
|
|
{
|
2021-02-07 12:37:46 -06:00
|
|
|
|
if (DetectHeader(fileOrHeader) != HeaderType.IMD5)
|
2021-02-06 22:53:40 -06:00
|
|
|
|
{
|
2021-02-06 21:19:03 -06:00
|
|
|
|
throw new Exception("No IMD5 Header found!");
|
2021-02-06 22:53:40 -06:00
|
|
|
|
}
|
|
|
|
|
|
2021-02-06 21:19:03 -06:00
|
|
|
|
Headers.IMD5 imD5 = new Headers.IMD5();
|
|
|
|
|
MemoryStream memoryStream = new MemoryStream(fileOrHeader);
|
|
|
|
|
try
|
|
|
|
|
{
|
2021-02-10 13:32:35 -06:00
|
|
|
|
imD5.PrivParseHeader(memoryStream);
|
2021-02-06 21:19:03 -06:00
|
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{
|
|
|
|
|
memoryStream.Dispose();
|
|
|
|
|
throw;
|
|
|
|
|
}
|
|
|
|
|
memoryStream.Dispose();
|
|
|
|
|
return imD5;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Loads the IMD5 Header of a stream.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="fileOrHeader"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public static Headers.IMD5 Load(Stream fileOrHeader)
|
|
|
|
|
{
|
2021-02-07 12:37:46 -06:00
|
|
|
|
if (DetectHeader(fileOrHeader) != HeaderType.IMD5)
|
2021-02-06 22:53:40 -06:00
|
|
|
|
{
|
2021-02-06 21:19:03 -06:00
|
|
|
|
throw new Exception("No IMD5 Header found!");
|
2021-02-06 22:53:40 -06:00
|
|
|
|
}
|
|
|
|
|
|
2021-02-06 21:19:03 -06:00
|
|
|
|
Headers.IMD5 imD5 = new Headers.IMD5();
|
2021-02-10 13:32:35 -06:00
|
|
|
|
imD5.PrivParseHeader(fileOrHeader);
|
2021-02-06 21:19:03 -06:00
|
|
|
|
return imD5;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Creates a new IMD5 Header.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="file"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public static Headers.IMD5 Create(byte[] file)
|
|
|
|
|
{
|
2021-02-06 22:53:40 -06:00
|
|
|
|
IMD5 imD5 = new IMD5
|
|
|
|
|
{
|
|
|
|
|
fileSize = (uint)file.Length
|
|
|
|
|
};
|
2021-02-10 13:32:35 -06:00
|
|
|
|
imD5.PrivComputeHash(file);
|
2021-02-06 21:19:03 -06:00
|
|
|
|
return imD5;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Adds an IMD5 Header to a file.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="pathToFile"></param>
|
|
|
|
|
public static void AddHeader(string pathToFile)
|
|
|
|
|
{
|
2021-02-07 12:37:46 -06:00
|
|
|
|
byte[] buffer = AddHeader(File.ReadAllBytes(pathToFile));
|
2021-02-06 21:19:03 -06:00
|
|
|
|
File.Delete(pathToFile);
|
2021-02-06 22:53:40 -06:00
|
|
|
|
using FileStream fileStream = new FileStream(pathToFile, FileMode.Create);
|
|
|
|
|
fileStream.Write(buffer, 0, buffer.Length);
|
2021-02-06 21:19:03 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Adds an IMD5 Header to a byte array.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="file"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public static byte[] AddHeader(byte[] file)
|
|
|
|
|
{
|
2021-02-07 12:37:46 -06:00
|
|
|
|
Headers.IMD5 imD5 = Create(file);
|
2021-02-06 21:19:03 -06:00
|
|
|
|
MemoryStream memoryStream1 = new MemoryStream();
|
|
|
|
|
MemoryStream memoryStream2 = memoryStream1;
|
2021-02-10 13:32:35 -06:00
|
|
|
|
imD5.PrivWriteToStream(memoryStream2);
|
2021-02-06 21:19:03 -06:00
|
|
|
|
memoryStream1.Write(file, 0, file.Length);
|
|
|
|
|
byte[] array = memoryStream1.ToArray();
|
|
|
|
|
memoryStream1.Dispose();
|
|
|
|
|
return array;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Removes the IMD5 Header of a file.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="pathToFile"></param>
|
|
|
|
|
public static void RemoveHeader(string pathToFile)
|
|
|
|
|
{
|
2021-02-07 12:37:46 -06:00
|
|
|
|
byte[] buffer = RemoveHeader(File.ReadAllBytes(pathToFile));
|
2021-02-06 21:19:03 -06:00
|
|
|
|
File.Delete(pathToFile);
|
2021-02-06 22:53:40 -06:00
|
|
|
|
using FileStream fileStream = new FileStream(pathToFile, FileMode.Create);
|
|
|
|
|
fileStream.Write(buffer, 0, buffer.Length);
|
2021-02-06 21:19:03 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Removes the IMD5 Header of a byte array.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="file"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Returns the IMD5 Header as a memory stream.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public MemoryStream ToMemoryStream()
|
|
|
|
|
{
|
|
|
|
|
MemoryStream memoryStream = new MemoryStream();
|
|
|
|
|
try
|
|
|
|
|
{
|
2021-02-10 13:32:35 -06:00
|
|
|
|
PrivWriteToStream(memoryStream);
|
2021-02-06 21:19:03 -06:00
|
|
|
|
return memoryStream;
|
|
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{
|
|
|
|
|
memoryStream.Dispose();
|
|
|
|
|
throw;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Returns the IMD5 Header as a byte array.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns></returns>
|
2021-02-06 22:53:40 -06:00
|
|
|
|
public byte[] ToByteArray()
|
|
|
|
|
{
|
|
|
|
|
return ToMemoryStream().ToArray();
|
|
|
|
|
}
|
2021-02-06 21:19:03 -06:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Writes the IMD5 Header to the given stream.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="writeStream"></param>
|
2021-02-06 22:53:40 -06:00
|
|
|
|
public void Write(Stream writeStream)
|
|
|
|
|
{
|
2021-02-10 13:32:35 -06:00
|
|
|
|
PrivWriteToStream(writeStream);
|
2021-02-06 22:53:40 -06:00
|
|
|
|
}
|
2021-02-06 21:19:03 -06:00
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Private Functions
|
2021-02-10 13:32:35 -06:00
|
|
|
|
private void PrivWriteToStream(Stream writeStream)
|
2021-02-06 21:19:03 -06:00
|
|
|
|
{
|
|
|
|
|
writeStream.Seek(0L, SeekOrigin.Begin);
|
2021-02-06 22:53:40 -06:00
|
|
|
|
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);
|
2021-02-06 21:19:03 -06:00
|
|
|
|
}
|
|
|
|
|
|
2021-02-10 13:32:35 -06:00
|
|
|
|
private void PrivComputeHash(byte[] bytesToHash)
|
2021-02-06 21:19:03 -06:00
|
|
|
|
{
|
|
|
|
|
MD5 md5 = MD5.Create();
|
2021-02-06 22:53:40 -06:00
|
|
|
|
hash = md5.ComputeHash(bytesToHash);
|
2021-02-06 21:19:03 -06:00
|
|
|
|
md5.Clear();
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-10 13:32:35 -06:00
|
|
|
|
private void PrivParseHeader(Stream headerStream)
|
2021-02-06 21:19:03 -06:00
|
|
|
|
{
|
|
|
|
|
headerStream.Seek(0L, SeekOrigin.Begin);
|
|
|
|
|
byte[] buffer = new byte[4];
|
|
|
|
|
headerStream.Read(buffer, 0, 4);
|
2021-02-06 22:53:40 -06:00
|
|
|
|
if ((int)Shared.Swap(BitConverter.ToUInt32(buffer, 0)) != (int)imd5Magic)
|
|
|
|
|
{
|
|
|
|
|
throw new Exception("Invalid Magic!");
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-06 21:19:03 -06:00
|
|
|
|
headerStream.Read(buffer, 0, 4);
|
2021-02-06 22:53:40 -06:00
|
|
|
|
fileSize = Shared.Swap(BitConverter.ToUInt32(buffer, 0));
|
|
|
|
|
headerStream.Read(padding, 0, padding.Length);
|
|
|
|
|
headerStream.Read(hash, 0, hash.Length);
|
2021-02-06 21:19:03 -06:00
|
|
|
|
}
|
|
|
|
|
#endregion
|
2020-12-28 22:28:44 -06:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|