From 2a58052bfdb2ae1dbd74460b4bf83d6eafcfeff2 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Tue, 21 Apr 2020 15:52:57 -0700 Subject: [PATCH] So, I added DD support --- DICUI.Library/Aaru/Parameters.cs | 4 +- DICUI.Library/DD/Constants.cs | 31 ++ DICUI.Library/DD/Converters.cs | 82 ++++ DICUI.Library/DD/Enumerations.cs | 34 ++ DICUI.Library/DD/Parameters.cs | 469 ++++++++++++++++++- DICUI.Library/DiscImageCreator/Parameters.cs | 2 +- 6 files changed, 614 insertions(+), 8 deletions(-) create mode 100644 DICUI.Library/DD/Constants.cs create mode 100644 DICUI.Library/DD/Enumerations.cs diff --git a/DICUI.Library/Aaru/Parameters.cs b/DICUI.Library/Aaru/Parameters.cs index 0805f24d..e3578a3f 100644 --- a/DICUI.Library/Aaru/Parameters.cs +++ b/DICUI.Library/Aaru/Parameters.cs @@ -212,7 +212,7 @@ namespace DICUI.Aaru { BaseCommand = Command.MediaDump; - InputValue = $"\\\\?\\{driveLetter.ToString()}:"; + InputValue = $"\\\\?\\{driveLetter}:"; OutputValue = filename; this[Flag.Force] = true; @@ -305,7 +305,7 @@ namespace DICUI.Aaru #endregion if (BaseCommand != Command.NONE) - parameters.Add(Converters.LongName((Command)BaseCommand)); + parameters.Add(Converters.LongName(BaseCommand)); else return null; diff --git a/DICUI.Library/DD/Constants.cs b/DICUI.Library/DD/Constants.cs new file mode 100644 index 00000000..e6d3cdca --- /dev/null +++ b/DICUI.Library/DD/Constants.cs @@ -0,0 +1,31 @@ +namespace DICUI.DD +{ + /// + /// Top-level commands for DD + /// + public static class CommandStrings + { + public const string List = "--list"; + } + + /// + /// Dumping flags for DD + /// + public static class FlagStrings + { + // Boolean flags + public const string Progress = "--progress"; + public const string Size = "--size"; + + // Int64 flags + public const string BlockSize = "bs"; + public const string Count = "count"; + public const string Seek = "seek"; + public const string Skip = "skip"; + + // String flags + public const string Filter = "--filter"; + public const string InputFile = "if"; + public const string OutputFile = "of"; + } +} \ No newline at end of file diff --git a/DICUI.Library/DD/Converters.cs b/DICUI.Library/DD/Converters.cs index 6e8a1068..509e2dac 100644 --- a/DICUI.Library/DD/Converters.cs +++ b/DICUI.Library/DD/Converters.cs @@ -18,5 +18,87 @@ namespace DICUI.DD } #endregion + + #region Convert to Long Name + + /// + /// Get the string representation of the Command enum values + /// + /// Command value to convert + /// String representing the value, if possible + public static string LongName(Command command) + { + switch (command) + { + case Command.List: + return CommandStrings.List; + + case Command.NONE: + default: + return ""; + } + } + + /// + /// Get the string representation of the Flag enum values + /// + /// Flag value to convert + /// String representing the value, if possible + public static string LongName(Flag flag) + { + switch (flag) + { + // Boolean flags + case Flag.Progress: + return FlagStrings.Progress; + case Flag.Size: + return FlagStrings.Size; + + // Int64 flags + case Flag.BlockSize: + return FlagStrings.BlockSize; + case Flag.Count: + return FlagStrings.Count; + case Flag.Seek: + return FlagStrings.Seek; + case Flag.Skip: + return FlagStrings.Skip; + + // String flags + case Flag.Filter: + return FlagStrings.Filter; + case Flag.InputFile: + return FlagStrings.InputFile; + case Flag.OutputFile: + return FlagStrings.OutputFile; + + case Flag.NONE: + default: + return ""; + } + } + + #endregion + + #region Convert From String + + /// + /// Get the Command enum value for a given string + /// + /// String value to convert + /// Command represented by the string(s), if possible + public static Command StringToCommand(string command) + { + switch (command) + { + case CommandStrings.List: + return Command.List; + + default: + return Command.NONE; + } + } + + #endregion } } \ No newline at end of file diff --git a/DICUI.Library/DD/Enumerations.cs b/DICUI.Library/DD/Enumerations.cs new file mode 100644 index 00000000..29a307e1 --- /dev/null +++ b/DICUI.Library/DD/Enumerations.cs @@ -0,0 +1,34 @@ +namespace DICUI.DD +{ + /// + /// Supported DD commands + /// + public enum Command: int + { + NONE = 0, // For DD, this represents a normal dump + List, + } + + /// + /// Supported DD flags + /// + public enum Flag : int + { + NONE = 0, + + // Boolean flags + Progress, + Size, + + // Int64 flags + BlockSize, + Count, + Seek, + Skip, + + // String flags + Filter, + InputFile, + OutputFile, + } +} \ No newline at end of file diff --git a/DICUI.Library/DD/Parameters.cs b/DICUI.Library/DD/Parameters.cs index 2d4a93bb..549ff272 100644 --- a/DICUI.Library/DD/Parameters.cs +++ b/DICUI.Library/DD/Parameters.cs @@ -1,4 +1,8 @@ -using DICUI.Data; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using DICUI.Data; namespace DICUI.DD { @@ -7,6 +11,50 @@ namespace DICUI.DD /// public class Parameters : BaseParameters { + /// + /// Base command to run + /// + public Command BaseCommand { get; set; } + + /// + /// Set of flags to pass to the executable + /// + protected Dictionary _flags = new Dictionary(); + public bool? this[Flag key] + { + get + { + if (_flags.ContainsKey(key)) + return _flags[key]; + + return null; + } + set + { + _flags[key] = value; + } + } + protected internal IEnumerable Keys => _flags.Keys; + + #region Flag Values + + public long? BlockSizeValue { get; set; } + + public long? CountValue { get; set; } + + // fixed, removable, disk, partition + public string FilterValue { get; set; } + + public string InputFileValue { get; set; } + + public string OutputFileValue { get; set; } + + public long? SeekValue { get; set; } + + public long? SkipValue { get; set; } + + #endregion + /// /// Populate a Parameters object from a param string /// @@ -39,6 +87,90 @@ namespace DICUI.DD /// Correctly formatted parameter string, null on error public override string GenerateParameters() { + List parameters = new List(); + + if (BaseCommand != Command.NONE) + parameters.Add(Converters.LongName(BaseCommand)); + + #region Boolean flags + + // Progress + if (GetSupportedCommands(Flag.Progress).Contains(BaseCommand)) + { + if (this[Flag.Progress] == true) + parameters.Add($"{this[Flag.Progress]}"); + } + + // Size + if (GetSupportedCommands(Flag.Size).Contains(BaseCommand)) + { + if (this[Flag.Size] == true) + parameters.Add($"{this[Flag.Size]}"); + } + + #endregion + + #region Int64 flags + + // Block Size + if (GetSupportedCommands(Flag.BlockSize).Contains(BaseCommand)) + { + if (this[Flag.BlockSize] == true && BlockSizeValue != null) + parameters.Add($"{Converters.LongName(Flag.BlockSize)}={BlockSizeValue}"); + } + + // Count + if (GetSupportedCommands(Flag.Count).Contains(BaseCommand)) + { + if (this[Flag.Count] == true && CountValue != null) + parameters.Add($"{Converters.LongName(Flag.Count)}={CountValue}"); + } + + // Seek + if (GetSupportedCommands(Flag.Seek).Contains(BaseCommand)) + { + if (this[Flag.Seek] == true && SeekValue != null) + parameters.Add($"{Converters.LongName(Flag.Seek)}={SeekValue}"); + } + + // Skip + if (GetSupportedCommands(Flag.Skip).Contains(BaseCommand)) + { + if (this[Flag.Skip] == true && SkipValue != null) + parameters.Add($"{Converters.LongName(Flag.Skip)}={SkipValue}"); + } + + #endregion + + #region String flags + + // Filter + if (GetSupportedCommands(Flag.Filter).Contains(BaseCommand)) + { + if (this[Flag.Filter] == true && FilterValue != null) + parameters.Add($"{Converters.LongName(Flag.Filter)}={FilterValue}"); + } + + // Input File + if (GetSupportedCommands(Flag.InputFile).Contains(BaseCommand)) + { + if (this[Flag.InputFile] == true && InputFileValue != null) + parameters.Add($"{Converters.LongName(Flag.InputFile)}={InputFileValue}"); + else + return null; + } + + // Output File + if (GetSupportedCommands(Flag.OutputFile).Contains(BaseCommand)) + { + if (this[Flag.OutputFile] == true && OutputFileValue != null) + parameters.Add($"{Converters.LongName(Flag.OutputFile)}={OutputFileValue}"); + else + return null; + } + + #endregion + return string.Empty; } @@ -46,24 +178,26 @@ namespace DICUI.DD /// Get the input path from the implementation /// /// String representing the path, null on error - public override string InputPath() => string.Empty; + public override string InputPath() => InputFileValue; /// /// Get the output path from the implementation /// /// String representing the path, null on error - public override string OutputPath() => string.Empty; + public override string OutputPath() => OutputFileValue; /// /// Get the processing speed from the implementation /// /// int? representing the speed, null on error - public override int? GetSpeed() => null; + /// DD does not support drive speeds + public override int? GetSpeed() => 1; /// /// Set the processing speed int the implementation /// /// int? representing the speed + /// DD does not support drive speeds public override void SetSpeed(int? speed) { } @@ -72,19 +206,39 @@ namespace DICUI.DD /// Get the MediaType from the current set of parameters /// /// MediaType value if successful, null on error + /// DD does not know the difference between media types public override 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 override bool IsDumpingCommand() => true; + public override bool IsDumpingCommand() + { + switch (this.BaseCommand) + { + case Command.List: + return false; + default: + return true; + } + } /// /// Reset all special variables to have default values /// protected override void ResetValues() { + BaseCommand = Command.NONE; + + _flags = new Dictionary(); + + BlockSizeValue = null; + CountValue = null; + InputFileValue = null; + OutputFileValue = null; + SeekValue = null; + SkipValue = null; } /// @@ -106,6 +260,17 @@ namespace DICUI.DD bool paranoid, int retryCount) { + BaseCommand = Command.NONE; + + InputFileValue = $"\\\\?\\{driveLetter}:"; + OutputFileValue = filename; + + // TODO: Refine the block size more based on media type (1440K for FDD, etc.) + this[Flag.BlockSize] = true; + BlockSizeValue = 1024 * 1024 * 1024; + + this[Flag.Progress] = true; + this[Flag.Size] = true; } /// @@ -115,7 +280,301 @@ namespace DICUI.DD /// protected override bool ValidateAndSetParameters(string parameters) { + // The string has to be valid by itself first + if (string.IsNullOrWhiteSpace(parameters)) + return false; + + // Now split the string into parts for easier validation + // https://stackoverflow.com/questions/14655023/split-a-string-that-has-white-spaces-unless-they-are-enclosed-within-quotes + parameters = parameters.Trim(); + List parts = Regex.Matches(parameters, @"[\""].+?[\""]|[^ ]+") + .Cast() + .Select(m => m.Value) + .ToList(); + + // Determine what the commandline should look like given the first item + int start = 0; + BaseCommand = Converters.StringToCommand(parts[0]); + if (BaseCommand != Command.NONE) + start = 1; + + // Loop through all auxilary flags, if necessary + int i = 0; + for (i = start; i < parts.Count; i++) + { + // Flag read-out values + long? longValue = null; + string stringValue = null; + + // Keep a count of keys to determine if we should break out to filename handling or not + int keyCount = Keys.Count(); + + #region Boolean flags + + // Progress + ProcessBooleanParameter(parts, FlagStrings.Progress, Flag.Progress, ref i); + + // Size + ProcessBooleanParameter(parts, FlagStrings.Size, Flag.Size, ref i); + + #endregion + + #region Int64 flags + + // Block Size + longValue = ProcessInt64Parameter(parts, FlagStrings.BlockSize, Flag.BlockSize, ref i); + if (longValue == Int64.MinValue) + return false; + else if (longValue != null) + BlockSizeValue = longValue; + + // Count + longValue = ProcessInt64Parameter(parts, FlagStrings.Count, Flag.Count, ref i); + if (longValue == Int64.MinValue) + return false; + else if (longValue != null) + CountValue = longValue; + + // Seek + longValue = ProcessInt64Parameter(parts, FlagStrings.Seek, Flag.Seek, ref i); + if (longValue == Int64.MinValue) + return false; + else if (longValue != null) + SeekValue = longValue; + + // Skip + longValue = ProcessInt64Parameter(parts, FlagStrings.Skip, Flag.Skip, ref i); + if (longValue == Int64.MinValue) + return false; + else if (longValue != null) + SkipValue = longValue; + + #endregion + + #region String flags + + // Filter (fixed, removable, disk, partition) + stringValue = ProcessStringParameter(parts, FlagStrings.Filter, Flag.Filter, ref i); + if (!string.IsNullOrEmpty(stringValue)) + FilterValue = stringValue; + + // Input File + stringValue = ProcessStringParameter(parts, FlagStrings.InputFile, Flag.InputFile, ref i); + if (string.Equals(stringValue, string.Empty)) + return false; + else if (stringValue != null) + InputFileValue = stringValue; + + // Output File + stringValue = ProcessStringParameter(parts, FlagStrings.OutputFile, Flag.OutputFile, ref i); + if (string.Equals(stringValue, string.Empty)) + return false; + else if (stringValue != null) + OutputFileValue = stringValue; + + #endregion + } + return true; } + + /// + /// Get the list of commands that use a given flag + /// + /// Flag value to get commands for + /// List of Commands, if possible + private List GetSupportedCommands(Flag flag) + { + var commands = new List(); + switch (flag) + { + #region Boolean flags + + case Flag.Progress: + commands.Add(Command.NONE); + break; + case Flag.Size: + commands.Add(Command.NONE); + break; + + #endregion + + #region Int64 flags + + case Flag.BlockSize: + commands.Add(Command.NONE); + break; + case Flag.Count: + commands.Add(Command.NONE); + break; + case Flag.Seek: + commands.Add(Command.NONE); + break; + case Flag.Skip: + commands.Add(Command.NONE); + break; + + #endregion + + #region String flags + + case Flag.Filter: + commands.Add(Command.NONE); + break; + case Flag.InputFile: + commands.Add(Command.NONE); + break; + case Flag.OutputFile: + commands.Add(Command.NONE); + break; + + #endregion + + case Flag.NONE: + default: + return commands; + } + + return commands; + } + + /// + /// Process a boolean parameter + /// + /// List of parts to be referenced + /// Flag string to check + /// Flag value corresponding to the flag + /// Reference to the position in the parts + /// True if the parameter was processed successfully or skipped, false otherwise + private bool ProcessBooleanParameter(List parts, string flagString, Flag flag, ref int i) + { + if (parts == null) + return false; + + if (parts[i] == flagString) + { + if (!GetSupportedCommands(flag).Contains(BaseCommand)) + return false; + + this[flag] = true; + } + + return true; + } + + /// + /// Process an Int64 parameter + /// + /// List of parts to be referenced + /// Flag string to check + /// Flag value corresponding to the flag + /// Reference to the position in the parts + /// Int64 value if success, Int64.MinValue if skipped, null on error/returns> + private long? ProcessInt64Parameter(List parts, string flagString, Flag flag, ref int i) + { + if (parts == null) + return null; + + if (parts[i].StartsWith(flagString)) + { + if (!GetSupportedCommands(flag).Contains(BaseCommand)) + return null; + + string[] commandParts = parts[i].Split('='); + if (commandParts.Length != 2) + return null; + + string valuePart = commandParts[1]; + long factor = 1; + + // Characters + if (valuePart.EndsWith("c", StringComparison.Ordinal)) + { + factor = 1; + valuePart.TrimEnd('c'); + } + + // Words + else if (valuePart.EndsWith("w", StringComparison.Ordinal)) + { + factor = 2; + valuePart.TrimEnd('w'); + } + + // Double Words + else if (valuePart.EndsWith("d", StringComparison.Ordinal)) + { + factor = 4; + valuePart.TrimEnd('d'); + } + + // Quad Words + else if (valuePart.EndsWith("q", StringComparison.Ordinal)) + { + factor = 8; + valuePart.TrimEnd('q'); + } + + // Kilobytes + else if (valuePart.EndsWith("k", StringComparison.Ordinal)) + { + factor = 1024; + valuePart.TrimEnd('k'); + } + + // Megabytes + else if (valuePart.EndsWith("M", StringComparison.Ordinal)) + { + factor = 1024 * 1024; + valuePart.TrimEnd('M'); + } + + // Gigabytes + else if (valuePart.EndsWith("G", StringComparison.Ordinal)) + { + factor = 1024 * 1024 * 1024; + valuePart.TrimEnd('G'); + } + + if (!IsValidInt64(valuePart)) + return null; + + this[flag] = true; + return long.Parse(valuePart) * factor; + } + + return Int64.MinValue; + } + + /// + /// Process a string parameter + /// + /// List of parts to be referenced + /// Flag string to check + /// Flag value corresponding to the flag + /// Reference to the position in the parts + /// String value if possible, string.Empty on missing, null on error + private string ProcessStringParameter(List parts, string flagString, Flag flag, ref int i) + { + if (parts == null) + return null; + + if (parts[i] == flagString) + { + if (!GetSupportedCommands(flag).Contains(BaseCommand)) + return null; + + string[] commandParts = parts[i].Split('='); + if (commandParts.Length != 2) + return null; + + string valuePart = commandParts[1]; + + this[flag] = true; + return valuePart.Trim('"'); + } + + return string.Empty; + } } } diff --git a/DICUI.Library/DiscImageCreator/Parameters.cs b/DICUI.Library/DiscImageCreator/Parameters.cs index 237c47cc..1e5a789f 100644 --- a/DICUI.Library/DiscImageCreator/Parameters.cs +++ b/DICUI.Library/DiscImageCreator/Parameters.cs @@ -163,7 +163,7 @@ namespace DICUI.DiscImageCreator List parameters = new List(); if (BaseCommand != Command.NONE) - parameters.Add(Converters.LongName((Command)BaseCommand)); + parameters.Add(Converters.LongName(BaseCommand)); else return null;