Create and use main features for CLI and Check

This commit is contained in:
Matt Nadareski
2025-10-06 15:54:14 -04:00
parent 68932ab473
commit 896caec9cd
7 changed files with 427 additions and 213 deletions

View File

@@ -9,6 +9,7 @@
- Minor cleanup around last added
- Add placeholder command set creation
- Reduce unnecessary shared code
- Create and use main features for CLI and Check
### 3.4.2 (2025-09-30)

View File

@@ -45,6 +45,7 @@ namespace MPF.CLI.Features
Options = OptionsLoader.LoadFromConfig();
}
/// <inheritdoc/>
public override bool Execute()
{
// Create return values

View File

@@ -0,0 +1,157 @@
using MPF.Frontend;
using MPF.Frontend.Tools;
using SabreTools.CommandLine.Inputs;
namespace MPF.CLI.Features
{
internal sealed class MainFeature : SabreTools.CommandLine.Feature
{
#region Feature Definition
public const string DisplayName = "main";
/// <remarks>Flags are unused</remarks>
private static readonly string[] _flags = [];
/// <remarks>Description is unused</remarks>
internal const string _description = "";
#endregion
#region Inputs
private const string _customName = "custom";
private static readonly StringInput _customInput = new(_customName, ["-c", "--custom"], "Custom parameters to use");
private const string _deviceName = "device";
private static readonly StringInput _deviceInput = new(_deviceName, ["-d", "--device"], "Physical drive path (Required if no custom parameters set)");
private const string _fileName = "file";
private static readonly StringInput _fileInput = new(_fileName, ["-f", "--file"], "Output file path (Required if no custom parameters set)");
private const string _mediaTypeName = "media-type";
private static readonly StringInput _mediaTypeInput = new(_mediaTypeName, ["-t", "--mediatype"], "Set media type for dumping (Required for DIC)");
private const string _mountedName = "mounted";
private static readonly StringInput _mountedInput = new(_mountedName, ["-m", "--mounted"], "Mounted filesystem path for additional checks");
private const string _speedName = "speed";
private static readonly Int32Input _speedInput = new(_speedName, ["-s", "--speed"], "Override default dumping speed");
private const string _useName = "use";
private static readonly StringInput _useInput = new(_useName, ["-u", "--use"], "Override configured dumping program name");
#endregion
#region Properties
/// <summary>
/// Progrma-specific options
/// </summary>
public Program.CommandOptions CommandOptions { get; private set; }
/// <summary>
/// User-defined options
/// </summary>
public Options Options { get; }
#endregion
public MainFeature()
: base(DisplayName, _flags, _description)
{
CommandOptions = new Program.CommandOptions();
Options = new Options()
{
// Internal Program
InternalProgram = InternalProgram.NONE,
// Extra Dumping Options
ScanForProtection = false,
AddPlaceholders = true,
PullAllInformation = false,
AddFilenameSuffix = false,
OutputSubmissionJSON = false,
IncludeArtifacts = false,
CompressLogFiles = false,
DeleteUnnecessaryFiles = false,
CreateIRDAfterDumping = false,
// Protection Scanning Options
ScanArchivesForProtection = true,
IncludeDebugProtectionInformation = false,
HideDriveLetters = false,
// Redump Login Information
RetrieveMatchInformation = true,
RedumpUsername = null,
RedumpPassword = null,
};
Add(_useInput);
Add(_mediaTypeInput);
Add(_deviceInput);
Add(_mountedInput);
Add(_fileInput);
Add(_speedInput);
Add(_customInput);
}
/// <inheritdoc/>
public override bool ProcessArgs(string[] args, int index)
{
// If we have no arguments, just return
if (args == null || args.Length == 0)
return true;
// If we have an invalid start index, just return
if (index < 0 || index >= args.Length)
return false;
// Loop through the arguments and parse out values
for (; index < args.Length; index++)
{
// Use specific program
if (_useInput.ProcessInput(args, ref index))
Options.InternalProgram = _useInput.Value.ToInternalProgram();
// Set a media type
else if (_mediaTypeInput.ProcessInput(args, ref index))
CommandOptions.MediaType = OptionsLoader.ToMediaType(_mediaTypeInput.Value?.Trim('"'));
// Use a device path
else if (_deviceInput.ProcessInput(args, ref index))
CommandOptions.DevicePath = _deviceInput.Value;
// Use a mounted path for physical checks
else if (_mountedInput.ProcessInput(args, ref index))
CommandOptions.MountedPath = _mountedInput.Value;
// Use a file path
else if (_fileInput.ProcessInput(args, ref index))
CommandOptions.FilePath = _fileInput.Value;
// Set an override speed
else if (_speedInput.ProcessInput(args, ref index))
CommandOptions.DriveSpeed = _speedInput.Value;
// Use a custom parameters
else if (_customInput.ProcessInput(args, ref index))
CommandOptions.CustomParams = _customInput.Value;
// Default, add to inputs
else
Inputs.Add(args[index]);
}
return true;
}
/// <inheritdoc/>
public override bool Execute() => true;
/// <inheritdoc/>
public override bool VerifyInputs() => true;
}
}

View File

@@ -104,7 +104,11 @@ namespace MPF.CLI
// Default Behavior
default:
// Parse the system from the first argument
var mainFeature = new MainFeature();
mainFeature.ProcessArgs(args, 0);
opts = mainFeature.CommandOptions;
options = mainFeature.Options;
knownSystem = Extensions.ToRedumpSystem(featureName.Trim('"'));
// Validate the supplied credentials
@@ -123,9 +127,6 @@ namespace MPF.CLI
Console.WriteLine(message);
}
// Process any custom parameters
int startIndex = 1;
opts = LoadFromArguments(args, options, ref startIndex);
break;
}
@@ -332,64 +333,6 @@ namespace MPF.CLI
Console.WriteLine();
}
/// <summary>
/// Load the current set of options from application arguments
/// </summary>
private static CommandOptions LoadFromArguments(string[] args, Options options, ref int startIndex)
{
// Create return values
var opts = new CommandOptions();
// If we have no arguments, just return
if (args == null || args.Length == 0)
{
startIndex = 0;
return opts;
}
// If we have an invalid start index, just return
if (startIndex < 0 || startIndex >= args.Length)
return opts;
// Loop through the arguments and parse out values
for (; startIndex < args.Length; startIndex++)
{
// Use specific program
if (_useInput.ProcessInput(args, ref startIndex))
options.InternalProgram = _useInput.Value.ToInternalProgram();
// Set a media type
else if (_mediaTypeInput.ProcessInput(args, ref startIndex))
opts.MediaType = OptionsLoader.ToMediaType(_mediaTypeInput.Value?.Trim('"'));
// Use a device path
else if (_deviceInput.ProcessInput(args, ref startIndex))
opts.DevicePath = _deviceInput.Value;
// Use a mounted path for physical checks
else if (_mountedInput.ProcessInput(args, ref startIndex))
opts.MountedPath = _mountedInput.Value;
// Use a file path
else if (_fileInput.ProcessInput(args, ref startIndex))
opts.FilePath = _fileInput.Value;
// Set an override speed
else if (_speedInput.ProcessInput(args, ref startIndex))
opts.DriveSpeed = _speedInput.Value;
// Use a custom parameters
else if (_customInput.ProcessInput(args, ref startIndex))
opts.CustomParams = _customInput.Value;
// Default, we fall out
else
break;
}
return opts;
}
/// <summary>
/// Represents commandline options
/// </summary>

View File

@@ -69,6 +69,7 @@ namespace MPF.Check.Features
};
}
/// <inheritdoc/>
public override bool Execute()
{
// Create return values

View File

@@ -0,0 +1,256 @@
using MPF.Frontend;
using SabreTools.CommandLine.Inputs;
using SabreTools.RedumpLib;
namespace MPF.Check.Features
{
internal sealed class MainFeature : SabreTools.CommandLine.Feature
{
#region Feature Definition
public const string DisplayName = "main";
/// <remarks>Flags are unused</remarks>
private static readonly string[] _flags = [];
/// <remarks>Description is unused</remarks>
internal const string _description = "";
#endregion
#region Inputs
internal const string _createIrdName = "create-ird";
private readonly FlagInput _createIrdInput = new(_createIrdName, "--create-ird", "Create IRD from output files (PS3 only)");
internal const string _deleteName = "delete";
private readonly FlagInput _deleteInput = new(_deleteName, ["-d", "--delete"], "Enable unnecessary file deletion");
internal const string _disableArchivesName = "disable-archives";
private readonly FlagInput _disableArchivesInput = new(_disableArchivesName, "--disable-archives", "Disable scanning archives (requires --scan)");
internal const string _enableDebugName = "enable-debug";
private readonly FlagInput _enableDebugInput = new(_enableDebugName, "--enable-debug", "Enable debug protection information (requires --scan)");
internal const string _hideDriveLettersName = "hide-drive-letters";
private readonly FlagInput _hideDriveLettersInput = new(_hideDriveLettersName, "--hide-drive-letters", "Hide drive letters from scan output (requires --scan)");
internal const string _includeArtifactsName = "include-artifacts";
private readonly FlagInput _includeArtifactsInput = new(_includeArtifactsName, "--include-artifacts", "Include artifacts in JSON (requires --json)");
internal const string _jsonName = "json";
private readonly FlagInput _jsonInput = new(_jsonName, ["-j", "--json"], "Enable submission JSON output");
internal const string _loadSeedName = "load-seed";
private readonly StringInput _loadSeedInput = new(_loadSeedName, "--load-seed", "Load a seed submission JSON for user information");
internal const string _noPlaceholdersName = "no-placeholders";
private readonly FlagInput _noPlaceholdersInput = new(_noPlaceholdersName, "--no-placeholders", "Disable placeholder values in submission info");
internal const string _noRetrieveName = "no-retrieve";
private readonly FlagInput _noRetrieveInput = new(_noRetrieveName, "--no-retrieve", "Disable retrieving match information from Redump");
internal const string _pathName = "path";
private readonly StringInput _pathInput = new(_pathName, ["-p", "--path"], "Physical drive path for additional checks");
internal const string _pullAllName = "pull-all";
private readonly FlagInput _pullAllInput = new(_pullAllName, "--pull-all", "Pull all information from Redump (requires --credentials)");
internal const string _scanName = "scan";
private readonly FlagInput _scanInput = new(_scanName, ["-s", "--scan"], "Enable copy protection scan (requires --path)");
internal const string _suffixName = "suffix";
private readonly FlagInput _suffixInput = new(_suffixName, ["-x", "--suffix"], "Enable adding filename suffix");
internal const string _useName = "use";
private readonly StringInput _useInput = new(_useName, ["-u", "--use"], "Override configured dumping program name");
internal const string _zipName = "zip";
private readonly FlagInput _zipInput = new(_zipName, ["-z", "--zip"], "Enable log file compression");
#endregion
#region Properties
/// <summary>
/// Progrma-specific options
/// </summary>
public Program.CommandOptions CommandOptions { get; private set; }
/// <summary>
/// User-defined options
/// </summary>
public Options Options { get; }
#endregion
public MainFeature()
: base(DisplayName, _flags, _description)
{
CommandOptions = new Program.CommandOptions();
Options = new Options()
{
// Internal Program
InternalProgram = InternalProgram.NONE,
// Extra Dumping Options
ScanForProtection = false,
AddPlaceholders = true,
PullAllInformation = false,
AddFilenameSuffix = false,
OutputSubmissionJSON = false,
IncludeArtifacts = false,
CompressLogFiles = false,
DeleteUnnecessaryFiles = false,
CreateIRDAfterDumping = false,
// Protection Scanning Options
ScanArchivesForProtection = true,
IncludeDebugProtectionInformation = false,
HideDriveLetters = false,
// Redump Login Information
RetrieveMatchInformation = true,
RedumpUsername = null,
RedumpPassword = null,
};
Add(_useInput);
Add(_loadSeedInput);
Add(_noPlaceholdersInput);
Add(_createIrdInput);
Add(_noRetrieveInput);
// TODO: Figure out how to work with the credentials input
Add(_pullAllInput);
Add(_pathInput);
Add(_scanInput);
Add(_disableArchivesInput);
Add(_enableDebugInput);
Add(_hideDriveLettersInput);
Add(_suffixInput);
Add(_jsonInput);
Add(_includeArtifactsInput);
Add(_zipInput);
Add(_deleteInput);
}
/// <inheritdoc/>
public override bool ProcessArgs(string[] args, int index)
{
// These values require multiple parts to be active
bool scan = false,
enableArchives = true,
enableDebug = false,
hideDriveLetters = false;
// If we have no arguments, just return
if (args == null || args.Length == 0)
return true;
// Invalid index values are not processed
if (index < 0 || index >= args.Length)
return false;
// Loop through the arguments and parse out values
for (; index < args.Length; index++)
{
// Use specific program
if (_useInput.ProcessInput(args, ref index))
Options.InternalProgram = _useInput.Value.ToInternalProgram();
// Include seed info file
else if (_loadSeedInput.ProcessInput(args, ref index))
CommandOptions.Seed = Builder.CreateFromFile(_loadSeedInput.Value);
// Disable placeholder values in submission info
else if (_noPlaceholdersInput.ProcessInput(args, ref index))
Options.AddPlaceholders = false;
// Create IRD from output files (PS3 only)
else if (_createIrdInput.ProcessInput(args, ref index))
Options.CreateIRDAfterDumping = true;
// Retrieve Redump match information
else if (_noRetrieveInput.ProcessInput(args, ref index))
Options.RetrieveMatchInformation = false;
// Redump login
else if (args[index].StartsWith("-c=") || args[index].StartsWith("--credentials="))
{
string[] credentials = args[index].Split('=')[1].Split(';');
Options.RedumpUsername = credentials[0];
Options.RedumpPassword = credentials[1];
}
else if (args[index] == "-c" || args[index] == "--credentials")
{
Options.RedumpUsername = args[index + 1];
Options.RedumpPassword = args[index + 2];
index += 2;
}
// Pull all information (requires Redump login)
else if (_pullAllInput.ProcessInput(args, ref index))
Options.PullAllInformation = true;
// Use a device path for physical checks
else if (_pathInput.ProcessInput(args, ref index))
CommandOptions.DevicePath = _pathInput.Value;
// Scan for protection (requires device path)
else if (_scanInput.ProcessInput(args, ref index))
scan = true;
// Disable scanning archives (requires --scan)
else if (_scanInput.ProcessInput(args, ref index))
enableArchives = false;
// Enable debug protection information (requires --scan)
else if (_enableDebugInput.ProcessInput(args, ref index))
enableDebug = true;
// Hide drive letters from scan output (requires --scan)
else if (_hideDriveLettersInput.ProcessInput(args, ref index))
hideDriveLetters = true;
// Add filename suffix
else if (_suffixInput.ProcessInput(args, ref index))
Options.AddFilenameSuffix = true;
// Output submission JSON
else if (_jsonInput.ProcessInput(args, ref index))
Options.OutputSubmissionJSON = true;
// Include JSON artifacts
else if (_includeArtifactsInput.ProcessInput(args, ref index))
Options.IncludeArtifacts = true;
// Compress log and extraneous files
else if (_zipInput.ProcessInput(args, ref index))
Options.CompressLogFiles = true;
// Delete unnecessary files
else if (_deleteInput.ProcessInput(args, ref index))
Options.DeleteUnnecessaryFiles = true;
// Default, add to inputs
else
Inputs.Add(args[index]);
}
// Now deal with the complex options
Options.ScanForProtection = scan && !string.IsNullOrEmpty(CommandOptions.DevicePath);
Options.ScanArchivesForProtection = enableArchives && scan && !string.IsNullOrEmpty(CommandOptions.DevicePath);
Options.IncludeDebugProtectionInformation = enableDebug && scan && !string.IsNullOrEmpty(CommandOptions.DevicePath);
Options.HideDriveLetters = hideDriveLetters && scan && !string.IsNullOrEmpty(CommandOptions.DevicePath);
return true;
}
/// <inheritdoc/>
public override bool Execute() => true;
/// <inheritdoc/>
public override bool VerifyInputs() => true;
}
}

View File

@@ -10,7 +10,6 @@ using MPF.Frontend.Features;
using SabreTools.CommandLine;
using SabreTools.CommandLine.Features;
using SabreTools.CommandLine.Inputs;
using SabreTools.RedumpLib;
using SabreTools.RedumpLib.Data;
using SabreTools.RedumpLib.Web;
@@ -115,40 +114,16 @@ namespace MPF.Check
// Default Behavior
default:
// Create a default options object
options = new Options()
{
// Internal Program
InternalProgram = InternalProgram.NONE,
startIndex = 0;
// Extra Dumping Options
ScanForProtection = false,
AddPlaceholders = true,
PullAllInformation = false,
AddFilenameSuffix = false,
OutputSubmissionJSON = false,
IncludeArtifacts = false,
CompressLogFiles = false,
DeleteUnnecessaryFiles = false,
CreateIRDAfterDumping = false,
var mainFeature = new MainFeature();
mainFeature.ProcessArgs(args, 0);
// Protection Scanning Options
ScanArchivesForProtection = true,
IncludeDebugProtectionInformation = false,
HideDriveLetters = false,
// Redump Login Information
RetrieveMatchInformation = true,
RedumpUsername = null,
RedumpPassword = null,
};
// Parse the system from the first argument
opts = mainFeature.CommandOptions;
options = mainFeature.Options;
knownSystem = Extensions.ToRedumpSystem(featureName.Trim('"'));
// Loop through and process options
startIndex = 1;
opts = LoadFromArguments(args, options, ref startIndex);
args = [.. mainFeature.Inputs];
break;
}
@@ -302,126 +277,6 @@ namespace MPF.Check
Console.WriteLine();
}
/// <summary>
/// Load the current set of options from application arguments
/// </summary>
private static CommandOptions LoadFromArguments(string[] args, Options options, ref int startIndex)
{
// Create return values
var opts = new CommandOptions();
// These values require multiple parts to be active
bool scan = false,
enableArchives = true,
enableDebug = false,
hideDriveLetters = false;
// If we have no arguments, just return
if (args == null || args.Length == 0)
{
startIndex = 0;
return opts;
}
// If we have an invalid start index, just return
if (startIndex < 0 || startIndex >= args.Length)
return opts;
// Loop through the arguments and parse out values
for (; startIndex < args.Length; startIndex++)
{
// Use specific program
if (_useInput.ProcessInput(args, ref startIndex))
options.InternalProgram = _useInput.Value.ToInternalProgram();
// Include seed info file
else if (_loadSeedInput.ProcessInput(args, ref startIndex))
opts.Seed = Builder.CreateFromFile(_loadSeedInput.Value);
// Disable placeholder values in submission info
else if (_noPlaceholdersInput.ProcessInput(args, ref startIndex))
options.AddPlaceholders = false;
// Create IRD from output files (PS3 only)
else if (_createIrdInput.ProcessInput(args, ref startIndex))
options.CreateIRDAfterDumping = true;
// Retrieve Redump match information
else if (_noRetrieveInput.ProcessInput(args, ref startIndex))
options.RetrieveMatchInformation = false;
// Redump login
else if (args[startIndex].StartsWith("-c=") || args[startIndex].StartsWith("--credentials="))
{
string[] credentials = args[startIndex].Split('=')[1].Split(';');
options.RedumpUsername = credentials[0];
options.RedumpPassword = credentials[1];
}
else if (args[startIndex] == "-c" || args[startIndex] == "--credentials")
{
options.RedumpUsername = args[startIndex + 1];
options.RedumpPassword = args[startIndex + 2];
startIndex += 2;
}
// Pull all information (requires Redump login)
else if (_pullAllInput.ProcessInput(args, ref startIndex))
options.PullAllInformation = true;
// Use a device path for physical checks
else if (_pathInput.ProcessInput(args, ref startIndex))
opts.DevicePath = _pathInput.Value;
// Scan for protection (requires device path)
else if (_scanInput.ProcessInput(args, ref startIndex))
scan = true;
// Disable scanning archives (requires --scan)
else if (_scanInput.ProcessInput(args, ref startIndex))
enableArchives = false;
// Enable debug protection information (requires --scan)
else if (_enableDebugInput.ProcessInput(args, ref startIndex))
enableDebug = true;
// Hide drive letters from scan output (requires --scan)
else if (_hideDriveLettersInput.ProcessInput(args, ref startIndex))
hideDriveLetters = true;
// Add filename suffix
else if (_suffixInput.ProcessInput(args, ref startIndex))
options.AddFilenameSuffix = true;
// Output submission JSON
else if (_jsonInput.ProcessInput(args, ref startIndex))
options.OutputSubmissionJSON = true;
// Include JSON artifacts
else if (_includeArtifactsInput.ProcessInput(args, ref startIndex))
options.IncludeArtifacts = true;
// Compress log and extraneous files
else if (_zipInput.ProcessInput(args, ref startIndex))
options.CompressLogFiles = true;
// Delete unnecessary files
else if (_deleteInput.ProcessInput(args, ref startIndex))
options.DeleteUnnecessaryFiles = true;
// Default, we fall out
else
break;
}
// Now deal with the complex options
options.ScanForProtection = scan && !string.IsNullOrEmpty(opts.DevicePath);
options.ScanArchivesForProtection = enableArchives && scan && !string.IsNullOrEmpty(opts.DevicePath);
options.IncludeDebugProtectionInformation = enableDebug && scan && !string.IsNullOrEmpty(opts.DevicePath);
options.HideDriveLetters = hideDriveLetters && scan && !string.IsNullOrEmpty(opts.DevicePath);
return opts;
}
/// <summary>
/// Represents commandline options
/// </summary>