mirror of
https://github.com/SabreTools/MPF.git
synced 2026-07-02 17:24:48 +00:00
696 lines
29 KiB
C#
696 lines
29 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using BinaryObjectScanner;
|
|
using MPF.Core.Data;
|
|
using MPF.Core.ExecutionContexts;
|
|
using MPF.Core.Processors;
|
|
using MPF.Core.Utilities;
|
|
using SabreTools.RedumpLib;
|
|
using SabreTools.RedumpLib.Data;
|
|
|
|
namespace MPF.Core
|
|
{
|
|
/// <summary>
|
|
/// Represents the state of all settings to be used during dumping
|
|
/// </summary>
|
|
public class DumpEnvironment
|
|
{
|
|
#region Output paths
|
|
|
|
/// <summary>
|
|
/// Base output file path to write files to
|
|
/// </summary>
|
|
public string OutputPath { get; private set; }
|
|
|
|
#endregion
|
|
|
|
#region UI information
|
|
|
|
/// <summary>
|
|
/// Drive object representing the current drive
|
|
/// </summary>
|
|
public Drive? Drive { get; }
|
|
|
|
/// <summary>
|
|
/// ExecutionContext object representing how to invoke the internal program
|
|
/// </summary>
|
|
private BaseExecutionContext? _executionContext;
|
|
|
|
/// <summary>
|
|
/// Currently selected dumping program
|
|
/// </summary>
|
|
private readonly InternalProgram _internalProgram;
|
|
|
|
/// <summary>
|
|
/// Options object representing user-defined options
|
|
/// </summary>
|
|
private readonly Data.Options _options;
|
|
|
|
/// <summary>
|
|
/// Processor object representing how to process the outputs
|
|
/// </summary>
|
|
private BaseProcessor? _processor;
|
|
|
|
/// <summary>
|
|
/// Currently selected system
|
|
/// </summary>
|
|
private readonly RedumpSystem? _system;
|
|
|
|
/// <summary>
|
|
/// Currently selected media type
|
|
/// </summary>
|
|
private readonly MediaType? _type;
|
|
|
|
#endregion
|
|
|
|
#region Passthrough Fields
|
|
|
|
/// <inheritdoc cref="BaseExecutionContext.InputPath"/>
|
|
public string? ContextInputPath => _executionContext?.InputPath;
|
|
|
|
/// <inheritdoc cref="BaseExecutionContext.OutputPath"/>
|
|
public string? ContextOutputPath => _executionContext?.OutputPath;
|
|
|
|
/// <inheritdoc cref="BaseExecutionContext.Speed"/>
|
|
public int? Speed
|
|
{
|
|
get => _executionContext?.Speed;
|
|
set
|
|
{
|
|
if (_executionContext != null)
|
|
_executionContext.Speed = value;
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc cref="Extensions.LongName(RedumpSystem?)/>
|
|
public string? SystemName => _system.LongName();
|
|
|
|
#endregion
|
|
|
|
#region Event Handlers
|
|
|
|
/// <summary>
|
|
/// Generic way of reporting a message
|
|
/// </summary>
|
|
public EventHandler<StringEventArgs>? ReportStatus;
|
|
|
|
/// <summary>
|
|
/// Queue of items that need to be logged
|
|
/// </summary>
|
|
private ProcessingQueue<string>? outputQueue;
|
|
|
|
/// <summary>
|
|
/// Event handler for data returned from a process
|
|
/// </summary>
|
|
private void OutputToLog(object? proc, StringEventArgs args) => outputQueue?.Enqueue(args);
|
|
|
|
/// <summary>
|
|
/// Process the outputs in the queue
|
|
/// </summary>
|
|
private void ProcessOutputs(string nextOutput) => ReportStatus?.Invoke(this, nextOutput);
|
|
|
|
#endregion
|
|
|
|
/// <summary>
|
|
/// Constructor for a full DumpEnvironment object from user information
|
|
/// </summary>
|
|
/// <param name="options"></param>
|
|
/// <param name="outputPath"></param>
|
|
/// <param name="drive"></param>
|
|
/// <param name="system"></param>
|
|
/// <param name="type"></param>
|
|
/// <param name="internalProgram"></param>
|
|
/// <param name="parameters"></param>
|
|
public DumpEnvironment(Data.Options options,
|
|
string outputPath,
|
|
Drive? drive,
|
|
RedumpSystem? system,
|
|
MediaType? type,
|
|
InternalProgram? internalProgram,
|
|
string? parameters)
|
|
{
|
|
// Set options object
|
|
_options = options;
|
|
|
|
// Output paths
|
|
OutputPath = InfoTool.NormalizeOutputPaths(outputPath, false);
|
|
|
|
// UI information
|
|
Drive = drive;
|
|
_system = system ?? options.DefaultSystem;
|
|
_type = type ?? MediaType.NONE;
|
|
_internalProgram = internalProgram ?? options.InternalProgram;
|
|
|
|
// Dumping program
|
|
SetExecutionContext(parameters);
|
|
SetProcessor();
|
|
}
|
|
|
|
#region Internal Program Management
|
|
|
|
/// <summary>
|
|
/// Check output path for matching logs from all dumping programs
|
|
/// </summary>
|
|
public InternalProgram? CheckForMatchingProgram(string? outputDirectory, string outputFilename)
|
|
{
|
|
// If a complete dump exists from a different program
|
|
InternalProgram? programFound = null;
|
|
if (programFound == null && _internalProgram != InternalProgram.Aaru)
|
|
{
|
|
var processor = new Processors.Aaru(_system, _type);
|
|
(bool foundOtherFiles, _) = processor.FoundAllFiles(outputDirectory, outputFilename, true);
|
|
if (foundOtherFiles)
|
|
programFound = InternalProgram.Aaru;
|
|
}
|
|
if (programFound == null && _internalProgram != InternalProgram.DiscImageCreator)
|
|
{
|
|
var processor = new Processors.DiscImageCreator(_system, _type);
|
|
(bool foundOtherFiles, _) = processor.FoundAllFiles(outputDirectory, outputFilename, true);
|
|
if (foundOtherFiles)
|
|
programFound = InternalProgram.DiscImageCreator;
|
|
}
|
|
if (programFound == null && _internalProgram != InternalProgram.Redumper)
|
|
{
|
|
var processor = new Processors.Redumper(_system, _type);
|
|
(bool foundOtherFiles, _) = processor.FoundAllFiles(outputDirectory, outputFilename, true);
|
|
if (foundOtherFiles)
|
|
programFound = InternalProgram.Redumper;
|
|
}
|
|
|
|
return programFound;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set the parameters object based on the internal program and parameters string
|
|
/// </summary>
|
|
/// <param name="parameters">String representation of the parameters</param>
|
|
public bool SetExecutionContext(string? parameters)
|
|
{
|
|
_executionContext = _internalProgram switch
|
|
{
|
|
InternalProgram.Aaru => new ExecutionContexts.Aaru.ExecutionContext(parameters) { ExecutablePath = _options.AaruPath },
|
|
InternalProgram.DiscImageCreator => new ExecutionContexts.DiscImageCreator.ExecutionContext(parameters) { ExecutablePath = _options.DiscImageCreatorPath },
|
|
InternalProgram.Redumper => new ExecutionContexts.Redumper.ExecutionContext(parameters) { ExecutablePath = _options.RedumperPath },
|
|
|
|
// If no dumping program found, set to null
|
|
InternalProgram.NONE => null,
|
|
_ => null,
|
|
};
|
|
|
|
// Set system and type
|
|
if (_executionContext != null)
|
|
{
|
|
_executionContext.System = _system;
|
|
_executionContext.Type = _type;
|
|
}
|
|
|
|
return _executionContext != null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set the processor object based on the internal program
|
|
/// </summary>
|
|
public bool SetProcessor()
|
|
{
|
|
_processor = _internalProgram switch
|
|
{
|
|
InternalProgram.Aaru => new Processors.Aaru(_system, _type),
|
|
InternalProgram.CleanRip => new CleanRip(_system, _type),
|
|
InternalProgram.DiscImageCreator => new DiscImageCreator(_system, _type),
|
|
InternalProgram.PS3CFW => new PS3CFW(_system, _type),
|
|
InternalProgram.Redumper => new Redumper(_system, _type),
|
|
InternalProgram.UmdImageCreator => new UmdImageCreator(_system, _type),
|
|
InternalProgram.XboxBackupCreator => new XboxBackupCreator(_system, _type),
|
|
|
|
// If no dumping program found, set to null
|
|
InternalProgram.NONE => null,
|
|
_ => null,
|
|
};
|
|
|
|
return _processor != null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the full parameter string for either DiscImageCreator or Aaru
|
|
/// </summary>
|
|
/// <param name="driveSpeed">Nullable int representing the drive speed</param>
|
|
/// <returns>String representing the params, null on error</returns>
|
|
public string? GetFullParameters(int? driveSpeed)
|
|
{
|
|
// Populate with the correct params for inputs (if we're not on the default option)
|
|
if (_system != null && _type != MediaType.NONE)
|
|
{
|
|
// If drive letter is invalid, skip this
|
|
if (Drive == null)
|
|
return null;
|
|
|
|
// Set the proper parameters
|
|
_executionContext = _internalProgram switch
|
|
{
|
|
InternalProgram.Aaru => new ExecutionContexts.Aaru.ExecutionContext(_system, _type, Drive.Name, OutputPath, driveSpeed, _options),
|
|
InternalProgram.DiscImageCreator => new ExecutionContexts.DiscImageCreator.ExecutionContext(_system, _type, Drive.Name, OutputPath, driveSpeed, _options),
|
|
InternalProgram.Redumper => new ExecutionContexts.Redumper.ExecutionContext(_system, _type, Drive.Name, OutputPath, driveSpeed, _options),
|
|
|
|
// If no dumping program found, set to null
|
|
InternalProgram.NONE => null,
|
|
_ => null,
|
|
};
|
|
|
|
// Generate and return the param string
|
|
return _executionContext?.GenerateParameters();
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Passthrough Functionality
|
|
|
|
/// <inheritdoc cref="Extensions.DetectedByWindows(RedumpSystem?)"/>
|
|
public bool DetectedByWindows() => _system.DetectedByWindows();
|
|
|
|
/// <inheritdoc cref="EnumExtensions.DoesSupportDriveSpeed(MediaType?)"/>
|
|
public bool DoesSupportDriveSpeed() => _type.DoesSupportDriveSpeed();
|
|
|
|
/// <inheritdoc cref="BaseProcessor.FoundAllFiles(string?, string, bool)"/>
|
|
public bool FoundAllFiles(string? outputDirectory, string outputFilename, bool preCheck)
|
|
{
|
|
if (_processor == null)
|
|
return false;
|
|
|
|
return _processor.FoundAllFiles(outputDirectory, outputFilename, preCheck).Item1;
|
|
}
|
|
|
|
/// <inheritdoc cref="BaseExecutionContext.GetDefaultExtension(MediaType?)"/>
|
|
public string? GetDefaultExtension(MediaType? mediaType)
|
|
{
|
|
if (_executionContext == null)
|
|
return null;
|
|
|
|
return _executionContext.GetDefaultExtension(mediaType);
|
|
}
|
|
|
|
/// <inheritdoc cref="BaseExecutionContext.GetMediaType()"/>
|
|
public MediaType? GetMediaType()
|
|
{
|
|
if (_executionContext == null)
|
|
return null;
|
|
|
|
return _executionContext.GetMediaType();
|
|
}
|
|
|
|
/// <inheritdoc cref="Tools.GetSupportStatus(RedumpSystem?, MediaType?)"/>
|
|
public ResultEventArgs GetSupportStatus() => Tools.GetSupportStatus(_system, _type);
|
|
|
|
/// <inheritdoc cref="BaseExecutionContext.IsDumpingCommand()"/>
|
|
public bool IsDumpingCommand()
|
|
{
|
|
if (_executionContext == null)
|
|
return false;
|
|
|
|
return _executionContext.IsDumpingCommand();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Dumping
|
|
|
|
/// <summary>
|
|
/// Cancel an in-progress dumping process
|
|
/// </summary>
|
|
public void CancelDumping() => _executionContext?.KillInternalProgram();
|
|
|
|
/// <summary>
|
|
/// Eject the disc using DiscImageCreator
|
|
/// </summary>
|
|
public async Task<string?> EjectDisc() =>
|
|
await RunStandaloneDiscImageCreatorCommand(ExecutionContexts.DiscImageCreator.CommandStrings.Eject);
|
|
|
|
/// <summary>
|
|
/// Reset the current drive using DiscImageCreator
|
|
/// </summary>
|
|
public async Task<string?> ResetDrive() =>
|
|
await RunStandaloneDiscImageCreatorCommand(ExecutionContexts.DiscImageCreator.CommandStrings.Reset);
|
|
|
|
/// <summary>
|
|
/// Execute the initial invocation of the dumping programs
|
|
/// </summary>
|
|
/// <param name="progress">Optional result progress callback</param>
|
|
#if NET40
|
|
public ResultEventArgs Run(IProgress<ResultEventArgs>? progress = null)
|
|
#else
|
|
public async Task<ResultEventArgs> Run(IProgress<ResultEventArgs>? progress = null)
|
|
#endif
|
|
{
|
|
// If we don't have parameters
|
|
if (_executionContext == null)
|
|
return ResultEventArgs.Failure("Error! Current configuration is not supported!");
|
|
|
|
// Check that we have the basics for dumping
|
|
ResultEventArgs result = IsValidForDump();
|
|
if (!result)
|
|
return result;
|
|
|
|
// Invoke output processing, if needed
|
|
if (!_options.ToolsInSeparateWindow)
|
|
{
|
|
outputQueue = new ProcessingQueue<string>(ProcessOutputs);
|
|
if (_executionContext.ReportStatus != null)
|
|
_executionContext.ReportStatus += OutputToLog;
|
|
}
|
|
|
|
// Execute internal tool
|
|
progress?.Report(ResultEventArgs.Success($"Executing {_internalProgram}... {(_options.ToolsInSeparateWindow ? "please wait!" : "see log for output!")}"));
|
|
|
|
var directoryName = Path.GetDirectoryName(OutputPath);
|
|
if (!string.IsNullOrEmpty(directoryName))
|
|
Directory.CreateDirectory(directoryName);
|
|
|
|
#if NET40
|
|
var executeTask = Task.Factory.StartNew(() => _executionContext.ExecuteInternalProgram(_options.ToolsInSeparateWindow));
|
|
executeTask.Wait();
|
|
#else
|
|
await Task.Run(() => _executionContext.ExecuteInternalProgram(_options.ToolsInSeparateWindow));
|
|
#endif
|
|
progress?.Report(ResultEventArgs.Success($"{_internalProgram} has finished!"));
|
|
|
|
// Remove event handler if needed
|
|
if (!_options.ToolsInSeparateWindow)
|
|
{
|
|
outputQueue?.Dispose();
|
|
_executionContext.ReportStatus -= OutputToLog;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Verify that the current environment has a complete dump and create submission info is possible
|
|
/// </summary>
|
|
/// <param name="resultProgress">Optional result progress callback</param>
|
|
/// <param name="protectionProgress">Optional protection progress callback</param>
|
|
/// <param name="processUserInfo">Optional user prompt to deal with submission information</param>
|
|
/// <param name="seedInfo">A seed SubmissionInfo object that contains user data</param>
|
|
/// <returns>Result instance with the outcome</returns>
|
|
public async Task<ResultEventArgs> VerifyAndSaveDumpOutput(
|
|
IProgress<ResultEventArgs>? resultProgress = null,
|
|
IProgress<ProtectionProgress>? protectionProgress = null,
|
|
Func<SubmissionInfo?, (bool?, SubmissionInfo?)>? processUserInfo = null,
|
|
SubmissionInfo? seedInfo = null)
|
|
{
|
|
if (_processor == null)
|
|
return ResultEventArgs.Failure("Error! Current configuration is not supported!");
|
|
|
|
resultProgress?.Report(ResultEventArgs.Success("Gathering submission information... please wait!"));
|
|
|
|
// Get the output directory and filename separately
|
|
var outputDirectory = Path.GetDirectoryName(OutputPath);
|
|
var outputFilename = Path.GetFileName(OutputPath);
|
|
|
|
// Check to make sure that the output had all the correct files
|
|
(bool foundFiles, List<string> missingFiles) = _processor.FoundAllFiles(outputDirectory, outputFilename, false);
|
|
if (!foundFiles)
|
|
{
|
|
resultProgress?.Report(ResultEventArgs.Failure($"There were files missing from the output:\n{string.Join("\n", [.. missingFiles])}"));
|
|
return ResultEventArgs.Failure("Error! Please check output directory as dump may be incomplete!");
|
|
}
|
|
|
|
// Extract the information from the output files
|
|
resultProgress?.Report(ResultEventArgs.Success("Extracting output information from output files..."));
|
|
var submissionInfo = await SubmissionInfoTool.ExtractOutputInformation(
|
|
OutputPath,
|
|
Drive,
|
|
_system,
|
|
_type,
|
|
_options,
|
|
_processor,
|
|
resultProgress,
|
|
protectionProgress);
|
|
resultProgress?.Report(ResultEventArgs.Success("Extracting information complete!"));
|
|
|
|
// Inject seed submission info data, if necessary
|
|
if (seedInfo != null)
|
|
{
|
|
resultProgress?.Report(ResultEventArgs.Success("Injecting user-supplied information..."));
|
|
Builder.InjectSubmissionInformation(submissionInfo, seedInfo);
|
|
resultProgress?.Report(ResultEventArgs.Success("Information injection complete!"));
|
|
}
|
|
|
|
// Eject the disc automatically if configured to
|
|
if (_options.EjectAfterDump == true)
|
|
{
|
|
resultProgress?.Report(ResultEventArgs.Success($"Ejecting disc in drive {Drive?.Name}"));
|
|
await EjectDisc();
|
|
}
|
|
|
|
// Reset the drive automatically if configured to
|
|
if (_internalProgram == InternalProgram.DiscImageCreator && _options.DICResetDriveAfterDump)
|
|
{
|
|
resultProgress?.Report(ResultEventArgs.Success($"Resetting drive {Drive?.Name}"));
|
|
await ResetDrive();
|
|
}
|
|
|
|
// Get user-modifiable information if confugured to
|
|
if (_options.PromptForDiscInformation && processUserInfo != null)
|
|
{
|
|
resultProgress?.Report(ResultEventArgs.Success("Waiting for additional disc information..."));
|
|
|
|
bool? filledInfo;
|
|
(filledInfo, submissionInfo) = processUserInfo(submissionInfo);
|
|
|
|
if (filledInfo == true)
|
|
resultProgress?.Report(ResultEventArgs.Success("Additional disc information added!"));
|
|
else
|
|
resultProgress?.Report(ResultEventArgs.Success("Disc information skipped!"));
|
|
}
|
|
|
|
// Process special fields for site codes
|
|
resultProgress?.Report(ResultEventArgs.Success("Processing site codes..."));
|
|
Formatter.ProcessSpecialFields(submissionInfo);
|
|
resultProgress?.Report(ResultEventArgs.Success("Processing complete!"));
|
|
|
|
// Format the information for the text output
|
|
resultProgress?.Report(ResultEventArgs.Success("Formatting information..."));
|
|
(var formattedValues, var formatResult) = Formatter.FormatOutputData(submissionInfo, _options.EnableRedumpCompatibility);
|
|
if (formattedValues == null)
|
|
resultProgress?.Report(ResultEventArgs.Failure(formatResult));
|
|
else
|
|
resultProgress?.Report(ResultEventArgs.Success(formatResult));
|
|
|
|
// Get the filename suffix for auto-generated files
|
|
var filenameSuffix = _options.AddFilenameSuffix ? Path.GetFileNameWithoutExtension(outputFilename) : null;
|
|
|
|
// Write the text output
|
|
resultProgress?.Report(ResultEventArgs.Success("Writing information to !submissionInfo.txt..."));
|
|
(bool txtSuccess, string txtResult) = InfoTool.WriteOutputData(outputDirectory, filenameSuffix, formattedValues);
|
|
if (txtSuccess)
|
|
resultProgress?.Report(ResultEventArgs.Success(txtResult));
|
|
else
|
|
resultProgress?.Report(ResultEventArgs.Failure(txtResult));
|
|
|
|
// Write the copy protection output
|
|
if (submissionInfo?.CopyProtection?.FullProtections != null && submissionInfo.CopyProtection.FullProtections.Any())
|
|
{
|
|
if (_options.ScanForProtection && _options.OutputSeparateProtectionFile)
|
|
{
|
|
resultProgress?.Report(ResultEventArgs.Success("Writing protection to !protectionInfo.txt..."));
|
|
bool scanSuccess = InfoTool.WriteProtectionData(outputDirectory, filenameSuffix, submissionInfo, _options.HideDriveLetters);
|
|
if (scanSuccess)
|
|
resultProgress?.Report(ResultEventArgs.Success("Writing complete!"));
|
|
else
|
|
resultProgress?.Report(ResultEventArgs.Failure("Writing could not complete!"));
|
|
}
|
|
}
|
|
|
|
// Write the JSON output, if required
|
|
if (_options.OutputSubmissionJSON)
|
|
{
|
|
resultProgress?.Report(ResultEventArgs.Success($"Writing information to !submissionInfo.json{(_options.IncludeArtifacts ? ".gz" : string.Empty)}..."));
|
|
bool jsonSuccess = InfoTool.WriteOutputData(outputDirectory, filenameSuffix, submissionInfo, _options.IncludeArtifacts);
|
|
if (jsonSuccess)
|
|
resultProgress?.Report(ResultEventArgs.Success("Writing complete!"));
|
|
else
|
|
resultProgress?.Report(ResultEventArgs.Failure("Writing could not complete!"));
|
|
}
|
|
|
|
// Compress the logs, if required
|
|
if (_options.CompressLogFiles)
|
|
{
|
|
resultProgress?.Report(ResultEventArgs.Success("Compressing log files..."));
|
|
(bool compressSuccess, string compressResult) = InfoTool.CompressLogFiles(outputDirectory, filenameSuffix, outputFilename, _processor);
|
|
if (compressSuccess)
|
|
resultProgress?.Report(ResultEventArgs.Success(compressResult));
|
|
else
|
|
resultProgress?.Report(ResultEventArgs.Failure(compressResult));
|
|
}
|
|
|
|
// Delete unnecessary files, if required
|
|
if (_options.DeleteUnnecessaryFiles)
|
|
{
|
|
resultProgress?.Report(ResultEventArgs.Success("Deleting unnecessary files..."));
|
|
(bool deleteSuccess, string deleteResult) = InfoTool.DeleteUnnecessaryFiles(outputDirectory, outputFilename, _processor);
|
|
if (deleteSuccess)
|
|
resultProgress?.Report(ResultEventArgs.Success(deleteResult));
|
|
else
|
|
resultProgress?.Report(ResultEventArgs.Failure(deleteResult));
|
|
}
|
|
|
|
// Create PS3 IRD, if required
|
|
if (_options.CreateIRDAfterDumping && _system == RedumpSystem.SonyPlayStation3 && _type == MediaType.BluRay)
|
|
{
|
|
resultProgress?.Report(ResultEventArgs.Success("Creating IRD... please wait!"));
|
|
(bool deleteSuccess, string deleteResult) = await InfoTool.WriteIRD(OutputPath, submissionInfo?.Extras?.DiscKey, submissionInfo?.Extras?.DiscID, submissionInfo?.Extras?.PIC, submissionInfo?.SizeAndChecksums?.Layerbreak, submissionInfo?.SizeAndChecksums?.CRC32);
|
|
if (deleteSuccess)
|
|
resultProgress?.Report(ResultEventArgs.Success(deleteResult));
|
|
else
|
|
resultProgress?.Report(ResultEventArgs.Failure(deleteResult));
|
|
}
|
|
|
|
resultProgress?.Report(ResultEventArgs.Success("Submission information process complete!"));
|
|
return ResultEventArgs.Success();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks if the parameters are valid
|
|
/// </summary>
|
|
/// <returns>True if the configuration is valid, false otherwise</returns>
|
|
internal bool ParametersValid()
|
|
{
|
|
// Missing drive means it can never be valid
|
|
if (Drive == null)
|
|
return false;
|
|
|
|
bool parametersValid = _executionContext?.IsValid() ?? false;
|
|
bool floppyValid = !(Drive.InternalDriveType == InternalDriveType.Floppy ^ _type == MediaType.FloppyDisk);
|
|
|
|
// TODO: HardDisk being in the Removable category is a hack, fix this later
|
|
bool removableDiskValid = !((Drive.InternalDriveType == InternalDriveType.Removable || Drive.InternalDriveType == InternalDriveType.HardDisk)
|
|
^ (_type == MediaType.CompactFlash || _type == MediaType.SDCard || _type == MediaType.FlashDrive || _type == MediaType.HardDisk));
|
|
|
|
return parametersValid && floppyValid && removableDiskValid;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Run internal program async with an input set of parameters
|
|
/// </summary>
|
|
/// <param name="executionContext">ExecutionContext object representing how to invoke the internal program</param>
|
|
/// <returns>Standard output from commandline window</returns>
|
|
private static async Task<string> ExecuteInternalProgram(BaseExecutionContext parameters)
|
|
{
|
|
Process childProcess;
|
|
#if NET40
|
|
string output = await Task.Factory.StartNew(() =>
|
|
#else
|
|
string output = await Task.Run(() =>
|
|
#endif
|
|
{
|
|
childProcess = new Process()
|
|
{
|
|
StartInfo = new ProcessStartInfo()
|
|
{
|
|
FileName = parameters.ExecutablePath!,
|
|
Arguments = parameters.GenerateParameters()!,
|
|
CreateNoWindow = true,
|
|
UseShellExecute = false,
|
|
RedirectStandardInput = true,
|
|
RedirectStandardOutput = true,
|
|
},
|
|
};
|
|
childProcess.Start();
|
|
childProcess.WaitForExit(1000);
|
|
|
|
// Just in case, we want to push a button 5 times to clear any errors
|
|
for (int i = 0; i < 5; i++)
|
|
childProcess.StandardInput.WriteLine("Y");
|
|
|
|
string stdout = childProcess.StandardOutput.ReadToEnd();
|
|
childProcess.Dispose();
|
|
return stdout;
|
|
});
|
|
|
|
return output;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Validate the current environment is ready for a dump
|
|
/// </summary>
|
|
/// <returns>Result instance with the outcome</returns>
|
|
private ResultEventArgs IsValidForDump()
|
|
{
|
|
// Validate that everything is good
|
|
if (_executionContext == null || !ParametersValid())
|
|
return ResultEventArgs.Failure("Error! Current configuration is not supported!");
|
|
|
|
// Fix the output paths, just in case
|
|
OutputPath = InfoTool.NormalizeOutputPaths(OutputPath, false);
|
|
|
|
// Validate that the output path isn't on the dumping drive
|
|
if (Drive?.Name != null && OutputPath.StartsWith(Drive.Name))
|
|
return ResultEventArgs.Failure("Error! Cannot output to same drive that is being dumped!");
|
|
|
|
// Validate that the required program exists
|
|
if (!File.Exists(_executionContext.ExecutablePath))
|
|
return ResultEventArgs.Failure($"Error! {_executionContext.ExecutablePath} does not exist!");
|
|
|
|
// Validate that the dumping drive doesn't contain the executable
|
|
string fullExecutablePath = Path.GetFullPath(_executionContext.ExecutablePath!);
|
|
if (Drive?.Name != null && fullExecutablePath.StartsWith(Drive.Name))
|
|
return ResultEventArgs.Failure("Error! Cannot dump same drive that executable resides on!");
|
|
|
|
// Validate that the current configuration is supported
|
|
return Tools.GetSupportStatus(_system, _type);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Validate that DIscImageCreator is able to be found
|
|
/// </summary>
|
|
/// <returns>True if DiscImageCreator is found properly, false otherwise</returns>
|
|
private bool RequiredProgramsExist()
|
|
{
|
|
// Validate that the path is configured
|
|
if (string.IsNullOrEmpty(_options.DiscImageCreatorPath))
|
|
return false;
|
|
|
|
// Validate that the required program exists
|
|
if (!File.Exists(_options.DiscImageCreatorPath))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Run a standalone DiscImageCreator command
|
|
/// </summary>
|
|
/// <param name="command">Command string to run</param>
|
|
/// <returns>The output of the command on success, null on error</returns>
|
|
private async Task<string?> RunStandaloneDiscImageCreatorCommand(string command)
|
|
{
|
|
// Validate that DiscImageCreator is all set
|
|
if (!RequiredProgramsExist())
|
|
return null;
|
|
|
|
// Validate we're not trying to eject a non-optical
|
|
if (Drive == null || Drive.InternalDriveType != InternalDriveType.Optical)
|
|
return null;
|
|
|
|
CancelDumping();
|
|
|
|
var parameters = new ExecutionContexts.DiscImageCreator.ExecutionContext(string.Empty)
|
|
{
|
|
BaseCommand = command,
|
|
DrivePath = Drive.Name,
|
|
ExecutablePath = _options.DiscImageCreatorPath,
|
|
};
|
|
|
|
return await ExecuteInternalProgram(parameters);
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|