static int Main(string[] args) { Console.WriteLine($"Famicom Dumper Client v{Assembly.GetExecutingAssembly().GetName().Version.Major}.{Assembly.GetExecutingAssembly().GetName().Version.Minor}"); Console.WriteLine($" Commit {Properties.Resources.gitCommit} @ https://github.com/ClusterM/famicom-dumper-client"); Console.WriteLine(" (c) Alexey 'Cluster' Avdyukhin / https://clusterrr.com / [email protected]"); Console.WriteLine(); startTime = DateTime.Now; string port = "auto"; string mapper = "0"; string psize = null; string csize = null; string filename = null; bool battery = false; string csFile = null; string[] csArgs = new string[0]; string unifName = null; string unifAuthor = null; bool reset = false; bool silent = true; bool needCheck = false; bool writePBBs = false; List <int> badSectors = new List <int>(); int testCount = -1; uint tcpPort = 26672; bool ignoreBadSectors = false; string remoteHost = null; byte fdsSides = 1; bool fdsUseHeader = true; bool fdsDumpHiddenFiles = false; try { if (args.Length == 0 || args.Contains("help") || args.Contains("--help")) { PrintHelp(); return(0); } string command = args[0].ToLower(); for (int i = 1; i < args.Length; i++) { string param = args[i]; if (param == "-") { csArgs = args.Skip(i + 1).ToArray(); break; } while (param.StartsWith("-") || param.StartsWith("—")) { param = param.Substring(1); } string value = i < args.Length - 1 ? args[i + 1] : ""; switch (param.ToLower()) { case "p": case "port": port = value; i++; break; case "m": case "mapper": mapper = value; i++; break; case "f": case "file": filename = value; i++; break; case "fds-sides": fdsSides = byte.Parse(value); i++; break; case "fds-no-header": fdsUseHeader = false; break; case "fds-dump-hidden": fdsDumpHiddenFiles = true; break; case "csfile": case "cs-file": case "scriptfile": case "script-file": csFile = value; i++; break; case "psize": case "prg-size": psize = value; i++; break; case "csize": case "chr-size": csize = value; i++; break; case "battery": battery = true; break; case "unifname": case "unif-name": unifName = value; i++; break; case "unifauthor": case "unif-author": unifAuthor = value; i++; break; case "reset": reset = true; break; case "sound": silent = false; break; case "check": case "verify": needCheck = true; break; case "lock": writePBBs = true; break; case "testcount": case "test-count": testCount = int.Parse(value); i++; break; case "badsectors": case "bad-sectors": foreach (var v in value.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)) { badSectors.Add(int.Parse(v)); } i++; break; case "tcpport": case "tcp-port": tcpPort = uint.Parse(value); i++; break; case "host": remoteHost = value; i++; break; case "ignorebadsectors": case "ignore-bad-sectors": ignoreBadSectors = true; break; default: Console.WriteLine("Unknown option: " + param); PrintHelp(); return(2); } } if (command == "list-mappers") { ListMappers(); return(0); } FamicomDumperConnection dumper; if (string.IsNullOrEmpty(remoteHost)) { dumper = new FamicomDumperConnection(port); dumper.Open(); } else { BinaryServerFormatterSinkProvider binaryServerFormatterSinkProvider = new BinaryServerFormatterSinkProvider(); BinaryClientFormatterSinkProvider binaryClientFormatterSinkProvider = new BinaryClientFormatterSinkProvider(); binaryServerFormatterSinkProvider.TypeFilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full; var dict = new System.Collections.Hashtable(); dict["name"] = "FamicomDumperClient"; dict["secure"] = false; var channel = new TcpChannel(dict, binaryClientFormatterSinkProvider, binaryServerFormatterSinkProvider); ChannelServices.RegisterChannel(channel, false); dumper = (FamicomDumperConnection)Activator.GetObject(typeof(IFamicomDumperConnection), $"tcp://{remoteHost}:{tcpPort}/dumper"); var lifetime = dumper.GetLifetimeService(); } try { Console.Write("Dumper initialization... "); bool prgInit = dumper.DumperInit(); if (!prgInit) { throw new IOException("Can't init dumper"); } Console.WriteLine("OK"); if (reset) { Reset(dumper); } if (!string.IsNullOrEmpty(csFile)) { CompileAndExecute(csFile, dumper, csArgs); } switch (command) { case "reset": if (!reset) { Reset(dumper); } break; case "list-mappers": ListMappers(); break; case "dump": Dump(dumper, filename ?? "output.nes", mapper, ParseSize(psize), ParseSize(csize), unifName, unifAuthor, battery); break; case "dump-fds": FDS.DumpFDS(dumper, filename ?? "output.fds", fdsSides, fdsDumpHiddenFiles, fdsUseHeader); break; case "read-prg-ram": case "dump-prg-ram": case "dump-sram": ReadPrgRam(dumper, filename ?? "savegame.sav", mapper); break; case "write-fds": if (string.IsNullOrEmpty(filename)) { throw new ArgumentNullException("Please specify ROM filename using --file argument"); } FDS.WriteFDS(dumper, filename, needCheck); break; case "write-prg-ram": case "write-sram": if (string.IsNullOrEmpty(filename)) { throw new ArgumentNullException("Please specify ROM filename using --file argument"); } WritePrgRam(dumper, filename, mapper); break; case "test-prg-ram": case "test-sram": TestPrgRam(dumper, mapper); break; case "test-prg-coolgirl": case "test-prg-ram-coolgirl": case "test-sram-coolgirl": CoolgirlWriter.TestPrgRam(dumper); break; case "test-battery": TestBattery(dumper, mapper); break; case "test-chr-ram": TestChrRam(dumper); break; case "test-chr-coolgirl": case "test-chr-ram-coolgirl": CoolgirlWriter.TestChrRam(dumper, chrSize: ParseSize(csize)); break; case "test-coolgirl": CoolgirlWriter.FullTest(dumper, testCount, chrSize: ParseSize(csize)); break; case "test-bads-coolgirl": CoolgirlWriter.FindBads(dumper, silent); break; case "read-crc-coolgirl": CoolgirlWriter.ReadCrc(dumper); break; case "dump-tiles": DumpTiles(dumper, filename ?? "output.png", mapper, ParseSize(csize)); break; case "write-coolboy": case "write-coolboy-direct": case "write-coolboy-gpio": // for backward compatibility if (string.IsNullOrEmpty(filename)) { throw new ArgumentNullException("Please specify ROM filename using --file argument"); } CoolboyWriter.Write(dumper, filename, badSectors, silent, needCheck, writePBBs, ignoreBadSectors); break; case "write-coolgirl": if (string.IsNullOrEmpty(filename)) { throw new ArgumentNullException("Please specify ROM filename using --file argument"); } CoolgirlWriter.Write(dumper, filename, badSectors, silent, needCheck, writePBBs, ignoreBadSectors); break; case "write-eeprom": if (string.IsNullOrEmpty(filename)) { throw new ArgumentNullException("Please specify ROM filename using --file argument"); } WriteEeprom(dumper, filename); break; case "info-coolboy": CoolboyWriter.PrintFlashInfo(dumper); break; case "info-coolgirl": CoolgirlWriter.PringFlashInfo(dumper); break; case "bootloader": Bootloader(dumper); break; case "script": if (string.IsNullOrEmpty(csFile)) { throw new ArgumentNullException("Please specify C# script using --cs-file argument"); } break; case "server": StartServer(dumper, tcpPort); break; default: Console.WriteLine("Unknown command: " + command); PrintHelp(); return(2); } #if DEBUG var timePassed = DateTime.Now - startTime; if (timePassed.TotalMinutes >= 60) { Console.WriteLine($"Done in {timePassed.Hours}:{timePassed.Minutes:D2}:{timePassed.Seconds:D2}"); } else if (timePassed.TotalSeconds >= 10) { Console.WriteLine($"Done in {timePassed.Minutes:D2}:{timePassed.Seconds:D2}"); } else { Console.WriteLine($"Done in {(int)timePassed.TotalMilliseconds}ms"); } #endif if (!silent) { PlayDoneSound(); } } finally { if (string.IsNullOrEmpty(remoteHost)) { dumper.Dispose(); } } } catch (Exception ex) { Console.WriteLine($"ERROR {ex.GetType()}: " + ex.Message #if DEBUG + ex.StackTrace #endif ); if (!silent) { PlayErrorSound(); } return(1); } return(0); }