From 7d263c9a101c6a47d545e1a3a81d04c57e92eeb7 Mon Sep 17 00:00:00 2001 From: zeromus Date: Tue, 6 Mar 2012 07:51:41 +0000 Subject: [PATCH] nes-support 8KB PRG roms --- .../Consoles/Nintendo/NES/BoardSystem.cs | 15 +- .../Consoles/Nintendo/NES/Boards/NROM.cs | 2 +- .../Consoles/Nintendo/NES/NES.cs | 39 ++-- BizHawk.Emulation/Database/Database.cs | 179 +++++++++--------- 4 files changed, 124 insertions(+), 111 deletions(-) diff --git a/BizHawk.Emulation/Consoles/Nintendo/NES/BoardSystem.cs b/BizHawk.Emulation/Consoles/Nintendo/NES/BoardSystem.cs index 4b19a8e010..0ab243d6c1 100644 --- a/BizHawk.Emulation/Consoles/Nintendo/NES/BoardSystem.cs +++ b/BizHawk.Emulation/Consoles/Nintendo/NES/BoardSystem.cs @@ -276,14 +276,17 @@ namespace BizHawk.Emulation.Consoles.Nintendo /// /// looks up from the bootgod DB /// - CartInfo IdentifyFromBootGodDB(string hash_sha1) + CartInfo IdentifyFromBootGodDB(IEnumerable hash_sha1) { BootGodDB.Initialize(); - List choices = BootGodDB.Instance.Identify(hash_sha1); - if (choices.Count == 0) return null; - - //pick the first board for this hash arbitrarily. it probably doesn't make a difference - return choices[0]; + foreach (var hash in hash_sha1) + { + List choices = BootGodDB.Instance.Identify(hash); + //pick the first board for this hash arbitrarily. it probably doesn't make a difference + if (choices.Count != 0) + return choices[0]; + } + return null; } /// diff --git a/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/NROM.cs b/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/NROM.cs index e068fc9cc4..fc1abc7532 100644 --- a/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/NROM.cs +++ b/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/NROM.cs @@ -27,7 +27,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo case "NAMCOT-3301": case "HVC-HROM": //Donkey Kong Jr. (J) case "JALECO-JF-01": //Exerion (J) - AssertPrg(16, 32); AssertChr(8); AssertVram(0); AssertWram(0, 8); + AssertPrg(8, 16, 32); AssertChr(8); AssertVram(0); AssertWram(0, 8); break; case "NROM-HOMEBREW": diff --git a/BizHawk.Emulation/Consoles/Nintendo/NES/NES.cs b/BizHawk.Emulation/Consoles/Nintendo/NES/NES.cs index 1f2b9592dc..c053d1ba24 100644 --- a/BizHawk.Emulation/Consoles/Nintendo/NES/NES.cs +++ b/BizHawk.Emulation/Consoles/Nintendo/NES/NES.cs @@ -404,30 +404,38 @@ namespace BizHawk.Emulation.Consoles.Nintendo header->Cleanup(); //now that we know we have an iNES header, we can try to ignore it. - string hash_sha1; - string hash_md5; - using (var sha1 = System.Security.Cryptography.SHA1.Create()) - { - sha1.TransformFinalBlock(file, 16, file.Length - 16); - hash_sha1 = "sha1:" + Util.BytesToHexString(sha1.Hash); - } - using (var md5 = System.Security.Cryptography.MD5.Create()) - { - md5.TransformFinalBlock(file, 16, file.Length - 16); - hash_md5 = "md5:" + Util.BytesToHexString(md5.Hash); - } + + List hash_sha1_several = new List(); + string hash_sha1 = "sha1:" + Util.Hash_SHA1(file,16,file.Length - 16); + hash_sha1_several.Add(hash_sha1); + string hash_md5 = "md5:" + Util.Hash_MD5(file, 16, file.Length - 16); LoadWriteLine("Found iNES header:"); CartInfo iNesHeaderInfo = header->Analyze(); - LoadWriteLine("Since this is iNES we can confidently parse PRG/CHR banks to hash."); + LoadWriteLine("Since this is iNES we can (somewhat) confidently parse PRG/CHR banks to hash."); LoadWriteLine("headerless rom hash: {0}", hash_sha1); LoadWriteLine("headerless rom hash: {0}", hash_md5); + if (iNesHeaderInfo.prg_size == 16) + { + //8KB prg can't be stored in iNES format, which counts 16KB prg banks. + //so a correct hash will include only 8KB. + LoadWriteLine("Since this rom has a 16 KB PRG, we'll hash it as 8KB too for bootgod's DB:"); + var msTemp = new MemoryStream(); + msTemp.Write(file, 16, 8 * 1024); //add prg + msTemp.Write(file, 16 + 16 * 1024, iNesHeaderInfo.chr_size * 1024); //add chr + msTemp.Flush(); + var bytes = msTemp.ToArray(); + var hash = "sha1:" + Util.Hash_SHA1(bytes, 0, bytes.Length); + LoadWriteLine("PRG (8KB) + CHR hash: {0}", hash); + hash_sha1_several.Add(hash); + } + Type boardType = null; CartInfo choice = null; if (USE_DATABASE) - choice = IdentifyFromBootGodDB(hash_sha1); + choice = IdentifyFromBootGodDB(hash_sha1_several); if (choice == null) { LoadWriteLine("Could not locate game in nescartdb"); @@ -547,7 +555,8 @@ namespace BizHawk.Emulation.Consoles.Nintendo if (choice.chr_size > 0) { board.VROM = new byte[choice.chr_size * 1024]; - Array.Copy(file, 16 + board.ROM.Length, board.VROM, 0, board.VROM.Length); + int vrom_offset = iNesHeaderInfo.prg_size * 1024; + Array.Copy(file, 16 + vrom_offset, board.VROM, 0, board.VROM.Length); } //create the vram and wram if necessary diff --git a/BizHawk.Emulation/Database/Database.cs b/BizHawk.Emulation/Database/Database.cs index 5adb014d22..de035ed5e2 100644 --- a/BizHawk.Emulation/Database/Database.cs +++ b/BizHawk.Emulation/Database/Database.cs @@ -5,18 +5,18 @@ using System.Threading; namespace BizHawk { - internal class CompactGameInfo - { - public string Name; - public string System; - public string MetaData; - public string Hash; - public RomStatus Status; - } + internal class CompactGameInfo + { + public string Name; + public string System; + public string MetaData; + public string Hash; + public RomStatus Status; + } - public static class Database - { - private static Dictionary db = new Dictionary(); + public static class Database + { + private static Dictionary db = new Dictionary(); static string RemoveHashType(string hash) { @@ -28,15 +28,15 @@ namespace BizHawk public static GameInfo CheckDatabase(string hash) { - CompactGameInfo cgi; + CompactGameInfo cgi; string hash_notype = RemoveHashType(hash); db.TryGetValue(hash_notype, out cgi); - if (cgi == null) - { - Console.WriteLine("Game with hash " + hash + " was not in game database."); - return null; - } - return new GameInfo(cgi); + if (cgi == null) + { + Console.WriteLine("DB: hash " + hash + " not in game database."); + return null; + } + return new GameInfo(cgi); } static void LoadDatabase_Escape(string line) @@ -52,97 +52,98 @@ namespace BizHawk Console.WriteLine("BENIGN: missing external game database {0}", line); } - public static void LoadDatabase(string path) - { - using (var reader = new StreamReader(new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))) - { - while (reader.EndOfStream == false) - { - string line = reader.ReadLine(); - try - { + public static void LoadDatabase(string path) + { + using (var reader = new StreamReader(new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))) + { + while (reader.EndOfStream == false) + { + string line = reader.ReadLine(); + try + { if (line.StartsWith(";")) continue; //comment if (line.StartsWith("#")) { LoadDatabase_Escape(line); continue; } - if (line.Trim().Length == 0) continue; - string[] items = line.Split('\t'); + if (line.Trim().Length == 0) continue; + string[] items = line.Split('\t'); - var Game = new CompactGameInfo(); + var Game = new CompactGameInfo(); //remove a hash type identifier. well don't really need them for indexing (theyre just there for human purposes) Game.Hash = RemoveHashType(items[0].ToUpper()); - switch (items[1].Trim()) - { - case "B": Game.Status = RomStatus.BadDump; break; - case "V": Game.Status = RomStatus.BadDump; break; - case "T": Game.Status = RomStatus.TranslatedRom; break; - case "O": Game.Status = RomStatus.Overdump; break; - case "I": Game.Status = RomStatus.BIOS; break; - case "D": Game.Status = RomStatus.Homebrew; break; - case "H": Game.Status = RomStatus.Hack; break; - default: Game.Status = RomStatus.GoodDump; break; - } - Game.Name = items[2]; - Game.System = items[3]; - Game.MetaData = items.Length >= 6 ? items[5] : null; + switch (items[1].Trim()) + { + case "B": Game.Status = RomStatus.BadDump; break; + case "V": Game.Status = RomStatus.BadDump; break; + case "T": Game.Status = RomStatus.TranslatedRom; break; + case "O": Game.Status = RomStatus.Overdump; break; + case "I": Game.Status = RomStatus.BIOS; break; + case "D": Game.Status = RomStatus.Homebrew; break; + case "H": Game.Status = RomStatus.Hack; break; + default: Game.Status = RomStatus.GoodDump; break; + } + Game.Name = items[2]; + Game.System = items[3]; + Game.MetaData = items.Length >= 6 ? items[5] : null; - if (db.ContainsKey(Game.Hash)) - Console.WriteLine("gamedb: Multiple hash entries {0}, duplicate detected on {1}",Game.Hash, Game.Name); + if (db.ContainsKey(Game.Hash)) + Console.WriteLine("gamedb: Multiple hash entries {0}, duplicate detected on {1}", Game.Hash, Game.Name); - db[Game.Hash] = Game; - } catch - { - Console.WriteLine("Error parsing database entry: "+line); - } - } - } - } + db[Game.Hash] = Game; + } + catch + { + Console.WriteLine("Error parsing database entry: " + line); + } + } + } + } - public static GameInfo GetGameInfo(byte[] RomData, string fileName) - { + public static GameInfo GetGameInfo(byte[] RomData, string fileName) + { CompactGameInfo cgi; string hash = string.Format("{0:X8}", CRC32.Calculate(RomData)); - if (db.TryGetValue(hash, out cgi)) - return new GameInfo(cgi); + if (db.TryGetValue(hash, out cgi)) + return new GameInfo(cgi); - hash = Util.BytesToHexString(System.Security.Cryptography.MD5.Create().ComputeHash(RomData)); - if (db.TryGetValue(hash, out cgi)) - return new GameInfo(cgi); + hash = Util.BytesToHexString(System.Security.Cryptography.MD5.Create().ComputeHash(RomData)); + if (db.TryGetValue(hash, out cgi)) + return new GameInfo(cgi); hash = Util.BytesToHexString(System.Security.Cryptography.SHA1.Create().ComputeHash(RomData)); - if (db.TryGetValue(hash, out cgi)) - return new GameInfo(cgi); + if (db.TryGetValue(hash, out cgi)) + return new GameInfo(cgi); - // rom is not in database. make some best-guesses - var Game = new GameInfo { Hash = hash, Status = RomStatus.NotInDatabase, NotInDatabase = true }; - Console.WriteLine("Game was not in DB. CRC: {0:X8} MD5: {1}", - CRC32.Calculate(RomData), - Util.BytesToHexString(System.Security.Cryptography.MD5.Create().ComputeHash(RomData))); + // rom is not in database. make some best-guesses + var Game = new GameInfo { Hash = hash, Status = RomStatus.NotInDatabase, NotInDatabase = true }; + Console.WriteLine("Game was not in DB. CRC: {0:X8} MD5: {1}", + CRC32.Calculate(RomData), + Util.BytesToHexString(System.Security.Cryptography.MD5.Create().ComputeHash(RomData))); - string ext = Path.GetExtension(fileName).ToUpperInvariant(); + string ext = Path.GetExtension(fileName).ToUpperInvariant(); - switch (ext) - { - case ".SMS": Game.System = "SMS"; break; - case ".GG" : Game.System = "GG"; break; - case ".SG" : Game.System = "SG"; break; - case ".PCE": Game.System = "PCE"; break; - case ".SGX": Game.System = "SGX"; break; - case ".GB" : Game.System = "GB"; break; - case ".BIN": - case ".GEN": - case ".SMD": Game.System = "GEN"; break; - case ".NES": Game.System = "NES"; break; - } + switch (ext) + { + case ".SMS": Game.System = "SMS"; break; + case ".GG": Game.System = "GG"; break; + case ".SG": Game.System = "SG"; break; + case ".PCE": Game.System = "PCE"; break; + case ".SGX": Game.System = "SGX"; break; + case ".GB": Game.System = "GB"; break; + case ".BIN": + case ".GEN": + case ".SMD": Game.System = "GEN"; break; + case ".NES": Game.System = "NES"; break; + } - Game.Name = Path.GetFileNameWithoutExtension(fileName).Replace('_', ' '); - // If filename is all-caps, then attempt to proper-case the title. - if (Game.Name == Game.Name.ToUpperInvariant()) - Game.Name = Thread.CurrentThread.CurrentCulture.TextInfo.ToTitleCase(Game.Name.ToLower()); + Game.Name = Path.GetFileNameWithoutExtension(fileName).Replace('_', ' '); + // If filename is all-caps, then attempt to proper-case the title. + if (Game.Name == Game.Name.ToUpperInvariant()) + Game.Name = Thread.CurrentThread.CurrentCulture.TextInfo.ToTitleCase(Game.Name.ToLower()); - return Game; - } - } + return Game; + } + } }