diff --git a/src/BizHawk.Client.EmuHawk/ArgParser.cs b/src/BizHawk.Client.EmuHawk/ArgParser.cs index f34a3deaff..640469c5f2 100644 --- a/src/BizHawk.Client.EmuHawk/ArgParser.cs +++ b/src/BizHawk.Client.EmuHawk/ArgParser.cs @@ -1,4 +1,6 @@ -using System; +#nullable enable + +using System; using System.Collections.Generic; using System.Linq; using System.IO; @@ -7,188 +9,304 @@ using BizHawk.Client.Common; namespace BizHawk.Client.EmuHawk { - public class ArgParser - //parses command line arguments and adds the values to a class attribute - //default values are null for strings and false for boolean - //the last value will overwrite previously set values - //unrecognized parameters are simply ignored or in the worst case assumed to be a ROM name [cmdRom] + /// + /// Parses command line flags from a string array into various instance fields. + /// + /// + /// If a flag is given multiple times, the last is taken.
+ /// If a flag that isn't recognised is given, it is parsed as a filename. As noted above, the last filename is taken. + ///
+ public static class ArgParser { - public string cmdRom = null; - public string cmdLoadSlot = null; - public string cmdLoadState = null; - public string cmdConfigPath = null; - public string cmdConfigFile = null; - public string cmdMovie = null; - public string cmdDumpType = null; - public string cmdDumpName = null; - public HashSet _currAviWriterFrameList; - public int _autoDumpLength; - public bool _autoCloseOnDump = false; - // chrome is never shown, even in windowed mode - public bool _chromeless = false; - public bool startFullscreen = false; - public string luaScript = null; - public bool luaConsole = false; - public bool printVersion = false; - public int socket_port = 0; - public string socket_ip = null; - public string mmf_filename = null; - public string URL_get = null; - public string URL_post = null; - public bool? audiosync = null; - public HttpCommunication httpCommunication = null; - public SocketServer socketServer = null; - public MemoryMappedFiles memoryMappedFiles = null; - public string openExtToolDll; - /// --socket_ip passed without specifying --socket_port or vice-versa - public void ParseArguments(string[] args, Func takeScreenshotCallback) + public static void ParseArguments(out ParsedCLIFlags parsed, string[] args, Func takeScreenshotCallback) { - for (int i = 0; i < args.Length; i++) + string? cmdLoadSlot = null; + string? cmdLoadState = null; + string? cmdConfigPath = null; + string? cmdConfigFile = null; + string? cmdMovie = null; + string? cmdDumpType = null; + HashSet? currAviWriterFrameList = null; + int? autoDumpLength = null; + bool? printVersion = null; + string? cmdDumpName = null; + bool? autoCloseOnDump = null; + bool? chromeless = null; + bool? startFullscreen = null; + string? luaScript = null; + bool? luaConsole = null; + int? socketPort = null; + string? socketIP = null; + string? mmfFilename = null; + string? urlGet = null; + string? urlPost = null; + bool? audiosync = null; + string? openExtToolDll = null; + string? cmdRom = null; + + for (var i = 0; i < args.Length; i++) { - // For some reason sometimes visual studio will pass this to us on the commandline. it makes no sense. - if (args[i] == ">") + var arg = args[i]; + + if (arg == ">") { - i++; - var stdout = args[i]; + // For some reason sometimes visual studio will pass this to us on the commandline. it makes no sense. + var stdout = args[++i]; Console.SetOut(new StreamWriter(stdout)); continue; } - var arg = args[i].ToLower(); - if (arg.StartsWith("--load-slot=")) + var argDowncased = arg.ToLower(); + if (argDowncased.StartsWith("--load-slot=")) { - cmdLoadSlot = arg.Substring(arg.IndexOf('=') + 1); + cmdLoadSlot = argDowncased.Substring(argDowncased.IndexOf('=') + 1); } - - if (arg.StartsWith("--load-state=")) + else if (argDowncased.StartsWith("--load-state=")) { - cmdLoadState = args[i].Substring(args[i].IndexOf('=') + 1); + cmdLoadState = arg.Substring(arg.IndexOf('=') + 1); } - if (arg.StartsWith("--config=")) + else if (argDowncased.StartsWith("--config=")) { - cmdConfigFile = args[i].Substring(args[i].IndexOf('=') + 1); + cmdConfigFile = arg.Substring(arg.IndexOf('=') + 1); } - else if (arg.StartsWith("--movie=")) + else if (argDowncased.StartsWith("--movie=")) { - cmdMovie = args[i].Substring(args[i].IndexOf('=') + 1); + cmdMovie = arg.Substring(arg.IndexOf('=') + 1); } - else if (arg.StartsWith("--dump-type=")) + else if (argDowncased.StartsWith("--dump-type=")) { - cmdDumpType = arg.Substring(arg.IndexOf('=') + 1); + cmdDumpType = argDowncased.Substring(argDowncased.IndexOf('=') + 1); } - else if (arg.StartsWith("--dump-frames=")) + else if (argDowncased.StartsWith("--dump-frames=")) { - string list = arg.Substring(arg.IndexOf('=') + 1); + string list = argDowncased.Substring(argDowncased.IndexOf('=') + 1); string[] items = list.Split(','); - _currAviWriterFrameList = new HashSet(); + currAviWriterFrameList = new HashSet(); foreach (string item in items) { - _currAviWriterFrameList.Add(int.Parse(item)); + currAviWriterFrameList.Add(int.Parse(item)); } // automatically set dump length to maximum frame - _autoDumpLength = _currAviWriterFrameList.OrderBy(x => x).Last(); + autoDumpLength = currAviWriterFrameList.OrderBy(x => x).Last(); } - else if (arg.StartsWith("--version")) + else if (argDowncased.StartsWith("--version")) { printVersion = true; } - else if (arg.StartsWith("--dump-name=")) + else if (argDowncased.StartsWith("--dump-name=")) { - cmdDumpName = args[i].Substring(args[i].IndexOf('=') + 1); + cmdDumpName = arg.Substring(arg.IndexOf('=') + 1); } - else if (arg.StartsWith("--dump-length=")) + else if (argDowncased.StartsWith("--dump-length=")) { - int.TryParse(arg.Substring(arg.IndexOf('=') + 1), out _autoDumpLength); + int.TryParse(argDowncased.Substring(argDowncased.IndexOf('=') + 1), out var len); + autoDumpLength = len; } - else if (arg.StartsWith("--dump-close")) + else if (argDowncased.StartsWith("--dump-close")) { - _autoCloseOnDump = true; + autoCloseOnDump = true; } - else if (arg.StartsWith("--chromeless")) + else if (argDowncased.StartsWith("--chromeless")) { - _chromeless = true; + // chrome is never shown, even in windowed mode + chromeless = true; } - else if (arg.StartsWith("--fullscreen")) + else if (argDowncased.StartsWith("--fullscreen")) { startFullscreen = true; } - else if (arg.StartsWith("--lua=")) + else if (argDowncased.StartsWith("--lua=")) { - luaScript = args[i].Substring(args[i].IndexOf('=') + 1); + luaScript = arg.Substring(arg.IndexOf('=') + 1); luaConsole = true; } - else if (arg.StartsWith("--luaconsole")) + else if (argDowncased.StartsWith("--luaconsole")) { luaConsole = true; } - else if (arg.StartsWith("--socket_port=")) + else if (argDowncased.StartsWith("--socket_port=")) { - int.TryParse(arg.Substring(arg.IndexOf('=') + 1), out socket_port); + int.TryParse(argDowncased.Substring(argDowncased.IndexOf('=') + 1), out var port); + if (port > 0) socketPort = port; } - else if (arg.StartsWith("--socket_ip=")) + else if (argDowncased.StartsWith("--socket_ip=")) { - socket_ip = arg.Substring(arg.IndexOf('=') + 1); + socketIP = argDowncased.Substring(argDowncased.IndexOf('=') + 1); } - else if (arg.StartsWith("--mmf=")) + else if (argDowncased.StartsWith("--mmf=")) { - mmf_filename = args[i].Substring(args[i].IndexOf('=') + 1); + mmfFilename = arg.Substring(arg.IndexOf('=') + 1); } - else if (arg.StartsWith("--url_get=")) + else if (argDowncased.StartsWith("--url_get=")) { - URL_get = args[i].Substring(args[i].IndexOf('=') + 1); + urlGet = arg.Substring(arg.IndexOf('=') + 1); } - else if (arg.StartsWith("--url_post=")) + else if (argDowncased.StartsWith("--url_post=")) { - URL_post = args[i].Substring(args[i].IndexOf('=') + 1); + urlPost = arg.Substring(arg.IndexOf('=') + 1); } - else if (arg.StartsWith("--audiosync=")) + else if (argDowncased.StartsWith("--audiosync=")) { - audiosync = arg.Substring(arg.IndexOf('=') + 1) == "true"; + audiosync = argDowncased.Substring(argDowncased.IndexOf('=') + 1) == "true"; } - else if (arg.StartsWith("--open-ext-tool-dll=")) + else if (argDowncased.StartsWith("--open-ext-tool-dll=")) { // the first ext. tool from ExternalToolManager.ToolStripMenu which satisfies both of these will be opened: // - available (no load errors, correct system/rom, etc.) // - dll path matches given string; or dll filename matches given string with or without `.dll` - openExtToolDll = args[i].Substring(20); + openExtToolDll = arg.Substring(20); } else { - cmdRom = args[i]; + cmdRom = arg; } } - httpCommunication = URL_get == null && URL_post == null + var httpCommunication = urlGet == null && urlPost == null ? null // don't bother - : new HttpCommunication(takeScreenshotCallback, URL_get, URL_post); - memoryMappedFiles = mmf_filename == null + : new HttpCommunication(takeScreenshotCallback, urlGet, urlPost); + var memoryMappedFiles = mmfFilename == null ? null // don't bother - : new MemoryMappedFiles(takeScreenshotCallback, mmf_filename); - if (socket_ip == null && socket_port <= 0) + : new MemoryMappedFiles(takeScreenshotCallback, mmfFilename); + SocketServer? socketServer; + if (socketIP == null && socketPort == null) { socketServer = null; // don't bother } - else if (socket_ip == null || socket_port <= 0) + else if (socketIP == null || socketPort == null) { throw new ArgParserException("Socket server needs both --socket_ip and --socket_port. Socket server was not started"); } else { - socketServer = new SocketServer(takeScreenshotCallback, socket_ip, socket_port); + socketServer = new SocketServer(takeScreenshotCallback, socketIP, socketPort.Value); } + + parsed = new ParsedCLIFlags( + cmdLoadSlot: cmdLoadSlot, + cmdLoadState: cmdLoadState, + cmdConfigPath: cmdConfigPath, + cmdConfigFile: cmdConfigFile, + cmdMovie: cmdMovie, + cmdDumpType: cmdDumpType, + currAviWriterFrameList: currAviWriterFrameList, + autoDumpLength: autoDumpLength ?? 0, + printVersion: printVersion ?? false, + cmdDumpName: cmdDumpName, + autoCloseOnDump: autoCloseOnDump ?? false, + chromeless: chromeless ?? false, + startFullscreen: startFullscreen ?? false, + luaScript: luaScript, + luaConsole: luaConsole ?? false, + socketServer: socketServer, + memoryMappedFiles: memoryMappedFiles, + httpCommunication: httpCommunication, + audiosync: audiosync, + openExtToolDll: openExtToolDll, + cmdRom: cmdRom + ); } - public static string GetCmdConfigFile(string[] args) + public static string? GetCmdConfigFile(string[] args) { return args.FirstOrDefault(arg => arg.StartsWith("--config=", StringComparison.InvariantCultureIgnoreCase))?.Substring(9); } } - public class ArgParserException : Exception + + public sealed class ArgParserException : Exception { - public ArgParserException(string message) : base(message) + public ArgParserException(string message) : base(message) {} + } + + public /*readonly*/ struct ParsedCLIFlags + { + public readonly string? cmdLoadSlot; + + public readonly string? cmdLoadState; + + public readonly string? cmdConfigPath; + + public readonly string? cmdConfigFile; + + public readonly string? cmdMovie; + + public readonly string? cmdDumpType; + + public readonly HashSet? _currAviWriterFrameList; + + public /*readonly*/ int _autoDumpLength; + + public readonly bool printVersion; + + public readonly string? cmdDumpName; + + public readonly bool _autoCloseOnDump; + + public readonly bool _chromeless; + + public readonly bool startFullscreen; + + public readonly string? luaScript; + + public readonly bool luaConsole; + + public readonly SocketServer? socketServer; + + public readonly MemoryMappedFiles? memoryMappedFiles; + + public readonly HttpCommunication? httpCommunication; + + public readonly bool? audiosync; + + public readonly string? openExtToolDll; + + public readonly string? cmdRom; + + public ParsedCLIFlags(string? cmdLoadSlot, + string? cmdLoadState, + string? cmdConfigPath, + string? cmdConfigFile, + string? cmdMovie, + string? cmdDumpType, + HashSet? currAviWriterFrameList, + int autoDumpLength, + bool printVersion, + string? cmdDumpName, + bool autoCloseOnDump, + bool chromeless, + bool startFullscreen, + string? luaScript, + bool luaConsole, + SocketServer? socketServer, + MemoryMappedFiles? memoryMappedFiles, + HttpCommunication? httpCommunication, + bool? audiosync, + string? openExtToolDll, + string? cmdRom) { + this.cmdLoadSlot = cmdLoadSlot; + this.cmdLoadState = cmdLoadState; + this.cmdConfigPath = cmdConfigPath; + this.cmdConfigFile = cmdConfigFile; + this.cmdMovie = cmdMovie; + this.cmdDumpType = cmdDumpType; + _currAviWriterFrameList = currAviWriterFrameList; + _autoDumpLength = autoDumpLength; + this.printVersion = printVersion; + this.cmdDumpName = cmdDumpName; + _autoCloseOnDump = autoCloseOnDump; + _chromeless = chromeless; + this.startFullscreen = startFullscreen; + this.luaScript = luaScript; + this.luaConsole = luaConsole; + this.socketServer = socketServer; + this.memoryMappedFiles = memoryMappedFiles; + this.httpCommunication = httpCommunication; + this.audiosync = audiosync; + this.openExtToolDll = openExtToolDll; + this.cmdRom = cmdRom; } } } diff --git a/src/BizHawk.Client.EmuHawk/MainForm.cs b/src/BizHawk.Client.EmuHawk/MainForm.cs index 26db071523..bdf9b84454 100644 --- a/src/BizHawk.Client.EmuHawk/MainForm.cs +++ b/src/BizHawk.Client.EmuHawk/MainForm.cs @@ -318,7 +318,8 @@ namespace BizHawk.Client.EmuHawk try { - _argParser.ParseArguments( + ArgParser.ParseArguments( + out _argParser, args, () => (byte[]) new ImageConverter().ConvertTo(MakeScreenshotImage().ToSysdrawingBitmap(), typeof(byte[])) ); @@ -1523,7 +1524,7 @@ namespace BizHawk.Client.EmuHawk private int _lastOpenRomFilter; - private readonly ArgParser _argParser = new ArgParser(); + private ParsedCLIFlags _argParser; // Resources private Bitmap _statusBarDiskLightOnImage;