BizHawk/BizHawk.Client.DBMan/RomHasher.cs

262 lines
6.3 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.DiscSystem;
namespace BizHawk.Client.DBMan
{
public class InitialRomInfo
{
public string FileName;
public string Name;
public string VersionTags;
public string GuessedSystem;
public string GuessedRegion;
public string CRC32;
public string MD5;
public string SHA1;
public long Size;
public override string ToString()
{
return FileName;
}
}
public static class RomHasher
{
public static InitialRomInfo Generate(string file)
{
//if (isDiscImage(file))
// return HashDiscImage(file);
return GenerateRomHashDirect(file, File.ReadAllBytes(file));
}
static char[] modifierStartChars = { '(', '[' };
static InitialRomInfo GenerateRomHashDirect(string file, byte[] filebytes)
{
var info = new InitialRomInfo();
var fileInfo = new FileInfo(file);
string ext = fileInfo.Extension.ToLowerInvariant().Replace(".", "");
info.FileName = fileInfo.Name;
// Parse the filename to guess things about the rom
var name = Path.GetFileNameWithoutExtension(fileInfo.Name);
if (name.StartsWith("[BIOS] "))
name = name.Replace("[BIOS] ","") + " [BIOS]";
string modifiers = "";
int modIndex = name.IndexOfAny(modifierStartChars);
if (modIndex > 0)
{
modifiers = name.Substring(modIndex);
name = name.Substring(0, modIndex);
}
info.Name = name.Trim();
// parse out modifiers
var mods = new List<string>();
modifiers = modifiers.Replace(")", ";").Replace("]",";");
modifiers = modifiers.Replace("(", "").Replace("[", "");
var m_ = modifiers.Split(';');
foreach (var mi in m_)
{
var m = mi.Trim();
if (m.Length == 0) continue;
mods.Add(m);
}
info.VersionTags = "";
foreach (var mi in mods)
{
if (info.VersionTags.Length != 0)
info.VersionTags += ";";
switch (mi.ToLower())
{
case "j":
case "jp":
case "jpn":
case "japan":
info.GuessedRegion = "Japan";
break;
case "usa":
case "us":
case "u":
info.GuessedRegion = "USA";
break;
case "europe":
case "eur":
case "e":
info.GuessedRegion = "Europe";
break;
case "world":
case "w":
info.GuessedRegion = "World";
break;
case "korea":
case "kr":
case "k":
info.GuessedRegion = "Korea";
break;
case "brazil":
case "br":
info.GuessedRegion = "Brazil";
break;
case "taiwan":
case "tw":
info.GuessedRegion = "Taiwan";
break;
case "usa, europe":
info.GuessedRegion = "USA;Europe";
break;
case "japan, europe":
info.GuessedRegion = "Europe;Japan";
break;
case "japan, usa":
info.GuessedRegion = "USA;Japan";
break;
default:
info.VersionTags += mi;
break;
}
}
// transform binary to canonical binary representation (de-header/de-stripe/de-swap)
byte[] romBytes = filebytes;
switch (ext)
{
case "sms":
case "gg":
case "sg":
case "pce":
case "sgx":
romBytes = MaybeStripHeader512(filebytes);
break;
case "smd":
if (filebytes.Length % 1024 == 512)
System.Windows.Forms.MessageBox.Show("derp");
romBytes = DeInterleaveSMD(filebytes);
break;
case "z64":
case "n64":
case "v64":
throw new NotImplementedException("n64 demutate not done");
}
// guess system
switch (ext)
{
case "sms": info.GuessedSystem = "SMS"; break;
case "gg": info.GuessedSystem = "GG"; break;
case "sg": info.GuessedSystem = "SG"; break;
case "pce": info.GuessedSystem = "PCE"; break;
case "sgx": info.GuessedSystem = "SGX"; break;
case "smd":
case "gen": info.GuessedSystem = "GEN"; break;
case "nes": info.GuessedSystem = "NES"; break;
default: info.GuessedSystem = "Unknown"; break;
}
// Perform hashing
info.CRC32 = Hash_CRC32(romBytes);
info.MD5 = Hash_MD5(romBytes);
info.SHA1 = Hash_SHA1(romBytes);
info.Size = romBytes.Length;
return info;
}
static string HashDiscImage(string file)
{
try
{
string ext = new FileInfo(file).Extension.ToLowerInvariant();
using (var disc = ext == ".iso" ? Disc.FromIsoPath(file) : Disc.FromCuePath(file, new CueBinPrefs()))
{
return disc.GetHash();
}
}
catch
{
return "Error Hashing Disc";
}
}
static string Hash_CRC32(byte[] data)
{
return string.Format("{0:X8}", CRC32.Calculate(data));
}
static string Hash_SHA1(byte[] data)
{
using (var sha1 = System.Security.Cryptography.SHA1.Create())
{
sha1.TransformFinalBlock(data, 0, data.Length);
return BytesToHexString(sha1.Hash);
}
}
static string Hash_MD5(byte[] data)
{
using (var md5 = System.Security.Cryptography.MD5.Create())
{
md5.TransformFinalBlock(data, 0, data.Length);
return BytesToHexString(md5.Hash);
}
}
static string BytesToHexString(byte[] bytes)
{
var sb = new StringBuilder();
foreach (var b in bytes)
sb.AppendFormat("{0:X2}", b);
return sb.ToString();
}
static byte[] MaybeStripHeader512(byte[] fileBytes)
{
if (fileBytes.Length % 1024 != 512)
return fileBytes;
var romBytes = new byte[fileBytes.Length - 512];
Array.Copy(fileBytes, 512, romBytes, 0, fileBytes.Length - 512);
return romBytes;
}
static byte[] DeInterleaveSMD(byte[] source)
{
int size = source.Length;
if (size > 0x400000)
size = 0x400000;
int pages = size / 0x4000;
var output = new byte[size];
for (int page = 0; page < pages; page++)
{
for (int i = 0; i < 0x2000; i++)
{
output[(page * 0x4000) + (i * 2) + 0] = source[(page * 0x4000) + 0x2000 + i];
output[(page * 0x4000) + (i * 2) + 1] = source[(page * 0x4000) + 0x0000 + i];
}
}
return output;
}
static bool isDiscImage(string file)
{
var ext = new FileInfo(file).Extension.ToLowerInvariant();
if (ext == ".cue" || ext == ".iso")
return true;
return false;
}
}
}