From 787470df3506db4b3f8780b7f26f4e85feb0c049 Mon Sep 17 00:00:00 2001
From: alyosha-tas <alexei.f.k@gmail.com>
Date: Mon, 29 May 2017 20:27:07 -0400
Subject: [PATCH] NES: allow headerless ROMs to be loaded if in DB

---
 .../Consoles/Nintendo/NES/NES.cs              | 98 +++++++++++++------
 1 file changed, 66 insertions(+), 32 deletions(-)

diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.cs
index 053fbb842b..1f8d4735e3 100644
--- a/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.cs
+++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.cs
@@ -501,42 +501,58 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
 				byte[] nesheader = new byte[16];
 				Buffer.BlockCopy(file, 0, nesheader, 0, 16);
 
+				bool exists = true;
+
 				if (!DetectFromINES(nesheader, out iNesHeaderInfo, out iNesHeaderInfoV2))
-					throw new InvalidOperationException("iNES header not found");
-
-				//now that we know we have an iNES header, we can try to ignore it.
-
-				hash_sha1 = "sha1:" + file.HashSHA1(16, file.Length - 16);
-				hash_sha1_several.Add(hash_sha1);
-				hash_md5 = "md5:" + file.HashMD5(16, file.Length - 16);
-
-				LoadWriteLine("Found iNES header:");
-				LoadWriteLine(iNesHeaderInfo.ToString());
-				if (iNesHeaderInfoV2 != null)
 				{
-					LoadWriteLine("Found iNES V2 header:");
-					LoadWriteLine(iNesHeaderInfoV2);
+					// we don't have an ines header, check if the game hash is in the game db
+					exists = false;
+					Console.WriteLine("headerless ROM, using Game DB");
+					hash_md5 = "md5:" + file.HashMD5(0, file.Length);
+					hash_sha1 = "sha1:" + file.HashSHA1(0, file.Length);
+					if (hash_md5 != null) choice = IdentifyFromGameDB(hash_md5);
+					if (choice == null)
+						choice = IdentifyFromGameDB(hash_sha1);
+					if (choice == null)
+						throw new InvalidOperationException("iNES header not found and no gamedb entry");
 				}
-				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)
+				if (exists)
 				{
-					//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:" + bytes.HashSHA1(0, bytes.Length);
-					LoadWriteLine("  PRG (8KB) + CHR hash: {0}", hash);
-					hash_sha1_several.Add(hash);
-					hash = "md5:" + bytes.HashMD5(0, bytes.Length);
-					LoadWriteLine("  PRG (8KB) + CHR hash:  {0}", hash);
+					//now that we know we have an iNES header, we can try to ignore it.
+
+					hash_sha1 = "sha1:" + file.HashSHA1(16, file.Length - 16);
+					hash_sha1_several.Add(hash_sha1);
+					hash_md5 = "md5:" + file.HashMD5(16, file.Length - 16);
+
+					LoadWriteLine("Found iNES header:");
+					LoadWriteLine(iNesHeaderInfo.ToString());
+					if (iNesHeaderInfoV2 != null)
+					{
+						LoadWriteLine("Found iNES V2 header:");
+						LoadWriteLine(iNesHeaderInfoV2);
+					}
+					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:" + bytes.HashSHA1(0, bytes.Length);
+						LoadWriteLine("  PRG (8KB) + CHR hash: {0}", hash);
+						hash_sha1_several.Add(hash);
+						hash = "md5:" + bytes.HashMD5(0, bytes.Length);
+						LoadWriteLine("  PRG (8KB) + CHR hash:  {0}", hash);
+					}
 				}
 			}
 
@@ -727,11 +743,29 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
 				if (choice.prg_size != iNesHeaderInfo.prg_size || choice.chr_size != iNesHeaderInfo.chr_size)
 					LoadWriteLine("Warning: Detected choice has different filesizes than the INES header!");
 			}
-			else
+			else if (unif != null)
 			{
 				Board.ROM = unif.PRG;
 				Board.VROM = unif.CHR;
 			}
+			else
+			{
+				// we should only get here for boards with no header
+				var ms = new MemoryStream(file, false);
+				ms.Seek(0, SeekOrigin.Begin);
+
+				Board.ROM = new byte[choice.prg_size * 1024];
+				ms.Read(Board.ROM, 0, Board.ROM.Length);
+
+				if (choice.chr_size > 0)
+				{
+					Board.VROM = new byte[choice.chr_size * 1024];
+					int vrom_copy_size = ms.Read(Board.VROM, 0, Board.VROM.Length);
+
+					if (vrom_copy_size < Board.VROM.Length)
+						LoadWriteLine("Less than the expected VROM was found in the file: {0} < {1}", vrom_copy_size, Board.VROM.Length);
+				}
+			}
 
 			LoadReport.Flush();
 			CoreComm.RomStatusDetails = LoadReport.ToString();