Esempio n. 1
0
        /// <summary>
        /// finds a board class which can handle the provided cart
        /// </summary>
        static Type FindBoard(CartInfo cart, EDetectionOrigin origin, Dictionary <string, string> properties)
        {
            NES nes = new NES();

            nes.cart = cart;
            Type ret = null;

            lock (INESBoardImplementors)
                foreach (var type in INESBoardImplementors)
                {
                    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.InitialRegisterValues = properties;
                        if (board.Configure(origin))
                        {
#if DEBUG
                            if (ret != null)
                            {
                                throw new Exception(string.Format("Boards {0} and {1} both responded to Configure!", ret, type));
                            }
                            else
                            {
                                ret = type;
                            }
#else
                            return(type);
#endif
                        }
                    }
                }
            return(ret);
        }
Esempio n. 2
0
        /// <summary>
        /// looks up from the game DB
        /// </summary>
        private CartInfo IdentifyFromGameDB(string hash)
        {
            var gi = Database.CheckDatabase(hash);

            if (gi == null)
            {
                return(null);
            }

            CartInfo cart = new CartInfo();

            //try generating a bootgod cart descriptor from the game database
            var dict = gi.GetOptions();

            cart.GameInfo = gi;
            if (!dict.TryGetValue("board", out var board))
            {
                throw new Exception("NES gamedb entries must have a board identifier!");
            }
            cart.BoardType = board;
            if (dict.TryGetValue("system", out var system))
            {
                cart.System = system;
            }

            cart.PrgSize  = dict.TryGetValue("PRG", out var prgSizeStr) ? short.Parse(prgSizeStr) : -1;
            cart.ChrSize  = dict.TryGetValue("CHR", out var chrSizeStr) ? short.Parse(chrSizeStr) : -1;
            cart.VramSize = dict.TryGetValue("VRAM", out var vramSizeStr) ? short.Parse(vramSizeStr) : -1;
            cart.WramSize = dict.TryGetValue("WRAM", out var wramSizeStr) ? short.Parse(wramSizeStr) : -1;

            if (dict.TryGetValue("PAD_H", out var padHStr))
            {
                cart.PadH = byte.Parse(padHStr);
            }
            if (dict.TryGetValue("PAD_V", out var padVStr))
            {
                cart.PadV = byte.Parse(padVStr);
            }
            if (dict.TryGetValue("MIR", out var mirStr))
            {
                if (mirStr == "H")
                {
                    cart.PadV = 1; cart.PadH = 0;
                }
                else if (mirStr == "V")
                {
                    cart.PadH = 1; cart.PadV = 0;
                }
            }

            if (dict.ContainsKey("BAD"))
            {
                cart.Bad = true;
            }
            if (dict.TryGetValue("MMC3", out var mmc3))
            {
                cart.Chips.Add(mmc3);
            }
            if (dict.TryGetValue("PCB", out var pcb))
            {
                cart.Pcb = pcb;
            }
            if (dict.TryGetValue("BATT", out var batteryStr))
            {
                cart.WramBattery = bool.Parse(batteryStr);
            }
            if (dict.TryGetValue("palette", out var palette))
            {
                cart.Palette = palette;
            }
            if (dict.TryGetValue("vs_security", out var vsSecurityStr))
            {
                cart.VsSecurity = byte.Parse(vsSecurityStr);
            }

            return(cart);
        }
Esempio n. 3
0
        public BootGodDb(string basePath)
        {
            // notes: there can be multiple each of prg,chr,wram,vram
            // we aren't tracking the individual hashes yet.

            string xmlPath = Path.Combine(basePath, "NesCarts.xml");
            string x7zPath = Path.Combine(basePath, "NesCarts.7z");
            bool   loadXml = File.Exists(xmlPath);

            using var nesCartFile = new HawkFile(loadXml ? xmlPath : x7zPath);
            if (!loadXml)
            {
                nesCartFile.BindFirst();
            }

            var stream = nesCartFile.GetStream();

            // in anticipation of any slowness annoying people, and just for shits and giggles, i made a super fast parser
            int      state     = 0;
            var      xmlReader = XmlReader.Create(stream);
            CartInfo currCart  = null;
            string   currName  = null;

            while (xmlReader.Read())
            {
                switch (state)
                {
                case 0:
                    if (xmlReader.NodeType == XmlNodeType.Element && xmlReader.Name == "game")
                    {
                        currName = xmlReader.GetAttribute("name");
                        state    = 1;
                    }
                    break;

                case 2:
                    if (xmlReader.NodeType == XmlNodeType.Element && xmlReader.Name == "board")
                    {
                        currCart.BoardType = xmlReader.GetAttribute("type");
                        currCart.Pcb       = xmlReader.GetAttribute("pcb");
                        int mapper = int.Parse(xmlReader.GetAttribute("mapper"));
                        if (validate && mapper > 255)
                        {
                            throw new Exception("didnt expect mapper>255!");
                        }
                        // we don't actually use this value at all; only the board name
                        state = 3;
                    }
                    break;

                case 3:
                    if (xmlReader.NodeType == XmlNodeType.Element)
                    {
                        switch (xmlReader.Name)
                        {
                        case "prg":
                            currCart.PrgSize += (short)ParseSize(xmlReader.GetAttribute("size"));
                            break;

                        case "chr":
                            currCart.ChrSize += (short)ParseSize(xmlReader.GetAttribute("size"));
                            break;

                        case "vram":
                            currCart.VramSize += (short)ParseSize(xmlReader.GetAttribute("size"));
                            break;

                        case "wram":
                            currCart.WramSize += (short)ParseSize(xmlReader.GetAttribute("size"));
                            if (xmlReader.GetAttribute("battery") != null)
                            {
                                currCart.WramBattery = true;
                            }
                            break;

                        case "pad":
                            currCart.PadH = byte.Parse(xmlReader.GetAttribute("h"));
                            currCart.PadV = byte.Parse(xmlReader.GetAttribute("v"));
                            break;

                        case "chip":
                            currCart.Chips.Add(xmlReader.GetAttribute("type"));
                            if (xmlReader.GetAttribute("battery") != null)
                            {
                                currCart.WramBattery = true;
                            }
                            break;
                        }
                    }
                    else
                    if (xmlReader.NodeType == XmlNodeType.EndElement && xmlReader.Name == "board")
                    {
                        state = 4;
                    }
                    break;

                case 4:
                    if (xmlReader.NodeType == XmlNodeType.EndElement && xmlReader.Name == "cartridge")
                    {
                        _sha1Table[currCart.Sha1].Add(currCart);
                        currCart = null;
                        state    = 5;
                    }
                    break;

                case 5:
                case 1:
                    if (xmlReader.NodeType == XmlNodeType.Element && xmlReader.Name == "cartridge")
                    {
                        currCart        = new CartInfo();
                        currCart.System = xmlReader.GetAttribute("system");
                        currCart.Sha1   = "sha1:" + xmlReader.GetAttribute("sha1");
                        currCart.Name   = currName;
                        state           = 2;
                    }
                    if (xmlReader.NodeType == XmlNodeType.EndElement && xmlReader.Name == "game")
                    {
                        currName = null;
                        state    = 0;
                    }
                    break;
                }
            }             //end xmlreader loop
        }
        /// <summary>
        /// looks up from the game DB
        /// </summary>
        private CartInfo IdentifyFromGameDB(string hash)
        {
            var gi = Database.CheckDatabase(hash);

            if (gi == null)
            {
                return(null);
            }

            CartInfo cart = new CartInfo();

            //try generating a bootgod cart descriptor from the game database
            var dict = gi.GetOptions();

            cart.GameInfo = gi;
            if (!dict.ContainsKey("board"))
            {
                throw new Exception("NES gamedb entries must have a board identifier!");
            }
            cart.BoardType = dict["board"];
            if (dict.ContainsKey("system"))
            {
                cart.System = dict["system"];
            }
            cart.PrgSize  = -1;
            cart.VramSize = -1;
            cart.WramSize = -1;
            cart.ChrSize  = -1;
            if (dict.ContainsKey("PRG"))
            {
                cart.PrgSize = short.Parse(dict["PRG"]);
            }
            if (dict.ContainsKey("CHR"))
            {
                cart.ChrSize = short.Parse(dict["CHR"]);
            }
            if (dict.ContainsKey("VRAM"))
            {
                cart.VramSize = short.Parse(dict["VRAM"]);
            }
            if (dict.ContainsKey("WRAM"))
            {
                cart.WramSize = short.Parse(dict["WRAM"]);
            }
            if (dict.ContainsKey("PAD_H"))
            {
                cart.PadH = byte.Parse(dict["PAD_H"]);
            }
            if (dict.ContainsKey("PAD_V"))
            {
                cart.PadV = byte.Parse(dict["PAD_V"]);
            }
            if (dict.ContainsKey("MIR"))
            {
                if (dict["MIR"] == "H")
                {
                    cart.PadV = 1; cart.PadH = 0;
                }
                else if (dict["MIR"] == "V")
                {
                    cart.PadH = 1; cart.PadV = 0;
                }
            }
            if (dict.ContainsKey("BAD"))
            {
                cart.Bad = true;
            }
            if (dict.ContainsKey("MMC3"))
            {
                cart.Chips.Add(dict["MMC3"]);
            }
            if (dict.ContainsKey("PCB"))
            {
                cart.Pcb = dict["PCB"];
            }
            if (dict.ContainsKey("BATT"))
            {
                cart.WramBattery = bool.Parse(dict["BATT"]);
            }

            if (dict.ContainsKey("palette"))
            {
                cart.Palette = dict["palette"];
            }

            if (dict.ContainsKey("vs_security"))
            {
                cart.VsSecurity = byte.Parse(dict["vs_security"]);
            }

            return(cart);
        }
Esempio n. 5
0
            public BootGodDB()
            {
                //notes: there can be multiple each of prg,chr,wram,vram
                //we arent tracking the individual hashes yet.

                //in anticipation of any slowness annoying people, and just for shits and giggles, i made a super fast parser
                int      state     = 0;
                var      xmlreader = XmlReader.Create(new MemoryStream(_GetDatabaseBytes()));
                CartInfo currCart  = null;
                string   currName  = null;

                while (xmlreader.Read())
                {
                    switch (state)
                    {
                    case 0:
                        if (xmlreader.NodeType == XmlNodeType.Element && xmlreader.Name == "game")
                        {
                            currName = xmlreader.GetAttribute("name");
                            state    = 1;
                        }
                        break;

                    case 2:
                        if (xmlreader.NodeType == XmlNodeType.Element && xmlreader.Name == "board")
                        {
                            currCart.board_type = xmlreader.GetAttribute("type");
                            currCart.pcb        = xmlreader.GetAttribute("pcb");
                            int mapper = int.Parse(xmlreader.GetAttribute("mapper"));
                            if (validate && mapper > 255)
                            {
                                throw new Exception("didnt expect mapper>255!");
                            }
                            // we don't actually use this value at all; only the board name
                            state = 3;
                        }
                        break;

                    case 3:
                        if (xmlreader.NodeType == XmlNodeType.Element)
                        {
                            switch (xmlreader.Name)
                            {
                            case "prg":
                                currCart.prg_size += (short)ParseSize(xmlreader.GetAttribute("size"));
                                break;

                            case "chr":
                                currCart.chr_size += (short)ParseSize(xmlreader.GetAttribute("size"));
                                break;

                            case "vram":
                                currCart.vram_size += (short)ParseSize(xmlreader.GetAttribute("size"));
                                break;

                            case "wram":
                                currCart.wram_size += (short)ParseSize(xmlreader.GetAttribute("size"));
                                if (xmlreader.GetAttribute("battery") != null)
                                {
                                    currCart.wram_battery = true;
                                }
                                break;

                            case "pad":
                                currCart.pad_h = byte.Parse(xmlreader.GetAttribute("h"));
                                currCart.pad_v = byte.Parse(xmlreader.GetAttribute("v"));
                                break;

                            case "chip":
                                currCart.chips.Add(xmlreader.GetAttribute("type"));
                                break;
                            }
                        }
                        else
                        if (xmlreader.NodeType == XmlNodeType.EndElement && xmlreader.Name == "board")
                        {
                            state = 4;
                        }
                        break;

                    case 4:
                        if (xmlreader.NodeType == XmlNodeType.EndElement && xmlreader.Name == "cartridge")
                        {
                            sha1_table[currCart.sha1].Add(currCart);
                            currCart = null;
                            state    = 5;
                        }
                        break;

                    case 5:
                    case 1:
                        if (xmlreader.NodeType == XmlNodeType.Element && xmlreader.Name == "cartridge")
                        {
                            currCart        = new CartInfo();
                            currCart.system = xmlreader.GetAttribute("system");
                            currCart.sha1   = "sha1:" + xmlreader.GetAttribute("sha1");
                            currCart.name   = currName;
                            state           = 2;
                        }
                        if (xmlreader.NodeType == XmlNodeType.EndElement && xmlreader.Name == "game")
                        {
                            currName = null;
                            state    = 0;
                        }
                        break;
                    }
                }                 //end xmlreader loop
            }
Esempio n. 6
0
        /// <summary>
        /// looks up from the game DB
        /// </summary>
        CartInfo IdentifyFromGameDB(string hash)
        {
            var gi = Database.CheckDatabase(hash);

            if (gi == null)
            {
                return(null);
            }

            CartInfo cart = new CartInfo();

            //try generating a bootgod cart descriptor from the game database
            var dict = gi.GetOptionsDict();

            cart.DB_GameInfo = gi;
            if (!dict.ContainsKey("board"))
            {
                throw new Exception("NES gamedb entries must have a board identifier!");
            }
            cart.board_type = dict["board"];
            if (dict.ContainsKey("system"))
            {
                cart.system = dict["system"];
            }
            cart.prg_size  = -1;
            cart.vram_size = -1;
            cart.wram_size = -1;
            cart.chr_size  = -1;
            if (dict.ContainsKey("PRG"))
            {
                cart.prg_size = short.Parse(dict["PRG"]);
            }
            if (dict.ContainsKey("CHR"))
            {
                cart.chr_size = short.Parse(dict["CHR"]);
            }
            if (dict.ContainsKey("VRAM"))
            {
                cart.vram_size = short.Parse(dict["VRAM"]);
            }
            if (dict.ContainsKey("WRAM"))
            {
                cart.wram_size = short.Parse(dict["WRAM"]);
            }
            if (dict.ContainsKey("PAD_H"))
            {
                cart.pad_h = byte.Parse(dict["PAD_H"]);
            }
            if (dict.ContainsKey("PAD_V"))
            {
                cart.pad_v = byte.Parse(dict["PAD_V"]);
            }
            if (dict.ContainsKey("MIR"))
            {
                if (dict["MIR"] == "H")
                {
                    cart.pad_v = 1; cart.pad_h = 0;
                }
                else if (dict["MIR"] == "V")
                {
                    cart.pad_h = 1; cart.pad_v = 0;
                }
            }
            if (dict.ContainsKey("BAD"))
            {
                cart.bad = true;
            }
            if (dict.ContainsKey("MMC3"))
            {
                cart.chips.Add(dict["MMC3"]);
            }
            if (dict.ContainsKey("PCB"))
            {
                cart.pcb = dict["PCB"];
            }
            if (dict.ContainsKey("BATT"))
            {
                cart.wram_battery = bool.Parse(dict["BATT"]);
            }

            if (dict.ContainsKey("palette"))
            {
                cart.palette = dict["palette"];
            }

            if (dict.ContainsKey("vs_security"))
            {
                cart.vs_security = byte.Parse(dict["vs_security"]);
            }

            return(cart);
        }
Esempio n. 7
0
        public void Init(GameInfo gameInfo, byte[] rom, byte[] fdsbios = null)
        {
            LoadReport = new StringWriter();
            LoadWriteLine("------");
            LoadWriteLine("BEGIN NES rom analysis:");
            byte[] file = rom;

            Type          boardType = null;
            CartInfo      choice = null;
            CartInfo      iNesHeaderInfo = null;
            CartInfo      iNesHeaderInfoV2 = null;
            List <string> hash_sha1_several = new List <string>();
            string        hash_sha1 = null, hash_md5 = null;
            Unif          unif = null;

            Dictionary <string, string> InitialMapperRegisterValues = new Dictionary <string, string>(SyncSettings.BoardProperties);

            origin = EDetectionOrigin.None;

            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")))
            {
                unif = new Unif(new MemoryStream(file));
                LoadWriteLine("Found UNIF header:");
                LoadWriteLine(unif.CartInfo);
                LoadWriteLine("Since this is UNIF we can confidently parse PRG/CHR banks to hash.");
                hash_sha1 = unif.CartInfo.sha1;
                hash_sha1_several.Add(hash_sha1);
                LoadWriteLine("headerless rom hash: {0}", hash_sha1);
            }
            else if (file.Take(5).SequenceEqual(System.Text.Encoding.ASCII.GetBytes("NESM\x1A")))
            {
                origin = EDetectionOrigin.NSF;
                LoadWriteLine("Loading as NSF");
                var nsf = new NSFFormat();
                nsf.WrapByteArray(file);

                cart = new CartInfo();
                var nsfboard = new NSFBoard();
                nsfboard.Create(this);
                nsfboard.ROM = rom;
                nsfboard.InitNSF(nsf);
                nsfboard.InitialRegisterValues = InitialMapperRegisterValues;
                nsfboard.Configure(origin);
                nsfboard.WRAM = new byte[cart.wram_size * 1024];
                Board         = nsfboard;
                Board.PostConfigure();
                AutoMapperProps.Populate(Board, SyncSettings);

                Console.WriteLine("Using NTSC display type for NSF for now");
                _display_type = Common.DisplayType.NTSC;

                HardReset();

                return;
            }
            else if (file.Take(4).SequenceEqual(System.Text.Encoding.ASCII.GetBytes("FDS\x1A")) ||
                     file.Take(4).SequenceEqual(System.Text.Encoding.ASCII.GetBytes("\x01*NI")))
            {
                // danger!  this is a different codepath with an early return.  accordingly, some
                // code is duplicated twice...

                // FDS roms are just fed to the board, we don't do much else with them
                origin = EDetectionOrigin.FDS;
                LoadWriteLine("Found FDS header.");
                if (fdsbios == null)
                {
                    throw new MissingFirmwareException("Missing FDS Bios");
                }
                cart = new CartInfo();
                var fdsboard = new FDS();
                fdsboard.biosrom = fdsbios;
                fdsboard.SetDiskImage(rom);
                fdsboard.Create(this);
                // at the moment, FDS doesn't use the IRVs, but it could at some point in the future
                fdsboard.InitialRegisterValues = InitialMapperRegisterValues;
                fdsboard.Configure(origin);

                Board = fdsboard;

                //create the vram and wram if necessary
                if (cart.wram_size != 0)
                {
                    Board.WRAM = new byte[cart.wram_size * 1024];
                }
                if (cart.vram_size != 0)
                {
                    Board.VRAM = new byte[cart.vram_size * 1024];
                }

                Board.PostConfigure();
                AutoMapperProps.Populate(Board, SyncSettings);

                Console.WriteLine("Using NTSC display type for FDS disk image");
                _display_type = Common.DisplayType.NTSC;

                HardReset();

                return;
            }
            else
            {
                byte[] nesheader = new byte[16];
                Buffer.BlockCopy(file, 0, nesheader, 0, 16);

                bool exists = true;

                if (!DetectFromINES(nesheader, out iNesHeaderInfo, out 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)
                    {
                        hash_sha1_several.Add(hash_sha1);
                        choice = IdentifyFromBootGodDB(hash_sha1_several);
                        if (choice == null)
                        {
                            LoadWriteLine("Could not locate game in nescartdb");
                        }
                        else
                        {
                            LoadWriteLine("Chose board from nescartdb:");
                            LoadWriteLine(choice);
                            origin = EDetectionOrigin.BootGodDB;
                        }
                    }
                    if (choice == null)
                    {
                        throw new InvalidOperationException("iNES header not found and no gamedb entry");
                    }
                }

                if (exists)
                {
                    //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
                        if (file.Length >= (16 * 1024 + iNesHeaderInfo.chr_size * 1024 + 16))
                        {
                            // This assumes that even though the PRG is only 8k the CHR is still written
                            // 16k into the file, which is not always the case (e.x. Galaxian RevA)
                            msTemp.Write(file, 16 + 16 * 1024, iNesHeaderInfo.chr_size * 1024);                             //add chr
                        }
                        else if (file.Length >= (8 * 1024 + iNesHeaderInfo.chr_size * 1024 + 16))
                        {
                            // maybe the PRG is only 8k
                            msTemp.Write(file, 16 + 8 * 1024, iNesHeaderInfo.chr_size * 1024);                             //add chr
                        }
                        else
                        {
                            // we failed somehow
                            // most likely the header is wrong
                            Console.WriteLine("WARNING: 16kb PRG iNES header but unable to parse");
                        }
                        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);
                    }
                }
            }

            if (USE_DATABASE)
            {
                if (hash_md5 != null)
                {
                    choice = IdentifyFromGameDB(hash_md5);
                }
                if (choice == null)
                {
                    choice = IdentifyFromGameDB(hash_sha1);
                }
                if (choice == null)
                {
                    LoadWriteLine("Could not locate game in bizhawk gamedb");
                }
                else
                {
                    origin = EDetectionOrigin.GameDB;
                    LoadWriteLine("Chose board from bizhawk gamedb: " + choice.board_type);
                    //gamedb entries that dont specify prg/chr sizes can infer it from the ines header
                    if (iNesHeaderInfo != null)
                    {
                        if (choice.prg_size == -1)
                        {
                            choice.prg_size = iNesHeaderInfo.prg_size;
                        }
                        if (choice.chr_size == -1)
                        {
                            choice.chr_size = iNesHeaderInfo.chr_size;
                        }
                        if (choice.vram_size == -1)
                        {
                            choice.vram_size = iNesHeaderInfo.vram_size;
                        }
                        if (choice.wram_size == -1)
                        {
                            choice.wram_size = iNesHeaderInfo.wram_size;
                        }
                    }
                    else if (unif != null)
                    {
                        if (choice.prg_size == -1)
                        {
                            choice.prg_size = unif.CartInfo.prg_size;
                        }
                        if (choice.chr_size == -1)
                        {
                            choice.chr_size = unif.CartInfo.chr_size;
                        }
                        // unif has no wram\vram sizes; hope the board impl can figure it out...
                        if (choice.vram_size == -1)
                        {
                            choice.vram_size = 0;
                        }
                        if (choice.wram_size == -1)
                        {
                            choice.wram_size = 0;
                        }
                    }
                }

                //if this is still null, we have to try it some other way. nescartdb perhaps?
                if (choice == null)
                {
                    choice = IdentifyFromBootGodDB(hash_sha1_several);
                    if (choice == null)
                    {
                        LoadWriteLine("Could not locate game in nescartdb");
                    }
                    else
                    {
                        LoadWriteLine("Chose board from nescartdb:");
                        LoadWriteLine(choice);
                        origin = EDetectionOrigin.BootGodDB;
                    }
                }
            }

            //if choice is still null, try UNIF and iNES
            if (choice == null)
            {
                if (unif != null)
                {
                    LoadWriteLine("Using information from UNIF header");
                    choice = unif.CartInfo;
                    //ok, i have this Q-Boy rom with no VROM and no VRAM.
                    //we also certainly have games with VROM and no VRAM.
                    //looks like FCEUX policy is to allocate 8KB of chr ram no matter what UNLESS certain flags are set. but what's the justification for this? please leave a note if you go debugging in it again.
                    //well, we know we can't have much of a NES game if there's no VROM unless there's VRAM instead.
                    //so if the VRAM isn't set, choose 8 for it.
                    //TODO - unif loading code may need to use VROR flag to transform chr_size=8 to vram_size=8 (need example)
                    if (choice.chr_size == 0 && choice.vram_size == 0)
                    {
                        choice.vram_size = 8;
                    }
                    //(do we need to suppress this in case theres a CHR rom? probably not. nes board base will use ram if no rom is available)
                    origin = EDetectionOrigin.UNIF;
                }
                if (iNesHeaderInfo != null)
                {
                    LoadWriteLine("Attempting inference from iNES header");
                    // try to spin up V2 header first, then V1 header
                    if (iNesHeaderInfoV2 != null)
                    {
                        try
                        {
                            boardType = FindBoard(iNesHeaderInfoV2, origin, InitialMapperRegisterValues);
                        }
                        catch { }
                        if (boardType == null)
                        {
                            LoadWriteLine("Failed to load as iNES V2");
                        }
                        else
                        {
                            choice = iNesHeaderInfoV2;
                        }

                        // V2 might fail but V1 might succeed because we don't have most V2 aliases setup; and there's
                        // no reason to do so except when needed
                    }
                    if (boardType == null)
                    {
                        choice    = iNesHeaderInfo;                      // we're out of options, really
                        boardType = FindBoard(iNesHeaderInfo, origin, InitialMapperRegisterValues);
                        if (boardType == null)
                        {
                            LoadWriteLine("Failed to load as iNES V1");
                        }

                        // do not further meddle in wram sizes.  a board that is being loaded from a "MAPPERxxx"
                        // entry should know and handle the situation better for the individual board
                    }

                    LoadWriteLine("Chose board from iNES heuristics:");
                    LoadWriteLine(choice);
                    origin = EDetectionOrigin.INES;
                }
            }

            game_name = choice.name;

            //find a INESBoard to handle this
            if (choice != null)
            {
                boardType = FindBoard(choice, origin, InitialMapperRegisterValues);
            }
            else
            {
                throw new Exception("Unable to detect ROM");
            }
            if (boardType == null)
            {
                throw new Exception("No class implements the necessary board type: " + choice.board_type);
            }

            if (choice.DB_GameInfo != null)
            {
                choice.bad = choice.DB_GameInfo.IsRomStatusBad();
            }

            LoadWriteLine("Final game detection results:");
            LoadWriteLine(choice);
            LoadWriteLine("\"" + game_name + "\"");
            LoadWriteLine("Implemented by: class " + boardType.Name);
            if (choice.bad)
            {
                LoadWriteLine("~~ ONE WAY OR ANOTHER, THIS DUMP IS KNOWN TO BE *BAD* ~~");
                LoadWriteLine("~~ YOU SHOULD FIND A BETTER FILE ~~");
            }

            LoadWriteLine("END NES rom analysis");
            LoadWriteLine("------");

            Board = CreateBoardInstance(boardType);

            cart = choice;
            Board.Create(this);
            Board.InitialRegisterValues = InitialMapperRegisterValues;
            Board.Configure(origin);

            if (origin == EDetectionOrigin.BootGodDB)
            {
                RomStatus = RomStatus.GoodDump;
                CoreComm.RomStatusAnnotation = "Identified from BootGod's database";
            }
            if (origin == EDetectionOrigin.UNIF)
            {
                RomStatus = RomStatus.NotInDatabase;
                CoreComm.RomStatusAnnotation = "Inferred from UNIF header; somewhat suspicious";
            }
            if (origin == EDetectionOrigin.INES)
            {
                RomStatus = RomStatus.NotInDatabase;
                CoreComm.RomStatusAnnotation = "Inferred from iNES header; potentially wrong";
            }
            if (origin == EDetectionOrigin.GameDB)
            {
                if (choice.bad)
                {
                    RomStatus = RomStatus.BadDump;
                }
                else
                {
                    RomStatus = choice.DB_GameInfo.Status;
                }
            }

            byte[] trainer = null;

            //create the board's rom and vrom
            if (iNesHeaderInfo != null)
            {
                var ms = new MemoryStream(file, false);
                ms.Seek(16, SeekOrigin.Begin);                 // ines header
                //pluck the necessary bytes out of the file
                if (iNesHeaderInfo.trainer_size != 0)
                {
                    trainer = new byte[512];
                    ms.Read(trainer, 0, 512);
                }

                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);
                    }
                }
                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 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();

            // IF YOU DO ANYTHING AT ALL BELOW THIS LINE, MAKE SURE THE APPROPRIATE CHANGE IS MADE TO FDS (if applicable)

            //create the vram and wram if necessary
            if (cart.wram_size != 0)
            {
                Board.WRAM = new byte[cart.wram_size * 1024];
            }
            if (cart.vram_size != 0)
            {
                Board.VRAM = new byte[cart.vram_size * 1024];
            }

            Board.PostConfigure();
            AutoMapperProps.Populate(Board, SyncSettings);

            // set up display type

            NESSyncSettings.Region fromrom      = DetectRegion(cart.system);
            NESSyncSettings.Region fromsettings = SyncSettings.RegionOverride;

            if (fromsettings != NESSyncSettings.Region.Default)
            {
                Console.WriteLine("Using system region override");
                fromrom = fromsettings;
            }
            switch (fromrom)
            {
            case NESSyncSettings.Region.Dendy:
                _display_type = Common.DisplayType.Dendy;
                break;

            case NESSyncSettings.Region.NTSC:
                _display_type = Common.DisplayType.NTSC;
                break;

            case NESSyncSettings.Region.PAL:
                _display_type = Common.DisplayType.PAL;
                break;

            default:
                _display_type = Common.DisplayType.NTSC;
                break;
            }
            Console.WriteLine("Using NES system region of {0}", _display_type);

            HardReset();

            if (trainer != null)
            {
                Console.WriteLine("Applying trainer");
                for (int i = 0; i < 512; i++)
                {
                    WriteMemory((ushort)(0x7000 + i), trainer[i]);
                }
            }
        }
Esempio n. 8
0
        public static bool DetectFromINES(byte[] data, out CartInfo Cart, out CartInfo CartV2)
        {
            byte[] ID = new byte[4];
            Buffer.BlockCopy(data, 0, ID, 0, 4);
            if (!ID.SequenceEqual(Encoding.ASCII.GetBytes("NES\x1A")))
            {
                Cart   = null;
                CartV2 = null;
                return(false);
            }

            if ((data[7] & 0x0c) == 0x08)
            {
                // process as iNES v2
                CartV2 = new CartInfo
                {
                    prg_size     = data[4] | data[9] << 8 & 0xf00,
                        chr_size = data[5] | data[9] << 4 & 0xf00
                };

                CartV2.prg_size *= 16;
                CartV2.chr_size *= 8;

                CartV2.wram_battery = (data[6] & 2) != 0;                 // should this be respected in v2 mode??

                int wrambat = iNES2Wram(data[10] >> 4);
                int wramnon = iNES2Wram(data[10] & 15);
                CartV2.wram_battery |= wrambat > 0;
                // fixme - doesn't handle sizes not divisible by 1024
                CartV2.wram_size = (short)((wrambat + wramnon) / 1024);

                int mapper    = data[6] >> 4 | data[7] & 0xf0 | data[8] << 8 & 0xf00;
                int submapper = data[8] >> 4;
                CartV2.board_type = $"MAPPER{mapper:d4}-{submapper:d2}";

                int vrambat = iNES2Wram(data[11] >> 4);
                int vramnon = iNES2Wram(data[11] & 15);
                // hopefully a game with battery backed vram understands what to do internally
                CartV2.wram_battery |= vrambat > 0;
                CartV2.vram_size     = (vrambat + vramnon) / 1024;

                CartV2.inesmirroring = data[6] & 1 | data[6] >> 2 & 2;
                switch (CartV2.inesmirroring)
                {
                case 0: CartV2.pad_v = 1; break;

                case 1: CartV2.pad_h = 1; break;
                }
                switch (data[12] & 1)
                {
                case 0:
                    CartV2.system = "NES-NTSC";
                    break;

                case 1:
                    CartV2.system = "NES-PAL";
                    break;
                }

                if ((data[6] & 4) != 0)
                {
                    CartV2.trainer_size = 512;
                }
            }
            else
            {
                CartV2 = null;
            }

            // process as iNES v1
            // the DiskDude cleaning is no longer; get better roms
            Cart = new CartInfo
            {
                prg_size = data[4],
                chr_size = data[5]
            };

            if (Cart.prg_size == 0)
            {
                Cart.prg_size = 256;
            }
            Cart.prg_size *= 16;
            Cart.chr_size *= 8;


            Cart.wram_battery = (data[6] & 2) != 0;
            Cart.wram_size    = 8;          // should be data[8], but that never worked

            {
                int mapper = data[6] >> 4 | data[7] & 0xf0;
                Cart.board_type = $"MAPPER{mapper:d3}";
            }

            Cart.vram_size = Cart.chr_size > 0 ? 0 : 8;

            Cart.inesmirroring = data[6] & 1 | data[6] >> 2 & 2;
            switch (Cart.inesmirroring)
            {
            case 0: Cart.pad_v = 1; break;

            case 1: Cart.pad_h = 1; break;
            }

            if (data[6].Bit(2))
            {
                Cart.trainer_size = 512;
            }

            return(true);
        }
Esempio n. 9
0
        public void Init(GameInfo gameInfo, byte[] rom, byte[] fdsbios = null)
        {
            LoadReport = new StringWriter();
            LoadWriteLine("------");
            LoadWriteLine("BEGIN NES rom analysis:");
            byte[] file = rom;

            Type          boardType = null;
            CartInfo      choice = null;
            CartInfo      iNesHeaderInfo = null;
            CartInfo      iNesHeaderInfoV2 = null;
            List <string> hash_sha1_several = new List <string>();
            string        hash_sha1 = null, hash_md5 = null;
            Unif          unif = null;

            Dictionary <string, string> InitialMapperRegisterValues = new Dictionary <string, string>(SyncSettings.BoardProperties);

            origin = EDetectionOrigin.None;

            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")))
            {
                unif = new Unif(new MemoryStream(file));
                LoadWriteLine("Found UNIF header:");
                LoadWriteLine(unif.CartInfo);
                LoadWriteLine("Since this is UNIF we can confidently parse PRG/CHR banks to hash.");
                hash_sha1 = unif.CartInfo.sha1;
                hash_sha1_several.Add(hash_sha1);
                LoadWriteLine("headerless rom hash: {0}", hash_sha1);
            }
            else if (file.Take(4).SequenceEqual(System.Text.Encoding.ASCII.GetBytes("FDS\x1A")) ||
                     file.Take(4).SequenceEqual(System.Text.Encoding.ASCII.GetBytes("\x01*NI")))
            {
                // danger!  this is a different codepath with an early return.  accordingly, some
                // code is duplicated twice...

                // FDS roms are just fed to the board, we don't do much else with them
                origin = EDetectionOrigin.FDS;
                LoadWriteLine("Found FDS header.");
                if (fdsbios == null)
                {
                    throw new MissingFirmwareException("Missing FDS Bios");
                }
                cart = new CartInfo();
                var fdsboard = new FDS();
                fdsboard.biosrom = fdsbios;
                fdsboard.SetDiskImage(rom);
                fdsboard.Create(this);
                // at the moment, FDS doesn't use the IRVs, but it could at some point in the future
                fdsboard.InitialRegisterValues = InitialMapperRegisterValues;
                fdsboard.Configure(origin);

                board = fdsboard;

                //create the vram and wram if necessary
                if (cart.wram_size != 0)
                {
                    board.WRAM = new byte[cart.wram_size * 1024];
                }
                if (cart.vram_size != 0)
                {
                    board.VRAM = new byte[cart.vram_size * 1024];
                }

                board.PostConfigure();

                Console.WriteLine("Using NTSC display type for FDS disk image");
                _display_type = Common.DisplayType.NTSC;

                HardReset();

                return;
            }
            else
            {
                byte[] nesheader = new byte[16];
                Buffer.BlockCopy(file, 0, nesheader, 0, 16);

                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);
                }
                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);
                }
            }

            if (USE_DATABASE)
            {
                if (hash_md5 != null)
                {
                    choice = IdentifyFromGameDB(hash_md5);
                }
                if (choice == null)
                {
                    choice = IdentifyFromGameDB(hash_sha1);
                }
                if (choice == null)
                {
                    LoadWriteLine("Could not locate game in bizhawk gamedb");
                }
                else
                {
                    origin = EDetectionOrigin.GameDB;
                    LoadWriteLine("Chose board from bizhawk gamedb: " + choice.board_type);
                    //gamedb entries that dont specify prg/chr sizes can infer it from the ines header
                    if (iNesHeaderInfo != null)
                    {
                        if (choice.prg_size == -1)
                        {
                            choice.prg_size = iNesHeaderInfo.prg_size;
                        }
                        if (choice.chr_size == -1)
                        {
                            choice.chr_size = iNesHeaderInfo.chr_size;
                        }
                        if (choice.vram_size == -1)
                        {
                            choice.vram_size = iNesHeaderInfo.vram_size;
                        }
                        if (choice.wram_size == -1)
                        {
                            choice.wram_size = iNesHeaderInfo.wram_size;
                        }
                    }
                    else if (unif != null)
                    {
                        if (choice.prg_size == -1)
                        {
                            choice.prg_size = unif.CartInfo.prg_size;
                        }
                        if (choice.chr_size == -1)
                        {
                            choice.chr_size = unif.CartInfo.chr_size;
                        }
                        // unif has no wram\vram sizes; hope the board impl can figure it out...
                        if (choice.vram_size == -1)
                        {
                            choice.vram_size = 0;
                        }
                        if (choice.wram_size == -1)
                        {
                            choice.wram_size = 0;
                        }
                    }
                }

                //if this is still null, we have to try it some other way. nescartdb perhaps?

                if (choice == null)
                {
                    choice = IdentifyFromBootGodDB(hash_sha1_several);
                    if (choice == null)
                    {
                        LoadWriteLine("Could not locate game in nescartdb");
                    }
                    else
                    {
                        LoadWriteLine("Chose board from nescartdb:");
                        LoadWriteLine(choice);
                        origin = EDetectionOrigin.BootGodDB;
                    }
                }
            }

            //if choice is still null, try UNIF and iNES
            if (choice == null)
            {
                if (unif != null)
                {
                    LoadWriteLine("Using information from UNIF header");
                    choice = unif.CartInfo;
                    origin = EDetectionOrigin.UNIF;
                }
                if (iNesHeaderInfo != null)
                {
                    LoadWriteLine("Attempting inference from iNES header");
                    // try to spin up V2 header first, then V1 header
                    if (iNesHeaderInfoV2 != null)
                    {
                        try
                        {
                            boardType = FindBoard(iNesHeaderInfoV2, origin, InitialMapperRegisterValues);
                        }
                        catch { }
                        if (boardType == null)
                        {
                            LoadWriteLine("Failed to load as iNES V2");
                        }
                        else
                        {
                            choice = iNesHeaderInfoV2;
                        }

                        // V2 might fail but V1 might succeed because we don't have most V2 aliases setup; and there's
                        // no reason to do so except when needed
                    }
                    if (boardType == null)
                    {
                        choice    = iNesHeaderInfo;                      // we're out of options, really
                        boardType = FindBoard(iNesHeaderInfo, origin, InitialMapperRegisterValues);
                        if (boardType == null)
                        {
                            LoadWriteLine("Failed to load as iNES V1");
                        }

                        // do not further meddle in wram sizes.  a board that is being loaded from a "MAPPERxxx"
                        // entry should know and handle the situation better for the individual board
                    }

                    LoadWriteLine("Chose board from iNES heuristics:");
                    LoadWriteLine(choice);
                    origin = EDetectionOrigin.INES;
                }
            }

            game_name = choice.name;

            //find a INESBoard to handle this
            if (choice != null)
            {
                boardType = FindBoard(choice, origin, InitialMapperRegisterValues);
            }
            else
            {
                throw new Exception("Unable to detect ROM");
            }
            if (boardType == null)
            {
                throw new Exception("No class implements the necessary board type: " + choice.board_type);
            }

            if (choice.DB_GameInfo != null)
            {
                choice.bad = choice.DB_GameInfo.IsRomStatusBad();
            }

            LoadWriteLine("Final game detection results:");
            LoadWriteLine(choice);
            LoadWriteLine("\"" + game_name + "\"");
            LoadWriteLine("Implemented by: class " + boardType.Name);
            if (choice.bad)
            {
                LoadWriteLine("~~ ONE WAY OR ANOTHER, THIS DUMP IS KNOWN TO BE *BAD* ~~");
                LoadWriteLine("~~ YOU SHOULD FIND A BETTER FILE ~~");
            }

            LoadWriteLine("END NES rom analysis");
            LoadWriteLine("------");

            board = CreateBoardInstance(boardType);

            cart = choice;
            board.Create(this);
            board.InitialRegisterValues = InitialMapperRegisterValues;
            board.Configure(origin);

            if (origin == EDetectionOrigin.BootGodDB)
            {
                RomStatus = RomStatus.GoodDump;
                CoreComm.RomStatusAnnotation = "Identified from BootGod's database";
            }
            if (origin == EDetectionOrigin.UNIF)
            {
                RomStatus = RomStatus.NotInDatabase;
                CoreComm.RomStatusAnnotation = "Inferred from UNIF header; somewhat suspicious";
            }
            if (origin == EDetectionOrigin.INES)
            {
                RomStatus = RomStatus.NotInDatabase;
                CoreComm.RomStatusAnnotation = "Inferred from iNES header; potentially wrong";
            }
            if (origin == EDetectionOrigin.GameDB)
            {
                if (choice.bad)
                {
                    RomStatus = RomStatus.BadDump;
                }
                else
                {
                    RomStatus = choice.DB_GameInfo.Status;
                }
            }

            LoadReport.Flush();
            CoreComm.RomStatusDetails = LoadReport.ToString();

            //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];
                Array.Copy(file, 16, board.ROM, 0, board.ROM.Length);
                if (choice.chr_size > 0)
                {
                    board.VROM = new byte[choice.chr_size * 1024];
                    int vrom_offset = iNesHeaderInfo.prg_size * 1024;
                    // if file isn't long enough for VROM, truncate

                    Array.Copy(file, 16 + vrom_offset, board.VROM, 0, Math.Min(board.VROM.Length, file.Length - 16 - vrom_offset));
                }
            }
            else
            {
                board.ROM  = unif.PRG;
                board.VROM = unif.CHR;
            }

            // IF YOU DO ANYTHING AT ALL BELOW THIS LINE, MAKE SURE THE APPROPRIATE CHANGE IS MADE TO FDS (if applicable)

            //create the vram and wram if necessary
            if (cart.wram_size != 0)
            {
                board.WRAM = new byte[cart.wram_size * 1024];
            }
            if (cart.vram_size != 0)
            {
                board.VRAM = new byte[cart.vram_size * 1024];
            }

            board.PostConfigure();

            // set up display type

            NESSyncSettings.Region fromrom      = DetectRegion(cart.system);
            NESSyncSettings.Region fromsettings = SyncSettings.RegionOverride;

            if (fromsettings != NESSyncSettings.Region.Default)
            {
                Console.WriteLine("Using system region override");
                fromrom = fromsettings;
            }
            switch (fromrom)
            {
            case NESSyncSettings.Region.Dendy:
                _display_type = Common.DisplayType.DENDY;
                break;

            case NESSyncSettings.Region.NTSC:
                _display_type = Common.DisplayType.NTSC;
                break;

            case NESSyncSettings.Region.PAL:
                _display_type = Common.DisplayType.PAL;
                break;

            default:
                _display_type = Common.DisplayType.NTSC;
                break;
            }
            Console.WriteLine("Using NES system region of {0}", _display_type);

            HardReset();
        }