Пример #1
0
        public void ImportCodes(string AFileName, bool AQuiet = false)
        {
            if (File.Exists(AFileName))
            {
                XmlDocument  lXml       = new XmlDocument();
                XmlNodeList  lCodes     = null;
                XmlNode      lCodeNode  = null;
                XmlAttribute lAttribute = null;

                lXml.Load(AFileName);
                lCodes = lXml.SelectNodes("//genie/..");

                FModified = true;

                XmlNode lDeleteNode = GameNode.FirstChild;
                while (lDeleteNode != null)
                {
                    GameNode.RemoveChild(GameNode.FirstChild);
                    lDeleteNode = GameNode.FirstChild;
                }
                GameCodes.Clear();

                string lGameFileName = Path.Combine(Path.Combine(Path.Combine(Program.BaseDirectoryExternal, "games"), FGame.Code), FGame.Code + ".nes");
                foreach (XmlNode lCurCode in lCodes)
                {
                    NesFile lGame = new NesFile(lGameFileName);
                    try
                    {
                        lGame.PRG = GameGeniePatcherNes.Patch(lGame.PRG, lCurCode["genie"].InnerText);

                        lCodeNode = FXml.CreateElement("gamegenie");
                        GameNode.AppendChild(lCodeNode);

                        lAttribute       = FXml.CreateAttribute("code");
                        lAttribute.Value = lCurCode["genie"].InnerText.ToUpper().Trim();
                        lCodeNode.Attributes.Append(lAttribute);

                        lAttribute       = FXml.CreateAttribute("description");
                        lAttribute.Value = lCurCode["description"].InnerText;
                        lCodeNode.Attributes.Append(lAttribute);

                        GameCodes.Add(new GameGenieCode(lCurCode["genie"].InnerText.ToUpper().Trim(), lCurCode["description"].InnerText));
                    }
                    catch (GameGenieFormatException)
                    {
                        if (!AQuiet)
                        {
                            Tasks.MessageForm.Show(Resources.Error, string.Format(Resources.GameGenieFormatError, lCurCode["genie"].InnerText, FGame.Name), Resources.sign_error);
                        }
                    }
                    catch (GameGenieNotFoundException)
                    {
                        if (!AQuiet)
                        {
                            Tasks.MessageForm.Show(Resources.Error, string.Format(Resources.GameGenieNotFound, lCurCode["genie"].InnerText, FGame.Name), Resources.sign_error);
                        }
                    }
                }
            }
        }
Пример #2
0
        public void ApplyGameGenie()
        {
            if (!string.IsNullOrEmpty(GameGenie))
            {
                bool wasCompressed = DecompressPossible().Length > 0;
                if (wasCompressed)
                {
                    Decompress();
                }

                var codes    = GameGenie.Split(new char[] { ',', '\t', ' ', ';' }, StringSplitOptions.RemoveEmptyEntries);
                var nesFiles = Directory.GetFiles(this.basePath, "*.nes", SearchOption.TopDirectoryOnly);
                foreach (var f in nesFiles)
                {
                    var nesFile = new NesFile(f);
                    foreach (var code in codes)
                    {
                        nesFile.PRG = GameGeniePatcherNes.Patch(nesFile.PRG, code.Trim());
                    }
                    nesFile.Save(f);
                }

                if (wasCompressed)
                {
                    Compress();
                }
            }
        }
Пример #3
0
        private void buttonOk_Click(object sender, EventArgs e)
        {
            if (string.IsNullOrEmpty(textBoxCode.Text.Trim()))
            {
                MessageBox.Show(this, Resources.GGCodeEmpty, Resources.Error, MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }

            if (FGame != null)
            {
                NesFile lGame = new NesFile(FGame.NesPath);
                try
                {
                    lGame.PRG = GameGenie.Patch(lGame.PRG, textBoxCode.Text);
                }
                catch (GameGenieFormatException)
                {
                    MessageBox.Show(this, string.Format(Resources.GameGenieFormatError, textBoxCode.Text, FGame.Name), Resources.Error, MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }
                catch (GameGenieNotFoundException)
                {
                    MessageBox.Show(this, string.Format(Resources.GameGenieNotFound, textBoxCode.Text, FGame.Name), Resources.Error, MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }
            }

            if (string.IsNullOrEmpty(textBoxDescription.Text.Trim()))
            {
                MessageBox.Show(this, Resources.GGDescriptionEmpty, Resources.Error, MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }
            textBoxCode.Text = textBoxCode.Text.ToUpper().Trim();
            DialogResult     = System.Windows.Forms.DialogResult.OK;
        }
Пример #4
0
        public NesGame(string gamesDirectory, string nesFileName)
        {
            var nesFile = new NesFile(nesFileName);

            nesFile.CorrectRom();
            if (!supportedMappers.Contains(nesFile.Mapper))
            {
                throw new Exception(string.Format(Resources.MapperNotSupported, Path.GetFileName(nesFileName), nesFile.Mapper));
            }
            Code = string.Format("CLV-H-{0}{1}{2}{3}{4}",
                                 (char)('A' + (nesFile.CRC32 % 26)),
                                 (char)('A' + (nesFile.CRC32 >> 5) % 26),
                                 (char)('A' + ((nesFile.CRC32 >> 10) % 26)),
                                 (char)('A' + ((nesFile.CRC32 >> 15) % 26)),
                                 (char)('A' + ((nesFile.CRC32 >> 20) % 26)));
            GamePath   = Path.Combine(gamesDirectory, Code);
            ConfigPath = Path.Combine(GamePath, Code + ".desktop");
            Directory.CreateDirectory(GamePath);
            NesPath = Path.Combine(GamePath, Code + ".nes");
            nesFile.Save(NesPath);
            Name = Path.GetFileNameWithoutExtension(nesFileName);
            Name = Regex.Replace(Name, @" ?\(.*?\)", string.Empty).Trim();
            Name = Regex.Replace(Name, @" ?\[.*?\]", string.Empty).Trim();
            Name = Name.Replace(", The", "").Replace("_", " ").Replace("  ", " ").Trim();

            Players       = 1;
            ReleaseDate   = "1983-07-15";
            Publisher     = "Nintendo";
            Args          = "--guest-overscan-dimensions 0,0,9,3 --initial-fadein-durations 3,2 --volume 75 --enable-armet";
            IconPath      = Path.Combine(GamePath, Code + ".png");
            SmallIconPath = Path.Combine(GamePath, Code + "_small.png");
            SetImage(Resources.blank);
            Save();
        }
Пример #5
0
        public NesGame(string gamesDirectory, string nesFileName, bool ignoreMapper = false)
        {
            if (!Path.GetExtension(nesFileName).ToLower().Equals(".fds"))
            {
                var nesFile = new NesFile(nesFileName);
                nesFile.CorrectRom();
                if (!supportedMappers.Contains(nesFile.Mapper) && !ignoreMapper)
                {
                    throw new UnsupportedMapperException(nesFile);
                }
                var crc32 = nesFile.CRC32;
                Code = string.Format("CLV-H-{0}{1}{2}{3}{4}",
                                     (char)('A' + (crc32 % 26)),
                                     (char)('A' + (crc32 >> 5) % 26),
                                     (char)('A' + ((crc32 >> 10) % 26)),
                                     (char)('A' + ((crc32 >> 15) % 26)),
                                     (char)('A' + ((crc32 >> 20) % 26)));
                GamePath   = Path.Combine(gamesDirectory, Code);
                ConfigPath = Path.Combine(GamePath, Code + ".desktop");
                Directory.CreateDirectory(GamePath);
                NesPath = Path.Combine(GamePath, Code + ".nes");
                nesFile.Save(NesPath);
            }
            else
            {
                var fdsData = File.ReadAllBytes(nesFileName);
                var crc32   = CRC32(fdsData);
                Code = string.Format("CLV-H-{0}{1}{2}{3}{4}",
                                     (char)('A' + (crc32 % 26)),
                                     (char)('A' + (crc32 >> 5) % 26),
                                     (char)('A' + ((crc32 >> 10) % 26)),
                                     (char)('A' + ((crc32 >> 15) % 26)),
                                     (char)('A' + ((crc32 >> 20) % 26)));
                GamePath   = Path.Combine(gamesDirectory, Code);
                ConfigPath = Path.Combine(GamePath, Code + ".desktop");
                Directory.CreateDirectory(GamePath);
                NesPath = Path.Combine(GamePath, Code + ".nes");
                File.WriteAllBytes(NesPath, fdsData);
            }

            Name          = Path.GetFileNameWithoutExtension(nesFileName);
            Name          = Regex.Replace(Name, @" ?\(.*?\)", string.Empty).Trim();
            Name          = Regex.Replace(Name, @" ?\[.*?\]", string.Empty).Trim();
            Name          = Name.Replace(", The", "").Replace("_", " ").Replace("  ", " ").Trim();
            Players       = 1;
            ReleaseDate   = "1983-07-15";
            Publisher     = "Nintendo";
            Args          = "--guest-overscan-dimensions 0,0,9,3 --initial-fadein-durations 3,2 --volume 75 --enable-armet";
            IconPath      = Path.Combine(GamePath, Code + ".png");
            SmallIconPath = Path.Combine(GamePath, Code + "_small.png");
            SetImage(Resources.blank);
            Save();
        }
Пример #6
0
 public void ApplyGameGenie()
 {
     if (!string.IsNullOrEmpty(GameGenie))
     {
         var codes   = GameGenie.Split(new char[] { ',', '\t', ' ', ';' }, StringSplitOptions.RemoveEmptyEntries);
         var nesFile = new NesFile(NesPath);
         foreach (var code in codes)
         {
             nesFile.PRG = GameGeniePatcher.Patch(nesFile.PRG, code.Trim());
         }
         nesFile.Save(NesPath);
     }
 }
Пример #7
0
        public Computer(NesFile file, TextLogger logger)
        {
            if (file.Version != 0)
            {
                throw new System.Exception(string.Format("Version {0} not implemented", file.Version));
            }

            if (file.BatteryRAM)
            {
                throw new System.Exception("Battery backed RAM not implemented");
            }

            if (file.HasTrainer)
            {
                throw new System.Exception("Trainer not implemented");
            }

            switch (file.Mapper)
            {
            case 0x00:     // NROM

                //CPU $6000 -$7FFF: Family Basic only: PRG RAM, mirrored as necessary to fill entire 8 KiB window, write protectable with an external switch
                //CPU $8000 -$BFFF: First 16 KB of ROM.
                //CPU $C000 -$FFFF: Last 16 KB of ROM(NROM - 256) or mirror of $8000 -$BFFF(NROM - 128).

                break;

            default:
                throw new System.Exception(string.Format("Mapper {0} not implemented", file.Mapper));
            }

            if (file.VSUnisystem)
            {
                throw new System.Exception("VSUnisystem not implemented");
            }

            if (file.PlayChoice10)
            {
                throw new System.Exception("PlayChoice10 not implemented");
            }

            PpuMap = new PpuMap(new Ram(0x2000), new Rom(file.chr_rom), file.VRAMLayout);

            Ppu        = new Ppu(PpuMap);
            Ppu.Logger = logger;

            CpuMap = new CpuMap(new Ram(0x0800), new Rom(file.prg_rom), Ppu);

            Cpu        = new Cpu(CpuMap);
            Cpu.Logger = logger;
        }
Пример #8
0
        public Computer(NesFile file, TextLogger logger)
        {
            if (file.Version != 0)
            {
                throw new System.Exception(string.Format("Version {0} not implemented", file.Version));
            }

            if(file.BatteryRAM)
            {
                throw new System.Exception("Battery backed RAM not implemented");
            }

            if (file.HasTrainer)
            {
                throw new System.Exception("Trainer not implemented");
            }

            switch (file.Mapper)
            {
                case 0x00: // NROM

                    //CPU $6000 -$7FFF: Family Basic only: PRG RAM, mirrored as necessary to fill entire 8 KiB window, write protectable with an external switch
                    //CPU $8000 -$BFFF: First 16 KB of ROM.
                    //CPU $C000 -$FFFF: Last 16 KB of ROM(NROM - 256) or mirror of $8000 -$BFFF(NROM - 128).

                    break;

                default:
                    throw new System.Exception(string.Format("Mapper {0} not implemented", file.Mapper));
            }

            if (file.VSUnisystem)
            {
                throw new System.Exception("VSUnisystem not implemented");
            }

            if (file.PlayChoice10)
            {
                throw new System.Exception("PlayChoice10 not implemented");
            }

            PpuMap = new PpuMap(new Ram(0x2000), new Rom(file.chr_rom), file.VRAMLayout);

            Ppu = new Ppu(PpuMap);
            Ppu.Logger = logger;

            CpuMap = new CpuMap(new Ram(0x0800), new Rom(file.prg_rom), Ppu);

            Cpu = new Cpu(CpuMap);
            Cpu.Logger = logger;
        }
Пример #9
0
        private async void InitializeComponent()
        {
            // TODO: iOS does not support dynamically loading assemblies
            // so we must refer to this resource DLL statically. For
            // now I am doing that here. But we need a better solution!!
            var theme = new Avalonia.Themes.Default.DefaultTheme();

            theme.TryGetResource("Button", out _);
            AvaloniaXamlLoader.Load(this);

            var file = await NesFile.FromStream(File.OpenRead("lj65.nes")).ConfigureAwait(false);

            _nesSystem.Cartridge.InsertNesFile(file);
            _nesSystem.PowerUp();
        }
Пример #10
0
 public void ApplyGameGenie()
 {
     if (!string.IsNullOrEmpty(GameGenie))
     {
         var codes    = GameGenie.Split(new char[] { ',', '\t', ' ', ';' }, StringSplitOptions.RemoveEmptyEntries);
         var nesFiles = Directory.GetFiles(this.GamePath, "*.nes", SearchOption.TopDirectoryOnly);
         foreach (var f in nesFiles)
         {
             var nesFile = new NesFile(f);
             foreach (var code in codes)
             {
                 nesFile.PRG = GameGeniePatcherNes.Patch(nesFile.PRG, code.Trim());
             }
             nesFile.Save(f);
         }
     }
 }
Пример #11
0
        public NesDebugger()
        {
            InitializeComponent();

            if (File.Exists("nes.log"))
            {
                File.Delete("nes.log");
            }
            File.Create("nes.log");

            string src_file = "..\\..\\..\\..\\NES Test (USA).nes";

            NesFile file = new NesFile(src_file);

            m_nes = new Computer(file, log);
            update();

            textBoxDisassembly.Text = m_nes.Cpu.Disassemble(0x8000);
        }
Пример #12
0
 public bool ApplyGameGenie(out byte[] gameFileData)
 {
     gameFileData = null;
     if (!string.IsNullOrEmpty(GameGenie))
     {
         var    codes        = GameGenie.Split(new char[] { ',', '\t', ' ', ';' }, StringSplitOptions.RemoveEmptyEntries);
         string gameFilePath = GameFilePath;
         if (gameFilePath != null)
         {
             byte[] data = GameFileData;
             if (data != null)
             {
                 var nesFile = new NesFile(data);
                 foreach (var code in codes)
                 {
                     nesFile.PRG = GameGeniePatcherNes.Patch(nesFile.PRG, code.Trim());
                 }
                 gameFileData = nesFile.GetRaw();
                 return(true);
             }
         }
     }
     return(false);
 }
Пример #13
0
        public NesGame(string gamesDirectory, string nesFileName, bool?ignoreMapper, ref bool?needPatch, NeedPatchDelegate needPatchCallback, Form parentForm = null, byte[] rawRomData = null)
        {
            uint crc32;

            if (!Path.GetExtension(nesFileName).ToLower().Equals(".fds"))
            {
                NesFile nesFile;
                if (rawRomData != null)
                {
                    nesFile = new NesFile(rawRomData);
                }
                else
                {
                    nesFile = new NesFile(nesFileName);
                }
                nesFile.CorrectRom();
                crc32    = nesFile.CRC32;
                Code     = GenerateCode(crc32);
                GamePath = Path.Combine(gamesDirectory, Code);
                Args     = DefaultArgs;
                Type     = GameType.Cartridge;
                NesPath  = Path.Combine(GamePath, Code + ".nes");
                var patchesDirectory = Path.Combine(Path.GetDirectoryName(Application.ExecutablePath), "patches");
                Directory.CreateDirectory(patchesDirectory);
                Directory.CreateDirectory(GamePath);
                var patches = Directory.GetFiles(patchesDirectory, string.Format("{0:X8}*.ips", crc32), SearchOption.AllDirectories);
                if (patches.Length > 0 && needPatch != false)
                {
                    if (needPatch == true || ((needPatchCallback != null) && needPatchCallback(parentForm, Path.GetFileName(nesFileName)))) /*MessageBox.Show(parentForm, string.Format(Resources.PatchQ, Path.GetFileName(nesFileName)), Resources.PatchAvailable, MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes*/
                    {
                        needPatch = true;
                        var patch = patches[0];
                        if (rawRomData == null)
                        {
                            rawRomData = File.ReadAllBytes(nesFileName);
                        }
                        Debug.WriteLine(string.Format("Patching {0}", nesFileName));
                        IpsPatcher.Patch(patch, ref rawRomData);
                        nesFile = new NesFile(rawRomData);
                    }
                    else
                    {
                        needPatch = false;
                    }
                }

                if (nesFile.Mapper == 71)
                {
                    nesFile.Mapper = 2;                       // games by Codemasters/Camerica - this is UNROM clone. One exception - Fire Hawk
                }
                if (nesFile.Mapper == 88)
                {
                    nesFile.Mapper = 4;                       // Compatible with MMC3... sometimes
                }
                if (nesFile.Mapper == 95)
                {
                    nesFile.Mapper = 4;                       // Compatible with MMC3
                }
                if (nesFile.Mapper == 206)
                {
                    nesFile.Mapper = 4;                        // Compatible with MMC3
                }
                if (!supportedMappers.Contains(nesFile.Mapper) && (ignoreMapper != true))
                {
                    Directory.Delete(GamePath, true);
                    if (ignoreMapper != false)
                    {
                        throw new UnsupportedMapperException(nesFile);
                    }
                    else
                    {
                        Debug.WriteLine(string.Format("Game {0} has mapper #{1}, skipped", nesFileName, nesFile.Mapper));
                        return;
                    }
                }
                if ((nesFile.Mirroring == NesFile.MirroringType.FourScreenVram) && (ignoreMapper != true))
                {
                    Directory.Delete(GamePath, true);
                    if (ignoreMapper != false)
                    {
                        throw new UnsupportedFourScreenException(nesFile);
                    }
                    else
                    {
                        Debug.WriteLine(string.Format("Game {0} has four-screen mirroring, skipped", nesFileName, nesFile.Mapper));
                        return;
                    }
                }
                // TODO: Make trainer check. I think that NES Mini doesn't support it.

                ConfigPath = Path.Combine(GamePath, Code + ".desktop");
                nesFile.Save(NesPath);
            }
            else
            {
                byte[] fdsData;
                if (rawRomData != null)
                {
                    fdsData = rawRomData;
                }
                else
                {
                    fdsData = File.ReadAllBytes(nesFileName);
                }
                if (Encoding.ASCII.GetString(fdsData, 0, 3) == "FDS") // header? cut it!
                {
                    var fdsDataNoHeader = new byte[fdsData.Length - 0x10];
                    Array.Copy(fdsData, 0x10, fdsDataNoHeader, 0, fdsDataNoHeader.Length);
                    fdsData = fdsDataNoHeader;
                }
                crc32    = CRC32(fdsData);
                Code     = GenerateCode(crc32);
                GamePath = Path.Combine(gamesDirectory, Code);
                Args     = DefaultArgs + " --fds-auto-disk-side-switch-on-keypress"; // seems like need to make it default
                Type     = GameType.FDS;
                Directory.CreateDirectory(GamePath);
                ConfigPath = Path.Combine(GamePath, Code + ".desktop");
                NesPath    = Path.Combine(GamePath, Code + ".fds");
                File.WriteAllBytes(NesPath, fdsData);
            }

            Name        = Path.GetFileNameWithoutExtension(nesFileName);
            Players     = 1;
            ReleaseDate = DefaultReleaseDate;
            Publisher   = DefaultPublisher;
            if (nesFileName.Contains("(J)"))
            {
                Region = "Japan";
            }

            TryAutofill(crc32);

            Name          = Regex.Replace(Name, @" ?\(.*?\)", string.Empty).Trim();
            Name          = Regex.Replace(Name, @" ?\[.*?\]", string.Empty).Trim();
            Name          = Name.Replace("_", " ").Replace("  ", " ") /*.Replace(", The", "")*/.Trim();
            IconPath      = Path.Combine(GamePath, Code + ".png");
            SmallIconPath = Path.Combine(GamePath, Code + "_small.png");
            GameGeniePath = Path.Combine(GamePath, GameGenieFileName);

            // Trying to find cover file
            Image cover = null;

            if (!string.IsNullOrEmpty(nesFileName))
            {
                var imagePath = Path.Combine(Path.GetDirectoryName(nesFileName), Path.GetFileNameWithoutExtension(nesFileName) + ".png");
                if (File.Exists(imagePath))
                {
                    cover = LoadBitmap(imagePath);
                }
                imagePath = Path.Combine(Path.GetDirectoryName(nesFileName), Path.GetFileNameWithoutExtension(nesFileName) + ".jpg");
                if (File.Exists(imagePath))
                {
                    cover = LoadBitmap(imagePath);
                }
                var artDirectory = Path.Combine(Path.GetDirectoryName(Application.ExecutablePath), "art");
                Directory.CreateDirectory(artDirectory);
                imagePath = Path.Combine(artDirectory, Path.GetFileNameWithoutExtension(nesFileName) + ".png");
                if (File.Exists(imagePath))
                {
                    cover = LoadBitmap(imagePath);
                }
                imagePath = Path.Combine(artDirectory, Path.GetFileNameWithoutExtension(nesFileName) + ".jpg");
                if (File.Exists(imagePath))
                {
                    cover = LoadBitmap(imagePath);
                }
                var covers = Directory.GetFiles(artDirectory, string.Format("{0:X8}*.*", crc32), SearchOption.AllDirectories);
                if (covers.Length > 0)
                {
                    cover = LoadBitmap(covers[0]);
                }
            }
            if (cover != null)
            {
                SetImage(cover, ConfigIni.EightBitPngCompression);
            }
            else
            {
                SetImage(null, ConfigIni.EightBitPngCompression);
            }
            Save();
        }
Пример #14
0
        public static NesGame Import(string nesFileName, bool?ignoreMapper, ref bool?needPatch, NeedPatchDelegate needPatchCallback, Form parentForm = null, byte[] rawRomData = null)
        {
            NesFile nesFile;

            if (rawRomData != null)
            {
                nesFile = new NesFile(rawRomData);
            }
            else
            {
                nesFile = new NesFile(nesFileName);
            }
            nesFile.CorrectRom();
            var crc32            = nesFile.CRC32;
            var code             = GenerateCode(crc32, prefixCode);
            var gamePath         = Path.Combine(GamesDirectory, code);
            var nesPath          = Path.Combine(gamePath, code + ".nes");
            var patchesDirectory = Path.Combine(Path.GetDirectoryName(Application.ExecutablePath), "patches");

            Directory.CreateDirectory(patchesDirectory);
            Directory.CreateDirectory(gamePath);
            var patches = Directory.GetFiles(patchesDirectory, string.Format("{0:X8}*.ips", crc32), SearchOption.AllDirectories);

            if (patches.Length > 0 && needPatch != false)
            {
                if (needPatch == true || ((needPatchCallback != null) && needPatchCallback(parentForm, Path.GetFileName(nesFileName)))) /*MessageBox.Show(parentForm, string.Format(Resources.PatchQ, Path.GetFileName(nesFileName)), Resources.PatchAvailable, MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes*/
                {
                    needPatch = true;
                    var patch = patches[0];
                    if (rawRomData == null)
                    {
                        rawRomData = File.ReadAllBytes(nesFileName);
                    }
                    Debug.WriteLine(string.Format("Patching {0}", nesFileName));
                    IpsPatcher.Patch(patch, ref rawRomData);
                    nesFile = new NesFile(rawRomData);
                }
                else
                {
                    needPatch = false;
                }
            }

            if (nesFile.Mapper == 71)
            {
                nesFile.Mapper = 2;                       // games by Codemasters/Camerica - this is UNROM clone. One exception - Fire Hawk
            }
            if (nesFile.Mapper == 88)
            {
                nesFile.Mapper = 4;                       // Compatible with MMC3... sometimes
            }
            if (nesFile.Mapper == 95)
            {
                nesFile.Mapper = 4;                       // Compatible with MMC3
            }
            if (nesFile.Mapper == 206)
            {
                nesFile.Mapper = 4;                        // Compatible with MMC3
            }
            if (!supportedMappers.Contains(nesFile.Mapper) && (ignoreMapper != true))
            {
                Directory.Delete(gamePath, true);
                if (ignoreMapper != false)
                {
                    throw new UnsupportedMapperException(nesFile);
                }
                else
                {
                    Debug.WriteLine(string.Format("Game {0} has mapper #{1}, skipped", nesFileName, nesFile.Mapper));
                    return(null);
                }
            }
            if ((nesFile.Mirroring == NesFile.MirroringType.FourScreenVram) && (ignoreMapper != true))
            {
                Directory.Delete(gamePath, true);
                if (ignoreMapper != false)
                {
                    throw new UnsupportedFourScreenException(nesFile);
                }
                else
                {
                    Debug.WriteLine(string.Format("Game {0} has four-screen mirroring, skipped", nesFileName, nesFile.Mapper));
                    return(null);
                }
            }
            // TODO: Make trainer check. I think that NES Mini doesn't support it.

            nesFile.Save(nesPath);
            var game = new NesGame(gamePath, true);

            game.Name = Path.GetFileNameWithoutExtension(nesFileName);
            if (game.Name.Contains("(J)"))
            {
                game.region = "Japan";
            }
            game.TryAutofill(crc32);
            game.Name = Regex.Replace(game.Name, @" ?\(.*?\)", string.Empty).Trim();
            game.Name = Regex.Replace(game.Name, @" ?\[.*?\]", string.Empty).Trim();
            game.Name = game.Name.Replace("_", " ").Replace("  ", " ") /*.Replace(", The", "")*/.Trim();

            // Trying to find cover file
            Image cover = null;

            if (!string.IsNullOrEmpty(nesFileName))
            {
                var imagePath = Path.Combine(Path.GetDirectoryName(nesFileName), Path.GetFileNameWithoutExtension(nesFileName) + ".png");
                if (File.Exists(imagePath))
                {
                    cover = LoadBitmap(imagePath);
                }
                imagePath = Path.Combine(Path.GetDirectoryName(nesFileName), Path.GetFileNameWithoutExtension(nesFileName) + ".jpg");
                if (File.Exists(imagePath))
                {
                    cover = LoadBitmap(imagePath);
                }
                var artDirectory = Path.Combine(Path.GetDirectoryName(Application.ExecutablePath), "art");
                Directory.CreateDirectory(artDirectory);
                imagePath = Path.Combine(artDirectory, Path.GetFileNameWithoutExtension(nesFileName) + ".png");
                if (File.Exists(imagePath))
                {
                    cover = LoadBitmap(imagePath);
                }
                imagePath = Path.Combine(artDirectory, Path.GetFileNameWithoutExtension(nesFileName) + ".jpg");
                if (File.Exists(imagePath))
                {
                    cover = LoadBitmap(imagePath);
                }
                var covers = Directory.GetFiles(artDirectory, string.Format("{0:X8}*.*", crc32), SearchOption.AllDirectories);
                if (covers.Length > 0)
                {
                    cover = LoadBitmap(covers[0]);
                }
            }
            if (cover == null)
            {
                if (game.region == "Japan")
                {
                    cover = Resources.blank_jp;
                }
                else
                {
                    cover = Resources.blank;
                }
            }
            game.Image = cover;
            game.Args  = DefaultArgs;
            game.Save();
            return(game);
        }
Пример #15
0
        public static bool Patch(string inputFileName, ref byte[] rawRomData, ref char prefix, ref string application, ref string outputFileName, ref string args, ref Image cover, ref byte saveCount, ref uint crc32)
        {
            // Try to patch before mapper check, maybe it will patch mapper
            var     patched = FindPatch(ref rawRomData, inputFileName, crc32);
            NesFile nesFile;

            try
            {
                nesFile = new NesFile(rawRomData);
            }
            catch
            {
                application = "/bin/nes";
                return(true);
            }
            crc32 = nesFile.CRC32;
            // Also search for patch using internal CRC32
            if (!patched)
            {
                if (FindPatch(ref rawRomData, inputFileName, crc32))
                {
                    nesFile = new NesFile(rawRomData);
                }
            }
            nesFile.CorrectRom();

            if (ConfigIni.ConsoleType == MainForm.ConsoleType.NES || ConfigIni.ConsoleType == MainForm.ConsoleType.Famicom)
            {
                application = "/bin/clover-kachikachi-wr";
                args        = DefaultArgs;
            }
            else
            {
                application = "/bin/nes";
            }

            //if (nesFile.Mapper == 71) nesFile.Mapper = 2; // games by Codemasters/Camerica - this is UNROM clone. One exception - Fire Hawk
            //if (nesFile.Mapper == 88) nesFile.Mapper = 4; // Compatible with MMC3... sometimes
            //if (nesFile.Mapper == 95) nesFile.Mapper = 4; // Compatible with MMC3
            //if (nesFile.Mapper == 206) nesFile.Mapper = 4; // Compatible with MMC3
            if (!supportedMappers.Contains(nesFile.Mapper) &&
                (ConfigIni.ConsoleType == MainForm.ConsoleType.NES || ConfigIni.ConsoleType == MainForm.ConsoleType.Famicom) &&
                (IgnoreMapper != true))
            {
                if (IgnoreMapper != false)
                {
                    var r = WorkerForm.MessageBoxFromThread(ParentForm,
                                                            string.Format(Resources.MapperNotSupported, System.IO.Path.GetFileName(inputFileName), nesFile.Mapper),
                                                            Resources.AreYouSure,
                                                            MessageBoxButtons.AbortRetryIgnore,
                                                            MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2, true);
                    if (r == DialogResult.Abort)
                    {
                        IgnoreMapper = true;
                    }
                    if (r == DialogResult.Ignore)
                    {
                        return(false);
                    }
                }
                else
                {
                    return(false);
                }
            }
            if ((nesFile.Mirroring == NesFile.MirroringType.FourScreenVram) &&
                (ConfigIni.ConsoleType == MainForm.ConsoleType.NES || ConfigIni.ConsoleType == MainForm.ConsoleType.Famicom) &&
                (IgnoreMapper != true))
            {
                var r = WorkerForm.MessageBoxFromThread(ParentForm,
                                                        string.Format(Resources.FourScreenNotSupported, System.IO.Path.GetFileName(inputFileName)),
                                                        Resources.AreYouSure,
                                                        MessageBoxButtons.AbortRetryIgnore,
                                                        MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2, true);
                if (r == DialogResult.Abort)
                {
                    IgnoreMapper = true;
                }
                if (r == DialogResult.No)
                {
                    return(false);
                }
            }

            // TODO: Make trainer check. I think that the NES Mini doesn't support it.
            rawRomData = nesFile.GetRaw();
            if (inputFileName.Contains("(J)"))
            {
                cover = Resources.blank_jp;
            }

            if (nesFile.Battery)
            {
                saveCount = 3;
            }

            return(true);
        }
 public UnsupportedMapperException(NesFile nesFile)
 {
     ROM = nesFile;
 }
Пример #17
0
 public Game(string fileName, string menuName = null, Dictionary <uint, GameFix> fixes = null)
 {
     // Separators
     if (fileName == "-")
     {
         MenuName = (string.IsNullOrWhiteSpace(menuName) || menuName == "-") ? "" : menuName;
         FileName = "";
         Flags   |= GameFlags.Separator;
     }
     else
     {
         Console.WriteLine("Loading {0}...", Path.GetFileName(fileName));
         FileName = fileName;
         if (string.IsNullOrWhiteSpace(menuName))
         {
             // Menu name based on filename
             MenuName = Regex.Replace(Path.GetFileNameWithoutExtension(fileName), @"( ?\[.*?\])|( \(.\))", string.Empty).Replace("_", " ").ToUpper().Replace(", THE", "").Trim();
         }
         else
         {
             MenuName = menuName.Trim();
             if (MenuName == "?")
             {
                 Flags |= GameFlags.Hidden;
             }
         }
         // Strip long names
         if (MenuName.Length > 28)
         {
             MenuName = MenuName.Substring(0, 25).Trim() + "...";
         }
         uint crc;
         try
         {
             var nesFile   = new NesFile(fileName);
             var fixResult = nesFile.CorrectRom();
             if (fixResult != 0)
             {
                 Console.WriteLine(" Invalid header. Fix: " + fixResult);
             }
             PRG           = nesFile.PRG;
             PrgSize       = (uint)nesFile.PRG.Count();
             CHR           = nesFile.CHR;
             ChrSize       = (uint)nesFile.CHR.Count();
             Battery       = nesFile.Battery;
             Mapper        = $"{nesFile.Mapper:D3}" + ((nesFile.Submapper > 0) ? $":{nesFile.Submapper}" : "");
             Mirroring     = nesFile.Mirroring;
             ContainerType = NesContainerType.iNES;
             if (nesFile.Trainer != null && nesFile.Trainer.Count() > 0)
             {
                 throw new NotImplementedException(string.Format("{0} - trained games are not supported yet", Path.GetFileName(fileName)));
             }
             if (nesFile.Version == NesFile.iNesVersion.NES20)
             {
                 PrgRamSize = nesFile.PrgRamSize + nesFile.PrgNvRamSize;
                 ChrRamSize = nesFile.ChrRamSize + nesFile.ChrNvRamSize;
             }
             crc = nesFile.CalculateCRC32();
         }
         catch (InvalidDataException)
         {
             var unifFile = new UnifFile(fileName);
             PRG     = unifFile.Fields.Where(k => k.Key.StartsWith("PRG")).OrderBy(k => k.Key).SelectMany(i => i.Value);
             PrgSize = (uint)PRG.Count();
             CHR     = unifFile.Fields.Where(k => k.Key.StartsWith("CHR")).OrderBy(k => k.Key).SelectMany(i => i.Value);
             ChrSize = (uint)CHR.Count();
             Battery = unifFile.Battery;
             var mapper = unifFile.Mapper;
             if (mapper.StartsWith("NES-") || mapper.StartsWith("UNL-") || mapper.StartsWith("HVC-") || mapper.StartsWith("BTL-") || mapper.StartsWith("BMC-"))
             {
                 mapper = mapper.Substring(4);
             }
             Mapper        = mapper;
             Mirroring     = unifFile.Mirroring;
             ContainerType = NesContainerType.UNIF;
             crc           = unifFile.CalculateCRC32();
         }
         // Check for fixes database
         if (fixes != null)
         {
             GameFix fix = null;
             if (fixes.TryGetValue(crc, out fix))
             {
                 if (fix.PrgRamSize.HasValue)
                 {
                     PrgRamSize = fix.PrgRamSize * 1024;
                 }
                 if (fix.ChrRamSize.HasValue)
                 {
                     ChrRamSize = fix.ChrRamSize * 1024;
                 }
                 if (fix.Battery.HasValue)
                 {
                     Battery = fix.Battery.Value;
                 }
                 if (fix.WillNotWorkOnPal)
                 {
                     Flags |= GameFlags.WillNotWorkOnPal;
                 }
                 if (fix.WillNotWorkOnNtsc)
                 {
                     Flags |= GameFlags.WillNotWorkOnNtsc;
                 }
                 if (fix.WillNotWorkOnDendy)
                 {
                     Flags |= GameFlags.WillNotWorkOnDendy;
                 }
                 if (fix.WillNotWorkOnNewFamiclone)
                 {
                     Flags |= GameFlags.WillNotWorkOnNewFamiclone;
                 }
             }
         }
         // External NTRAM is not supported on new famiclones
         if (Mirroring == NesFile.MirroringType.FourScreenVram)
         {
             Flags |= GameFlags.WillNotWorkOnNewFamiclone;
         }
         // Check for round sizes
         if (PrgSize > 0)
         {
             uint roundSize = 1;
             while (roundSize < PrgSize)
             {
                 roundSize <<= 1;
             }
             if (roundSize > PrgSize)
             {
                 var newPrg = new byte[roundSize];
                 for (uint i = PrgSize; i < roundSize; i++)
                 {
                     newPrg[i] = 0xFF;
                 }
                 Array.Copy(PRG.ToArray(), newPrg, PrgSize);
                 PRG     = newPrg;
                 PrgSize = roundSize;
             }
         }
         if (ChrSize > 0)
         {
             uint roundSize = 1;
             while (roundSize < ChrSize)
             {
                 roundSize <<= 1;
             }
             if (roundSize > ChrSize)
             {
                 var newChr = new byte[roundSize];
                 for (uint i = ChrSize; i < roundSize; i++)
                 {
                     newChr[i] = 0xFF;
                 }
                 Array.Copy(CHR.ToArray(), newChr, ChrSize);
                 CHR     = newChr;
                 ChrSize = roundSize;
             }
         }
     }
 }
Пример #18
0
 public UnsupportedFourScreenException(NesFile nesFile)
 {
     ROM = nesFile;
 }
Пример #19
0
        public static bool Patch(string inputFileName, ref byte[] rawRomData, ref char prefix, ref string application, ref string outputFileName, ref string args, ref Image cover, ref byte saveCount, ref uint crc32)
        {
            // Try to patch before mapper check, maybe it will patch mapper
            var     patched = FindPatch(ref rawRomData, inputFileName, crc32);
            NesFile nesFile;

            try
            {
                nesFile = new NesFile(rawRomData);
            }
            catch
            {
                return(true);
            }
            crc32 = nesFile.CRC32;

            // Also search for patch using internal CRC32
            if (!patched)
            {
                if (FindPatch(ref rawRomData, inputFileName, crc32))
                {
                    nesFile = new NesFile(rawRomData);
                }
            }
            nesFile.CorrectRom();

            application = "/bin/clover-kachikachi-wr";
            args        = DefaultArgs;

            if (!supportedMappers.Contains(nesFile.Mapper) && (IgnoreMapper != true))
            {
                if (IgnoreMapper != false)
                {
                    var result = Tasks.MessageForm.Show(ParentForm, Resources.AreYouSure,
                                                        string.Format(Resources.MapperNotSupported, Path.GetFileName(inputFileName), nesFile.Mapper),
                                                        Resources.sign_warning,
                                                        new Tasks.MessageForm.Button[] { Tasks.MessageForm.Button.YesToAll, Tasks.MessageForm.Button.Yes, Tasks.MessageForm.Button.No },
                                                        Tasks.MessageForm.DefaultButton.Button2);
                    if (result == Tasks.MessageForm.Button.YesToAll)
                    {
                        IgnoreMapper = true;
                    }
                    if (result == Tasks.MessageForm.Button.No)
                    {
                        return(false);
                    }
                }
                else
                {
                    return(false);
                }
            }

            if ((nesFile.Mirroring == NesFile.MirroringType.FourScreenVram) && (IgnoreMapper != true))
            {
                var result = Tasks.MessageForm.Show(ParentForm, Resources.AreYouSure,
                                                    string.Format(Resources.FourScreenNotSupported, Path.GetFileName(inputFileName)),
                                                    Resources.sign_warning,
                                                    new Tasks.MessageForm.Button[] { Tasks.MessageForm.Button.YesToAll, Tasks.MessageForm.Button.Yes, Tasks.MessageForm.Button.No },
                                                    Tasks.MessageForm.DefaultButton.Button2);
                if (result == Tasks.MessageForm.Button.YesToAll)
                {
                    IgnoreMapper = true;
                }
                if (result == Tasks.MessageForm.Button.No)
                {
                    return(false);
                }
            }

            // TODO: Make trainer check. I think that the NES Mini doesn't support it.
            rawRomData = nesFile.GetRaw();
            if (inputFileName.Contains("(J)"))
            {
                cover = Resources.blank_jp;
            }

            if (nesFile.Battery)
            {
                saveCount = 3;
            }

            return(true);
        }
Пример #20
0
        /// <summary>
        /// Fix ROM header using database of popular incorrect ROMs
        /// </summary>
        /// <returns>Flags showing what has been changed</returns>
        public static NesFixType CorrectRom(this NesFile nes)
        {
            NesFixType fixType = NesFixType.NoFix;
            var        crc32   = nes.CalculateCRC32();

            for (int i = 0; i < correct.GetLength(0); i++)
            {
                if (crc32 == correct[i, 0])
                {
                    var mapper    = correct[i, 1] & 0x3FF;
                    var mask      = ((correct[i, 1] & 0x1000) != 0) ? 0xFFF : 0xFF;
                    var mirroring = correct[i, 2];
                    if ((correct[i, 1] >= 0) && (mapper >= 0) && (nes.Mapper != (mapper & mask)))
                    {
                        // invalid mapper
                        nes.Mapper = (ushort)(mapper & mask);
                        fixType   |= NesFixType.Mapper;
                    }
                    if (mirroring >= 0)
                    {
                        if (mirroring == 8 && nes.Mirroring == NesFile.MirroringType.FourScreenVram)
                        {
                            // no four-screen
                            nes.Mirroring = NesFile.MirroringType.Horizontal;
                            fixType      |= NesFixType.Mirroring;
                        }
                        NesFile.MirroringType needMirroring = NesFile.MirroringType.Unknown;
                        switch (mirroring)
                        {
                        case 0:
                            needMirroring = NesFile.MirroringType.Horizontal;
                            break;

                        case 1:
                            needMirroring = NesFile.MirroringType.Vertical;
                            break;

                        case 2:
                            needMirroring = NesFile.MirroringType.FourScreenVram;
                            break;
                        }
                        if (needMirroring != NesFile.MirroringType.Unknown && needMirroring != nes.Mirroring)
                        {
                            nes.Mirroring = needMirroring;
                            fixType      |= NesFixType.Mirroring;
                        }
                    }
                    if ((correct[i, 1] >= 0) && ((correct[i, 1] & 0x800) != 0) && (nes.CHR.Count() > 0))
                    {
                        // no CHR
                        nes.CHR  = Array.Empty <byte>();
                        fixType |= NesFixType.NoChr;
                    }
                }
            }

            var   md5        = nes.CalculateMD5();
            ulong partialmd5 = 0;

            for (int x = 0; x < 8; x++)
            {
                partialmd5 |= (ulong)md5[15 - x] << (x * 8);
            }
            // maybe this games uses battery saves?
            foreach (var sav in savie)
            {
                if (!nes.Battery && sav == partialmd5)
                {
                    nes.Battery = true;
                    fixType    |= NesFixType.Battery;
                }
            }

            return(fixType);
        }
Пример #21
0
        static int Main(string[] args)
        {
            var mappers = new Dictionary<byte, MapperInfo>();
            mappers[0] = new MapperInfo { MapperReg = 0x00, PrgMode = 0, ChrMode = 0, WramEnabled = false }; // NROM
            mappers[2] = new MapperInfo { MapperReg = 0x01, PrgMode = 0, ChrMode = 0, WramEnabled = false }; // UxROM
            mappers[71] = new MapperInfo { MapperReg = 0x01, PrgMode = 0, ChrMode = 0, WramEnabled = false, MapperFlags = 1 }; // Codemasters
            mappers[3] = new MapperInfo { MapperReg = 0x02, PrgMode = 0, ChrMode = 0, WramEnabled = false }; // CNROM
            mappers[78] = new MapperInfo { MapperReg = 0x03, PrgMode = 0, ChrMode = 0, WramEnabled = false }; // Holy Diver
            mappers[97] = new MapperInfo { MapperReg = 0x04, PrgMode = 1, ChrMode = 0, WramEnabled = false }; // Irem's TAM-S1
            mappers[93] = new MapperInfo { MapperReg = 0x05, PrgMode = 0, ChrMode = 0, WramEnabled = false }; // Sunsoft-2
            mappers[163] = new MapperInfo { MapperReg = 0x06, PrgMode = 7, ChrMode = 0, WramEnabled = true }; // Mapper 163 (Final Fantasy & chinese shit)
            mappers[18] = new MapperInfo { MapperReg = 0x07, PrgMode = 4, ChrMode = 7, WramEnabled = false }; // Jaleco SS88006
            mappers[7] = new MapperInfo { MapperReg = 0x08, PrgMode = 7, ChrMode = 0, WramEnabled = false }; // AxROM
            mappers[241] = new MapperInfo { MapperReg = 0x08, PrgMode = 7, ChrMode = 0, WramEnabled = false, MapperFlags = 1 }; // BNROM - is it just AxROM clone whith fixed mirroring? Using flag.
            mappers[228] = new MapperInfo { MapperReg = 0x09, PrgMode = 7, ChrMode = 0, WramEnabled = false }; // Cheetahmen 2
            mappers[11] = new MapperInfo { MapperReg = 0x0A, PrgMode = 7, ChrMode = 0, WramEnabled = false }; // Color Dreams
            mappers[66] = new MapperInfo { MapperReg = 0x0B, PrgMode = 7, ChrMode = 0, WramEnabled = false }; // GxROM
            mappers[87] = new MapperInfo { MapperReg = 0x0C, PrgMode = 0, ChrMode = 0, WramEnabled = false }; // Mapper #87
            mappers[90] = new MapperInfo { MapperReg = 0x0D, PrgMode = 4, ChrMode = 7, WramEnabled = false }; // Mapper #90
            mappers[65] = new MapperInfo { MapperReg = 0x0E, PrgMode = 4, ChrMode = 7, WramEnabled = false }; // Mapper #65 - Irem's H3001
            mappers[5] = new MapperInfo { MapperReg = 0x0F, PrgMode = 4, ChrMode = 7, WramEnabled = true }; // MMC5
            mappers[1] = new MapperInfo { MapperReg = 0x10, PrgMode = 0, ChrMode = 0, WramEnabled = true }; // MMC1
            mappers[9] = new MapperInfo { MapperReg = 0x11, PrgMode = 4, ChrMode = 5, WramEnabled = true }; // MMC2
            mappers[10] = new MapperInfo { MapperReg = 0x11, PrgMode = 0, ChrMode = 5, WramEnabled = true, MapperFlags = 1 }; // MMC4
            mappers[152] = new MapperInfo { MapperReg = 0x12, PrgMode = 0, ChrMode = 0, WramEnabled = false }; // Mapper #152
            mappers[73] = new MapperInfo { MapperReg = 0x13, PrgMode = 0, ChrMode = 0, WramEnabled = true }; // VRC3
            mappers[4] = new MapperInfo { MapperReg = 0x14, PrgMode = 4, ChrMode = 2, WramEnabled = true }; // MMC3
            mappers[118] = new MapperInfo { MapperReg = 0x14, PrgMode = 4, ChrMode = 2, WramEnabled = true, MapperFlags = 1 }; // TxSROM (MMC3 with flag)
            mappers[189] = new MapperInfo { MapperReg = 0x14, PrgMode = 7, ChrMode = 2, WramEnabled = false, MapperFlags = 2 }; // Mapper #189
            mappers[112] = new MapperInfo { MapperReg = 0x15, PrgMode = 4, ChrMode = 2, WramEnabled = true }; // Mapper #112
            mappers[4] = new MapperInfo { MapperReg = 0x14, PrgMode = 4, ChrMode = 2, WramEnabled = true }; // MMC3
            mappers[33] = new MapperInfo { MapperReg = 0x16, PrgMode = 4, ChrMode = 2, WramEnabled = true }; // Taito
            mappers[48] = new MapperInfo { MapperReg = 0x16, PrgMode = 4, ChrMode = 2, WramEnabled = true, MapperFlags = 1 }; // Taito
            mappers[21] = new MapperInfo { MapperReg = 0x18, PrgMode = 4, ChrMode = 7, WramEnabled = true, MapperFlags = 1 }; // VRC4a
            mappers[22] = new MapperInfo { MapperReg = 0x18, PrgMode = 4, ChrMode = 7, WramEnabled = true, MapperFlags = 1 | 2 }; // VRC2a
            mappers[23] = new MapperInfo { MapperReg = 0x18, PrgMode = 4, ChrMode = 7, WramEnabled = true }; // VRC2b
            mappers[25] = new MapperInfo { MapperReg = 0x18, PrgMode = 4, ChrMode = 7, WramEnabled = true, MapperFlags = 1 }; // VRC2c, VRC4
            mappers[69] = new MapperInfo { MapperReg = 0x19, PrgMode = 4, ChrMode = 7, WramEnabled = true }; // Sunsoft FME-7
            mappers[32] = new MapperInfo { MapperReg = 0x1A, PrgMode = 4, ChrMode = 7, WramEnabled = true }; // Irem's G-101
            //mappers[255] = new MapperInfo { MapperReg = 0x1F, PrgMode = 7, ChrMode = 0, WramEnabled = false }; // Temp/test
            Console.WriteLine("COOLGIRL UNIF combiner");
            Console.WriteLine("(c) Cluster, 2016");
            Console.WriteLine("http://clusterrr.com");
            Console.WriteLine("*****@*****.**");
            Console.WriteLine();
            bool needShowHelp = false;

            string command = null;
            string optionGames = null;
            string optionAsm = null;
            string optionOffsets = null;
            string optionReport = null;
            string optionLoader = null;
            string optionUnif = null;
            string optionBin = null;
            string optionLanguage = "eng";
            bool optionNoSort = false;
            int optionMaxSize = 256;
            if (args.Length > 0) command = args[0].ToLower();
            if (command != "prepare" && command != "combine")
            {
                if (!string.IsNullOrEmpty(command))
                    Console.WriteLine("Unknown command: " + command);
                needShowHelp = true;
            }
            for (int i = 1; i < args.Length; i++)
            {
                string param = args[i];
                while (param.StartsWith("-")) param = param.Substring(1);
                string value = i < args.Length - 1 ? args[i + 1] : "";
                switch (param.ToLower())
                {
                    case "games":
                        optionGames = value;
                        i++;
                        break;
                    case "asm":
                        optionAsm = value;
                        i++;
                        break;
                    case "offsets":
                        optionOffsets = value;
                        i++;
                        break;
                    case "report":
                        optionReport = value;
                        i++;
                        break;
                    case "loader":
                        optionLoader = value;
                        i++;
                        break;
                    case "unif":
                        optionUnif = value;
                        i++;
                        break;
                    case "bin":
                        optionBin = value;
                        i++;
                        break;
                    case "nosort":
                        optionNoSort = true;
                        break;
                    case "maxsize":
                        optionMaxSize = int.Parse(value);
                        i++;
                        break;
                    case "language":
                        optionLanguage = value.ToLower();
                        i++;
                        break;
                    default:
                        Console.WriteLine("Unknown parameter: " + param);
                        needShowHelp = true;
                        break;
                }
            }

            if (command == "prepare")
            {
                if (optionGames == null)
                {
                    Console.WriteLine("Missing required parameter: --games");
                    needShowHelp = true;
                }
                if (optionAsm == null)
                {
                    Console.WriteLine("Missing required parameter: --asm");
                    needShowHelp = true;
                }
                if (optionOffsets == null)
                {
                    Console.WriteLine("Missing required parameter: --offsets");
                    needShowHelp = true;
                }
            }
            else if (command == "combine")
            {
                if (optionLoader == null)
                {
                    Console.WriteLine("Missing required parameter: --loader");
                    needShowHelp = true;
                }
                if (optionUnif == null && optionBin == null)
                {
                    Console.WriteLine("At least one parameter required: --unif or --bin");
                    needShowHelp = true;
                }
            }

            if (needShowHelp)
            {
                Console.WriteLine("");
                Console.WriteLine("--- Usage ---");
                Console.WriteLine("First step:");
                Console.WriteLine(" CoolgirlCombiner.exe prepare --games <games.txt> --asm <games.asm> --offsets <offsets.xml> [--report <report.txt>] [--nosort] [--maxsize sizemb] [--language <language>]");
                Console.WriteLine("  {0,-20}{1}", "games", "- input plain text file with list of ROM files");
                Console.WriteLine("  {0,-20}{1}", "asm", "- output file for loader");
                Console.WriteLine("  {0,-20}{1}", "offsets", "- output file with offsets for every game");
                Console.WriteLine("  {0,-20}{1}", "report", "- output report file (human readable)");
                Console.WriteLine("  {0,-20}{1}", "nosort", "- disable automatic sort by name");
                Console.WriteLine("  {0,-20}{1}", "maxsize", "- maximum size for final file (in megabytes)");
                Console.WriteLine("  {0,-20}{1}", "language", "- language for system messages: \"eng\" or \"rus\"");
                Console.WriteLine("Second step:");
                Console.WriteLine(" CoolgirlCombiner.exe combine --loader <menu.nes> --offsets <offsets.xml> [--unif <multirom.unf>] [--bin <multirom.bin>]");
                Console.WriteLine("  {0,-20}{1}", "loader", "- loader (compiled using asm file generated by first step)");
                Console.WriteLine("  {0,-20}{1}", "offsets", "- input file with offsets for every game (generated by first step)");
                Console.WriteLine("  {0,-20}{1}", "unif", "- output UNIF file");
                Console.WriteLine("  {0,-20}{1}", "bin", "- output raw binary file");
                return 1;
            }

            try
            {
                if (command == "prepare")
                {
                    var lines = File.ReadAllLines(optionGames);
                    var result = new byte?[256 * 1024 * 1024];
                    var regs = new Dictionary<string, List<String>>();
                    var games = new List<Game>();
                    var namesIncluded = new List<String>();

                    // Reserved for loader
                    for (int a = 0; a < 128 * 1024; a++)
                        result[a] = 0xff;

                    // Bad sector :(
                    // for (int a = 1908 * 0x8000; a < 1908 * 0x8000 + 128 * 1024; a++) result[a] = 0xff;

                    // Building list of ROMs
                    foreach (var line in lines)
                    {
                        if (string.IsNullOrEmpty(line.Trim())) continue;
                        if (line.StartsWith(";")) continue;
                        int sepPos = line.TrimEnd().IndexOf('|');

                        string fileName;
                        string menuName = null;
                        if (sepPos < 0)
                        {
                            fileName = line.Trim();
                        }
                        else
                        {
                            fileName = line.Substring(0, sepPos).Trim();
                            menuName = line.Substring(sepPos + 1).Trim();
                        }

                        if (fileName.EndsWith("/") || fileName.EndsWith("\\"))
                        {
                            Console.WriteLine("Loading directory: {0}", fileName);
                            var files = Directory.GetFiles(fileName, "*.nes");
                            foreach (var file in files)
                            {
                                LoadGames(games, file);
                            }
                        }
                        else
                        {
                            LoadGames(games, fileName, menuName);
                        }
                    }

                    // Removing separators
                    if (!optionNoSort)
                        games = new List<Game>((from game in games where !(string.IsNullOrEmpty(game.FileName) || game.FileName == "-") select game).ToArray());
                    // Sorting
                    if (optionNoSort)
                    {
                        games = new List<Game>((from game in games where !game.ToString().StartsWith("?") select game)
                        .Union(from game in games where game.ToString().StartsWith("?") orderby game.ToString().ToUpper() ascending select game)
                        .ToArray());
                    }
                    else
                    {
                        games = new List<Game>((from game in games where !game.ToString().StartsWith("?") orderby game.ToString().ToUpper() ascending select game)
                            .Union(from game in games where game.ToString().StartsWith("?") orderby game.ToString().ToUpper() ascending select game)
                            .ToArray());
                    }
                    int hiddenCount = games.Where(game => game.ToString().StartsWith("?")).Count();

                    byte saveId = 0;
                    foreach (var game in games)
                    {
                        if (game.ROM.Battery)
                        {
                            saveId++;
                            game.SaveId = saveId;
                        }
                    }

                    int usedSpace = 0;
                    var sortedPrgs = from game in games orderby game.ROM.PRG.Length descending select game;
                    foreach (var game in sortedPrgs)
                    {
                        int prgRoundSize = 1;
                        while (prgRoundSize < game.ROM.PRG.Length) prgRoundSize *= 2;

                        Console.WriteLine("Fitting PRG for {0} ({1}kbytes)...", game, prgRoundSize / 1024);
                        for (int pos = 0; pos < result.Length; pos += prgRoundSize)
                        {
                            if (WillFit(result, pos, prgRoundSize, game.ROM.PRG))
                            {
                                game.PrgPos = pos;
                                //Array.Copy(game.ROM.PRG, 0, result, pos, game.ROM.PRG.Length);
                                for (var i = 0; i < game.ROM.PRG.Length; i++)
                                    result[pos + i] = game.ROM.PRG[i];
                                usedSpace = Math.Max(usedSpace, pos + game.ROM.PRG.Length);
                                Console.WriteLine("Address: {0:X8}", pos);
                                break;
                            }
                        }
                        if (game.PrgPos < 0) throw new Exception("Can't fit " + game);
                    }

                    var sortedChrs = from game in games orderby game.ROM.CHR.Length descending select game;
                    foreach (var game in sortedChrs)
                    {
                        int chrSize = game.ROM.CHR.Length;
                        if (chrSize == 0) continue;

                        Console.WriteLine("Fitting CHR for {0} ({1}kbytes)...", game, chrSize / 1024);
                        for (int pos = 0; pos < result.Length; pos += 0x2000)
                        {
                            if (WillFit(result, pos, chrSize, game.ROM.CHR))
                            {
                                game.ChrPos = pos;
                                //Array.Copy(game.ROM.CHR, 0, result, pos, game.ROM.CHR.Length);
                                for (var i = 0; i < game.ROM.CHR.Length; i++)
                                    result[pos + i] = game.ROM.CHR[i];
                                usedSpace = Math.Max(usedSpace, pos + game.ROM.CHR.Length);
                                Console.WriteLine("Address: {0:X8}", pos);
                                break;
                            }
                        }
                        if (game.ChrPos < 0) throw new Exception("Can't fit " + game.FileName);
                    }
                    while (usedSpace % 0x8000 != 0)
                        usedSpace++;
                    int romSize = usedSpace;
                    usedSpace += 128 * 1024 * (int)Math.Ceiling(saveId / 4.0);

                    int totalSize = 0;
                    int maxChrSize = 0;
                    namesIncluded.Add(string.Format("{0,-33} {1,-10} {2,-10} {3,-10} {4,0}", "Game name", "Mapper", "Save ID", "Size", "Total size"));
                    namesIncluded.Add(string.Format("{0,-33} {1,-10} {2,-10} {3,-10} {4,0}", "------------", "-------", "-------", "-------", "--------------"));
                    var mapperStats = new Dictionary<byte, int>();
                    foreach (var game in games)
                    {
                        if (!game.ToString().StartsWith("?"))
                        {
                            totalSize += game.ROM.PRG.Length;
                            totalSize += game.ROM.CHR.Length;
                            namesIncluded.Add(string.Format("{0,-33} {1,-10} {2,-10} {3,-10} {4,0}", FirstCharToUpper(game.ToString().Replace("_", " ").Replace("+", "")), game.ROM.Mapper, game.SaveId == 0 ? "-" : game.SaveId.ToString(),
                                ((game.ROM.PRG.Length + game.ROM.CHR.Length) / 1024) + " KB", (totalSize / 1024) + " KB total"));
                            if (!mapperStats.ContainsKey(game.ROM.Mapper)) mapperStats[game.ROM.Mapper] = 0;
                            mapperStats[game.ROM.Mapper]++;
                        }
                        if (game.ROM.CHR.Length > maxChrSize)
                            maxChrSize = game.ROM.CHR.Length;
                    }
                    namesIncluded.Add("");
                    namesIncluded.Add(string.Format("{0,-10} {1,0}", "Mapper", "Count"));
                    namesIncluded.Add(string.Format("{0,-10} {1,0}", "------", "-----"));
                    foreach (var mapper in from m in mapperStats.Keys orderby m ascending select m)
                    {
                        namesIncluded.Add(string.Format("{0,-10} {1,0}", mapper, mapperStats[mapper]));
                    }

                    namesIncluded.Add("");
                    namesIncluded.Add("Total games: " + (games.Count - hiddenCount));
                    namesIncluded.Add("Final ROM size: " + Math.Round(usedSpace / 1024.0 / 1024.0, 3) + "MB");
                    namesIncluded.Add("Maximum CHR size: " + maxChrSize / 1024 + "KB");
                    namesIncluded.Add("Battery-backed games: " + saveId);

                    Console.WriteLine("Total games: " + (games.Count - hiddenCount));
                    Console.WriteLine("Final ROM size: " + Math.Round(usedSpace / 1024.0 / 1024.0, 3) + "MB");
                    Console.WriteLine("Maximum CHR size: " + maxChrSize / 1024 + "KB");
                    Console.WriteLine("Battery-backed games: " + saveId);

                    if (optionReport != null)
                        File.WriteAllLines(optionReport, namesIncluded.ToArray());

                    if (games.Count - hiddenCount == 0)
                        throw new Exception("Games list is empty");

                    if (usedSpace > optionMaxSize * 1024 * 1024)
                        throw new Exception(string.Format("ROM is too big: {0} MB", Math.Round(usedSpace / 1024.0 / 1024.0, 3)));
                    if (games.Count > 768)
                        throw new Exception("Too many ROMs: " + games.Count);
                    if (saveId > 128)
                        throw new Exception("Too many battery backed games: " + saveId);

                    regs["reg_0"] = new List<String>();
                    regs["reg_1"] = new List<String>();
                    regs["reg_2"] = new List<String>();
                    regs["reg_3"] = new List<String>();
                    regs["reg_4"] = new List<String>();
                    regs["reg_5"] = new List<String>();
                    regs["reg_6"] = new List<String>();
                    regs["reg_7"] = new List<String>();
                    regs["chr_start_bank_h"] = new List<String>();
                    regs["chr_start_bank_l"] = new List<String>();
                    regs["chr_start_bank_s"] = new List<String>();
                    regs["chr_count"] = new List<String>();
                    regs["game_save"] = new List<String>();
                    regs["game_type"] = new List<String>();
                    regs["cursor_pos"] = new List<String>();

                    int c = 0;
                    foreach (var game in games)
                    {
                        int prgSize = game.ROM.PRG.Length;
                        int chrSize = game.ROM.CHR.Length;
                        int prgPos = game.PrgPos;
                        int chrPos = Math.Max(game.ChrPos, 0);
                        int chrBase = (chrPos / 0x2000) >> 4;
                        int prgBase = (prgPos / 0x2000) >> 4;
                        int prgRoundSize = 1;
                        while (prgRoundSize < prgSize) prgRoundSize *= 2;
                        int chrRoundSize = 1;
                        while (chrRoundSize < chrSize || chrRoundSize < 0x2000) chrRoundSize *= 2;

                        MapperInfo mapperInfo;
                        if (!mappers.TryGetValue(game.ROM.Mapper, out mapperInfo))
                            throw new Exception(string.Format("Unknowm mapper #{0} for {1} ", game.ROM.Mapper, game.FileName));
                        if (chrSize > 256 * 1024)
                            throw new Exception(string.Format("CHR is too big in {0} ", game.FileName));
                        if (game.ROM.Mirroring == NesFile.MirroringType.FourScreenVram && game.ROM.CHR.Length > 256 * 1024 - 0x1000)
                            throw new Exception(string.Format("Four-screen and such big CHR is not supported for {0} ", game.FileName));

                        // Some unusual games
                        if (game.ROM.Mapper == 1) // MMC1 ?
                        {
                            switch (game.ROM.CRC32)
                            {
                                case 0xc6182024:	// Romance of the 3 Kingdoms
                                case 0x2225c20f:	// Genghis Khan
                                case 0x4642dda6:	// Nobunaga's Ambition
                                case 0x29449ba9:	// ""        "" (J)
                                case 0x2b11e0b0:	// ""        "" (J)
                                case 0xb8747abf:	// Best Play Pro Yakyuu Special (J)
                                case 0xc9556b36:	// Final Fantasy I & II (J) [!]
                                    Console.WriteLine("WARNING! {0} uses 16KB of WRAM", game.FileName);
                                    mapperInfo.MapperFlags |= 1; // flag to support 16KB of WRAM
                                    break;
                            }
                        }
                        if (game.ROM.Mapper == 4) // MMC3 ?
                        {
                            switch (game.ROM.CRC32)
                            {
                                case 0x93991433:	// Low-G-Man
                                case 0xaf65aa84:	// Low-G-Man
                                    Console.WriteLine("WARNING! WRAM will be disabled for {0}", Path.GetFileName(game.FileName));
                                    mapperInfo.WramEnabled = false; // disable WRAM
                                    break;
                            }
                        }
                        switch (game.ROM.CRC32)
                        {
                            case 0x78b657ac: // "Armadillo (J) [!].nes"
                            case 0xb3d92e78: // "Armadillo (J) [T+Eng1.01_Vice Translations].nes"
                            case 0x0fe6e6a5: // "Armadillo (J) [T+Rus1.00 Chief-Net (23.05.2012)].nes"
                            case 0xe62e3382: // "MiG 29 - Soviet Fighter (Camerica) [!].nes"
                            case 0x1bc686a8: // "Fire Hawk (Camerica) [!].nes"
                                Console.WriteLine("WARNING! {0} is not compatible with Dendy", Path.GetFileName(game.FileName));
                                game.Flags |= GameFlags.WillNotWorkOnDendy;
                                break;
                        }
                        if (string.IsNullOrEmpty(game.ToString()))
                            game.Flags = GameFlags.Separator;

                        byte @params = 0;
                        if (mapperInfo.WramEnabled) @params |= 1; // enable SRAM
                        if (chrSize == 0) @params |= 2; // enable CHR write
                        if (game.ROM.Mirroring == NesFile.MirroringType.Horizontal) @params |= 8; // default mirroring
                        if (game.ROM.Mirroring == NesFile.MirroringType.FourScreenVram)
                        {
                            @params |= 32; // four screen
                            game.Flags |= GameFlags.WillNotWorkOnNewDendy; // not external NTRAM on new famiclones :(
                        }
                        @params |= 0x80; // lockout

                        regs["reg_0"].Add(string.Format("${0:X2}", ((prgPos / 0x4000) >> 8) & 0xFF));    // PRG base
                        regs["reg_1"].Add(string.Format("${0:X2}", (prgPos / 0x4000) & 0xFF));  // PRG base
                        regs["reg_2"].Add(string.Format("${0:X2}", (0xFF ^ (prgRoundSize / 0x4000 - 1)) & 0xFF)); // PRG mask
                        regs["reg_3"].Add(string.Format("${0:X2}", (mapperInfo.PrgMode << 5) | 0)); // PRG mode
                        regs["reg_4"].Add(string.Format("${0:X2}", (mapperInfo.ChrMode << 5) | (0xFF ^ (chrRoundSize / 0x2000 - 1)) & 0x1F)); // CHR mode, CHR mask
                        regs["reg_5"].Add(string.Format("${0:X2}", (game.ROM.Battery ? 0x02 : 0x01)));    // SRAM page
                        regs["reg_6"].Add(string.Format("${0:X2}", (mapperInfo.MapperFlags << 5) | mapperInfo.MapperReg)); // Flags, mapper
                        regs["reg_7"].Add(string.Format("${0:X2}", @params));                   // Parameters
                        regs["chr_start_bank_h"].Add(string.Format("${0:X2}", ((chrPos / 0x8000) >> 7) & 0xFF));
                        regs["chr_start_bank_l"].Add(string.Format("${0:X2}", ((chrPos / 0x8000) << 1) & 0xFF));
                        regs["chr_start_bank_s"].Add(string.Format("${0:X2}", ((chrPos % 0x8000) >> 8) | 0x80));
                        regs["chr_count"].Add(string.Format("${0:X2}", chrSize / 0x2000));
                        regs["game_save"].Add(string.Format("${0:X2}", !game.ROM.Battery ? 0 : game.SaveId));
                        regs["game_type"].Add(string.Format("${0:X2}", (byte)game.Flags));
                        regs["cursor_pos"].Add(string.Format("${0:X2}", game.ToString().Length + 4 /*+ (++c).ToString().Length*/));
                    }

                    byte baseBank = 0;
                    var asmResult = new StringBuilder();
                    asmResult.AppendLine("; Games database");
                    int regCount = 0;
                    foreach (var reg in regs.Keys)
                    {
                        c = 0;
                        foreach (var r in regs[reg])
                        {
                            if (c % 256 == 0)
                            {
                                asmResult.AppendLine();
                                asmResult.AppendLine("  .bank " + (baseBank + c / 256 * 2));
                                asmResult.AppendLine(string.Format("  .org ${0:X4}", 0x8000 + regCount * 0x100));
                                asmResult.Append("loader_data_" + reg + (c == 0 ? "" : "_" + c.ToString()) + ":");
                            }
                            //asmResult.AppendLine("  .db " + string.Join(", ", regs[reg]));
                            if (c % 16 == 0)
                            {
                                asmResult.AppendLine();
                                asmResult.Append("  .db");
                            }
                            asmResult.AppendFormat(((c % 16 != 0) ? "," : "") + " {0}", r);
                            c++;
                        }
                        asmResult.AppendLine();
                        regCount++;
                    }

                    asmResult.AppendLine();
                    asmResult.AppendLine();
                    //asmResult.Append("  .dw");
                    c = 0;
                    foreach (var game in games)
                    {
                        if (c % 256 == 0)
                        {
                            asmResult.AppendLine();
                            asmResult.AppendLine("  .bank " + (baseBank + c / 256 * 2));
                            asmResult.AppendLine("  .org $9000");
                            asmResult.AppendLine("game_names_list" + (c == 0 ? "" : "_" + c.ToString()) + ":");
                            asmResult.AppendLine("  .dw game_names" + (c == 0 ? "" : "_" + c.ToString()));
                            asmResult.AppendLine("game_names" + (c == 0 ? "" : "_" + c.ToString()) + ":");
                        }
                        //asmResult.AppendFormat(((c > 0) ? "," : "") + " game_name_" + c);
                        asmResult.AppendLine("  .dw game_name_" + c);
                        c++;
                    }

                    asmResult.AppendLine();
                    asmResult.AppendLine();
                    asmResult.AppendLine("; Game names");
                    c = 0;
                    foreach (var game in games)
                    {
                        asmResult.AppendLine();
                        if (c % 256 == 0)
                        {
                            asmResult.AppendLine("  .bank " + (baseBank + c / 256 * 2 + 1));
                            if (baseBank + c / 256 * 2 + 1 >= 62) throw new Exception("Bank overflow! Too many games?");
                            asmResult.AppendLine("  .org $A000");
                        }
                        asmResult.AppendLine("; " + game.ToString());
                        asmResult.AppendLine("game_name_" + c + ":");
                        var name = StringToTiles(string.Format(/*"{0}. "+*/"{1}", c + 1, game.ToString()));
                        var asm = BytesToAsm(name);
                        asmResult.Append(asm);
                        c++;
                    }

                    asmResult.AppendLine();
                    asmResult.AppendLine("  .bank 14");
                    asmResult.AppendLine("  .org $C600");
                    asmResult.AppendLine();
                    asmResult.AppendLine("games_count:");
                    asmResult.AppendLine("  .dw " + (games.Count - hiddenCount));
                    asmResult.AppendLine();
                    asmResult.AppendLine();
                    asmResult.AppendLine("games_offset:");
                    asmResult.AppendLine("  .db " + ((games.Count - hiddenCount) > 10 ? 0 : 5 - (games.Count - hiddenCount) / 2));
                    asmResult.AppendLine();
                    asmResult.AppendLine();
                    asmResult.AppendLine("maximum_scroll:");
                    asmResult.AppendLine("  .dw " + Math.Max(0, games.Count - 11 - hiddenCount));
                    asmResult.AppendLine();
                    asmResult.AppendLine();
                    asmResult.AppendLine("build_info0:");
                    asmResult.Append(BytesToAsm(StringToTiles("FILE: " + Path.GetFileName(optionGames))));
                    asmResult.AppendLine("build_info2:");
                    asmResult.Append(BytesToAsm(StringToTiles("BUILD DATE: " + DateTime.Now.ToString("yyyy-MM-dd"))));
                    asmResult.AppendLine("build_info3:");
                    asmResult.Append(BytesToAsm(StringToTiles("BUILD TIME: " + DateTime.Now.ToString("HH:mm:ss"))));
                    asmResult.AppendLine("console_type_text:");
                    asmResult.Append(BytesToAsm(StringToTiles("CONSOLE TYPE:")));
                    asmResult.AppendLine("console_type_NTSC:");
                    asmResult.Append(BytesToAsm(StringToTiles("NTSC")));
                    asmResult.AppendLine("console_type_PAL:");
                    asmResult.Append(BytesToAsm(StringToTiles("PAL")));
                    asmResult.AppendLine("console_type_DENDY:");
                    asmResult.Append(BytesToAsm(StringToTiles("DENDY")));
                    asmResult.AppendLine("console_type_NEW:");
                    asmResult.Append(BytesToAsm(StringToTiles("NEW")));
                    asmResult.AppendLine("saving_text:");
                    if (optionLanguage == "rus")
                        asmResult.Append(BytesToAsm(StringToTiles("  СОХРАНЯЕМСЯ... НЕ ВЫКЛЮЧАЙ!   ")));
                    else
                        asmResult.Append(BytesToAsm(StringToTiles("   SAVING...  KEEP POWER ON!    ")));
                    File.WriteAllText(optionAsm, asmResult.ToString());
                    asmResult.AppendLine("incompatible_console_text:");
                    if (optionLanguage == "rus")
                        asmResult.Append(BytesToAsm(StringToTiles("     ИЗВИНИТЕ,  ДАННАЯ ИГРА       НЕСОВМЕСТИМА С ЭТОЙ КОНСОЛЬЮ                                        НАЖМИТЕ ЛЮБУЮ КНОПКУ      ")));
                    else
                        asmResult.Append(BytesToAsm(StringToTiles("    SORRY,  THIS GAME IS NOT      COMPATIBLE WITH THIS CONSOLE                                          PRESS ANY BUTTON        ")));
                    asmResult.AppendLine("sram_test_ok_text:");
                    asmResult.Append(BytesToAsm(StringToTiles("PRG RAM TEST: OK")));
                    asmResult.AppendLine("sram_test_failed_text:");
                    asmResult.Append(BytesToAsm(StringToTiles("PRG RAM TEST: FAILED")));
                    asmResult.AppendLine("chr_test_ok_text:");
                    asmResult.Append(BytesToAsm(StringToTiles("CHR RAM TEST: OK")));
                    asmResult.AppendLine("chr_test_failed_text:");
                    asmResult.Append(BytesToAsm(StringToTiles("CHR RAM TEST: FAILED")));
                    File.WriteAllText(optionAsm, asmResult.ToString());

                    XmlWriterSettings xmlSettings = new XmlWriterSettings();
                    xmlSettings.Indent = true;
                    StreamWriter str = new StreamWriter(optionOffsets);
                    XmlWriter offsetsXml = XmlWriter.Create(str, xmlSettings);
                    offsetsXml.WriteStartDocument();
                    offsetsXml.WriteStartElement("Offsets");

                    offsetsXml.WriteStartElement("Info");
                    offsetsXml.WriteElementString("Size", romSize.ToString());
                    offsetsXml.WriteElementString("RomCount", games.Count.ToString());
                    offsetsXml.WriteElementString("GamesFile", Path.GetFileName(optionGames));
                    offsetsXml.WriteEndElement();

                    offsetsXml.WriteStartElement("ROMs");
                    foreach (var game in games)
                    {
                        if (game.FileName == "-") continue;
                        offsetsXml.WriteStartElement("ROM");
                        offsetsXml.WriteElementString("FileName", game.FileName);
                        if (!game.ToString().StartsWith("?"))
                            offsetsXml.WriteElementString("MenuName", game.ToString());
                        offsetsXml.WriteElementString("PrgOffset", string.Format("{0:X8}", game.PrgPos));
                        if (game.ROM.CHR.Length > 0)
                            offsetsXml.WriteElementString("ChrOffset", string.Format("{0:X8}", game.ChrPos));
                        offsetsXml.WriteElementString("Mapper", game.ROM.Mapper.ToString());
                        if (game.SaveId > 0)
                            offsetsXml.WriteElementString("SaveId", game.SaveId.ToString());
                        offsetsXml.WriteEndElement();
                    }
                    offsetsXml.WriteEndElement();

                    offsetsXml.WriteEndElement();
                    offsetsXml.Close();
                }
                else
                {
                    using (var xmlFile = File.OpenRead(optionOffsets))
                    {
                        var offsetsXml = new XPathDocument(xmlFile);
                        XPathNavigator offsetsNavigator = offsetsXml.CreateNavigator();
                        XPathNodeIterator offsetsIterator = offsetsNavigator.Select("/Offsets/Info/Size");
                        int size = -1;
                        while (offsetsIterator.MoveNext())
                        {
                            size = offsetsIterator.Current.ValueAsInt;
                        }
                        //size = 64 * 1024 * 1024;

                        if (size < 0) throw new Exception("Invalid offsets file");
                        var result = new byte[size];

                        Console.Write("Loading loader... ");
                        var loaderFile = new NesFile(optionLoader);
                        Array.Copy(loaderFile.PRG, 0, result, 0, loaderFile.PRG.Length);
                        Console.WriteLine("OK.");

                        offsetsIterator = offsetsNavigator.Select("/Offsets/ROMs/ROM");
                        while (offsetsIterator.MoveNext())
                        {
                            var currentRom = offsetsIterator.Current;
                            string filename = null;
                            int prgOffset = -1;
                            int chrOffset = -1;
                            var descs = currentRom.SelectDescendants(XPathNodeType.Element, false);
                            while (descs.MoveNext())
                            {
                                var param = descs.Current;
                                switch (param.Name.ToLower())
                                {
                                    case "filename":
                                        filename = param.Value;
                                        break;
                                    case "prgoffset":
                                        prgOffset = int.Parse(param.Value, System.Globalization.NumberStyles.HexNumber);
                                        break;
                                    case "chroffset":
                                        chrOffset = int.Parse(param.Value, System.Globalization.NumberStyles.HexNumber);
                                        break;
                                }
                            }

                            if (!string.IsNullOrEmpty(filename))
                            {
                                Console.Write("Loading {0}... ", filename);
                                var nesFile = new NesFile(filename);
                                if (prgOffset >= 0)
                                    Array.Copy(nesFile.PRG, 0, result, prgOffset, nesFile.PRG.Length);
                                if (chrOffset >= 0)
                                    Array.Copy(nesFile.CHR, 0, result, chrOffset, nesFile.CHR.Length);
                                Console.WriteLine("OK.");
                            }
                        }

                        if (!string.IsNullOrEmpty(optionUnif))
                        {
                            var u = new UnifFile();
                            u.Mapper = "COOLGIRL";
                            u.Fields["MIRR"] = new byte[] { 5 };
                            u.Fields["PRG0"] = result;
                            u.Fields["BATR"] = new byte[] { 1 };
                            u.Version = 5;
                            u.Save(optionUnif);

                            uint sizeFixed = 1;
                            while (sizeFixed < result.Length) sizeFixed <<= 1;
                            var resultSizeFixed = new byte[sizeFixed];
                            Array.Copy(result, 0, resultSizeFixed, 0, result.Length);
                            for (int i = result.Length; i < sizeFixed; i++)
                                resultSizeFixed[i] = 0xFF;
                            var md5 = System.Security.Cryptography.MD5.Create();
                            var md5hash = md5.ComputeHash(resultSizeFixed);
                            Console.Write("ROM MD5: ");
                            foreach (var b in md5hash)
                                Console.Write("{0:x2}", b);
                            Console.WriteLine();
                        }
                        if (!string.IsNullOrEmpty(optionBin))
                        {
                            File.WriteAllBytes(optionBin, result);
                        }
                    }
                }
                Console.WriteLine("Done.");
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error: " + ex.Message + ex.StackTrace);
                return 2;
            }
            return 0;
        }
Пример #22
0
        private void AddMenu(NesMenuCollection menuCollection, List <NesMenuCollection> allMenus = null)
        {
            if (allMenus == null)
            {
                allMenus = new List <NesMenuCollection>();
            }
            if (!allMenus.Contains(menuCollection))
            {
                allMenus.Add(menuCollection);
            }
            int    menuIndex = allMenus.IndexOf(menuCollection);
            string targetDirectory;

            if (menuIndex == 0)
            {
                targetDirectory = gamesDirectory;
            }
            else
            {
                targetDirectory = Path.Combine(gamesDirectory, string.Format("sub{0:D3}", menuIndex));
            }
            if (Directory.Exists(targetDirectory))
            {
                return;
            }
            foreach (var element in menuCollection)
            {
                if (element is NesGame)
                {
                    var game    = element as NesGame;
                    var gameDir = Path.Combine(targetDirectory, game.Code);
                    Debug.WriteLine(string.Format("Processing {0}/{1}", game.Code, game.Name));
                    DirectoryCopy(game.GamePath, gameDir, true);
                    if (!string.IsNullOrEmpty(game.GameGenie))
                    {
                        var codes          = game.GameGenie.Split(new char[] { ',', '\t', ' ', ';' }, StringSplitOptions.RemoveEmptyEntries);
                        var newNesFilePath = Path.Combine(gameDir, game.Code + ".nes");
                        try
                        {
                            var nesFile = new NesFile(newNesFilePath);
                            foreach (var code in codes)
                            {
                                try
                                {
                                    nesFile.PRG = GameGenie.Patch(nesFile.PRG, code.Trim());
                                }
                                catch (GameGenieFormatException)
                                {
                                    ShowError(new GameGenieFormatException(string.Format(Resources.GameGenieFormatError, code, game)), dontStop: true);
                                }
                                catch (GameGenieNotFoundException)
                                {
                                    ShowError(new GameGenieNotFoundException(string.Format(Resources.GameGenieNotFound, code, game.Name)), dontStop: true);
                                }
                            }
                            nesFile.Save(newNesFilePath);
                            var ggFilePath = Path.Combine(gameDir, NesGame.GameGenieFileName);
                            if (File.Exists(ggFilePath))
                            {
                                File.Delete(ggFilePath);
                            }
                        }
                        catch // in case of FDS game... just ignore
                        {
                        }
                    }
                }
                if (element is NesMenuFolder)
                {
                    var folder = element as NesMenuFolder;
                    if (!allMenus.Contains(folder.Child))
                    {
                        allMenus.Add(folder.Child);
                    }
                    int childIndex = allMenus.IndexOf(folder.Child);
                    var folderDir  = Path.Combine(targetDirectory, folder.Code);
                    folder.Save(folderDir, childIndex);
                    AddMenu(folder.Child, allMenus);
                }
                if (element is NesDefaultGame)
                {
                    var game      = element as NesDefaultGame;
                    var gfilePath = Path.Combine(gamesDirectory, string.Format("gpath-{0}-{1}", game.Code, menuIndex));
                    Directory.CreateDirectory(Path.GetDirectoryName(gfilePath));
                    File.WriteAllText(gfilePath, menuIndex == 0 ? "." : string.Format("sub{0:D3}", menuIndex));
                }
            }
        }
Пример #23
0
        public static NesMiniApplication ImportNes(string nesFileName, bool?ignoreMapper, ref bool?needPatch, NeedPatchDelegate needPatchCallback = null, Form parentForm = null, byte[] rawRomData = null)
        {
            NesFile nesFile;

            try
            {
                if (rawRomData != null)
                {
                    nesFile = new NesFile(rawRomData);
                }
                else
                {
                    nesFile = new NesFile(nesFileName);
                }
            }
            catch
            {
                return(NesMiniApplication.Import(nesFileName, rawRomData));
            }
            nesFile.CorrectRom();
            var crc32            = nesFile.CRC32;
            var code             = GenerateCode(crc32, Prefix);
            var gamePath         = Path.Combine(GamesDirectory, code);
            var nesPath          = Path.Combine(gamePath, code + ".nes");
            var patchesDirectory = Path.Combine(Path.GetDirectoryName(Application.ExecutablePath), "patches");

            Directory.CreateDirectory(patchesDirectory);
            Directory.CreateDirectory(gamePath);
            var patches = Directory.GetFiles(patchesDirectory, string.Format("{0:X8}*.ips", crc32), SearchOption.AllDirectories);

            if (patches.Length > 0 && needPatch != false)
            {
                if (needPatch == true || ((needPatchCallback != null) && needPatchCallback(parentForm, Path.GetFileName(nesFileName))))
                {
                    needPatch = true;
                    var patch = patches[0];
                    if (rawRomData == null)
                    {
                        rawRomData = File.ReadAllBytes(nesFileName);
                    }
                    Debug.WriteLine(string.Format("Patching {0}", nesFileName));
                    IpsPatcher.Patch(patch, ref rawRomData);
                    nesFile = new NesFile(rawRomData);
                }
                else
                {
                    needPatch = false;
                }
            }

            if (nesFile.Mapper == 71)
            {
                nesFile.Mapper = 2;                       // games by Codemasters/Camerica - this is UNROM clone. One exception - Fire Hawk
            }
            if (nesFile.Mapper == 88)
            {
                nesFile.Mapper = 4;                       // Compatible with MMC3... sometimes
            }
            if (nesFile.Mapper == 95)
            {
                nesFile.Mapper = 4;                       // Compatible with MMC3
            }
            if (nesFile.Mapper == 206)
            {
                nesFile.Mapper = 4;                        // Compatible with MMC3
            }
            if (!supportedMappers.Contains(nesFile.Mapper) && (ignoreMapper != true))
            {
                Directory.Delete(gamePath, true);
                if (ignoreMapper != false)
                {
                    throw new UnsupportedMapperException(nesFile);
                }
                else
                {
                    Debug.WriteLine(string.Format("Game {0} has mapper #{1}, skipped", nesFileName, nesFile.Mapper));
                    return(null);
                }
            }
            if ((nesFile.Mirroring == NesFile.MirroringType.FourScreenVram) && (ignoreMapper != true))
            {
                Directory.Delete(gamePath, true);
                if (ignoreMapper != false)
                {
                    throw new UnsupportedFourScreenException(nesFile);
                }
                else
                {
                    Debug.WriteLine(string.Format("Game {0} has four-screen mirroring, skipped", nesFileName, nesFile.Mapper));
                    return(null);
                }
            }
            // TODO: Make trainer check. I think that the NES Mini doesn't support it.

            nesFile.Save(nesPath);
            var game = new NesGame(gamePath, true);

            game.Name = Path.GetFileNameWithoutExtension(nesFileName);
            if (game.Name.Contains("(J)"))
            {
                game.region = "Japan";
            }
            game.TryAutofill(crc32);
            game.Name = Regex.Replace(game.Name, @" ?\(.*?\)", string.Empty).Trim();
            game.Name = Regex.Replace(game.Name, @" ?\[.*?\]", string.Empty).Trim();
            game.Name = game.Name.Replace("_", " ").Replace("  ", " ");
            game.FindCover(nesFileName, (game.region == "Japan") ? Resources.blank_jp : Resources.blank_nes, crc32);
            game.Args = DefaultArgs;
            game.Save();
            return(game);
        }
Пример #24
0
 public void InsertNesFile(NesFile nesFile)
 {
     _nametableMirrorController.MirroringMode = nesFile.MirroringMode;
     _mapper = new Mapper0(nesFile);
 }
Пример #25
0
 public LuaMapper()
 {
     script = new Script();
     script.Globals["ReadPrg"] = script.Globals["ReadCpu"] = (Func <UInt16, int, List <byte> >) delegate(UInt16 address, int length)
     {
         if (Verbose)
         {
             Console.WriteLine("Reading {0} bytes from CPU:${1:X4}", length, address);
         }
         var result = new List <byte>();
         result.AddRange(dumper.ReadCpu(address, length));
         return(result);
     };
     script.Globals["WritePrg"] = script.Globals["WriteCpu"] = (Action <UInt16, List <byte> >) delegate(UInt16 address, List <byte> data)
     {
         if (Verbose)
         {
             var a = address;
             foreach (var v in data)
             {
                 Console.WriteLine("CPU write ${0:X2} => ${1:X4}", v, a);
                 a++;
             }
         }
         dumper.WriteCpu(address, data.ToArray());
     };
     script.Globals["AddPrg"] = script.Globals["AddPrgResult"] = (Action <List <byte> >) delegate(List <byte> r)
     {
         resultPrg.AddRange(r);
     };
     script.Globals["ReadAddPrg"] = script.Globals["ReadAddCpu"] = (Action <UInt16, int>) delegate(UInt16 address, int length)
     {
         if (Verbose)
         {
             Console.WriteLine("Reading {0} bytes from CPU:${1:X4}", length, address);
         }
         resultPrg.AddRange(dumper.ReadCpu(address, length));
     };
     script.Globals["ReadChr"] = script.Globals["ReadPpu"] = (Func <UInt16, int, List <byte> >) delegate(UInt16 address, int length)
     {
         if (Verbose)
         {
             Console.WriteLine("Reading {0} bytes from PPU:${1:X4}", length, address);
         }
         var result = new List <byte>();
         result.AddRange(dumper.ReadPpu(address, length));
         return(result);
     };
     script.Globals["WriteChr"] = script.Globals["WritePpu"] = (Action <UInt16, List <byte> >) delegate(UInt16 address, List <byte> data)
     {
         if (Verbose)
         {
             var a = address;
             foreach (var v in data)
             {
                 Console.WriteLine("PPU write ${0:X2} => ${1:X4}", v, a);
                 a++;
             }
         }
         dumper.WritePpu(address, data.ToArray());
     };
     script.Globals["ReadAddChr"] = script.Globals["ReadAddPpu"] = (Action <UInt16, int>) delegate(UInt16 address, int length)
     {
         if (Verbose)
         {
             Console.WriteLine("Reading {0} bytes from PPU:${1:$X4}", length, address);
         }
         resultChr.AddRange(dumper.ReadPpu(address, length));
     };
     script.Globals["AddChr"] = script.Globals["AddChrResult"] = (Action <List <byte> >) delegate(List <byte> r)
     {
         resultChr.AddRange(r);
     };
     script.Globals["Reset"] = (Action) delegate
     {
         if (Verbose)
         {
             Console.Write("Reset... ");
         }
         dumper.Reset();
         if (Verbose)
         {
             Console.WriteLine("OK");
         }
     };
     script.Globals["WriteFile"] = (Action <string, List <byte> >) delegate(string filename, List <byte> data)
     {
         if (Verbose)
         {
             Console.Write("Writing data to \"{0}\"... ", Path.GetFileName(filename));
         }
         File.WriteAllBytes(filename, data.ToArray());
         if (Verbose)
         {
             Console.WriteLine("OK");
         }
     };
     script.Globals["WriteNes"] = (WriteNesDelegate) delegate(string filename, List <byte> prgData, List <byte> chrData, byte mapper, bool vertical)
     {
         if (Verbose)
         {
             Console.Write("Writing data to NES file \"{0}\" (mapper={1}, mirroring={2})... ", Path.GetFileName(filename), mapper, vertical ? "vertical" : "horizontal");
         }
         var nesFile = new NesFile();
         nesFile.PRG       = prgData.ToArray();
         nesFile.CHR       = chrData.ToArray();
         nesFile.Mapper    = 0;
         nesFile.Mirroring = vertical ? NesFile.MirroringType.Vertical : NesFile.MirroringType.Horizontal;
         nesFile.Save(filename);
         if (Verbose)
         {
             Console.WriteLine("OK");
         }
     };
     script.Globals["Error"] = (Action <string>) delegate(string message)
     {
         throw new Exception(message);
     };
 }
Пример #26
0
 public Mapper0(NesFile nesFile)
 {
     _nesFile = nesFile;
 }