nes-unif support. we will need to explicitly add every UNIF board we support to their respective mappers, because

[1] each unif board name carries with it its own unique assumptions about which chips are present. 
[2] the unif board names may not be matching bootgod's, which we accept as canonical; 
also fix a small memory leak due to nes boards not being disposed during scan process.
This commit is contained in:
zeromus 2012-10-16 22:27:48 +00:00
parent cffc9293c1
commit 31c7edf8dd
9 changed files with 203 additions and 137 deletions

View File

@ -262,12 +262,15 @@ namespace BizHawk.Emulation.Consoles.Nintendo
protected void AssertVram(params int[] vram) { Assert_memtype(Cart.vram_size, "vram", vram); } protected void AssertVram(params int[] vram) { Assert_memtype(Cart.vram_size, "vram", vram); }
protected void Assert_memtype(int value, string name, int[] valid) protected void Assert_memtype(int value, string name, int[] valid)
{ {
if (DisableConfigAsserts) return;
foreach (int i in valid) if (value == i) return; foreach (int i in valid) if (value == i) return;
Assert(false, "unhandled {0} size of {1}", name,value); Assert(false, "unhandled {0} size of {1}", name,value);
} }
protected void AssertBattery(bool has_bat) { Assert(Cart.wram_battery == has_bat); } protected void AssertBattery(bool has_bat) { Assert(Cart.wram_battery == has_bat); }
public virtual void ApplyCustomAudio(short[] samples) { } public virtual void ApplyCustomAudio(short[] samples) { }
public bool DisableConfigAsserts = false;
} }
//this will be used to track classes that implement boards //this will be used to track classes that implement boards
@ -340,11 +343,21 @@ namespace BizHawk.Emulation.Consoles.Nintendo
nes.cart = cart; nes.cart = cart;
foreach (var type in INESBoardImplementors) foreach (var type in INESBoardImplementors)
{ {
INESBoard board = (INESBoard)Activator.CreateInstance(type); using (NESBoardBase board = (NESBoardBase)Activator.CreateInstance(type))
{
//unif demands that the boards set themselves up with expected legal values based on the board size
//except, i guess, for the rom/chr sizes. go figure.
//so, disable the asserts here
if (origin == EDetectionOrigin.UNIF)
board.DisableConfigAsserts = true;
board.Create(nes); board.Create(nes);
if (board.Configure(origin)) if (board.Configure(origin))
{
return type; return type;
} }
}
}
return null; return null;
} }

View File

@ -301,7 +301,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
public override void Dispose() public override void Dispose()
{ {
mmc3.Dispose(); if(mmc3 != null) mmc3.Dispose();
} }
public override void SyncState(Serializer ser) public override void SyncState(Serializer ser)

View File

@ -85,6 +85,10 @@ namespace BizHawk.Emulation.Consoles.Nintendo
AssertPrg(128, 256, 512); AssertChr(128, 256); AssertVram(0); AssertWram(8); AssertPrg(128, 256, 512); AssertChr(128, 256); AssertVram(0); AssertWram(8);
AssertBattery(false); AssertBattery(false);
break; break;
case "UNIF_TSROM":
Cart.wram_size = 8;
Cart.wram_battery = false;
break;
case "ACCLAIM-MC-ACC": //alien 3 (U) case "ACCLAIM-MC-ACC": //alien 3 (U)
AssertPrg(128); AssertChr(128); AssertVram(0); AssertWram(0); AssertPrg(128); AssertChr(128); AssertVram(0); AssertWram(0);
AssertBattery(false); AssertBattery(false);
@ -93,6 +97,8 @@ namespace BizHawk.Emulation.Consoles.Nintendo
AssertPrg(128); AssertChr(128); AssertVram(0); AssertWram(0); AssertPrg(128); AssertChr(128); AssertVram(0); AssertWram(0);
AssertBattery(false); AssertBattery(false);
break; break;
default: default:
return false; return false;
} }

View File

@ -42,7 +42,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
{ {
prg_regs_8k.Dispose(); prg_regs_8k.Dispose();
chr_regs_2k.Dispose(); chr_regs_2k.Dispose();
mmc3.Dispose(); if(mmc3 != null) mmc3.Dispose();
base.Dispose(); base.Dispose();
} }

View File

@ -107,6 +107,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
public override void Dispose() public override void Dispose()
{ {
if(mapper != null)
mapper.Dispose(); mapper.Dispose();
} }

View File

@ -21,6 +21,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
string game_name; //friendly name exposed to user and used as filename base string game_name; //friendly name exposed to user and used as filename base
CartInfo cart; //the current cart prototype. should be moved into the board, perhaps CartInfo cart; //the current cart prototype. should be moved into the board, perhaps
INESBoard board; //the board hardware that is currently driving things INESBoard board; //the board hardware that is currently driving things
EDetectionOrigin origin = EDetectionOrigin.None;
public bool SoundOn = true; public bool SoundOn = true;
int sprdma_countdown; int sprdma_countdown;
bool _irq_apu; //various irq signals that get merged to the cpu irq pin bool _irq_apu; //various irq signals that get merged to the cpu irq pin

View File

@ -409,7 +409,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
public enum EDetectionOrigin public enum EDetectionOrigin
{ {
None, BootGodDB, GameDB, INES None, BootGodDB, GameDB, INES, UNIF
} }
StringWriter LoadReport; StringWriter LoadReport;
@ -440,26 +440,41 @@ namespace BizHawk.Emulation.Consoles.Nintendo
LoadWriteLine("------"); LoadWriteLine("------");
LoadWriteLine("BEGIN NES rom analysis:"); LoadWriteLine("BEGIN NES rom analysis:");
byte[] file = rom; byte[] file = rom;
Type boardType = null;
CartInfo choice = null;
CartInfo iNesHeaderInfo = null;
List<string> hash_sha1_several = new List<string>();
string hash_sha1 = null, hash_md5 = null;
Unif unif = null;
origin = EDetectionOrigin.None;
if (file.Length < 16) throw new Exception("Alleged NES rom too small to be anything useful"); if (file.Length < 16) throw new Exception("Alleged NES rom too small to be anything useful");
if (file.Take(4).SequenceEqual(System.Text.Encoding.ASCII.GetBytes("UNIF"))) if (file.Take(4).SequenceEqual(System.Text.Encoding.ASCII.GetBytes("UNIF")))
throw new Exception("You've tried to open a UNIF rom. We don't have any UNIF roms to test with. Please consult the developers."); {
LoadWriteLine("Found UNIF header:");
LoadWriteLine("Since this is UNIF we can confidently parse PRG/CHR banks to hash.");
unif = new Unif(new MemoryStream(file));
hash_sha1 = unif.GetCartInfo().sha1;
hash_sha1_several.Add(hash_sha1);
}
else
{
fixed (byte* bfile = &file[0]) fixed (byte* bfile = &file[0])
{ {
var origin = EDetectionOrigin.None;
var header = (iNES_HEADER*)bfile; var header = (iNES_HEADER*)bfile;
if (!header->CheckID()) throw new InvalidOperationException("iNES header not found"); if (!header->CheckID()) throw new InvalidOperationException("iNES header not found");
header->Cleanup(); header->Cleanup();
//now that we know we have an iNES header, we can try to ignore it. //now that we know we have an iNES header, we can try to ignore it.
List<string> hash_sha1_several = new List<string>(); hash_sha1 = "sha1:" + Util.Hash_SHA1(file, 16, file.Length - 16);
string hash_sha1 = "sha1:" + Util.Hash_SHA1(file,16,file.Length - 16);
hash_sha1_several.Add(hash_sha1); hash_sha1_several.Add(hash_sha1);
string hash_md5 = "md5:" + Util.Hash_MD5(file, 16, file.Length - 16); hash_md5 = "md5:" + Util.Hash_MD5(file, 16, file.Length - 16);
LoadWriteLine("Found iNES header:"); LoadWriteLine("Found iNES header:");
CartInfo iNesHeaderInfo = header->Analyze(new MyWriter(LoadReport)); iNesHeaderInfo = header->Analyze(new MyWriter(LoadReport));
LoadWriteLine("Since this is iNES we can (somewhat) 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_sha1);
@ -481,9 +496,9 @@ namespace BizHawk.Emulation.Consoles.Nintendo
hash = "md5:" + Util.Hash_MD5(bytes, 0, bytes.Length); hash = "md5:" + Util.Hash_MD5(bytes, 0, bytes.Length);
LoadWriteLine(" PRG (8KB) + CHR hash: {0}", hash); LoadWriteLine(" PRG (8KB) + CHR hash: {0}", hash);
} }
}
}
Type boardType = null;
CartInfo choice = null;
if (USE_DATABASE) if (USE_DATABASE)
choice = IdentifyFromBootGodDB(hash_sha1_several); choice = IdentifyFromBootGodDB(hash_sha1_several);
if (choice == null) if (choice == null)
@ -491,7 +506,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
LoadWriteLine("Could not locate game in nescartdb"); LoadWriteLine("Could not locate game in nescartdb");
if (USE_DATABASE) if (USE_DATABASE)
{ {
choice = IdentifyFromGameDB(hash_md5); if(hash_md5 != null) choice = IdentifyFromGameDB(hash_md5);
if (choice == null) if (choice == null)
{ {
choice = IdentifyFromGameDB(hash_sha1); choice = IdentifyFromGameDB(hash_sha1);
@ -500,6 +515,16 @@ namespace BizHawk.Emulation.Consoles.Nintendo
if (choice == null) if (choice == null)
{ {
LoadWriteLine("Could not locate game in bizhawk gamedb"); LoadWriteLine("Could not locate game in bizhawk gamedb");
if (unif != null)
{
LoadWriteLine("Using information from UNIF header");
choice = unif.GetCartInfo();
choice.game = new NESGameInfo();
choice.game.name = gameInfo.Name;
origin = EDetectionOrigin.UNIF;
}
if (iNesHeaderInfo != null)
{
LoadWriteLine("Attempting inference from iNES header"); LoadWriteLine("Attempting inference from iNES header");
choice = iNesHeaderInfo; choice = iNesHeaderInfo;
string iNES_board = iNESBoardDetector.Detect(choice); string iNES_board = iNESBoardDetector.Detect(choice);
@ -530,6 +555,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
choice.game.name = gameInfo.Name; choice.game.name = gameInfo.Name;
origin = EDetectionOrigin.INES; origin = EDetectionOrigin.INES;
} }
}
else else
{ {
origin = EDetectionOrigin.GameDB; origin = EDetectionOrigin.GameDB;
@ -583,6 +609,11 @@ namespace BizHawk.Emulation.Consoles.Nintendo
RomStatus = RomStatus.GoodDump; RomStatus = RomStatus.GoodDump;
CoreOutputComm.RomStatusAnnotation = "Identified from BootGod's database"; CoreOutputComm.RomStatusAnnotation = "Identified from BootGod's database";
} }
if (origin == EDetectionOrigin.UNIF)
{
RomStatus = RomStatus.NotInDatabase;
CoreOutputComm.RomStatusAnnotation = "Inferred from UNIF header; somewhat suspicious";
}
if (origin == EDetectionOrigin.INES) if (origin == EDetectionOrigin.INES)
{ {
RomStatus = RomStatus.NotInDatabase; RomStatus = RomStatus.NotInDatabase;
@ -604,6 +635,9 @@ namespace BizHawk.Emulation.Consoles.Nintendo
CoreOutputComm.RomStatusDetails = LoadReport.ToString(); CoreOutputComm.RomStatusDetails = LoadReport.ToString();
//create the board's rom and vrom //create the board's rom and vrom
if (iNesHeaderInfo != null)
{
//pluck the necessary bytes out of the file
board.ROM = new byte[choice.prg_size * 1024]; board.ROM = new byte[choice.prg_size * 1024];
Array.Copy(file, 16, board.ROM, 0, board.ROM.Length); Array.Copy(file, 16, board.ROM, 0, board.ROM.Length);
if (choice.chr_size > 0) if (choice.chr_size > 0)
@ -612,6 +646,12 @@ namespace BizHawk.Emulation.Consoles.Nintendo
int vrom_offset = iNesHeaderInfo.prg_size * 1024; int vrom_offset = iNesHeaderInfo.prg_size * 1024;
Array.Copy(file, 16 + vrom_offset, board.VROM, 0, board.VROM.Length); Array.Copy(file, 16 + vrom_offset, board.VROM, 0, board.VROM.Length);
} }
}
else
{
board.ROM = unif.GetPRG();
board.VROM = unif.GetCHR();
}
//create the vram and wram if necessary //create the vram and wram if necessary
if (cart.wram_size != 0) if (cart.wram_size != 0)
@ -624,7 +664,6 @@ namespace BizHawk.Emulation.Consoles.Nintendo
HardReset(); HardReset();
SetupMemoryDomains(); SetupMemoryDomains();
} }
}
void SyncState(Serializer ser) void SyncState(Serializer ser)
{ {

View File

@ -67,18 +67,20 @@ namespace BizHawk.Emulation.Consoles.Nintendo
switch (tmp[0]) switch (tmp[0])
{ {
case 0: // hmirror case 0: // hmirror
ci.pad_h = 1;
ci.pad_v = 0;
break;
case 1: // vmirror
ci.pad_h = 0; ci.pad_h = 0;
ci.pad_v = 1; ci.pad_v = 1;
break; break;
case 1: // vmirror
ci.pad_h = 1;
ci.pad_v = 0;
break;
} }
} }
if (chunks.TryGetValue("MAPR", out tmp)) if (chunks.TryGetValue("MAPR", out tmp))
ci.board_type = Encoding.ASCII.GetString(tmp); ci.board_type = Encoding.ASCII.GetString(tmp);
ci.board_type = ci.board_type.TrimEnd('\0');
ci.board_type = "UNIF_" + ci.board_type;
// is there any way using System.Security.Cryptography.SHA1 to compute the hash of // is there any way using System.Security.Cryptography.SHA1 to compute the hash of
// prg concatentated with chr? i couldn't figure it out, so this implementation is dumb // prg concatentated with chr? i couldn't figure it out, so this implementation is dumb
@ -88,7 +90,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
ms.Write(chrrom, 0, chrrom.Length); ms.Write(chrrom, 0, chrrom.Length);
ms.Close(); ms.Close();
byte[] all = ms.ToArray(); byte[] all = ms.ToArray();
ci.sha1 = Util.Hash_SHA1(all, 0, all.Length); ci.sha1 = "sha1:" + Util.Hash_SHA1(all, 0, all.Length);
} }
} }

View File

@ -128,6 +128,11 @@ namespace BizHawk
switch (ext) switch (ext)
{ {
case ".NES":
case ".UNF":
case ".FDS":
Game.System = "NES";
break;
case ".SFC": case ".SFC":
case ".SMC": case ".SMC":
Game.System = "SNES"; Game.System = "SNES";
@ -144,7 +149,6 @@ namespace BizHawk
case ".GEN": case ".GEN":
case ".MD": case ".MD":
case ".SMD": Game.System = "GEN"; break; case ".SMD": Game.System = "GEN"; break;
case ".NES": Game.System = "NES"; break;
case ".A26": Game.System = "A26"; break; case ".A26": Game.System = "A26"; break;
case ".COL": Game.System = "COLV"; break; case ".COL": Game.System = "COLV"; break;
case ".ROM": case ".ROM":