static void Dump(FamicomDumperConnection dumper, string fileName, string mapperName, int prgSize, int chrSize, string unifName, string unifAuthor) { var mapper = GetMapper(mapperName); if (mapper.Number >= 0) { Console.WriteLine("Using mapper: #{0} ({1})", mapper.Number, mapper.Name); } else { Console.WriteLine("Using mapper: {0}", mapper.Name); } Console.WriteLine("Dumping..."); List <byte> prg = new List <byte>(); List <byte> chr = new List <byte>(); prgSize = prgSize >= 0 ? prgSize : mapper.DefaultPrgSize; chrSize = chrSize >= 0 ? chrSize : mapper.DefaultChrSize; Console.WriteLine("PRG memory size: {0}K", prgSize / 1024); mapper.DumpPrg(dumper, prg, prgSize); while (prg.Count % 0x4000 != 0) { prg.Add(0); } Console.WriteLine("CHR memory size: {0}K", chrSize / 1024); mapper.DumpChr(dumper, chr, chrSize); while (chr.Count % 0x2000 != 0) { chr.Add(0); } byte[] mirroringRaw = dumper.GetMirroring(); NesFile.MirroringType mirroring = NesFile.MirroringType.Unknown_none; if (mirroringRaw.Length == 1) { mirroring = (NesFile.MirroringType)mirroringRaw[0]; Console.WriteLine("Mirroring: " + mirroring); } else if (mirroringRaw.Length == 4) { switch (string.Format("{0:X2}{1:X2}{2:X2}{3:X2}", mirroringRaw[0], mirroringRaw[1], mirroringRaw[2], mirroringRaw[3])) { case "00000101": mirroring = NesFile.MirroringType.Horizontal; // Horizontal break; case "00010001": mirroring = NesFile.MirroringType.Vertical; // Vertical break; case "00000000": mirroring = NesFile.MirroringType.OneScreenA; // One-screen A break; case "01010101": mirroring = NesFile.MirroringType.OneScreenB; // One-screen B break; default: mirroring = NesFile.MirroringType.Unknown_none; // Unknown break; } Console.WriteLine("Mirroring: {0} ({1:X2} {2:X2} {3:X2} {4:X2})", mirroring, mirroringRaw[0], mirroringRaw[1], mirroringRaw[2], mirroringRaw[3]); } Console.WriteLine("Saving to {0}...", fileName); if (mapper.Number >= 0) { var nesFile = new NesFile(); nesFile.Mapper = (byte)mapper.Number; nesFile.Mirroring = mirroring; nesFile.TvSystem = NesFile.TvSystemType.Ntsc; nesFile.PRG = prg.ToArray(); nesFile.CHR = chr.ToArray(); nesFile.Save(fileName); } else { var unifFile = new UnifFile(); unifFile.Version = 4; unifFile.Mapper = mapper.UnifName; if (unifName != null) { unifFile.Fields["NAME"] = UnifFile.StringToUTF8N(unifName); } unifFile.Fields["PRG0"] = prg.ToArray(); if (chr.Count > 0) { unifFile.Fields["CHR0"] = chr.ToArray(); } unifFile.Fields["MIRR"] = new byte[] { 5 }; // mapper controlled unifFile.Save(fileName, unifAuthor); } }
/// <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); }
static void Dump(FamicomDumperConnection dumper, string fileName, string mapperName, int prgSize, int chrSize, string unifName, string unifAuthor, bool battery) { var mapper = GetMapper(mapperName); if (mapper.Number >= 0) { Console.WriteLine($"Using mapper: #{mapper.Number} ({mapper.Name})"); } else { Console.WriteLine($"Using mapper: {mapper.Name}"); } Console.WriteLine("Dumping..."); List <byte> prg = new List <byte>(); List <byte> chr = new List <byte>(); prgSize = prgSize >= 0 ? prgSize : mapper.DefaultPrgSize; chrSize = chrSize >= 0 ? chrSize : mapper.DefaultChrSize; if (prgSize > 0) { Console.WriteLine($"PRG memory size: {prgSize / 1024}KB"); mapper.DumpPrg(dumper, prg, prgSize); while (prg.Count % 0x4000 != 0) { prg.Add(0); } } if (chrSize > 0) { Console.WriteLine($"CHR memory size: {chrSize / 1024}KB"); mapper.DumpChr(dumper, chr, chrSize); while (chr.Count % 0x2000 != 0) { chr.Add(0); } } NesFile.MirroringType mirroring = NesFile.MirroringType.Unknown; // TODO: move GetMapper to IMapper, so it will not be optional //mirroring = mapper.GetMirroring(dumper); mirroring = GetMirroring(dumper, mapper); Console.WriteLine($"Mirroring: {mirroring}"); Console.Write($"Saving to {fileName}... "); if (mapper.Number >= 0) { // TODO: add RAM and NV-RAM settings for NES 2.0 var nesFile = new NesFile(); var submapper = GetSubmapper(mapper); nesFile.Version = (mapper.Number > 255 || submapper != 0) ? NesFile.iNesVersion.NES20 : NesFile.iNesVersion.iNES; nesFile.Mapper = (ushort)mapper.Number; nesFile.Submapper = submapper; nesFile.Mirroring = mirroring; nesFile.PRG = prg.ToArray(); nesFile.CHR = chr.ToArray(); nesFile.Battery = battery; nesFile.Save(fileName); } else { var unifFile = new UnifFile(); unifFile.Version = 4; unifFile.Mapper = mapper.UnifName; if (unifName != null) { unifFile.GameName = unifName; } unifFile.Fields["PRG0"] = prg.ToArray(); if (chr.Count > 0) { unifFile.Fields["CHR0"] = chr.ToArray(); } unifFile.Mirroring = mirroring; unifFile.Battery = battery; if (!string.IsNullOrEmpty(unifAuthor)) { unifFile.DumperName = unifAuthor; } unifFile.DumpingSoftware = "Famicom Dumper by Cluster / https://github.com/ClusterM/famicom-dumper-client"; unifFile.Save(fileName); } Console.WriteLine("OK"); }