From 88cadff9ef1dad9cfdf049579bbb41b27df39408 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Tue, 13 Dec 2022 11:48:26 -0800 Subject: [PATCH] General UI Cleanup (#438) * Update Nuget packages to newest stable * Simplify path selection in UI * Update changelog * Fix broken normalization test * Update drive info before dumping --- CHANGELIST.md | 4 + MPF.Check/MPF.Check.csproj | 2 +- MPF.Check/Program.cs | 2 +- MPF.Core/MPF.Core.csproj | 6 +- MPF.Library/DumpEnvironment.cs | 84 ++++----- MPF.Library/InfoTool.cs | 64 ++----- MPF.Library/MPF.Library.csproj | 4 +- MPF.Modules/BaseParameters.cs | 2 +- MPF.Test/Library/DumpEnvironmentTests.cs | 2 +- MPF.Test/Library/InfoToolTests.cs | 23 ++- MPF.Test/MPF.Test.csproj | 8 +- MPF.UI.Core/MPF.UI.Core.csproj | 1 + MPF/MPF.csproj | 4 +- MPF/ViewModels/MainViewModel.cs | 222 ++++++++--------------- MPF/Windows/MainWindow.xaml | 24 +-- 15 files changed, 175 insertions(+), 277 deletions(-) diff --git a/CHANGELIST.md b/CHANGELIST.md index a72b8916..4e5aaad9 100644 --- a/CHANGELIST.md +++ b/CHANGELIST.md @@ -7,6 +7,10 @@ - Add .NET 6.0 to tests, remove msbuild args - Add HD-DVD to speed definitions - Add PS3 internal serial and version parsing (tjanas) +- Update Nuget packages to newest stable +- Simplify path selection in UI +- Fix broken normalization test +- Update drive info before dumping ### 2.4 (2022-10-26) - Update to DIC 20211001 diff --git a/MPF.Check/MPF.Check.csproj b/MPF.Check/MPF.Check.csproj index 2a190b4f..f8c07849 100644 --- a/MPF.Check/MPF.Check.csproj +++ b/MPF.Check/MPF.Check.csproj @@ -28,7 +28,7 @@ - + runtime; compile; build; native; analyzers; buildtransitive diff --git a/MPF.Check/Program.cs b/MPF.Check/Program.cs index b59d8c6c..a2a19b23 100644 --- a/MPF.Check/Program.cs +++ b/MPF.Check/Program.cs @@ -59,7 +59,7 @@ namespace MPF.Check if (!string.IsNullOrWhiteSpace(path)) drive = Drive.Create(null, path); - var env = new DumpEnvironment(options, "", filepath, drive, knownSystem, mediaType, null); + var env = new DumpEnvironment(options, filepath, drive, knownSystem, mediaType, null); // Finally, attempt to do the output dance var result = env.VerifyAndSaveDumpOutput(resultProgress, protectionProgress).ConfigureAwait(false).GetAwaiter().GetResult(); diff --git a/MPF.Core/MPF.Core.csproj b/MPF.Core/MPF.Core.csproj index f1c33a51..e677b803 100644 --- a/MPF.Core/MPF.Core.csproj +++ b/MPF.Core/MPF.Core.csproj @@ -56,9 +56,9 @@ - - - + + + diff --git a/MPF.Library/DumpEnvironment.cs b/MPF.Library/DumpEnvironment.cs index 8a25beb1..539b4848 100644 --- a/MPF.Library/DumpEnvironment.cs +++ b/MPF.Library/DumpEnvironment.cs @@ -19,14 +19,9 @@ namespace MPF.Library #region Output paths /// - /// Base output directory to write files to + /// Base output file path to write files to /// - public string OutputDirectory { get; private set; } - - /// - /// Base output filename for output - /// - public string OutputFilename { get; private set; } + public string OutputPath { get; private set; } #endregion @@ -87,15 +82,13 @@ namespace MPF.Library /// Constructor for a full DumpEnvironment object from user information /// /// - /// - /// + /// /// /// /// /// public DumpEnvironment(Options options, - string outputDirectory, - string outputFilename, + string outputPath, Drive drive, RedumpSystem? system, MediaType? type, @@ -105,7 +98,7 @@ namespace MPF.Library this.Options = options; // Output paths - (this.OutputDirectory, this.OutputFilename) = InfoTool.NormalizeOutputPaths(outputDirectory, outputFilename); + this.OutputPath = InfoTool.NormalizeOutputPaths(outputPath); // UI information this.Drive = drive; @@ -127,20 +120,26 @@ namespace MPF.Library if (this.Parameters.InternalProgram != InternalProgram.DiscImageCreator) return; - // Replace all instances in the output directory - this.OutputDirectory = this.OutputDirectory.Replace('.', '_'); + try + { + // Replace all instances in the output directory + string outputDirectory = Path.GetDirectoryName(this.OutputPath); + outputDirectory = outputDirectory.Replace(".", "_"); - // Currently, only periods in directories matter - // Leave the following code commented in case filename handling breaks again + // Replace all instances in the output filename + string outputFilename = Path.GetFileNameWithoutExtension(this.OutputPath); + outputFilename = outputFilename.Replace(".", "_"); - // Replace all instances in the output filename, except the extension - //string tempFilename = Path.GetFileNameWithoutExtension(this.OutputFilename) - // .Replace('.', '_'); - //string tempExtension = Path.GetExtension(this.OutputFilename)?.TrimStart('.'); - //this.OutputFilename = $"{tempFilename}.{tempExtension}"; + // Get the extension for recreating the path + string outputExtension = Path.GetExtension(this.OutputPath).TrimStart('.'); - // Assign the path to the filename as well for dumping - ((Modules.DiscImageCreator.Parameters)this.Parameters).Filename = Path.Combine(this.OutputDirectory, this.OutputFilename); + // Rebuild the output path + this.OutputPath = Path.Combine(outputDirectory, $"{outputFilename}.{outputExtension}"); + + // Assign the path to the filename as well for dumping + ((Modules.DiscImageCreator.Parameters)this.Parameters).Filename = this.OutputPath; + } + catch { } } /// @@ -207,28 +206,27 @@ namespace MPF.Library return null; // Set the proper parameters - string filename = OutputDirectory + Path.DirectorySeparatorChar + OutputFilename; switch (Options.InternalProgram) { case InternalProgram.Aaru: - Parameters = new Modules.Aaru.Parameters(System, Type, Drive.Letter, filename, driveSpeed, Options); + Parameters = new Modules.Aaru.Parameters(System, Type, Drive.Letter, this.OutputPath, driveSpeed, Options); break; case InternalProgram.DD: - Parameters = new Modules.DD.Parameters(System, Type, Drive.Letter, filename, driveSpeed, Options); + Parameters = new Modules.DD.Parameters(System, Type, Drive.Letter, this.OutputPath, driveSpeed, Options); break; case InternalProgram.DiscImageCreator: - Parameters = new Modules.DiscImageCreator.Parameters(System, Type, Drive.Letter, filename, driveSpeed, Options); + Parameters = new Modules.DiscImageCreator.Parameters(System, Type, Drive.Letter, this.OutputPath, driveSpeed, Options); break; case InternalProgram.Redumper: - Parameters = new Modules.Redumper.Parameters(System, Type, Drive.Letter, filename, driveSpeed, Options); + Parameters = new Modules.Redumper.Parameters(System, Type, Drive.Letter, this.OutputPath, driveSpeed, Options); break; // This should never happen, but it needs a fallback default: - Parameters = new Modules.DiscImageCreator.Parameters(System, Type, Drive.Letter, filename, driveSpeed, Options); + Parameters = new Modules.DiscImageCreator.Parameters(System, Type, Drive.Letter, this.OutputPath, driveSpeed, Options); break; } @@ -280,7 +278,7 @@ namespace MPF.Library // Execute internal tool progress?.Report(Result.Success($"Executing {Options.InternalProgram}... {(Options.ToolsInSeparateWindow ? "please wait!" : "see log for output!")}")); - Directory.CreateDirectory(OutputDirectory); + Directory.CreateDirectory(Path.GetDirectoryName(this.OutputPath)); await Task.Run(() => Parameters.ExecuteInternalProgram(Options.ToolsInSeparateWindow)); progress?.Report(Result.Success($"{Options.InternalProgram} has finished!")); @@ -313,8 +311,12 @@ namespace MPF.Library { resultProgress?.Report(Result.Success("Gathering submission information... please wait!")); + // Get the output directory and filename separately + string outputDirectory = Path.GetDirectoryName(this.OutputPath); + string outputFilename = Path.GetFileName(this.OutputPath); + // Check to make sure that the output had all the correct files - (bool foundFiles, List missingFiles) = InfoTool.FoundAllFiles(this.OutputDirectory, this.OutputFilename, this.Parameters, false); + (bool foundFiles, List missingFiles) = InfoTool.FoundAllFiles(outputDirectory, outputFilename, this.Parameters, false); if (!foundFiles) { resultProgress?.Report(Result.Failure($"There were files missing from the output:\n{string.Join("\n", missingFiles)}")); @@ -324,8 +326,7 @@ namespace MPF.Library // Extract the information from the output files resultProgress?.Report(Result.Success("Extracting output information from output files...")); SubmissionInfo submissionInfo = await InfoTool.ExtractOutputInformation( - this.OutputDirectory, - this.OutputFilename, + this.OutputPath, this.Drive, this.System, this.Type, @@ -378,7 +379,7 @@ namespace MPF.Library // Write the text output resultProgress?.Report(Result.Success("Writing information to !submissionInfo.txt...")); - (bool txtSuccess, string txtResult) = InfoTool.WriteOutputData(this.OutputDirectory, formattedValues); + (bool txtSuccess, string txtResult) = InfoTool.WriteOutputData(outputDirectory, formattedValues); if (txtSuccess) resultProgress?.Report(Result.Success(txtResult)); else @@ -388,7 +389,7 @@ namespace MPF.Library if (Options.ScanForProtection && Options.OutputSeparateProtectionFile) { resultProgress?.Report(Result.Success("Writing protection to !protectionInfo.txt...")); - bool scanSuccess = InfoTool.WriteProtectionData(this.OutputDirectory, submissionInfo); + bool scanSuccess = InfoTool.WriteProtectionData(outputDirectory, submissionInfo); if (scanSuccess) resultProgress?.Report(Result.Success("Writing complete!")); else @@ -399,7 +400,7 @@ namespace MPF.Library if (Options.OutputSubmissionJSON) { resultProgress?.Report(Result.Success($"Writing information to !submissionInfo.json{(Options.IncludeArtifacts ? ".gz" : string.Empty)}...")); - bool jsonSuccess = InfoTool.WriteOutputData(this.OutputDirectory, submissionInfo, Options.IncludeArtifacts); + bool jsonSuccess = InfoTool.WriteOutputData(outputDirectory, submissionInfo, Options.IncludeArtifacts); if (jsonSuccess) resultProgress?.Report(Result.Success("Writing complete!")); else @@ -410,7 +411,7 @@ namespace MPF.Library if (Options.CompressLogFiles) { resultProgress?.Report(Result.Success("Compressing log files...")); - (bool compressSuccess, string compressResult) = InfoTool.CompressLogFiles(this.OutputDirectory, this.OutputFilename, this.Parameters); + (bool compressSuccess, string compressResult) = InfoTool.CompressLogFiles(outputDirectory, outputFilename, this.Parameters); if (compressSuccess) resultProgress?.Report(Result.Success(compressResult)); else @@ -491,12 +492,11 @@ namespace MPF.Library return Result.Failure("Error! Current configuration is not supported!"); // Fix the output paths, just in case - (OutputDirectory, OutputFilename) = InfoTool.NormalizeOutputPaths(OutputDirectory, OutputFilename); + this.OutputPath = InfoTool.NormalizeOutputPaths(this.OutputPath); // Validate that the output path isn't on the dumping drive - string fullOutputPath = Path.GetFullPath(Path.Combine(OutputDirectory, OutputFilename)); - if (fullOutputPath[0] == Drive.Letter) - return Result.Failure($"Error! Cannot output to same drive that is being dumped!"); + if (this.OutputPath[0] == Drive.Letter) + return Result.Failure("Error! Cannot output to same drive that is being dumped!"); // Validate that the required program exists if (!File.Exists(Parameters.ExecutablePath)) @@ -505,7 +505,7 @@ namespace MPF.Library // Validate that the dumping drive doesn't contain the executable string fullExecutablePath = Path.GetFullPath(Parameters.ExecutablePath); if (fullExecutablePath[0] == Drive.Letter) - return Result.Failure("$Error! Cannot dump same drive that executable resides on!"); + return Result.Failure("Error! Cannot dump same drive that executable resides on!"); // Validate that the current configuration is supported return Tools.GetSupportStatus(System, Type); diff --git a/MPF.Library/InfoTool.cs b/MPF.Library/InfoTool.cs index accf5d83..ec276058 100644 --- a/MPF.Library/InfoTool.cs +++ b/MPF.Library/InfoTool.cs @@ -26,8 +26,7 @@ namespace MPF.Library /// /// Extract all of the possible information from a given input combination /// - /// Output folder to write to - /// Output filename to use as the base path + /// Output path to write to /// Drive object representing the current drive /// Currently selected system /// Currently selected media type @@ -37,8 +36,7 @@ namespace MPF.Library /// Optional protection progress callback /// SubmissionInfo populated based on outputs, null on error public static async Task ExtractOutputInformation( - string outputDirectory, - string outputFilename, + string outputPath, Drive drive, RedumpSystem? system, MediaType? mediaType, @@ -51,6 +49,10 @@ namespace MPF.Library if (!system.MediaTypes().Contains(mediaType)) return null; + // Split the output path for easier use + string outputDirectory = Path.GetDirectoryName(outputPath); + string outputFilename = Path.GetFileName(outputPath); + // Check that all of the relevant files are there (bool foundFiles, List missingFiles) = FoundAllFiles(outputDirectory, outputFilename, parameters, false); if (!foundFiles) @@ -1655,57 +1657,31 @@ namespace MPF.Library /// /// Normalize a split set of paths /// - /// Directory name to normalize - /// Filename to normalize - public static (string, string) NormalizeOutputPaths(string directory, string filename) + /// Path value to normalize + public static string NormalizeOutputPaths(string path) { + // The easy way try { - // Cache if we had a directory separator or not - bool endedWithDirectorySeparator = directory.EndsWith(Path.DirectorySeparatorChar.ToString()) - || directory.EndsWith(Path.AltDirectorySeparatorChar.ToString()); + // Trim quotes from the path + path = path.Trim('"'); - // Combine the path to make things separate easier - string combinedPath = Path.Combine(directory, filename); + // Try getting the combined path and returning that directly + string fullPath = Path.GetFullPath(path); + string fullDirectory = Path.GetDirectoryName(fullPath); + string fullFile = Path.GetFileName(fullPath); - // If we have have a blank path, just return - if (string.IsNullOrWhiteSpace(combinedPath)) - return (directory, filename); - - // Now get the normalized paths - directory = Path.GetDirectoryName(combinedPath); - filename = Path.GetFileName(combinedPath); - - // Take care of extra path characters - directory = new StringBuilder(directory) - .Replace(':', '_', 0, directory.LastIndexOf(':') == -1 ? 0 : directory.LastIndexOf(':')) - .ToString(); - - // Sanitize the directory path - directory = directory.Replace('?', '_'); + // Remove invalid path characters foreach (char c in Path.GetInvalidPathChars()) - directory = directory.Replace(c, '_'); + fullDirectory = fullDirectory.Replace(c, '_'); - // Sanitize the filename - filename = filename.Replace('?', '_'); + // Remove invalid filename characters foreach (char c in Path.GetInvalidFileNameChars()) - filename = filename.Replace(c, '_'); - - // If we had a directory separator at the end before, add it again - if (endedWithDirectorySeparator) - directory += Path.DirectorySeparatorChar; - - // If we have a root directory, sanitize - if (Directory.Exists(directory)) - { - var possibleRootDir = new DirectoryInfo(directory); - if (possibleRootDir.Parent == null) - directory = directory.Replace($"{Path.DirectorySeparatorChar}{Path.DirectorySeparatorChar}", $"{Path.DirectorySeparatorChar}"); - } + fullFile = fullFile.Replace(c, '_'); } catch { } - return (directory, filename); + return path; } #endregion diff --git a/MPF.Library/MPF.Library.csproj b/MPF.Library/MPF.Library.csproj index 072cb6fc..03535b00 100644 --- a/MPF.Library/MPF.Library.csproj +++ b/MPF.Library/MPF.Library.csproj @@ -28,10 +28,10 @@ - + runtime; compile; build; native; analyzers; buildtransitive - + diff --git a/MPF.Modules/BaseParameters.cs b/MPF.Modules/BaseParameters.cs index 5f4541c3..c8c78c36 100644 --- a/MPF.Modules/BaseParameters.cs +++ b/MPF.Modules/BaseParameters.cs @@ -242,7 +242,7 @@ namespace MPF.Modules /// /// String possibly representing parameters /// True if the parameters were set correctly, false otherwise - protected virtual bool ValidateAndSetParameters(string parameters) => true; + protected virtual bool ValidateAndSetParameters(string parameters) => !string.IsNullOrWhiteSpace(parameters); #endregion diff --git a/MPF.Test/Library/DumpEnvironmentTests.cs b/MPF.Test/Library/DumpEnvironmentTests.cs index 6f58071a..d0717af4 100644 --- a/MPF.Test/Library/DumpEnvironmentTests.cs +++ b/MPF.Test/Library/DumpEnvironmentTests.cs @@ -24,7 +24,7 @@ namespace MPF.Test.Library ? Drive.Create(InternalDriveType.Floppy, letter.ToString()) : Drive.Create(InternalDriveType.Optical, letter.ToString()); - var env = new DumpEnvironment(options, string.Empty, string.Empty, drive, RedumpSystem.IBMPCcompatible, mediaType, parameters); + var env = new DumpEnvironment(options, string.Empty, drive, RedumpSystem.IBMPCcompatible, mediaType, parameters); bool actual = env.ParametersValid(); Assert.Equal(expected, actual); diff --git a/MPF.Test/Library/InfoToolTests.cs b/MPF.Test/Library/InfoToolTests.cs index 9478645d..88803914 100644 --- a/MPF.Test/Library/InfoToolTests.cs +++ b/MPF.Test/Library/InfoToolTests.cs @@ -48,19 +48,18 @@ namespace MPF.Test.Library } [Theory] - [InlineData(null, null, null, null)] - [InlineData(" ", "", " ", "")] - [InlineData("super", "blah.bin", "super", "blah.bin")] - [InlineData("super\\hero", "blah.bin", "super\\hero", "blah.bin")] - [InlineData("super.hero", "blah.bin", "super.hero", "blah.bin")] - [InlineData("superhero", "blah.rev.bin", "superhero", "blah.rev.bin")] - [InlineData("super&hero", "blah.bin", "super&hero", "blah.bin")] - [InlineData("superhero", "blah&foo.bin", "superhero", "blah&foo.bin")] - public void NormalizeOutputPathsTest(string outputDirectory, string outputFilename, string expectedOutputDirectory, string expectedOutputFilename) + [InlineData(null, null)] + [InlineData(" ", " ")] + [InlineData("super\\blah.bin", "super\\blah.bin")] + [InlineData("super\\hero\\blah.bin", "super\\hero\\blah.bin")] + [InlineData("super.hero\\blah.bin", "super.hero\\blah.bin")] + [InlineData("superhero\\blah.rev.bin", "superhero\\blah.rev.bin")] + [InlineData("super&hero\\blah.bin", "super&hero\\blah.bin")] + [InlineData("superhero\\blah&foo.bin", "superhero\\blah&foo.bin")] + public void NormalizeOutputPathsTest(string outputPath, string expectedPath) { - (string actualOutputDirectory, string actualOutputFilename) = InfoTool.NormalizeOutputPaths(outputDirectory, outputFilename); - Assert.Equal(expectedOutputDirectory, actualOutputDirectory); - Assert.Equal(expectedOutputFilename, actualOutputFilename); + string actualPath = InfoTool.NormalizeOutputPaths(outputPath); + Assert.Equal(expectedPath, actualPath); } [Fact] diff --git a/MPF.Test/MPF.Test.csproj b/MPF.Test/MPF.Test.csproj index 37184f37..bb873180 100644 --- a/MPF.Test/MPF.Test.csproj +++ b/MPF.Test/MPF.Test.csproj @@ -35,12 +35,12 @@ - - - + + + - + diff --git a/MPF.UI.Core/MPF.UI.Core.csproj b/MPF.UI.Core/MPF.UI.Core.csproj index b1559a8b..aa85e856 100644 --- a/MPF.UI.Core/MPF.UI.Core.csproj +++ b/MPF.UI.Core/MPF.UI.Core.csproj @@ -20,6 +20,7 @@ $(Version)-{chash:8} true false + 27abb4ca-bf7a-431e-932f-49153303d5ff diff --git a/MPF/MPF.csproj b/MPF/MPF.csproj index 2e1c25c1..18cc50dd 100644 --- a/MPF/MPF.csproj +++ b/MPF/MPF.csproj @@ -27,10 +27,10 @@ - + runtime; compile; build; native; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/MPF/ViewModels/MainViewModel.cs b/MPF/ViewModels/MainViewModel.cs index d3e69d1b..5db5fd2b 100644 --- a/MPF/ViewModels/MainViewModel.cs +++ b/MPF/ViewModels/MainViewModel.cs @@ -60,11 +60,6 @@ namespace MPF.UI.ViewModels /// private bool _canExecuteSelectionChanged = false; - /// - /// Indicates if TextChanged events can be executed - /// - private bool _canExecuteTextChanged = false; - #endregion /// @@ -249,11 +244,11 @@ namespace MPF.UI.ViewModels public void ExitApplication() => Application.Current.Shutdown(); /// - /// Set the output directory from a dialog box + /// Set the output path from a dialog box /// - public void SetOutputDirectory() + public void SetOutputPath() { - BrowseFolder(); + BrowseFile(); EnsureDiscInformation(); } @@ -572,7 +567,7 @@ namespace MPF.UI.ViewModels App.Instance.EnableParametersCheckBox.Click += EnableParametersCheckBoxClick; App.Instance.MediaScanButton.Click += MediaScanButtonClick; App.Instance.UpdateVolumeLabel.Click += UpdateVolumeLabelClick; - App.Instance.OutputDirectoryBrowseButton.Click += OutputDirectoryBrowseButtonClick; + App.Instance.OutputPathBrowseButton.Click += OutputPathBrowseButtonClick; App.Instance.StartStopButton.Click += StartStopButtonClick; // User Area SelectionChanged @@ -580,10 +575,6 @@ namespace MPF.UI.ViewModels App.Instance.MediaTypeComboBox.SelectionChanged += MediaTypeComboBoxSelectionChanged; App.Instance.DriveLetterComboBox.SelectionChanged += DriveLetterComboBoxSelectionChanged; App.Instance.DriveSpeedComboBox.SelectionChanged += DriveSpeedComboBoxSelectionChanged; - - // User Area TextChanged - App.Instance.OutputFilenameTextBox.TextChanged += OutputFilenameTextBoxTextChanged; - App.Instance.OutputDirectoryTextBox.TextChanged += OutputDirectoryTextBoxTextChanged; } /// @@ -592,7 +583,6 @@ namespace MPF.UI.ViewModels private void EnableEventHandlers() { _canExecuteSelectionChanged = true; - EnablePathEventHandlers(); } /// @@ -601,23 +591,6 @@ namespace MPF.UI.ViewModels private void DisableEventHandlers() { _canExecuteSelectionChanged = false; - DisablePathEventHandlers(); - } - - /// - /// Enable path textbox event handlers - /// - private void EnablePathEventHandlers() - { - _canExecuteTextChanged = true; - } - - /// - /// Disable path textbox event handlers - /// - private void DisablePathEventHandlers() - { - _canExecuteTextChanged = false; } /// @@ -628,9 +601,8 @@ namespace MPF.UI.ViewModels App.Instance.OptionsMenuItem.IsEnabled = false; App.Instance.SystemTypeComboBox.IsEnabled = false; App.Instance.MediaTypeComboBox.IsEnabled = false; - App.Instance.OutputFilenameTextBox.IsEnabled = false; - App.Instance.OutputDirectoryTextBox.IsEnabled = false; - App.Instance.OutputDirectoryBrowseButton.IsEnabled = false; + App.Instance.OutputPathTextBox.IsEnabled = false; + App.Instance.OutputPathBrowseButton.IsEnabled = false; App.Instance.DriveLetterComboBox.IsEnabled = false; App.Instance.DriveSpeedComboBox.IsEnabled = false; App.Instance.EnableParametersCheckBox.IsEnabled = false; @@ -648,9 +620,8 @@ namespace MPF.UI.ViewModels App.Instance.OptionsMenuItem.IsEnabled = true; App.Instance.SystemTypeComboBox.IsEnabled = true; App.Instance.MediaTypeComboBox.IsEnabled = true; - App.Instance.OutputFilenameTextBox.IsEnabled = true; - App.Instance.OutputDirectoryTextBox.IsEnabled = true; - App.Instance.OutputDirectoryBrowseButton.IsEnabled = true; + App.Instance.OutputPathTextBox.IsEnabled = true; + App.Instance.OutputPathBrowseButton.IsEnabled = true; App.Instance.DriveLetterComboBox.IsEnabled = true; App.Instance.DriveSpeedComboBox.IsEnabled = true; App.Instance.EnableParametersCheckBox.IsEnabled = true; @@ -798,24 +769,37 @@ namespace MPF.UI.ViewModels #region Helpers /// - /// Browse for an output folder + /// Browse for an output file path /// - private void BrowseFolder() + private void BrowseFile() { - string currentPath = App.Options.DefaultOutputPath; - if (string.IsNullOrWhiteSpace(currentPath) || !Directory.Exists(currentPath)) - currentPath = System.AppDomain.CurrentDomain.BaseDirectory; + // Get the current path, if possible + string currentPath = App.Instance.OutputPathTextBox.Text; + if (string.IsNullOrWhiteSpace(currentPath)) + currentPath = Path.Combine(App.Options.DefaultOutputPath, "track.bin"); + if (string.IsNullOrWhiteSpace(currentPath)) + currentPath = Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "track.bin"); - WinForms.FolderBrowserDialog folderDialog = new WinForms.FolderBrowserDialog + // Get the full path + currentPath = Path.GetFullPath(currentPath); + + // Get the directory + string directory = Path.GetDirectoryName(currentPath); + Directory.CreateDirectory(directory); + + // Get the filename + string filename = Path.GetFileName(currentPath); + + WinForms.FileDialog fileDialog = new WinForms.SaveFileDialog { - ShowNewFolderButton = false, - SelectedPath = currentPath, + FileName = filename, + InitialDirectory = directory, }; - WinForms.DialogResult result = folderDialog.ShowDialog(); + WinForms.DialogResult result = fileDialog.ShowDialog(); if (result == WinForms.DialogResult.OK) { - App.Instance.OutputDirectoryTextBox.Text = folderDialog.SelectedPath; + App.Instance.OutputPathTextBox.Text = fileDialog.FileName; } } @@ -881,39 +865,12 @@ namespace MPF.UI.ViewModels /// Filled DumpEnvironment instance private DumpEnvironment DetermineEnvironment() { - // Populate the new environment - var env = new DumpEnvironment(App.Options, - App.Instance.OutputDirectoryTextBox.Text, - App.Instance.OutputFilenameTextBox.Text, + return new DumpEnvironment(App.Options, + App.Instance.OutputPathTextBox.Text, App.Instance.DriveLetterComboBox.SelectedItem as Drive, App.Instance.SystemTypeComboBox.SelectedItem as RedumpSystemComboBoxItem, App.Instance.MediaTypeComboBox.SelectedItem as Element, App.Instance.ParametersTextBox.Text); - - // Disable automatic reprocessing of the textboxes until we're done - DisablePathEventHandlers(); - - // Save the current cursor positions - int outputDirectorySelectionStart = App.Instance.OutputDirectoryTextBox.SelectionStart; - int outputFilenameSelectionStart = App.Instance.OutputFilenameTextBox.SelectionStart; - - // Set the new text - App.Instance.OutputDirectoryTextBox.Text = env.OutputDirectory; - App.Instance.OutputFilenameTextBox.Text = env.OutputFilename; - - // Set the cursor position back to where it was - App.Instance.OutputDirectoryTextBox.SelectionStart = outputDirectorySelectionStart; - App.Instance.OutputDirectoryTextBox.SelectionLength = 0; - App.Instance.OutputFilenameTextBox.SelectionStart = outputFilenameSelectionStart; - App.Instance.OutputFilenameTextBox.SelectionLength = 0; - - // Re-enable automatic reprocessing of textboxes - EnablePathEventHandlers(); - - // Ensure the UI gets updated - App.Instance.UpdateLayout(); - - return env; } /// @@ -991,33 +948,25 @@ namespace MPF.UI.ViewModels // Get the extension for the file for the next two statements string extension = Env.Parameters?.GetDefaultExtension(mediaType); - // Disable automatic reprocessing of the textboxes until we're done - DisablePathEventHandlers(); + // Set the output filename, if it's not already + if (string.IsNullOrEmpty(App.Instance.OutputPathTextBox.Text)) + { + string label = drive?.FormattedVolumeLabel ?? systemType.LongName(); + string directory = App.Options.DefaultOutputPath; + string filename = $"{label}{extension ?? ".bin"}"; - // Save the current cursor positions - int outputDirectorySelectionStart = App.Instance.OutputDirectoryTextBox.SelectionStart; - int outputFilenameSelectionStart = App.Instance.OutputFilenameTextBox.SelectionStart; + App.Instance.OutputPathTextBox.Text = Path.Combine(directory, label, filename); + } - // Set the output filename, if we changed drives or it's not already - if (driveChanged || string.IsNullOrEmpty(App.Instance.OutputFilenameTextBox.Text)) - App.Instance.OutputFilenameTextBox.Text = (drive?.FormattedVolumeLabel ?? systemType.LongName()) + (extension ?? ".bin"); + // Set the output filename, if we changed drives + else if (driveChanged) + { + string label = drive?.FormattedVolumeLabel ?? systemType.LongName(); + string directory = Path.GetDirectoryName(App.Instance.OutputPathTextBox.Text); + string filename = $"{label}{extension ?? ".bin"}"; - // If the extension for the file changed, update that automatically - else if (Path.GetExtension(App.Instance.OutputFilenameTextBox.Text) != extension) - App.Instance.OutputFilenameTextBox.Text = Path.GetFileNameWithoutExtension(App.Instance.OutputFilenameTextBox.Text) + (extension ?? ".bin"); - - // Set the output directory, if we changed drives or it's not already - if (driveChanged || string.IsNullOrEmpty(App.Instance.OutputDirectoryTextBox.Text)) - App.Instance.OutputDirectoryTextBox.Text = Path.Combine(App.Options.DefaultOutputPath, Path.GetFileNameWithoutExtension(App.Instance.OutputFilenameTextBox.Text) ?? string.Empty); - - // Set the cursor position back to where it was - App.Instance.OutputDirectoryTextBox.SelectionStart = outputDirectorySelectionStart; - App.Instance.OutputDirectoryTextBox.SelectionLength = 0; - App.Instance.OutputFilenameTextBox.SelectionStart = outputFilenameSelectionStart; - App.Instance.OutputFilenameTextBox.SelectionLength = 0; - - // Re-enable automatic reprocessing of textboxes - EnablePathEventHandlers(); + App.Instance.OutputPathTextBox.Text = Path.Combine(directory, label, filename); + } // Ensure the UI gets updated App.Instance.UpdateLayout(); @@ -1047,34 +996,9 @@ namespace MPF.UI.ViewModels else Env.Parameters.Speed = App.Instance.DriveSpeedComboBox.SelectedValue as int?; - // Disable automatic reprocessing of the textboxes until we're done - DisablePathEventHandlers(); - - // Save the current cursor positions - int outputDirectorySelectionStart = App.Instance.OutputDirectoryTextBox.SelectionStart; - int outputFilenameSelectionStart = App.Instance.OutputFilenameTextBox.SelectionStart; - string trimmedPath = Env.Parameters.OutputPath?.Trim('"') ?? string.Empty; - string outputDirectory = Path.GetDirectoryName(trimmedPath); - string outputFilename = Path.GetFileName(trimmedPath); - (outputDirectory, outputFilename) = InfoTool.NormalizeOutputPaths(outputDirectory, outputFilename); - if (!string.IsNullOrWhiteSpace(outputDirectory)) - App.Instance.OutputDirectoryTextBox.Text = outputDirectory; - else - outputDirectory = App.Instance.OutputDirectoryTextBox.Text; - if (!string.IsNullOrWhiteSpace(outputFilename)) - App.Instance.OutputFilenameTextBox.Text = outputFilename; - else - outputFilename = App.Instance.OutputFilenameTextBox.Text; - - // Set the cursor position back to where it was - App.Instance.OutputDirectoryTextBox.SelectionStart = outputDirectorySelectionStart; - App.Instance.OutputDirectoryTextBox.SelectionLength = 0; - App.Instance.OutputFilenameTextBox.SelectionStart = outputFilenameSelectionStart; - App.Instance.OutputFilenameTextBox.SelectionLength = 0; - - // Re-enable automatic reprocessing of textboxes - EnablePathEventHandlers(); + trimmedPath = InfoTool.NormalizeOutputPaths(trimmedPath); + App.Instance.OutputPathTextBox.Text = trimmedPath; MediaType? mediaType = Env.Parameters.GetMediaType(); int mediaTypeIndex = MediaTypes.FindIndex(m => m == mediaType); @@ -1271,6 +1195,10 @@ namespace MPF.UI.ViewModels // Disable all UI elements apart from dumping button DisableAllUIElements(); + // Refresh the drive, if it wasn't null + if (Env.Drive != null) + Env.Drive.RefreshDrive(); + // Output to the label and log App.Instance.StatusLabel.Text = "Starting dumping process... Please wait!"; App.Logger.LogLn("Starting dumping process... Please wait!"); @@ -1330,6 +1258,14 @@ namespace MPF.UI.ViewModels /// True if dumping should start, false otherwise private bool ValidateBeforeDumping() { + // Validate that we have an output path of any sort + if (string.IsNullOrWhiteSpace(Env.OutputPath)) + { + MessageBoxResult mbresult = CustomMessageBox.Show("No output path was provided so dumping cannot continue.", "Missing Path", MessageBoxButton.OK, MessageBoxImage.Exclamation); + App.Logger.LogLn("Dumping aborted!"); + return false; + } + // Validate that the user explicitly wants an inactive drive to be considered for dumping if (!Env.Drive.MarkedActive) { @@ -1345,8 +1281,12 @@ namespace MPF.UI.ViewModels } } + // Pre-split the output path + string outputDirectory = Path.GetDirectoryName(Env.OutputPath); + string outputFilename = Path.GetFileName(Env.OutputPath); + // If a complete dump already exists - (bool foundFiles, List _) = InfoTool.FoundAllFiles(Env.OutputDirectory, Env.OutputFilename, Env.Parameters, true); + (bool foundFiles, List _) = InfoTool.FoundAllFiles(outputDirectory, outputFilename, Env.Parameters, true); if (foundFiles) { MessageBoxResult mbresult = CustomMessageBox.Show("A complete dump already exists! Are you sure you want to overwrite?", "Overwrite?", MessageBoxButton.YesNo, MessageBoxImage.Exclamation); @@ -1359,7 +1299,7 @@ namespace MPF.UI.ViewModels // Validate that at least some space exists // TODO: Tie this to the size of the disc, type of disc, etc. - string fullPath = Path.GetFullPath(Env.OutputDirectory); + string fullPath = Path.GetFullPath(outputDirectory); var driveInfo = new DriveInfo(Path.GetPathRoot(fullPath)); if (driveInfo.AvailableFreeSpace < Math.Pow(2, 30)) { @@ -1514,28 +1454,10 @@ namespace MPF.UI.ViewModels } /// - /// Handler for OutputDirectoryBrowseButton Click event + /// Handler for OutputPathBrowseButton Click event /// - private void OutputDirectoryBrowseButtonClick(object sender, RoutedEventArgs e) => - SetOutputDirectory(); - - /// - /// Handler for OutputFilenameTextBox TextInput event - /// - private void OutputDirectoryTextBoxTextChanged(object sender, TextChangedEventArgs e) - { - if (_canExecuteTextChanged) - EnsureDiscInformation(); - } - - /// - /// Handler for OutputFilenameTextBox TextInput event - /// - private void OutputFilenameTextBoxTextChanged(object sender, TextChangedEventArgs e) - { - if (_canExecuteTextChanged) - EnsureDiscInformation(); - } + private void OutputPathBrowseButtonClick(object sender, RoutedEventArgs e) => + SetOutputPath(); /// /// Handler for StartStopButton Click event diff --git a/MPF/Windows/MainWindow.xaml b/MPF/Windows/MainWindow.xaml index 1863aa35..fff10854 100644 --- a/MPF/Windows/MainWindow.xaml +++ b/MPF/Windows/MainWindow.xaml @@ -119,7 +119,6 @@ -