using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using MPF.Core.Data;
using MPF.Core.Utilities;
using SabreTools.RedumpLib.Data;
namespace MPF.Core.Modules
{
public abstract class BaseParameters
{
#region Event Handlers
#if NET20 || NET35 || NET40
///
/// Wrapper event args class for old .NET
///
public class StringEventArgs : EventArgs
{
public string? Value { get; set; }
}
///
/// Geneeic way of reporting a message
///
/// String value to report
public EventHandler? ReportStatus;
#else
///
/// Geneeic way of reporting a message
///
/// String value to report
public EventHandler? ReportStatus;
#endif
#endregion
#region Generic Dumping Information
///
/// Base command to run
///
public string? BaseCommand { get; set; }
///
/// Set of flags to pass to the executable
///
protected Dictionary flags = [];
protected internal IEnumerable Keys => flags.Keys;
///
/// Safe access to currently set flags
///
public bool? this[string key]
{
get
{
if (flags.TryGetValue(key, out bool? val))
return val;
return null;
}
set
{
flags[key] = value;
}
}
///
/// Process to track external program
///
private Process? process;
///
/// All found volume labels and their corresponding file systems
///
public Dictionary>? VolumeLabels;
#endregion
#region Virtual Dumping Information
///
/// Command to flag support mappings
///
public Dictionary>? CommandSupport => GetCommandSupport();
///
/// Input path for operations
///
public virtual string? InputPath => null;
///
/// Output path for operations
///
/// String representing the path, null on error
public virtual string? OutputPath => null;
///
/// Get the processing speed from the implementation
///
public virtual int? Speed { get; set; } = null;
#endregion
#region Metadata
///
/// Path to the executable
///
public string? ExecutablePath { get; set; }
///
/// Program that this set of parameters represents
///
public virtual InternalProgram InternalProgram { get; }
///
/// Currently represented system
///
public RedumpSystem? System { get; set; }
///
/// Currently represented media type
///
public MediaType? Type { get; set; }
#endregion
///
/// Populate a Parameters object from a param string
///
/// String possibly representing a set of parameters
public BaseParameters(string? parameters)
{
// If any parameters are not valid, wipe out everything
if (!ValidateAndSetParameters(parameters))
ResetValues();
}
///
/// Generate parameters based on a set of known inputs
///
/// RedumpSystem value to use
/// MediaType value to use
/// Drive path to use
/// Filename to use
/// Drive speed to use
/// Options object containing all settings that may be used for setting parameters
public BaseParameters(RedumpSystem? system, MediaType? type, string? drivePath, string filename, int? driveSpeed, Options options)
{
this.System = system;
this.Type = type;
SetDefaultParameters(drivePath, filename, driveSpeed, options);
}
#region Abstract Methods
///
/// Validate if all required output files exist
///
/// Base filename and path to use for checking
/// True if this is a check done before a dump, false if done after
/// Tuple of true if all required files exist, false otherwise and a list representing missing files
public abstract (bool, List) CheckAllOutputFilesExist(string basePath, bool preCheck);
///
/// Generate a SubmissionInfo for the output files
///
/// Base submission info to fill in specifics for
/// Options object representing user-defined options
/// Base filename and path to use for checking
/// Drive representing the disc to get information from
/// True to include output files as encoded artifacts, false otherwise
public abstract void GenerateSubmissionInfo(SubmissionInfo submissionInfo, Options options, string basePath, Drive? drive, bool includeArtifacts);
#endregion
#region Virtual Methods
///
/// Get all commands mapped to the supported flags
///
/// Mappings from command to supported flags
public virtual Dictionary>? GetCommandSupport() => null;
///
/// Blindly generate a parameter string based on the inputs
///
/// Parameter string for invocation, null on error
public virtual string? GenerateParameters() => null;
///
/// Get the default extension for a given media type
///
/// MediaType value to check
/// String representing the media type, null on error
public virtual string? GetDefaultExtension(MediaType? mediaType) => null;
///
/// Generate a list of all deleteable files generated
///
/// Base filename and path to use for checking
/// List of all deleteable file paths, empty otherwise
public virtual List GetDeleteableFilePaths(string basePath) => new();
///
/// Generate a list of all log files generated
///
/// Base filename and path to use for checking
/// List of all log file paths, empty otherwise
public virtual List GetLogFilePaths(string basePath) => new();
///
/// Get the MediaType from the current set of parameters
///
/// MediaType value if successful, null on error
public virtual MediaType? GetMediaType() => null;
///
/// Gets if the current command is considered a dumping command or not
///
/// True if it's a dumping command, false otherwise
public virtual bool IsDumpingCommand() => true;
///
/// Gets if the flag is supported by the current command
///
/// Flag value to check
/// True if the flag value is supported, false otherwise
public virtual bool IsFlagSupported(string flag)
{
if (CommandSupport == null)
return false;
if (this.BaseCommand == null)
return false;
if (!CommandSupport.TryGetValue(this.BaseCommand, out var supported))
return false;
return supported.Contains(flag);
}
///
/// Returns if the current Parameter object is valid
///
///
public bool IsValid() => GenerateParameters() != null;
///
/// Reset all special variables to have default values
///
protected virtual void ResetValues() { }
///
/// Set default parameters for a given system and media type
///
/// Drive path to use
/// Filename to use
/// Drive speed to use
/// Options object containing all settings that may be used for setting parameters
protected virtual void SetDefaultParameters(string? drivePath, string filename, int? driveSpeed, Options options) { }
///
/// Scan a possible parameter string and populate whatever possible
///
/// String possibly representing parameters
/// True if the parameters were set correctly, false otherwise
protected virtual bool ValidateAndSetParameters(string? parameters) => !string.IsNullOrEmpty(parameters);
#endregion
#region Execution
///
/// Run internal program
///
/// True to show in separate window, false otherwise
public void ExecuteInternalProgram(bool separateWindow)
{
// Create the start info
var startInfo = new ProcessStartInfo()
{
FileName = ExecutablePath!,
Arguments = GenerateParameters() ?? "",
CreateNoWindow = !separateWindow,
UseShellExecute = separateWindow,
RedirectStandardOutput = !separateWindow,
RedirectStandardError = !separateWindow,
};
// Create the new process
process = new Process() { StartInfo = startInfo };
// Start the process
process.Start();
// Start processing tasks, if necessary
if (!separateWindow)
{
#if NET40
Logging.OutputToLog(process.StandardOutput, this, ReportStatus);
Logging.OutputToLog(process.StandardError, this, ReportStatus);
#else
_ = Logging.OutputToLog(process.StandardOutput, this, ReportStatus);
_ = Logging.OutputToLog(process.StandardError, this, ReportStatus);
#endif
}
process.WaitForExit();
process.Close();
}
///
/// Cancel an in-progress dumping process
///
public void KillInternalProgram()
{
try
{
while (process != null && !process.HasExited)
{
process.Kill();
}
}
catch
{ }
}
#endregion
#region Parameter Parsing
///
/// Returns whether or not the selected item exists
///
/// List of parameters to check against
/// Current index
/// True if the next item exists, false otherwise
protected static bool DoesExist(List parameters, int index)
=> index < parameters.Count;
///
/// Get the Base64 representation of a string
///
/// String content to encode
/// Base64-encoded contents, if possible
protected static string? GetBase64(string? content)
{
if (string.IsNullOrEmpty(content))
return null;
byte[] temp = Encoding.UTF8.GetBytes(content);
return Convert.ToBase64String(temp);
}
///
/// Get the full lines from the input file, if possible
///
/// file location
/// True if should read as binary, false otherwise (default)
/// Full text of the file, null on error
protected static string? GetFullFile(string filename, bool binary = false)
{
// If the file doesn't exist, we can't get info from it
if (!File.Exists(filename))
return null;
// If we're reading as binary
if (binary)
{
byte[] bytes = File.ReadAllBytes(filename);
return BitConverter.ToString(bytes).Replace("-", string.Empty);
}
return File.ReadAllText(filename);
}
///
/// Returns whether a string is a valid drive letter
///
/// String value to check
/// True if it's a valid drive letter, false otherwise
protected static bool IsValidDriveLetter(string parameter)
=> Regex.IsMatch(parameter, @"^[A-Z]:?\\?$");
///
/// Returns whether a string is a valid bool
///
/// String value to check
/// True if it's a valid bool, false otherwise
protected static bool IsValidBool(string parameter)
=> bool.TryParse(parameter, out bool _);
///
/// Returns whether a string is a valid byte
///
/// String value to check
/// Lower bound (>=)
/// Upper bound (<=)
/// True if it's a valid byte, false otherwise
protected static bool IsValidInt8(string parameter, sbyte lowerBound = -1, sbyte upperBound = -1)
{
(string value, long _) = ExtractFactorFromValue(parameter);
if (!sbyte.TryParse(value, out sbyte temp))
return false;
else if (lowerBound != -1 && temp < lowerBound)
return false;
else if (upperBound != -1 && temp > upperBound)
return false;
return true;
}
///
/// Returns whether a string is a valid Int16
///
/// String value to check
/// Lower bound (>=)
/// Upper bound (<=)
/// True if it's a valid Int16, false otherwise
protected static bool IsValidInt16(string parameter, short lowerBound = -1, short upperBound = -1)
{
(string value, long _) = ExtractFactorFromValue(parameter);
if (!short.TryParse(value, out short temp))
return false;
else if (lowerBound != -1 && temp < lowerBound)
return false;
else if (upperBound != -1 && temp > upperBound)
return false;
return true;
}
///
/// Returns whether a string is a valid Int32
///
/// String value to check
/// Lower bound (>=)
/// Upper bound (<=)
/// True if it's a valid Int32, false otherwise
protected static bool IsValidInt32(string parameter, int lowerBound = -1, int upperBound = -1)
{
(string value, long _) = ExtractFactorFromValue(parameter);
if (!int.TryParse(value, out int temp))
return false;
else if (lowerBound != -1 && temp < lowerBound)
return false;
else if (upperBound != -1 && temp > upperBound)
return false;
return true;
}
///
/// Returns whether a string is a valid Int64
///
/// String value to check
/// Lower bound (>=)
/// Upper bound (<=)
/// True if it's a valid Int64, false otherwise
protected static bool IsValidInt64(string parameter, long lowerBound = -1, long upperBound = -1)
{
(string value, long _) = ExtractFactorFromValue(parameter);
if (!long.TryParse(value, out long temp))
return false;
else if (lowerBound != -1 && temp < lowerBound)
return false;
else if (upperBound != -1 && temp > upperBound)
return false;
return true;
}
///
/// Process a flag parameter
///
/// List of parts to be referenced
/// Flag string, if available
/// Reference to the position in the parts
/// True if the parameter was processed successfully or skipped, false otherwise
protected bool ProcessFlagParameter(List parts, string flagString, ref int i)
=> ProcessFlagParameter(parts, null, flagString, ref i);
///
/// Process a flag parameter
///
/// List of parts to be referenced
/// Short flag string, if available
/// Long flag string, if available
/// Reference to the position in the parts
/// True if the parameter was processed successfully or skipped, false otherwise
protected bool ProcessFlagParameter(List parts, string? shortFlagString, string longFlagString, ref int i)
{
if (parts == null)
return false;
if (parts[i] == shortFlagString || parts[i] == longFlagString)
{
if (!IsFlagSupported(longFlagString))
return false;
this[longFlagString] = true;
}
return true;
}
///
/// Process a boolean parameter
///
/// List of parts to be referenced
/// Flag string, if available
/// Reference to the position in the parts
/// True if missing values are allowed, false otherwise
/// True if the parameter was processed successfully or skipped, false otherwise
protected bool ProcessBooleanParameter(List parts, string flagString, ref int i, bool missingAllowed = false)
=> ProcessBooleanParameter(parts, null, flagString, ref i, missingAllowed);
///
/// Process a boolean parameter
///
/// List of parts to be referenced
/// Short flag string, if available
/// Long flag string, if available
/// Reference to the position in the parts
/// True if missing values are allowed, false otherwise
/// True if the parameter was processed successfully or skipped, false otherwise
protected bool ProcessBooleanParameter(List parts, string? shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
{
if (parts == 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;
}
///
/// Process a sbyte parameter
///
/// List of parts to be referenced
/// Flag string, if available
/// Reference to the position in the parts
/// True if missing values are allowed, false otherwise
/// SByte value if success, SByte.MinValue if skipped, null on error/returns>
protected sbyte? ProcessInt8Parameter(List parts, string flagString, ref int i, bool missingAllowed = false)
=> ProcessInt8Parameter(parts, null, flagString, ref i, missingAllowed);
///
/// Process an sbyte parameter
///
/// List of parts to be referenced
/// Short flag string, if available
/// Long flag string, if available
/// Reference to the position in the parts
/// True if missing values are allowed, false otherwise
/// SByte value if success, SByte.MinValue if skipped, null on error/returns>
protected sbyte? ProcessInt8Parameter(List parts, string? shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
{
if (parts == 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, long factor) = ExtractFactorFromValue(parts[i]);
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];
this[longFlagString] = true;
(string value, long factor) = ExtractFactorFromValue(valuePart);
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;
}
return SByte.MinValue;
}
///
/// Process an Int16 parameter
///
/// List of parts to be referenced
/// Flag string, if available
/// Reference to the position in the parts
/// True if missing values are allowed, false otherwise
/// Int16 value if success, Int16.MinValue if skipped, null on error/returns>
protected short? ProcessInt16Parameter(List parts, string flagString, ref int i, bool missingAllowed = false)
=> ProcessInt16Parameter(parts, null, flagString, ref i, missingAllowed);
///
/// Process an Int16 parameter
///
/// List of parts to be referenced
/// Short flag string, if available
/// Long flag string, if available
/// Reference to the position in the parts
/// True if missing values are allowed, false otherwise
/// Int16 value if success, Int16.MinValue if skipped, null on error/returns>
protected short? ProcessInt16Parameter(List parts, string? shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
{
if (parts == 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, long factor) = ExtractFactorFromValue(parts[i]);
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];
this[longFlagString] = true;
(string value, long factor) = ExtractFactorFromValue(valuePart);
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;
}
return Int16.MinValue;
}
///
/// Process an Int32 parameter
///
/// List of parts to be referenced
/// Flag string, if available
/// Reference to the position in the parts
/// True if missing values are allowed, false otherwise
/// Int32 value if success, Int32.MinValue if skipped, null on error/returns>
protected int? ProcessInt32Parameter(List parts, string flagString, ref int i, bool missingAllowed = false)
=> ProcessInt32Parameter(parts, null, flagString, ref i, missingAllowed);
///
/// Process an Int32 parameter
///
/// List of parts to be referenced
/// Short flag string, if available
/// Long flag string, if available
/// Reference to the position in the parts
/// True if missing values are allowed, false otherwise
/// Int32 value if success, Int32.MinValue if skipped, null on error/returns>
protected int? ProcessInt32Parameter(List parts, string? shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
{
if (parts == 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, long factor) = ExtractFactorFromValue(parts[i]);
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];
this[longFlagString] = true;
(string value, long factor) = ExtractFactorFromValue(valuePart);
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;
}
return Int32.MinValue;
}
///
/// Process an Int64 parameter
///
/// List of parts to be referenced
/// Flag string, if available
/// Reference to the position in the parts
/// True if missing values are allowed, false otherwise
/// Int64 value if success, Int64.MinValue if skipped, null on error/returns>
protected long? ProcessInt64Parameter(List parts, string flagString, ref int i, bool missingAllowed = false)
=> ProcessInt64Parameter(parts, null, flagString, ref i, missingAllowed);
///
/// Process an Int64 parameter
///
/// List of parts to be referenced
/// Short flag string, if available
/// Long flag string, if available
/// Reference to the position in the parts
/// True if missing values are allowed, false otherwise
/// Int64 value if success, Int64.MinValue if skipped, null on error/returns>
protected long? ProcessInt64Parameter(List parts, string? shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
{
if (parts == 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, long factor) = ExtractFactorFromValue(parts[i]);
if (long.TryParse(value, out long longValue))
return (long)(longValue * factor);
string hexValue = RemoveHexIdentifier(value);
if (long.TryParse(hexValue, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out long longHexValue))
return (long)(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];
this[longFlagString] = true;
(string value, long factor) = ExtractFactorFromValue(valuePart);
if (long.TryParse(value, out long longValue))
return (long)(longValue * factor);
string hexValue = RemoveHexIdentifier(value);
if (long.TryParse(hexValue, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out long longHexValue))
return (long)(longHexValue * factor);
return null;
}
return Int64.MinValue;
}
///
/// Process an string parameter
///
/// List of parts to be referenced
/// Flag string, if available
/// Reference to the position in the parts
/// True if missing values are allowed, false otherwise
/// String value if possible, string.Empty on missing, null on error
protected string? ProcessStringParameter(List parts, string flagString, ref int i, bool missingAllowed = false)
=> ProcessStringParameter(parts, null, flagString, ref i, missingAllowed);
///
/// Process a string parameter
///
/// List of parts to be referenced
/// Short flag string, if available
/// Long flag string, if available
/// Reference to the position in the parts
/// True if missing values are allowed, false otherwise
/// String value if possible, string.Empty on missing, null on error
protected string? ProcessStringParameter(List parts, string? shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
{
if (parts == 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;
string[] commandParts = parts[i].Split('=');
if (commandParts.Length != 2)
return null;
string valuePart = commandParts[1];
this[longFlagString] = true;
return valuePart.Trim('"');
}
return string.Empty;
}
///
/// Process a byte parameter
///
/// List of parts to be referenced
/// Flag string, if available
/// Reference to the position in the parts
/// True if missing values are allowed, false otherwise
/// Byte value if success, Byte.MinValue if skipped, null on error/returns>
protected byte? ProcessUInt8Parameter(List parts, string flagString, ref int i, bool missingAllowed = false)
=> ProcessUInt8Parameter(parts, null, flagString, ref i, missingAllowed);
///
/// Process a byte parameter
///
/// List of parts to be referenced
/// Short flag string, if available
/// Long flag string, if available
/// Reference to the position in the parts
/// True if missing values are allowed, false otherwise
/// Byte value if success, Byte.MinValue if skipped, null on error/returns>
protected byte? ProcessUInt8Parameter(List parts, string? shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
{
if (parts == 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, long factor) = ExtractFactorFromValue(parts[i]);
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;
}
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];
this[longFlagString] = true;
(string value, long factor) = ExtractFactorFromValue(valuePart);
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;
}
return Byte.MinValue;
}
///
/// Get the trimmed value and multiplication factor from a value
///
/// String value to treat as suffixed number
/// Trimmed value and multiplication factor
private static (string trimmed, long factor) ExtractFactorFromValue(string value)
{
value = value.Trim('"');
long 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, factor);
}
///
/// Removes a leading 0x if it exists, case insensitive
///
/// String with removed leading 0x
///
private 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);
}
#endregion
#region Methods to Move
///
/// Get the hex contents of the PIC file
///
/// Path to the PIC.bin file associated with the dump
/// Number of characters to trim the PIC to, if -1, ignored
/// PIC data as a hex string if possible, null on error
/// https://stackoverflow.com/questions/9932096/add-separator-to-string-at-every-n-characters
protected static string? GetPIC(string picPath, int trimLength = -1)
{
// If the file doesn't exist, we can't get the info
if (!File.Exists(picPath))
return null;
try
{
var hex = GetFullFile(picPath, true);
if (hex == null)
return null;
if (trimLength > -1)
hex = hex.Substring(0, trimLength);
return Regex.Replace(hex, ".{32}", "$0\n", RegexOptions.Compiled);
}
catch
{
// We don't care what the error was right now
return null;
}
}
#endregion
}
}