namespace Sandbox.Utility; /// /// Parses command line arguments into switches with optional values. /// Supports both +switch and -switch syntax with optional values. /// Example: -console +map "de_dust" -maxplayers 32 /// internal static class CommandLine { private static readonly Dictionary switches = new( StringComparer.OrdinalIgnoreCase ); private static string commandLine = ""; /// /// Returns the full command line string. /// public static string Full => commandLine; /// /// Allows unit tests to override the command line string. /// If null, uses Environment.CommandLine. /// internal static string CommandLineString { get; set; } static CommandLine() { Parse(); } /// /// Parses the command line into a dictionary of switches and values. /// Call this again if CommandLineString is modified. /// public static void Parse() { commandLine = CommandLineString ?? Environment.CommandLine; if ( string.IsNullOrEmpty( commandLine ) ) return; var currentKey = ""; var inQuotes = false; var quoteChar = '"'; var currentValue = new StringBuilder(); var isBuildingKey = false; for ( var i = 0; i < commandLine.Length; i++ ) { var c = commandLine[i]; // Handle quote toggling if ( c == quoteChar ) { inQuotes = !inQuotes; currentValue.Append( c ); continue; } // Check if we can start a new switch (must be at start or after whitespace) var canStartSwitch = i == 0 || char.IsWhiteSpace( commandLine[i - 1] ); // New switch detected ('+' or '-') if ( canStartSwitch && !inQuotes && c is '+' or '-' ) { // Save previous switch if it exists if ( !string.IsNullOrEmpty( currentKey ) ) { switches[currentKey] = currentValue.ToString().Trim(); } // Reset for new switch currentKey = ""; currentValue.Clear(); isBuildingKey = true; continue; } // Handle whitespace if ( !inQuotes && char.IsWhiteSpace( c ) ) { // If we're still building the key, this ends it if ( isBuildingKey ) { isBuildingKey = false; continue; } // Otherwise, preserve spaces in values currentValue.Append( ' ' ); continue; } // Append character to either key or value if ( isBuildingKey ) currentKey += c; else currentValue.Append( c ); } // Save final switch if ( !string.IsNullOrEmpty( currentKey ) ) { switches[currentKey] = currentValue.ToString().Trim(); } } /// /// Checks if a command line switch is present. /// /// Switch name (with or without + or - prefix) /// True if the switch was specified on the command line /// if ( HasSwitch( "-console" ) ) EnableConsole(); public static bool HasSwitch( string strName ) => switches.ContainsKey( strName.Trim( '+', '-' ) ); /// /// Gets the value of a command line switch, or a default if not present. /// /// Switch name (with or without + or - prefix) /// Default value if switch not found /// The switch value or default /// map = GetSwitch( "+map", "de_dust" ); public static string GetSwitch( string strName, string strDefault ) { return switches.GetValueOrDefault( strName.Trim( '+', '-' ), strDefault ); } /// /// Gets the integer value of a command line switch, or a default if not present or invalid. /// /// Switch name (with or without + or - prefix) /// Default value if switch not found or not an integer /// The parsed integer value or default /// maxplayers = GetSwitchInt( "+maxplayers", 32 ); public static int GetSwitchInt( string strName, int iDefault ) { if ( !switches.TryGetValue( strName.Trim( '+', '-' ), out var strValue ) ) return iDefault; return int.TryParse( strValue, out var outval ) ? outval : iDefault; } /// /// Gets all parsed command line switches and their values. /// /// Dictionary of switch names to values (switch prefixes removed) public static Dictionary GetSwitches() { return switches; } }