Files
MPF/MPF.ExecutionContexts/BaseExecutionContext.cs

1154 lines
45 KiB
C#
Raw Permalink Normal View History

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
2024-11-16 01:23:30 -05:00
using System.Text.RegularExpressions;
2023-09-05 00:08:09 -04:00
using SabreTools.RedumpLib.Data;
2024-05-23 15:40:12 -04:00
namespace MPF.ExecutionContexts
{
2024-05-21 13:37:09 -04:00
public abstract class BaseExecutionContext
{
#region Generic Dumping Information
/// <summary>
/// Base command to run
/// </summary>
2023-10-07 01:02:21 -04:00
public string? BaseCommand { get; set; }
/// <summary>
/// Set of flags to pass to the executable
/// </summary>
2023-11-14 23:40:41 -05:00
protected Dictionary<string, bool?> flags = [];
2024-11-16 01:07:47 -05:00
protected internal List<string> Keys => [.. flags.Keys];
/// <summary>
/// Safe access to currently set flags
/// </summary>
public bool? this[string key]
{
get
{
2023-11-20 13:24:17 -05:00
if (flags.TryGetValue(key, out bool? val))
return val;
return null;
}
set
{
flags[key] = value;
}
}
/// <summary>
/// Process to track external program
/// </summary>
2023-10-07 01:02:21 -04:00
private Process? process;
#endregion
#region Virtual Dumping Information
/// <summary>
/// Command to flag support mappings
/// </summary>
2023-10-07 01:02:21 -04:00
public Dictionary<string, List<string>>? CommandSupport => GetCommandSupport();
/// <summary>
/// Input path for operations
/// </summary>
2023-10-07 01:02:21 -04:00
public virtual string? InputPath => null;
/// <summary>
/// Output path for operations
/// </summary>
/// <returns>String representing the path, null on error</returns>
2023-10-07 01:02:21 -04:00
public virtual string? OutputPath => null;
/// <summary>
/// Get the processing speed from the implementation
/// </summary>
public virtual int? Speed { get; set; } = null;
#endregion
#region Metadata
/// <summary>
/// Path to the executable
/// </summary>
2023-10-07 01:02:21 -04:00
public string? ExecutablePath { get; set; }
/// <summary>
/// Currently represented system
/// </summary>
public RedumpSystem? RedumpSystem { get; set; }
/// <summary>
/// Currently represented media type
/// </summary>
public MediaType? MediaType { get; set; }
#endregion
/// <summary>
/// Populate a Parameters object from a param string
/// </summary>
/// <param name="parameters">String possibly representing a set of parameters</param>
2024-05-21 13:37:09 -04:00
public BaseExecutionContext(string? parameters)
{
// If any parameters are not valid, wipe out everything
if (!ValidateAndSetParameters(parameters))
ResetValues();
}
/// <summary>
/// Generate parameters based on a set of known inputs
/// </summary>
2021-08-18 22:13:38 -07:00
/// <param name="system">RedumpSystem value to use</param>
/// <param name="type">MediaType value to use</param>
2023-10-18 01:47:42 -04:00
/// <param name="drivePath">Drive path to use</param>
/// <param name="filename">Filename to use</param>
/// <param name="driveSpeed">Drive speed to use</param>
/// <param name="options">Dictionary object containing all settings that may be used for setting parameters</param>
public BaseExecutionContext(RedumpSystem? system,
MediaType? type,
string? drivePath,
string filename,
int? driveSpeed,
Dictionary<string, string?> options)
{
RedumpSystem = system;
MediaType = type;
2023-10-18 01:47:42 -04:00
SetDefaultParameters(drivePath, filename, driveSpeed, options);
}
#region Abstract Methods
/// <summary>
/// Get all commands mapped to the supported flags
/// </summary>
/// <returns>Mappings from command to supported flags</returns>
public abstract Dictionary<string, List<string>>? GetCommandSupport();
/// <summary>
/// Blindly generate a parameter string based on the inputs
/// </summary>
/// <returns>Parameter string for invocation, null on error</returns>
public abstract string? GenerateParameters();
/// <summary>
/// Get the default extension for a given media type
/// </summary>
/// <param name="mediaType">MediaType value to check</param>
/// <returns>String representing the media type, null on error</returns>
public abstract string? GetDefaultExtension(MediaType? mediaType);
/// <summary>
/// Get the MediaType from the current set of parameters
/// </summary>
/// <returns>MediaType value if successful, null on error</returns>
public abstract MediaType? GetMediaType();
/// <summary>
/// Gets if the current command is considered a dumping command or not
/// </summary>
/// <returns>True if it's a dumping command, false otherwise</returns>
public abstract bool IsDumpingCommand();
/// <summary>
/// Returns if the current Parameter object is valid
/// </summary>
/// <returns></returns>
2026-01-25 18:09:00 -05:00
public bool IsValid() => GenerateParameters() is not null;
/// <summary>
/// Reset all special variables to have default values
/// </summary>
protected abstract void ResetValues();
/// <summary>
/// Set default parameters for a given system and media type
/// </summary>
2023-10-18 01:47:42 -04:00
/// <param name="drivePath">Drive path to use</param>
/// <param name="filename">Filename to use</param>
/// <param name="driveSpeed">Drive speed to use</param>
/// <param name="options">Dictionary containing all settings that may be used for setting parameters</param>
protected abstract void SetDefaultParameters(string? drivePath,
string filename,
int? driveSpeed,
Dictionary<string, string?> options);
/// <summary>
/// Scan a possible parameter string and populate whatever possible
/// </summary>
/// <param name="parameters">String possibly representing parameters</param>
2021-03-08 14:56:26 -08:00
/// <returns>True if the parameters were set correctly, false otherwise</returns>
protected abstract bool ValidateAndSetParameters(string? parameters);
#endregion
#region Execution
/// <summary>
/// Run internal program
/// </summary>
2024-05-27 23:34:42 -04:00
public void ExecuteInternalProgram()
{
// Create the start info
var startInfo = new ProcessStartInfo()
{
FileName = ExecutablePath!,
Arguments = GenerateParameters() ?? "",
2024-05-27 23:34:42 -04:00
CreateNoWindow = false,
UseShellExecute = true,
RedirectStandardOutput = false,
RedirectStandardError = false,
};
// Create the new process
process = new Process() { StartInfo = startInfo };
// Start the process
process.Start();
process.WaitForExit();
process.Close();
}
/// <summary>
/// Cancel an in-progress dumping process
/// </summary>
public void KillInternalProgram()
{
try
{
2026-01-25 18:09:00 -05:00
while (process is not null && !process.HasExited)
{
process.Kill();
}
}
catch
{ }
}
2024-05-21 13:10:02 -04:00
#endregion
#region Option Processing
/// <summary>
/// Get a Boolean setting from a settings, dictionary
/// </summary>
/// <param name="settings">Dictionary representing the settings</param>
/// <param name="key">Setting key to get a value for</param>
/// <param name="defaultValue">Default value to return if no value is found</param>
/// <returns>Setting value if possible, default value otherwise</returns>
2024-12-04 15:57:33 -05:00
internal static bool GetBooleanSetting(Dictionary<string, string?> settings, string key, bool defaultValue)
{
if (settings.ContainsKey(key))
{
if (bool.TryParse(settings[key], out bool value))
return value;
else
return defaultValue;
}
else
{
return defaultValue;
}
}
/// <summary>
/// Get an Int32 setting from a settings, dictionary
/// </summary>
/// <param name="settings">Dictionary representing the settings</param>
/// <param name="key">Setting key to get a value for</param>
/// <param name="defaultValue">Default value to return if no value is found</param>
/// <returns>Setting value if possible, default value otherwise</returns>
2024-12-04 15:57:33 -05:00
internal static int GetInt32Setting(Dictionary<string, string?> settings, string key, int defaultValue)
{
if (settings.ContainsKey(key))
{
if (int.TryParse(settings[key], out int value))
return value;
else
return defaultValue;
}
else
{
return defaultValue;
}
}
/// <summary>
/// Get a String setting from a settings, dictionary
/// </summary>
/// <param name="settings">Dictionary representing the settings</param>
/// <param name="key">Setting key to get a value for</param>
/// <param name="defaultValue">Default value to return if no value is found</param>
/// <returns>Setting value if possible, default value otherwise</returns>
2024-12-04 15:57:33 -05:00
internal static string? GetStringSetting(Dictionary<string, string?> settings, string key, string? defaultValue)
{
if (settings.ContainsKey(key))
return settings[key];
else
return defaultValue;
}
2026-01-27 16:16:34 -05:00
/// <summary>
/// Get an UInt8 setting from a settings, dictionary
/// </summary>
/// <param name="settings">Dictionary representing the settings</param>
/// <param name="key">Setting key to get a value for</param>
/// <param name="defaultValue">Default value to return if no value is found</param>
/// <returns>Setting value if possible, default value otherwise</returns>
internal static byte GetUInt8Setting(Dictionary<string, string?> settings, string key, byte defaultValue)
{
if (settings.ContainsKey(key))
{
if (byte.TryParse(settings[key], out byte value))
return value;
else
return defaultValue;
}
else
{
return defaultValue;
}
}
#endregion
2020-06-05 14:15:06 -07:00
#region Parameter Parsing
2024-11-16 01:23:30 -05:00
/// <summary>
/// Split a parameters string into a list while taking quotes into account
/// </summary>
2024-12-04 15:57:33 -05:00
internal static string[] SplitParameterString(string parameters)
2024-11-16 01:23:30 -05:00
{
// Ensure the parameter string is trimmed
parameters = parameters.Trim();
// Split the string using Regex
2024-12-04 15:57:33 -05:00
var matches = Regex.Matches(parameters, @"([a-zA-Z0-9\-]*=)?[\""].+?[\""]|[^ ]+", RegexOptions.Compiled);
2024-11-16 01:23:30 -05:00
// Get just the values from the matches
var matchArr = new Match[matches.Count];
matches.CopyTo(matchArr, 0);
2024-12-04 15:57:33 -05:00
return Array.ConvertAll(matchArr, m => m.Value);
2024-11-16 01:23:30 -05:00
}
/// <summary>
/// Returns whether or not the selected item exists
/// </summary>
2024-12-04 15:57:33 -05:00
/// <param name="parts">Parts array to be referenced</param>
/// <param name="index">Current index</param>
/// <returns>True if the next item exists, false otherwise</returns>
2024-12-04 15:57:33 -05:00
internal static bool DoesExist(string[] parts, int index)
=> index >= 0 && index < parts.Length;
/// <summary>
/// Gets if the flag is supported by the current command
/// </summary>
/// <param name="flag">Flag value to check</param>
/// <returns>True if the flag value is supported, false otherwise</returns>
protected bool IsFlagSupported(string flag)
{
2026-01-25 18:09:00 -05:00
if (CommandSupport is null)
return false;
2026-01-25 18:09:00 -05:00
if (BaseCommand is null)
return false;
if (!CommandSupport.TryGetValue(BaseCommand, out var supported))
return false;
return supported.Contains(flag);
}
/// <summary>
/// Returns whether a string is a valid bool
/// </summary>
/// <param name="parameter">String value to check</param>
/// <returns>True if it's a valid bool, false otherwise</returns>
2024-12-04 15:57:33 -05:00
internal static bool IsValidBool(string parameter)
=> bool.TryParse(parameter, out bool _);
/// <summary>
/// Returns whether a string is a valid byte
/// </summary>
/// <param name="parameter">String value to check</param>
/// <param name="lowerBound">Lower bound (>=)</param>
/// <param name="upperBound">Upper bound (<=)</param>
/// <returns>True if it's a valid byte, false otherwise</returns>
2024-12-04 15:57:33 -05:00
internal static bool IsValidInt8(string parameter, sbyte? lowerBound = null, sbyte? upperBound = null)
{
string value = ExtractFactorFromValue(parameter, out _);
if (!sbyte.TryParse(value, out sbyte temp))
return false;
2026-01-25 18:09:00 -05:00
else if (lowerBound is not null && temp < lowerBound)
return false;
2026-01-25 18:09:00 -05:00
else if (upperBound is not null && temp > upperBound)
return false;
return true;
}
/// <summary>
/// Returns whether a string is a valid Int16
/// </summary>
/// <param name="parameter">String value to check</param>
/// <param name="lowerBound">Lower bound (>=)</param>
/// <param name="upperBound">Upper bound (<=)</param>
/// <returns>True if it's a valid Int16, false otherwise</returns>
2024-12-04 15:57:33 -05:00
internal static bool IsValidInt16(string parameter, short? lowerBound = null, short? upperBound = null)
{
string value = ExtractFactorFromValue(parameter, out _);
if (!short.TryParse(value, out short temp))
return false;
2026-01-25 18:09:00 -05:00
else if (lowerBound is not null && temp < lowerBound)
return false;
2026-01-25 18:09:00 -05:00
else if (upperBound is not null && temp > upperBound)
return false;
return true;
}
/// <summary>
/// Returns whether a string is a valid Int32
/// </summary>
/// <param name="parameter">String value to check</param>
/// <param name="lowerBound">Lower bound (>=)</param>
/// <param name="upperBound">Upper bound (<=)</param>
/// <returns>True if it's a valid Int32, false otherwise</returns>
2024-12-04 15:57:33 -05:00
internal static bool IsValidInt32(string parameter, int? lowerBound = null, int? upperBound = null)
{
string value = ExtractFactorFromValue(parameter, out _);
if (!int.TryParse(value, out int temp))
return false;
2026-01-25 18:09:00 -05:00
else if (lowerBound is not null && temp < lowerBound)
return false;
2026-01-25 18:09:00 -05:00
else if (upperBound is not null && temp > upperBound)
return false;
return true;
}
/// <summary>
/// Returns whether a string is a valid Int64
/// </summary>
/// <param name="parameter">String value to check</param>
/// <param name="lowerBound">Lower bound (>=)</param>
/// <param name="upperBound">Upper bound (<=)</param>
/// <returns>True if it's a valid Int64, false otherwise</returns>
2024-12-04 15:57:33 -05:00
internal static bool IsValidInt64(string parameter, long? lowerBound = null, long? upperBound = null)
{
string value = ExtractFactorFromValue(parameter, out _);
if (!long.TryParse(value, out long temp))
return false;
2026-01-25 18:09:00 -05:00
else if (lowerBound is not null && temp < lowerBound)
return false;
2026-01-25 18:09:00 -05:00
else if (upperBound is not null && temp > upperBound)
return false;
return true;
}
2020-06-05 14:15:06 -07:00
/// <summary>
/// Process a flag parameter
/// </summary>
2024-12-04 15:57:33 -05:00
/// <param name="parts">Parts array to be referenced</param>
/// <param name="flagString">Flag string, if available</param>
/// <param name="i">Reference to the position in the parts</param>
/// <returns>True if the parameter was processed successfully or skipped, false otherwise</returns>
2024-12-04 15:57:33 -05:00
protected bool ProcessFlagParameter(string[] parts, string flagString, ref int i)
=> ProcessFlagParameter(parts, null, flagString, ref i);
/// <summary>
/// Process a flag parameter
/// </summary>
2024-12-04 15:57:33 -05:00
/// <param name="parts">Parts array to be referenced</param>
/// <param name="shortFlagString">Short flag string, if available</param>
/// <param name="longFlagString">Long flag string, if available</param>
/// <param name="i">Reference to the position in the parts</param>
/// <returns>True if the parameter was processed successfully or skipped, false otherwise</returns>
2024-12-04 15:57:33 -05:00
protected bool ProcessFlagParameter(string[] parts, string? shortFlagString, string longFlagString, ref int i)
{
2026-01-25 18:09:00 -05:00
if (parts is null)
return false;
if (parts[i] == shortFlagString || parts[i] == longFlagString)
{
if (!IsFlagSupported(longFlagString))
return false;
this[longFlagString] = true;
}
return true;
}
/// <summary>
/// Process a boolean parameter
/// </summary>
2024-12-04 15:57:33 -05:00
/// <param name="parts">Parts array to be referenced</param>
/// <param name="flagString">Flag string, if available</param>
/// <param name="i">Reference to the position in the parts</param>
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
/// <returns>True if the parameter was processed successfully or skipped, false otherwise</returns>
2024-12-04 15:57:33 -05:00
protected bool ProcessBooleanParameter(string[] parts, string flagString, ref int i, bool missingAllowed = false)
=> ProcessBooleanParameter(parts, null, flagString, ref i, missingAllowed);
/// <summary>
/// Process a boolean parameter
/// </summary>
2024-12-04 15:57:33 -05:00
/// <param name="parts">Parts array to be referenced</param>
/// <param name="shortFlagString">Short flag string, if available</param>
/// <param name="longFlagString">Long flag string, if available</param>
/// <param name="i">Reference to the position in the parts</param>
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
/// <returns>True if the parameter was processed successfully or skipped, false otherwise</returns>
2024-12-04 15:57:33 -05:00
protected bool ProcessBooleanParameter(string[] parts, string? shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
{
2026-01-25 18:09:00 -05:00
if (parts is null)
return false;
if (parts[i] == shortFlagString || parts[i] == longFlagString)
{
if (!IsFlagSupported(longFlagString))
{
return false;
}
else if (!DoesExist(parts, i + 1))
{
if (missingAllowed)
{
this[longFlagString] = true;
return true;
}
else
{
return false;
}
}
else if (IsFlagSupported(parts[i + 1]))
{
if (missingAllowed)
{
this[longFlagString] = true;
return true;
}
else
{
return false;
}
}
else if (!IsValidBool(parts[i + 1]))
{
if (missingAllowed)
{
this[longFlagString] = true;
return true;
}
else
{
return false;
}
}
this[longFlagString] = bool.Parse(parts[i + 1]);
i++;
}
return true;
}
/// <summary>
/// Process a sbyte parameter
/// </summary>
2024-12-04 15:57:33 -05:00
/// <param name="parts">Parts array to be referenced</param>
/// <param name="flagString">Flag string, if available</param>
/// <param name="i">Reference to the position in the parts</param>
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
/// <returns>SByte value if success, SByte.MinValue if skipped, null on error/returns>
2024-12-04 15:57:33 -05:00
protected sbyte? ProcessInt8Parameter(string[] parts, string flagString, ref int i, bool missingAllowed = false)
=> ProcessInt8Parameter(parts, null, flagString, ref i, missingAllowed);
/// <summary>
/// Process an sbyte parameter
/// </summary>
2024-12-04 15:57:33 -05:00
/// <param name="parts">Parts array to be referenced</param>
/// <param name="shortFlagString">Short flag string, if available</param>
/// <param name="longFlagString">Long flag string, if available</param>
/// <param name="i">Reference to the position in the parts</param>
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
/// <returns>SByte value if success, SByte.MinValue if skipped, null on error/returns>
2024-12-04 15:57:33 -05:00
protected sbyte? ProcessInt8Parameter(string[] parts, string? shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
{
2026-01-25 18:09:00 -05:00
if (parts is null)
return null;
if (parts[i] == shortFlagString || parts[i] == longFlagString)
{
if (!IsFlagSupported(longFlagString))
{
return null;
}
else if (!DoesExist(parts, i + 1))
{
if (missingAllowed)
this[longFlagString] = true;
return null;
}
else if (IsFlagSupported(parts[i + 1]))
{
if (missingAllowed)
this[longFlagString] = true;
return null;
}
else if (!IsValidInt8(parts[i + 1]))
{
if (missingAllowed)
this[longFlagString] = true;
return null;
}
this[longFlagString] = true;
i++;
string value = ExtractFactorFromValue(parts[i], out long factor);
if (sbyte.TryParse(value, out sbyte sByteValue))
return (sbyte)(sByteValue * factor);
string hexValue = RemoveHexIdentifier(value);
if (sbyte.TryParse(hexValue, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out sbyte sByteHexValue))
return (sbyte)(sByteHexValue * factor);
return null;
}
else if (parts[i].StartsWith(shortFlagString + "=") || parts[i].StartsWith(longFlagString + "="))
{
if (!IsFlagSupported(longFlagString))
return null;
string[] commandParts = parts[i].Split('=');
if (commandParts.Length != 2)
return null;
string valuePart = commandParts[1];
2023-03-10 16:43:17 -05:00
this[longFlagString] = true;
string value = ExtractFactorFromValue(valuePart, out long factor);
if (sbyte.TryParse(value, out sbyte sByteValue))
return (sbyte)(sByteValue * factor);
string hexValue = RemoveHexIdentifier(value);
if (sbyte.TryParse(hexValue, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out sbyte sByteHexValue))
return (sbyte)(sByteHexValue * factor);
return null;
}
2026-01-25 18:09:00 -05:00
return sbyte.MinValue;
}
/// <summary>
/// Process an Int16 parameter
/// </summary>
2024-12-04 15:57:33 -05:00
/// <param name="parts">Parts array to be referenced</param>
/// <param name="flagString">Flag string, if available</param>
/// <param name="i">Reference to the position in the parts</param>
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
/// <returns>Int16 value if success, Int16.MinValue if skipped, null on error/returns>
2024-12-04 15:57:33 -05:00
protected short? ProcessInt16Parameter(string[] parts, string flagString, ref int i, bool missingAllowed = false)
=> ProcessInt16Parameter(parts, null, flagString, ref i, missingAllowed);
/// <summary>
/// Process an Int16 parameter
/// </summary>
2024-12-04 15:57:33 -05:00
/// <param name="parts">Parts array to be referenced</param>
/// <param name="shortFlagString">Short flag string, if available</param>
/// <param name="longFlagString">Long flag string, if available</param>
/// <param name="i">Reference to the position in the parts</param>
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
/// <returns>Int16 value if success, Int16.MinValue if skipped, null on error/returns>
2024-12-04 15:57:33 -05:00
protected short? ProcessInt16Parameter(string[] parts, string? shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
{
2026-01-25 18:09:00 -05:00
if (parts is null)
return null;
if (parts[i] == shortFlagString || parts[i] == longFlagString)
{
if (!IsFlagSupported(longFlagString))
{
return null;
}
else if (!DoesExist(parts, i + 1))
{
if (missingAllowed)
this[longFlagString] = true;
return null;
}
else if (IsFlagSupported(parts[i + 1]))
{
if (missingAllowed)
this[longFlagString] = true;
return null;
}
else if (!IsValidInt16(parts[i + 1]))
{
if (missingAllowed)
this[longFlagString] = true;
return null;
}
this[longFlagString] = true;
i++;
string value = ExtractFactorFromValue(parts[i], out long factor);
if (short.TryParse(value, out short shortValue))
return (short)(shortValue * factor);
string hexValue = RemoveHexIdentifier(value);
if (short.TryParse(hexValue, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out short shortHexValue))
return (short)(shortHexValue * factor);
return null;
}
else if (parts[i].StartsWith(shortFlagString + "=") || parts[i].StartsWith(longFlagString + "="))
{
if (!IsFlagSupported(longFlagString))
return null;
string[] commandParts = parts[i].Split('=');
if (commandParts.Length != 2)
return null;
string valuePart = commandParts[1];
2023-03-10 16:43:17 -05:00
this[longFlagString] = true;
string value = ExtractFactorFromValue(valuePart, out long factor);
if (short.TryParse(value, out short shortValue))
return (short)(shortValue * factor);
string hexValue = RemoveHexIdentifier(value);
if (short.TryParse(hexValue, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out short shortHexValue))
return (short)(shortHexValue * factor);
return null;
}
2026-01-25 18:09:00 -05:00
return short.MinValue;
}
/// <summary>
/// Process an Int32 parameter
/// </summary>
2024-12-04 15:57:33 -05:00
/// <param name="parts">Parts array to be referenced</param>
/// <param name="flagString">Flag string, if available</param>
/// <param name="i">Reference to the position in the parts</param>
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
/// <returns>Int32 value if success, Int32.MinValue if skipped, null on error/returns>
2024-12-04 15:57:33 -05:00
protected int? ProcessInt32Parameter(string[] parts, string flagString, ref int i, bool missingAllowed = false)
=> ProcessInt32Parameter(parts, null, flagString, ref i, missingAllowed);
/// <summary>
/// Process an Int32 parameter
/// </summary>
2024-12-04 15:57:33 -05:00
/// <param name="parts">Parts array to be referenced</param>
/// <param name="shortFlagString">Short flag string, if available</param>
/// <param name="longFlagString">Long flag string, if available</param>
/// <param name="i">Reference to the position in the parts</param>
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
/// <returns>Int32 value if success, Int32.MinValue if skipped, null on error/returns>
2024-12-04 15:57:33 -05:00
protected int? ProcessInt32Parameter(string[] parts, string? shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
{
2026-01-25 18:09:00 -05:00
if (parts is null)
return null;
if (parts[i] == shortFlagString || parts[i] == longFlagString)
{
if (!IsFlagSupported(longFlagString))
{
return null;
}
else if (!DoesExist(parts, i + 1))
{
if (missingAllowed)
this[longFlagString] = true;
return null;
}
else if (IsFlagSupported(parts[i + 1]))
{
if (missingAllowed)
this[longFlagString] = true;
return null;
}
else if (!IsValidInt32(parts[i + 1]))
{
if (missingAllowed)
this[longFlagString] = true;
return null;
}
this[longFlagString] = true;
i++;
string value = ExtractFactorFromValue(parts[i], out long factor);
if (int.TryParse(value, out int intValue))
return (int)(intValue * factor);
string hexValue = RemoveHexIdentifier(value);
if (int.TryParse(hexValue, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out int intHexValue))
return (int)(intHexValue * factor);
return null;
}
else if (parts[i].StartsWith(shortFlagString + "=") || parts[i].StartsWith(longFlagString + "="))
{
if (!IsFlagSupported(longFlagString))
return null;
string[] commandParts = parts[i].Split('=');
if (commandParts.Length != 2)
return null;
string valuePart = commandParts[1];
2023-03-10 16:43:17 -05:00
this[longFlagString] = true;
string value = ExtractFactorFromValue(valuePart, out long factor);
if (int.TryParse(value, out int intValue))
return (int)(intValue * factor);
string hexValue = RemoveHexIdentifier(value);
if (int.TryParse(hexValue, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out int intHexValue))
return (int)(intHexValue * factor);
return null;
}
2024-12-04 13:12:22 -05:00
return int.MinValue;
}
/// <summary>
/// Process an Int64 parameter
/// </summary>
2024-12-04 15:57:33 -05:00
/// <param name="parts">Parts array to be referenced</param>
/// <param name="flagString">Flag string, if available</param>
/// <param name="i">Reference to the position in the parts</param>
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
/// <returns>Int64 value if success, Int64.MinValue if skipped, null on error/returns>
2024-12-04 15:57:33 -05:00
protected long? ProcessInt64Parameter(string[] parts, string flagString, ref int i, bool missingAllowed = false)
=> ProcessInt64Parameter(parts, null, flagString, ref i, missingAllowed);
/// <summary>
/// Process an Int64 parameter
/// </summary>
2024-12-04 15:57:33 -05:00
/// <param name="parts">Parts array to be referenced</param>
/// <param name="shortFlagString">Short flag string, if available</param>
/// <param name="longFlagString">Long flag string, if available</param>
/// <param name="i">Reference to the position in the parts</param>
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
/// <returns>Int64 value if success, Int64.MinValue if skipped, null on error/returns>
2024-12-04 15:57:33 -05:00
protected long? ProcessInt64Parameter(string[] parts, string? shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
{
2026-01-25 18:09:00 -05:00
if (parts is null)
return null;
if (parts[i] == shortFlagString || parts[i] == longFlagString)
{
if (!IsFlagSupported(longFlagString))
{
return null;
}
else if (!DoesExist(parts, i + 1))
{
if (missingAllowed)
this[longFlagString] = true;
return null;
}
else if (IsFlagSupported(parts[i + 1]))
{
if (missingAllowed)
this[longFlagString] = true;
return null;
}
else if (!IsValidInt64(parts[i + 1]))
{
if (missingAllowed)
this[longFlagString] = true;
return null;
}
this[longFlagString] = true;
i++;
string value = ExtractFactorFromValue(parts[i], out long factor);
if (long.TryParse(value, out long longValue))
2026-01-25 18:09:00 -05:00
return longValue * factor;
string hexValue = RemoveHexIdentifier(value);
if (long.TryParse(hexValue, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out long longHexValue))
2026-01-25 18:09:00 -05:00
return longHexValue * factor;
return null;
}
else if (parts[i].StartsWith(shortFlagString + "=") || parts[i].StartsWith(longFlagString + "="))
{
if (!IsFlagSupported(longFlagString))
return null;
string[] commandParts = parts[i].Split('=');
if (commandParts.Length != 2)
return null;
string valuePart = commandParts[1];
2023-03-10 16:43:17 -05:00
this[longFlagString] = true;
string value = ExtractFactorFromValue(valuePart, out long factor);
if (long.TryParse(value, out long longValue))
2026-01-25 18:09:00 -05:00
return longValue * factor;
string hexValue = RemoveHexIdentifier(value);
if (long.TryParse(hexValue, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out long longHexValue))
2026-01-25 18:09:00 -05:00
return longHexValue * factor;
return null;
}
2024-12-04 13:12:22 -05:00
return long.MinValue;
}
/// <summary>
/// Process an string parameter
/// </summary>
2024-12-04 15:57:33 -05:00
/// <param name="parts">Parts array to be referenced</param>
/// <param name="flagString">Flag string, if available</param>
/// <param name="i">Reference to the position in the parts</param>
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
/// <returns>String value if possible, string.Empty on missing, null on error</returns>
2024-12-04 15:57:33 -05:00
protected string? ProcessStringParameter(string[] parts, string flagString, ref int i, bool missingAllowed = false)
2023-11-06 22:07:40 -05:00
=> ProcessStringParameter(parts, null, flagString, ref i, missingAllowed);
/// <summary>
/// Process a string parameter
/// </summary>
2024-12-04 15:57:33 -05:00
/// <param name="parts">Parts array to be referenced</param>
/// <param name="shortFlagString">Short flag string, if available</param>
/// <param name="longFlagString">Long flag string, if available</param>
/// <param name="i">Reference to the position in the parts</param>
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
/// <returns>String value if possible, string.Empty on missing, null on error</returns>
2024-12-04 15:57:33 -05:00
protected string? ProcessStringParameter(string[] parts, string? shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
{
2026-01-25 18:09:00 -05:00
if (parts is null)
return null;
if (parts[i] == shortFlagString || parts[i] == longFlagString)
{
if (!IsFlagSupported(longFlagString))
{
return null;
}
else if (!DoesExist(parts, i + 1))
{
if (missingAllowed)
this[longFlagString] = true;
return null;
}
else if (IsFlagSupported(parts[i + 1]))
{
if (missingAllowed)
this[longFlagString] = true;
return null;
}
else if (string.IsNullOrEmpty(parts[i + 1]))
{
if (missingAllowed)
this[longFlagString] = true;
return null;
}
this[longFlagString] = true;
i++;
return parts[i].Trim('"');
}
else if (parts[i].StartsWith(shortFlagString + "=") || parts[i].StartsWith(longFlagString + "="))
{
if (!IsFlagSupported(longFlagString))
return null;
int loc = parts[i].IndexOf('=');
string valuePart = parts[i].Substring(loc + 1);
this[longFlagString] = true;
return valuePart.Trim('"');
}
return string.Empty;
}
2022-09-28 12:22:14 -07:00
/// <summary>
/// Process a byte parameter
/// </summary>
2024-12-04 15:57:33 -05:00
/// <param name="parts">Parts array to be referenced</param>
2022-09-28 12:22:14 -07:00
/// <param name="flagString">Flag string, if available</param>
/// <param name="i">Reference to the position in the parts</param>
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
/// <returns>Byte value if success, Byte.MinValue if skipped, null on error/returns>
2024-12-04 15:57:33 -05:00
protected byte? ProcessUInt8Parameter(string[] parts, string flagString, ref int i, bool missingAllowed = false)
2022-09-28 12:22:14 -07:00
=> ProcessUInt8Parameter(parts, null, flagString, ref i, missingAllowed);
/// <summary>
/// Process a byte parameter
/// </summary>
2024-12-04 15:57:33 -05:00
/// <param name="parts">Parts array to be referenced</param>
2022-09-28 12:22:14 -07:00
/// <param name="shortFlagString">Short flag string, if available</param>
/// <param name="longFlagString">Long flag string, if available</param>
/// <param name="i">Reference to the position in the parts</param>
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
/// <returns>Byte value if success, Byte.MinValue if skipped, null on error/returns>
2024-12-04 15:57:33 -05:00
protected byte? ProcessUInt8Parameter(string[] parts, string? shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
2022-09-28 12:22:14 -07:00
{
2026-01-25 18:09:00 -05:00
if (parts is null)
2022-09-28 12:22:14 -07:00
return null;
if (parts[i] == shortFlagString || parts[i] == longFlagString)
{
if (!IsFlagSupported(longFlagString))
{
return null;
}
else if (!DoesExist(parts, i + 1))
{
if (missingAllowed)
this[longFlagString] = true;
return null;
}
else if (IsFlagSupported(parts[i + 1]))
{
if (missingAllowed)
this[longFlagString] = true;
return null;
}
else if (!IsValidInt8(parts[i + 1]))
{
if (missingAllowed)
this[longFlagString] = true;
return null;
}
this[longFlagString] = true;
i++;
string value = ExtractFactorFromValue(parts[i], out long factor);
if (byte.TryParse(value, out byte byteValue))
return (byte)(byteValue * factor);
string hexValue = RemoveHexIdentifier(value);
if (byte.TryParse(hexValue, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out byte byteHexValue))
return (byte)(byteHexValue * factor);
return null;
2022-09-28 12:22:14 -07:00
}
else if (parts[i].StartsWith(shortFlagString + "=") || parts[i].StartsWith(longFlagString + "="))
{
if (!IsFlagSupported(longFlagString))
return null;
string[] commandParts = parts[i].Split('=');
if (commandParts.Length != 2)
return null;
string valuePart = commandParts[1];
2023-03-10 16:43:17 -05:00
this[longFlagString] = true;
string value = ExtractFactorFromValue(valuePart, out long factor);
if (byte.TryParse(value, out byte byteValue))
return (byte)(byteValue * factor);
string hexValue = RemoveHexIdentifier(value);
if (byte.TryParse(hexValue, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out byte byteHexValue))
return (byte)(byteHexValue * factor);
return null;
2022-09-28 12:22:14 -07:00
}
2026-01-25 18:09:00 -05:00
return byte.MinValue;
2022-09-28 12:22:14 -07:00
}
/// <summary>
/// Get the trimmed value and multiplication factor from a value
/// </summary>
/// <param name="value">String value to treat as suffixed number</param>
/// <returns>Trimmed value and multiplication factor</returns>
2024-12-04 15:57:33 -05:00
internal static string ExtractFactorFromValue(string value, out long factor)
{
value = value.Trim('"');
factor = 1;
// Characters
if (value.EndsWith("c", StringComparison.Ordinal))
{
factor = 1;
value = value.TrimEnd('c');
}
// Words
else if (value.EndsWith("w", StringComparison.Ordinal))
{
factor = 2;
value = value.TrimEnd('w');
}
// Double Words
else if (value.EndsWith("d", StringComparison.Ordinal))
{
factor = 4;
value = value.TrimEnd('d');
}
// Quad Words
else if (value.EndsWith("q", StringComparison.Ordinal))
{
factor = 8;
value = value.TrimEnd('q');
}
// Kilobytes
else if (value.EndsWith("k", StringComparison.Ordinal))
{
factor = 1024;
value = value.TrimEnd('k');
}
// Megabytes
else if (value.EndsWith("M", StringComparison.Ordinal))
{
factor = 1024 * 1024;
value = value.TrimEnd('M');
}
// Gigabytes
else if (value.EndsWith("G", StringComparison.Ordinal))
{
factor = 1024 * 1024 * 1024;
value = value.TrimEnd('G');
}
return value;
}
/// <summary>
/// Removes a leading 0x if it exists, case insensitive
/// </summary>
/// <param name="value">String with removed leading 0x</param>
/// <returns></returns>
2024-12-04 15:57:33 -05:00
internal static string RemoveHexIdentifier(string value)
{
if (value.Length <= 2)
return value;
if (value[0] != '0')
return value;
if (value[1] != 'x' && value[1] != 'X')
return value;
return value.Substring(2);
}
2023-10-30 21:43:43 -04:00
#endregion
}
}