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