Ejemplo n.º 1
0
        private static void CheckRAMAdapter(IFamicomDumperConnectionExt dumper)
        {
            // Just simple test that RAM adapter is connected
            bool ramAdapterPresent = true;

            dumper.WriteCpu(0x4023, 0x01);       // enable disk registers
            dumper.WriteCpu(0x4026, 0x00);
            dumper.WriteCpu(0x4025, 0b00100110); // reset
            dumper.WriteCpu(0x0000, 0xFF);       // to prevent open bus read
            var ext = dumper.ReadCpu(0x4033);

            if (ext != 0x00)
            {
                ramAdapterPresent = false;
            }
            dumper.WriteCpu(0x4026, 0xFF);
            dumper.WriteCpu(0x0000, 0x00); // to prevent open bus read
            ext = dumper.ReadCpu(0x4033);
            if ((ext & 0x7F) != 0x7F)
            {
                ramAdapterPresent = false;
            }
            if (!ramAdapterPresent)
            {
                throw new IOException("RAM adapter IO error, is it connected?");
            }
        }
        public static void PrintFlashInfo(IFamicomDumperConnectionExt dumper)
        {
            Program.Reset(dumper);
            dumper.WriteCpu(0x5007, 0x04); // enable PRG write
            dumper.WriteCpu(0x5002, 0xFE); // mask = 32K
            var cfi = FlashHelper.GetCFIInfo(dumper);

            FlashHelper.PrintCFIInfo(cfi);
            FlashHelper.LockBitsCheckPrint(dumper);
            FlashHelper.PPBLockBitCheckPrint(dumper);
        }
        public static void PPBClear(IFamicomDumperConnectionExt dumper)
        {
            // enable PRG write
            dumper.WriteCpu(0x5007, 0x04);
            // mask = 32K
            dumper.WriteCpu(0x5002, 0xFE);
            // Sector 0
            dumper.WriteCpu(0x5000, 0);
            dumper.WriteCpu(0x5001, 0);

            FlashHelper.PPBClear(dumper);
        }
Ejemplo n.º 4
0
        public static void DumpFDS(IFamicomDumperConnectionExt dumper, string fileName, byte sides = 1, bool dumpHiddenFiles = true, bool useHeader = true)
        {
            if (dumper.ProtocolVersion < 3)
            {
                throw new NotSupportedException("Dumper firmware version is too old, update it to read/write FDS cards");
            }
            CheckRAMAdapter(dumper);

            var sideImages = new List <FdsDiskSide>();

            for (int side = 1; side <= sides; side++)
            {
                var driveStatus = dumper.ReadCpu(0x4032);
                if ((driveStatus & 1) != 0)
                {
                    Console.Write($"Please set disk card, side #{side}... ");
                    while ((driveStatus & 1) != 0)
                    {
                        Thread.Sleep(100);
                        driveStatus = dumper.ReadCpu(0x4032);
                    }
                    Console.WriteLine("OK");
                }
                var sideImage = DumpFDSSide(dumper, dumpHiddenFiles, printDiskInfo: true);
                sideImages.Add(sideImage);

                if (side < sides)
                {
                    driveStatus = dumper.ReadCpu(0x4032);
                    if ((driveStatus & 1) == 0)
                    {
                        Console.Write($"Please remove disk card... ");
                        while ((driveStatus & 1) == 0)
                        {
                            Thread.Sleep(100);
                            driveStatus = dumper.ReadCpu(0x4032);
                        }
                        Console.WriteLine("OK");
                    }
                }
            }
            Console.Write($"Saving to {fileName}... ");
            var fdsImage = new FdsFile(sideImages);

            fdsImage.Save(fileName, useHeader);
            Console.WriteLine("OK");
        }
Ejemplo n.º 5
0
        public static void PPBClear(IFamicomDumperConnectionExt dumper, ushort coolboyReg)
        {
            // Sector 0
            int  bank = 0;
            byte r0   = (byte)(((bank >> 3) & 0x07)          // 5, 4, 3 bits
                               | (((bank >> 9) & 0x03) << 4) // 10, 9 bits
                               | (1 << 6));                  // resets 4th mask bit
            byte r1 = (byte)((((bank >> 7) & 0x03) << 2)     // 8, 7
                             | (((bank >> 6) & 1) << 4)      // 6
                             | (1 << 7));                    // resets 5th mask bit
            byte r2 = 0;
            byte r3 = (byte)((1 << 4)                        // NROM mode
                             | ((bank & 7) << 1));           // 2, 1, 0 bits

            dumper.WriteCpu(coolboyReg, r0, r1, r2, r3);

            FlashHelper.PPBClear(dumper);
        }
Ejemplo n.º 6
0
        public static void StartServer(IFamicomDumperConnectionExt dumper, string url)
        {
            FamicomDumperService.dumper = dumper;
            Host.CreateDefaultBuilder()
            .ConfigureWebHostDefaults(webBuilder =>
            {
#if !DEBUG
                webBuilder.ConfigureLogging((context, logging) => logging.ClearProviders());
#endif
                webBuilder.UseUrls(url);
                webBuilder.ConfigureKestrel((options) =>
                {
                    options.ConfigureEndpointDefaults(lo => lo.Protocols = HttpProtocols.Http2);
                });
                webBuilder.UseStartup <GrpcStartup>();
            })
            .Build()
            .Run();
        }
Ejemplo n.º 7
0
        public static byte DetectVersion(IFamicomDumperConnectionExt dumper)
        {
            byte version;

            Console.Write("Detecting COOLBOY version... ");
            // 0th CHR bank using both methods
            dumper.WriteCpu(0x5000, 0, 0, 0, 0x10);
            dumper.WriteCpu(0x6000, 0, 0, 0, 0x10);
            // Writing 0
            dumper.WritePpu(0x0000, 0);
            // First CHR bank using both methods
            dumper.WriteCpu(0x5000, 0, 0, 1, 0x10);
            dumper.WriteCpu(0x6000, 0, 0, 1, 0x10);
            // Writing 1
            dumper.WritePpu(0x0000, 1);
            // 0th bank using first method
            dumper.WriteCpu(0x6000, 0, 0, 0, 0x10);
            byte v6000 = dumper.ReadPpu(0x0000);

            // return
            dumper.WriteCpu(0x6000, 0, 0, 1, 0x10);
            // 0th bank using second method
            dumper.WriteCpu(0x5000, 0, 0, 0, 0x10);
            byte v5000 = dumper.ReadPpu(0x0000);

            if (v6000 == 0 && v5000 == 1)
            {
                version = 1;
            }
            else if (v6000 == 1 && v5000 == 0)
            {
                version = 2;
            }
            else
            {
                throw new IOException("Can't detect COOLBOY version");
            }
            Console.WriteLine($"Version: {version}");
            return(version);
        }
Ejemplo n.º 8
0
        public static void PrintFlashInfo(IFamicomDumperConnectionExt dumper)
        {
            Program.Reset(dumper);
            var  version    = DetectVersion(dumper);
            var  CoolboyReg = (UInt16)(version == 2 ? 0x5000 : 0x6000);
            int  bank       = 0;
            byte r0         = (byte)(((bank >> 3) & 0x07)          // 5, 4, 3 bits
                                     | (((bank >> 9) & 0x03) << 4) // 10, 9 bits
                                     | (1 << 6));                  // resets 4th mask bit
            byte r1 = (byte)((((bank >> 7) & 0x03) << 2)           // 8, 7
                             | (((bank >> 6) & 1) << 4)            // 6
                             | (1 << 7));                          // resets 5th mask bit
            byte r2 = 0;
            byte r3 = (byte)((1 << 4)                              // NROM mode
                             | ((bank & 7) << 1));                 // 2, 1, 0 bits

            dumper.WriteCpu(CoolboyReg, r0, r1, r2, r3);
            var cfi = FlashHelper.GetCFIInfo(dumper);

            FlashHelper.PrintCFIInfo(cfi);
            FlashHelper.LockBitsCheckPrint(dumper);
            FlashHelper.PPBLockBitCheckPrint(dumper);
        }
Ejemplo n.º 9
0
        private static FdsDiskSide DumpFDSSide(IFamicomDumperConnectionExt dumper, bool dumpHiddenFiles = true, bool printDiskInfo = false)
        {
            if (dumper.ProtocolVersion < 3)
            {
                throw new NotSupportedException("Dumper firmware version is too old, update it to read/write FDS cards");
            }
            var oldTimeout = dumper.Timeout;

            try
            {
                dumper.Timeout = 30000;
                CheckRAMAdapter(dumper);

                IEnumerable <IFdsBlock> blocks;
                if (dumper.MaxReadPacketSize != ushort.MaxValue)
                {
                    // Reading block by block
                    blocks = DumpSlow(dumper, dumpHiddenFiles, printDiskInfo);
                }
                else
                {
                    // Reading the whole disk at once
                    blocks = DumpFast(dumper, dumpHiddenFiles, printDiskInfo);
                }
                var sideImage = new FdsDiskSide(blocks);
                if (sideImage.Files.Count < sideImage.FileAmount)
                {
                    throw new IOException($"Invalid file count: {sideImage.Files.Count} < {sideImage.FileAmount}");
                }
                return(sideImage);
            }
            finally
            {
                dumper.Timeout = oldTimeout;
            }
        }
Ejemplo n.º 10
0
        public static void Write(IFamicomDumperConnectionExt dumper, string fileName, IEnumerable <int> badSectors, bool silent, bool needCheck = false, bool writePBBs = false, bool ignoreBadSectors = false)
        {
            byte[] PRG;
            if (Path.GetExtension(fileName).ToLower() == ".bin")
            {
                PRG = File.ReadAllBytes(fileName);
            }
            else
            {
                try
                {
                    var nesFile = new NesFile(fileName);
                    PRG = nesFile.PRG.ToArray();
                }
                catch
                {
                    var nesFile = new UnifFile(fileName);
                    PRG = nesFile.Fields["PRG0"];
                }
            }

            int banks = PRG.Length / BANK_SIZE;

            Program.Reset(dumper);
            var version    = DetectVersion(dumper);
            var coolboyReg = (ushort)(version == 2 ? 0x5000 : 0x6000);

            FlashHelper.ResetFlash(dumper);
            var cfi = FlashHelper.GetCFIInfo(dumper);

            Console.WriteLine($"Device size: {cfi.DeviceSize / 1024 / 1024} MByte / {cfi.DeviceSize / 1024 / 1024 * 8} Mbit");
            Console.WriteLine($"Maximum number of bytes in multi-byte program: {cfi.MaximumNumberOfBytesInMultiProgram}");
            if (dumper.ProtocolVersion >= 3)
            {
                dumper.SetMaximumNumberOfBytesInMultiProgram(cfi.MaximumNumberOfBytesInMultiProgram);
            }
            FlashHelper.LockBitsCheckPrint(dumper);
            if (PRG.Length > cfi.DeviceSize)
            {
                throw new ArgumentOutOfRangeException("PRG.Length", "This ROM is too big for this cartridge");
            }
            try
            {
                PPBClear(dumper, coolboyReg);
            }
            catch (Exception ex)
            {
                if (!silent)
                {
                    Program.PlayErrorSound();
                }
                Console.WriteLine($"ERROR! {ex.Message}. Lets continue anyway.");
            }

            var writeStartTime    = DateTime.Now;
            var lastSectorTime    = DateTime.Now;
            var timeEstimated     = new TimeSpan();
            int totalErrorCount   = 0;
            int currentErrorCount = 0;
            var newBadSectorsList = new List <int>(badSectors);

            for (int bank = 0; bank < banks; bank++)
            {
                while (badSectors.Contains(bank / 8) || newBadSectorsList.Contains(bank / 8))
                {
                    bank += 8;                                                                           // bad sector :(
                }
                try
                {
                    byte r0 = (byte)(((bank >> 3) & 0x07)          // 5, 4, 3 bits
                                     | (((bank >> 9) & 0x03) << 4) // 10, 9 bits
                                     | (1 << 6));                  // resets 4th mask bit
                    byte r1 = (byte)((((bank >> 7) & 0x03) << 2)   // 8, 7
                                     | (((bank >> 6) & 1) << 4)    // 6
                                     | (1 << 7));                  // resets 5th mask bit
                    byte r2 = 0;
                    byte r3 = (byte)((1 << 4)                      // NROM mode
                                     | ((bank & 7) << 1));         // 2, 1, 0 bits
                    dumper.WriteCpu(coolboyReg, r0, r1, r2, r3);

                    var data = new byte[BANK_SIZE];
                    int pos  = bank * BANK_SIZE;
                    if (pos % (128 * 1024) == 0)
                    {
                        timeEstimated  = new TimeSpan((DateTime.Now - lastSectorTime).Ticks * (banks - bank) / 8);
                        timeEstimated  = timeEstimated.Add(DateTime.Now - writeStartTime);
                        lastSectorTime = DateTime.Now;
                        Console.Write($"Erasing sector #{bank / 8}... ");
                        dumper.EraseFlashSector();
                        Console.WriteLine("OK");
                    }
                    Array.Copy(PRG, pos, data, 0, data.Length);
                    var timePassed = DateTime.Now - writeStartTime;
                    Console.Write($"Writing bank #{bank}/{banks} ({100 * bank / banks}%, {timePassed.Hours:D2}:{timePassed.Minutes:D2}:{timePassed.Seconds:D2}/{timeEstimated.Hours:D2}:{timeEstimated.Minutes:D2}:{timeEstimated.Seconds:D2})... ");
                    dumper.WriteFlash(0x0000, data);
                    Console.WriteLine("OK");
                    if ((bank % 8 == 7) || (bank == banks - 1)) // After last bank in sector
                    {
                        if (writePBBs)
                        {
                            FlashHelper.PPBSet(dumper);
                        }
                        currentErrorCount = 0;
                    }
                }
                catch (Exception ex)
                {
                    totalErrorCount++;
                    currentErrorCount++;
                    Console.WriteLine($"Error {ex.GetType()}: {ex.Message}");
                    if (!silent)
                    {
                        Program.PlayErrorSound();
                    }
                    if (currentErrorCount >= 5)
                    {
                        if (!ignoreBadSectors)
                        {
                            throw;
                        }
                        else
                        {
                            newBadSectorsList.Add(bank / 8);
                            currentErrorCount = 0;
                            Console.WriteLine($"Lets skip sector #{bank / 8}");
                        }
                    }
                    else
                    {
                        Console.WriteLine("Lets try again");
                    }
                    bank = (bank & ~7) - 1;
                    Program.Reset(dumper);
                    FlashHelper.ResetFlash(dumper);
                    continue;
                }
            }

            if (totalErrorCount > 0)
            {
                Console.WriteLine($"Write error count: {totalErrorCount}");
            }
            if (newBadSectorsList.Any())
            {
                Console.WriteLine($"Can't write sectors: {string.Join(", ", newBadSectorsList.OrderBy(s => s))}");
            }

            var wrongCrcSectorsList = new List <int>();

            if (needCheck)
            {
                Console.WriteLine("Starting verification process");
                Program.Reset(dumper);

                var readStartTime = DateTime.Now;
                lastSectorTime = DateTime.Now;
                timeEstimated  = new TimeSpan();
                for (int bank = 0; bank < banks; bank++)
                {
                    while (badSectors.Contains(bank / 8))
                    {
                        bank += 8;                                 // bad sector :(
                    }
                    byte r0 = (byte)(((bank >> 3) & 0x07)          // 5, 4, 3 bits
                                     | (((bank >> 9) & 0x03) << 4) // 10, 9 bits
                                     | (1 << 6));                  // resets 4th mask bit
                    byte r1 = (byte)((((bank >> 7) & 0x03) << 2)   // 8, 7
                                     | (((bank >> 6) & 1) << 4)    // 6
                                     | (1 << 7));                  // resets 5th mask bit
                    byte r2 = 0;
                    byte r3 = (byte)((1 << 4)                      // NROM mode
                                     | ((bank & 7) << 1));         // 2, 1, 0 bits
                    dumper.WriteCpu(coolboyReg, r0, r1, r2, r3);

                    int pos = bank * BANK_SIZE;
                    if (pos % (128 * 1024) == 0)
                    {
                        timeEstimated  = new TimeSpan((DateTime.Now - lastSectorTime).Ticks * (banks - bank) / 8);
                        timeEstimated  = timeEstimated.Add(DateTime.Now - readStartTime);
                        lastSectorTime = DateTime.Now;
                    }
                    ushort crc        = Crc16Calculator.CalculateCRC16(PRG, pos, BANK_SIZE);
                    var    timePassed = DateTime.Now - readStartTime;
                    Console.Write($"Reading CRC of bank #{bank}/{banks} ({100 * bank / banks}%, {timePassed.Hours:D2}:{timePassed.Minutes:D2}:{timePassed.Seconds:D2}/{timeEstimated.Hours:D2}:{timeEstimated.Minutes:D2}:{timeEstimated.Seconds:D2})... ");
                    var crcr = dumper.ReadCpuCrc(0x8000, BANK_SIZE);
                    if (crcr != crc)
                    {
                        Console.WriteLine($"Verification failed: {crcr:X4} != {crc:X4}");
                        if (!silent)
                        {
                            Program.PlayErrorSound();
                        }
                        wrongCrcSectorsList.Add(bank / 8);
                    }
                    else
                    {
                        Console.WriteLine($"OK (CRC = {crcr:X4})");
                    }
                }
                if (totalErrorCount > 0)
                {
                    Console.WriteLine($"Write error count: {totalErrorCount}");
                }
                if (newBadSectorsList.Any())
                {
                    Console.WriteLine($"Can't write sectors: {string.Join(", ", newBadSectorsList.OrderBy(s => s))}");
                }
                if (wrongCrcSectorsList.Any())
                {
                    Console.WriteLine($"Sectors with wrong CRC: {string.Join(", ", wrongCrcSectorsList.Distinct().OrderBy(s => s))}");
                }
            }

            if (newBadSectorsList.Any() || wrongCrcSectorsList.Any())
            {
                throw new IOException("Cartridge is not writed correctly");
            }
        }
Ejemplo n.º 11
0
        private static IFdsBlock[] DumpFast(IFamicomDumperConnectionExt dumper, bool dumpHiddenFiles = false, bool printDiskInfo = false)
        {
            Console.Write($"Reading disk... ");
            var blocks = dumper.ReadFdsBlocks().ToArray();

            Console.WriteLine($"Done");
            List <IFdsBlock> result = new();

            if (blocks.Length == 0)
            {
                throw new IOException("Invalid disk info block");
            }
            if (!blocks[0].CrcOk)
            {
                throw new IOException($"Invalid CRC on disk info block");
            }
            var fdsBlockDiskInfo = FdsBlockDiskInfo.FromBytes(blocks[0].Data);

            if (!fdsBlockDiskInfo.IsValid)
            {
                throw new IOException($"Invalid disk info block type");
            }
            if (printDiskInfo)
            {
                PrintDiskHeaderInfo(fdsBlockDiskInfo);
            }
            result.Add(fdsBlockDiskInfo);
            if (blocks.Length == 1)
            {
                throw new IOException("Invalid file amount block");
            }
            if (!blocks[1].CrcOk)
            {
                throw new IOException($"Invalid CRC on file amount block");
            }
            var fdsBlockFileAmount = FdsBlockFileAmount.FromBytes(blocks[1].Data);

            if (!fdsBlockFileAmount.IsValid)
            {
                throw new IOException($"Invalid file amount block type");
            }
            var  fileAmount         = fdsBlockFileAmount.FileAmount;
            byte visibleBlockAmount = (byte)(2 + fileAmount * 2);

            if (printDiskInfo)
            {
                Console.WriteLine($"Number of non-hidden files: {fileAmount}");
            }
            result.Add(fdsBlockFileAmount);

            // Check files and print info
            for (int blockNumber = 2; blockNumber < (dumpHiddenFiles ? blocks.Length : visibleBlockAmount); blockNumber++)
            {
                if (!blocks[blockNumber].CrcOk)
                {
                    if (blocks.Length < visibleBlockAmount)
                    {
                        throw new IOException($"Invalid CRC on block #{blockNumber} (file #{(blockNumber - 2) / 2})");
                    }
                    else
                    {
                        if (printDiskInfo)
                        {
                            Console.WriteLine($"Invalid CRC on block #{blockNumber}, it's not hidden file, aboritng");
                        }
                        break;
                    }
                }
                IFdsBlock block = (blockNumber % 2 == 0)
                    ? FdsBlockFileHeader.FromBytes(blocks[blockNumber].Data)
                    : FdsBlockFileData.FromBytes(blocks[blockNumber].Data);
                if (!block.IsValid)
                {
                    if (blocks.Length < visibleBlockAmount)
                    {
                        throw new IOException($"Invalid block #{blockNumber} (file #{(blockNumber - 2) / 2}) type");
                    }
                    else
                    {
                        break;
                    }
                }
                if (block is FdsBlockFileHeader && printDiskInfo)
                {
                    Console.WriteLine($"File #{(blockNumber - 2) / 2}/{fileAmount}:");
                    PrintFileHeaderInfo(block as FdsBlockFileHeader);
                }
                result.Add(block);
            }
            if (blocks.Length < visibleBlockAmount)
            {
                throw new IOException($"Only {(blocks.Length - 2) / 2} of {fileAmount} valid files received");
            }
            if (dumpHiddenFiles && printDiskInfo)
            {
                Console.WriteLine($"Number of hidden files: {(blocks.Length - 2) / 2 - fileAmount}");
            }
            return(result.ToArray());
        }
Ejemplo n.º 12
0
        private static IEnumerable <IFdsBlock> DumpSlow(IFamicomDumperConnectionExt dumper, bool dumpHiddenFiles = false, bool printDiskInfo = false)
        {
            var  result             = new List <IFdsBlock>();
            byte blockNumber        = 0;
            byte fileAmount         = 0;
            byte visibleBlockAmount = 0;

            while (true)
            {
                switch (blockNumber)
                {
                case 0:
                    Console.Write("Reading block #0 (disk info block)... ");
                    break;

                case 1:
                    Console.Write("Reading block #1 (file amount block)... ");
                    break;

                default:
                    if ((blockNumber % 2) == 0)
                    {
                        Console.Write($"Reading block #{blockNumber} (file #{(blockNumber - 2) / 2}/{(result[1] as FdsBlockFileAmount).FileAmount} header block)... ");
                    }
                    else
                    {
                        Console.Write($"Reading block #{blockNumber} (file #{(blockNumber - 2) / 2}/{(result[1] as FdsBlockFileAmount).FileAmount} data block)... ");
                    }
                    break;
                }
                var fdsData = dumper.ReadFdsBlocks(blockNumber, 1);
                if (fdsData.Length == 0)
                {
                    throw new IOException($"Can't read block #{blockNumber}");
                }
                if (!fdsData[0].CrcOk)
                {
                    switch (blockNumber)
                    {
                    case 0:
                        throw new IOException($"Invalid CRC on disk info block");

                    case 1:
                        throw new IOException($"Invalid CRC on file amount block");
                    }
                    // Fatal error if bad CRC on non-hidden file
                    if (result.Count < visibleBlockAmount)
                    {
                        throw new IOException($"Invalid CRC on block");
                    }
                    else
                    {
                        break;
                    }
                }
                IFdsBlock block;
                switch (blockNumber)
                {
                case 0:
                    block = FdsBlockDiskInfo.FromBytes(fdsData[0].Data);
                    break;

                case 1:
                    block              = FdsBlockFileAmount.FromBytes(fdsData[0].Data);
                    fileAmount         = (block as FdsBlockFileAmount).FileAmount;
                    visibleBlockAmount = (byte)(2 + fileAmount * 2);
                    break;

                default:
                    if ((blockNumber % 2) == 0)
                    {
                        block = FdsBlockFileHeader.FromBytes(fdsData[0].Data);
                    }
                    else
                    {
                        block = FdsBlockFileData.FromBytes(fdsData[0].Data);
                    }
                    break;
                }
                if (!block.IsValid)
                {
                    switch (block.GetType().Name)
                    {
                    case nameof(FdsBlockDiskInfo):
                        throw new IOException($"Invalid disk info block");

                    case nameof(FdsBlockFileAmount):
                        throw new IOException($"Invalid file amount block");
                    }
                    // Fatal error if bad block ID on non-hidden file
                    if (result.Count < visibleBlockAmount)
                    {
                        throw new IOException($"Invalid block");
                    }
                    else
                    {
                        break;
                    }
                }
                result.Add(block);
                Console.WriteLine($"OK");

                // Some info
                if (printDiskInfo)
                {
                    switch (blockNumber)
                    {
                    case 0:
                        PrintDiskHeaderInfo(block as FdsBlockDiskInfo);
                        break;

                    case 1:
                        Console.WriteLine($"Number of non-hidden files: {fileAmount}");
                        break;

                    default:
                        if ((blockNumber % 2) == 0)
                        {
                            Console.WriteLine($"File #{(blockNumber - 2) / 2}:");
                            PrintFileHeaderInfo(block as FdsBlockFileHeader);
                        }
                        break;
                    }
                }
                // Abort if last file dumped
                if (!dumpHiddenFiles && (result.Count >= 2) && (result.Count >= visibleBlockAmount))
                {
                    break;
                }
                blockNumber++;
            }
            if (dumpHiddenFiles)
            {
                Console.WriteLine($"Number of hidden files: {(result.Count - 2) / 2 - (result[1] as FdsBlockFileAmount).FileAmount}");
            }
            return(result);
        }
Ejemplo n.º 13
0
        public static void WriteFDS(IFamicomDumperConnectionExt dumper, string fileName, bool needCheck = false)
        {
            if (dumper.ProtocolVersion < 3)
            {
                throw new NotSupportedException("Dumper firmware version is too old, update it to read/write FDS cards");
            }
            var oldTimeout = dumper.Timeout;

            try
            {
                dumper.Timeout = 30000;
                //CheckRAMAdapter(dumper);

                var rom = new FdsFile(fileName);

                for (int sideNumber = 0; sideNumber < rom.Sides.Count; sideNumber++)
                {
                    var driveStatus = dumper.ReadCpu(0x4032);
                    if ((driveStatus & 1) != 0)
                    {
                        Console.Write($"Please set disk card, side #{sideNumber + 1}... ");
                        while ((driveStatus & 1) != 0)
                        {
                            Thread.Sleep(100);
                            driveStatus = dumper.ReadCpu(0x4032);
                        }
                        Console.WriteLine("OK");
                    }

                    PrintDiskHeaderInfo(rom.Sides[sideNumber].DiskInfoBlock);
                    Console.WriteLine($"Number of non-hidden files: {rom.Sides[sideNumber].FileAmount}");
                    Console.WriteLine($"Number of hidden files: {rom.Sides[sideNumber].Files.Count - rom.Sides[sideNumber].FileAmount}");
                    var blocks = rom.Sides[sideNumber].GetBlocks().ToArray();
                    Console.WriteLine($"Total blocks to write: {blocks.Length}");
                    byte blocksWrited = 0;
                    while (blocksWrited < blocks.Length)
                    {
                        uint totalSize     = 1;
                        var  blockIDs      = new List <byte>();
                        var  blocksToWrite = new List <IFdsBlock>();

                        for (byte i = blocksWrited; i < blocks.Length; i++)
                        {
                            if (totalSize + blocks[i].Length + 3 <= dumper.MaxWritePacketSize)
                            {
                                blocksToWrite.Add(blocks[i]);
                                blockIDs.Add(i);
                                totalSize += blocks[i].Length + 3;
                            }
                            else
                            {
                                break;
                            }
                        }
                        if (!blocksToWrite.Any())
                        {
                            throw new OutOfMemoryException("Dumper has not enoght memory to write such big block");
                        }
                        Console.Write($"Writing block(s): {string.Join(", ", blockIDs)}... ");
                        dumper.WriteFdsBlocks(blockIDs.ToArray(), blocksToWrite.Select(b => b.ToBytes()).ToArray());
                        Console.WriteLine("OK");
                        blocksWrited += (byte)blocksToWrite.Count;
                    }

                    if (needCheck)
                    {
                        Console.WriteLine("Starting verification process");
                        var hiddenFiles = rom.Sides[sideNumber].Files.Count > rom.Sides[sideNumber].FileAmount;
                        var sideImage   = DumpFDSSide(dumper, dumpHiddenFiles: hiddenFiles, printDiskInfo: false);
                        if (!sideImage.DiskInfoBlock.Equals(rom.Sides[sideNumber].DiskInfoBlock))
                        {
                            throw new IOException("Disk info block verification failed");
                        }
                        if (!sideImage.FileAmount.Equals(rom.Sides[sideNumber].FileAmount))
                        {
                            throw new IOException("File amount block verification failed");
                        }
                        if (sideImage.Files.Count < rom.Sides[sideNumber].Files.Count)
                        {
                            throw new IOException($"Invalid file count: {sideImage.Files.Count} < {rom.Sides[sideNumber].Files.Count}");
                        }
                        for (int f = 0; f < rom.Sides[sideNumber].Files.Count; f++)
                        {
                            if (!sideImage.Files[f].HeaderBlock.Equals(rom.Sides[sideNumber].Files[f].HeaderBlock))
                            {
                                throw new IOException($"File #{f} header block verification failed");
                            }
                            if (!sideImage.Files[f].DataBlock.Equals(rom.Sides[sideNumber].Files[f].DataBlock))
                            {
                                throw new IOException($"File #{f} data block verification failed");
                            }
                        }
                        Console.WriteLine("Verification successful.");
                    }

                    if (sideNumber + 1 < rom.Sides.Count)
                    {
                        driveStatus = dumper.ReadCpu(0x4032);
                        if ((driveStatus & 1) == 0)
                        {
                            Console.Write($"Please remove disk card... ");
                            while ((driveStatus & 1) == 0)
                            {
                                Thread.Sleep(100);
                                driveStatus = dumper.ReadCpu(0x4032);
                            }
                            Console.WriteLine("OK");
                        }
                    }
                }
            }
            finally
            {
                dumper.Timeout = oldTimeout;
            }
        }