using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Windows; using System.Windows.Controls; using BurnOutSharp; using MPF.Core.Data; using MPF.Core.Utilities; using MPF.Library; using MPF.UI.Core.ComboBoxItems; using MPF.UI.Core.Windows; using SabreTools.RedumpLib.Data; using WPFCustomMessageBox; using WinForms = System.Windows.Forms; namespace MPF.UI.Core.ViewModels { public class MainViewModel { #region Fields /// /// Parent MainWindow object /// public MainWindow Parent { get; private set; } /// /// LogViewModel associated with the parent window /// public LogViewModel Logger => Parent.LogOutput.LogViewModel; /// /// Access to the current options /// public MPF.Core.Data.Options Options { get => _options; set { _options = value; OptionsLoader.SaveToConfig(_options); } } /// /// Currently selected or detected media type /// public MediaType? CurrentMediaType { get; set; } /// /// Current list of drives /// public List Drives { get; set; } /// /// Current dumping environment /// public DumpEnvironment Env { get; set; } /// /// Internal reference to Options /// private MPF.Core.Data.Options _options; #endregion #region Lists /// /// Current list of supported media types /// public List> MediaTypes { get; set; } = new List>(); /// /// Current list of supported system profiles /// public List Systems { get; set; } = RedumpSystemComboBoxItem.GenerateElements().ToList(); /// /// List of available internal programs /// public List> InternalPrograms { get; set; } = new List>(); #endregion #region Private Event Flags /// /// Indicates if SelectionChanged events can be executed /// private bool _canExecuteSelectionChanged = false; #endregion /// /// Generic constructor /// public MainViewModel() { _options = OptionsLoader.LoadFromConfig(); } /// /// Initialize the main window after loading /// public void Init(MainWindow parent) { // Set the parent window this.Parent = parent; // Load the log output this.Parent.LogPanel.IsExpanded = this.Options.OpenLogWindowAtStartup; // Disable buttons until we load fully this.Parent.StartStopButton.IsEnabled = false; this.Parent.MediaScanButton.IsEnabled = false; this.Parent.UpdateVolumeLabel.IsEnabled = false; this.Parent.CopyProtectScanButton.IsEnabled = false; // Add the click handlers to the UI AddEventHandlers(); // Display the debug option in the menu, if necessary if (this.Options.ShowDebugViewMenuItem) this.Parent.DebugViewMenuItem.Visibility = Visibility.Visible; // Finish initializing the rest of the values InitializeUIValues(removeEventHandlers: false, rescanDrives: true); // Check for updates, if necessary if (this.Options.CheckForUpdatesOnStartup) CheckForUpdates(showIfSame: false); } #region Population /// /// Get a complete list of active disc drives and fill the combo box /// /// TODO: Find a way for this to periodically run, or have it hook to a "drive change" event private void PopulateDrives() { if (this.Options.VerboseLogging) this.Logger.VerboseLogLn("Scanning for drives.."); // Always enable the media scan this.Parent.MediaScanButton.IsEnabled = true; this.Parent.UpdateVolumeLabel.IsEnabled = true; // If we have a selected drive, keep track of it char? lastSelectedDrive = (this.Parent.DriveLetterComboBox.SelectedValue as Drive)?.Letter; // Populate the list of drives and add it to the combo box Drives = Drive.CreateListOfDrives(this.Options.IgnoreFixedDrives); this.Parent.DriveLetterComboBox.ItemsSource = Drives; if (this.Parent.DriveLetterComboBox.Items.Count > 0) { if (this.Options.VerboseLogging) this.Logger.VerboseLogLn($"Found {Drives.Count} drives: {string.Join(", ", Drives.Select(d => d.Letter))}"); // Check for the last selected drive, if possible int index = -1; if (lastSelectedDrive != null) index = Drives.FindIndex(d => d.MarkedActive && d.Letter == lastSelectedDrive); // Check for active optical drives if (index == -1) index = Drives.FindIndex(d => d.MarkedActive && d.InternalDriveType == InternalDriveType.Optical); // Check for active floppy drives if (index == -1) index = Drives.FindIndex(d => d.MarkedActive && d.InternalDriveType == InternalDriveType.Floppy); // Check for any active drives if (index == -1) index = Drives.FindIndex(d => d.MarkedActive); // Set the selected index this.Parent.DriveLetterComboBox.SelectedIndex = (index != -1 ? index : 0); this.Parent.StatusLabel.Text = "Valid drive found! Choose your Media Type"; this.Parent.CopyProtectScanButton.IsEnabled = true; // Get the current system type if (index != -1) DetermineSystemType(); // Only enable the start/stop if we don't have the default selected this.Parent.StartStopButton.IsEnabled = ShouldEnableDumpingButton(); } else { if (this.Options.VerboseLogging) this.Logger.VerboseLogLn("Found no drives"); this.Parent.DriveLetterComboBox.SelectedIndex = -1; this.Parent.StatusLabel.Text = "No valid drive found!"; this.Parent.StartStopButton.IsEnabled = false; this.Parent.CopyProtectScanButton.IsEnabled = false; } // Ensure the UI gets updated this.Parent.UpdateLayout(); } /// /// Populate media type according to system type /// private void PopulateMediaType() { RedumpSystem? currentSystem = this.Parent.SystemTypeComboBox.SelectedItem as RedumpSystemComboBoxItem; if (currentSystem != null) { var mediaTypeValues = currentSystem.MediaTypes(); MediaTypes = Element.GenerateElements().Where(m => mediaTypeValues.Contains(m.Value)).ToList(); this.Parent.MediaTypeComboBox.ItemsSource = MediaTypes; this.Parent.MediaTypeComboBox.IsEnabled = MediaTypes.Count > 1; int currentIndex = MediaTypes.FindIndex(m => m == CurrentMediaType); this.Parent.MediaTypeComboBox.SelectedIndex = (currentIndex > -1 ? currentIndex : 0); } else { this.Parent.MediaTypeComboBox.IsEnabled = false; this.Parent.MediaTypeComboBox.ItemsSource = null; this.Parent.MediaTypeComboBox.SelectedIndex = -1; } // Ensure the UI gets updated this.Parent.UpdateLayout(); } /// /// Populate media type according to system type /// private void PopulateInternalPrograms() { // Get the current internal program InternalProgram internalProgram = this.Options.InternalProgram; // Create a static list of supported programs, not everything var internalPrograms = new List { InternalProgram.DiscImageCreator, InternalProgram.Aaru, InternalProgram.Redumper }; InternalPrograms = internalPrograms.Select(ip => new Element(ip)).ToList(); this.Parent.DumpingProgramComboBox.ItemsSource = InternalPrograms; // Select the current default dumping program int currentIndex = InternalPrograms.FindIndex(m => m == internalProgram); this.Parent.DumpingProgramComboBox.SelectedIndex = (currentIndex > -1 ? currentIndex : 0); // Ensure the UI gets updated this.Parent.UpdateLayout(); } #endregion #region UI Commands /// /// Change the currently selected dumping program /// public void ChangeDumpingProgram() { if (this.Options.VerboseLogging) this.Logger.VerboseLogLn($"Changed dumping program to: {(this.Parent.DumpingProgramComboBox.SelectedItem as Element).Name}"); EnsureDiscInformation(); GetOutputNames(false); } /// /// Change the currently selected media type /// public void ChangeMediaType(SelectionChangedEventArgs e) { // Only change the media type if the selection and not the list has changed if (e.RemovedItems.Count == 1 && e.AddedItems.Count == 1) { var selectedMediaType = this.Parent.MediaTypeComboBox.SelectedItem as Element; CurrentMediaType = selectedMediaType.Value; SetSupportedDriveSpeed(); } GetOutputNames(false); EnsureDiscInformation(); } /// /// Change the currently selected system /// public void ChangeSystem() { if (this.Options.VerboseLogging) this.Logger.VerboseLogLn($"Changed system to: {(this.Parent.SystemTypeComboBox.SelectedItem as RedumpSystemComboBoxItem).Name}"); PopulateMediaType(); GetOutputNames(false); EnsureDiscInformation(); } /// /// Check for available updates /// /// True to show the box even if it's the same, false to only show if it's different public void CheckForUpdates(bool showIfSame) { (bool different, string message, string url) = Tools.CheckForNewVersion(); // If we have a new version, put it in the clipboard if (different) Clipboard.SetText(url); this.Logger.SecretLogLn(message); if (url == null) message = "An exception occurred while checking for versions, please try again later. See the log window for more details."; if (showIfSame || different) CustomMessageBox.Show(message, "Version Update Check", MessageBoxButton.OK, different ? MessageBoxImage.Exclamation : MessageBoxImage.Information); } /// /// Shutdown the current application /// public static void ExitApplication() => Application.Current.Shutdown(); /// /// Set the output path from a dialog box /// public void SetOutputPath() { BrowseFile(); EnsureDiscInformation(); } /// /// Show the About text popup /// public void ShowAboutText() { string aboutText = $"Media Preservation Frontend (MPF)" + $"{Environment.NewLine}" + $"{Environment.NewLine}A community preservation frontend developed in C#." + $"{Environment.NewLine}Supports Redumper, Aaru, and DiscImageCreator." + $"{Environment.NewLine}Originally created to help the Redump project." + $"{Environment.NewLine}" + $"{Environment.NewLine}Thanks to everyone who has supported this project!" + $"{Environment.NewLine}" + $"{Environment.NewLine}Version {Tools.GetCurrentVersion()}"; this.Logger.SecretLogLn(aboutText); CustomMessageBox.Show(aboutText, "About", MessageBoxButton.OK, MessageBoxImage.Information); } /// /// Build a dummy SubmissionInfo and display it for testing /// public void ShowDebugDiscInfoWindow() { var submissionInfo = new SubmissionInfo() { SchemaVersion = 1, FullyMatchedID = 3, PartiallyMatchedIDs = new List { 0, 1, 2, 3 }, Added = DateTime.UtcNow, LastModified = DateTime.UtcNow, CommonDiscInfo = new CommonDiscInfoSection() { System = RedumpSystem.IBMPCcompatible, Media = DiscType.BD128, Title = "Game Title", ForeignTitleNonLatin = "Foreign Game Title", DiscNumberLetter = "1", DiscTitle = "Install Disc", Category = DiscCategory.Games, Region = Region.World, Languages = new Language?[] { Language.English, Language.Spanish, Language.French }, LanguageSelection = new LanguageSelection?[] { LanguageSelection.BiosSettings }, Serial = "Disc Serial", Layer0MasteringRing = "L0 Mastering Ring", Layer0MasteringSID = "L0 Mastering SID", Layer0ToolstampMasteringCode = "L0 Toolstamp", Layer0MouldSID = "L0 Mould SID", Layer0AdditionalMould = "L0 Additional Mould", Layer1MasteringRing = "L1 Mastering Ring", Layer1MasteringSID = "L1 Mastering SID", Layer1ToolstampMasteringCode = "L1 Toolstamp", Layer1MouldSID = "L1 Mould SID", Layer1AdditionalMould = "L1 Additional Mould", Layer2MasteringRing = "L2 Mastering Ring", Layer2MasteringSID = "L2 Mastering SID", Layer2ToolstampMasteringCode = "L2 Toolstamp", Layer3MasteringRing = "L3 Mastering Ring", Layer3MasteringSID = "L3 Mastering SID", Layer3ToolstampMasteringCode = "L3 Toolstamp", RingWriteOffset = "+12", Barcode = "UPC Barcode", EXEDateBuildDate = "19xx-xx-xx", ErrorsCount = "0", Comments = "Comment data line 1\r\nComment data line 2", #if NET48 CommentsSpecialFields = new Dictionary() #else CommentsSpecialFields = new Dictionary() #endif { [SiteCode.ISBN] = "ISBN", }, Contents = "Special contents 1\r\nSpecial contents 2", #if NET48 ContentsSpecialFields = new Dictionary() #else ContentsSpecialFields = new Dictionary() #endif { [SiteCode.PlayableDemos] = "Game Demo 1", }, }, VersionAndEditions = new VersionAndEditionsSection() { Version = "Original", VersionDatfile = "Alt", CommonEditions = new string[] { "Taikenban" }, OtherEditions = "Rerelease", }, EDC = new EDCSection() { EDC = YesNo.Yes, }, ParentCloneRelationship = new ParentCloneRelationshipSection() { ParentID = "12345", RegionalParent = false, }, Extras = new ExtrasSection() { PVD = "PVD with a stupidly long line and nothing else but a little more\nPVD with a stupidly long line and nothing else but a little more\nPVD with a stupidly long line and nothing else but a little more\nPVD with a stupidly long line and nothing else but a little more\nPVD with a stupidly long line and nothing else but a little more\nPVD with a stupidly long line and nothing else but a little more\n", DiscKey = "Disc key", DiscID = "Disc ID", PIC = "PIC", Header = "Header", BCA = "BCA", SecuritySectorRanges = "SSv1 Ranges", }, CopyProtection = new CopyProtectionSection() { AntiModchip = YesNo.Yes, LibCrypt = YesNo.No, LibCryptData = "LibCrypt data", Protection = "List of protections", SecuROMData = "SecuROM data", }, DumpersAndStatus = new DumpersAndStatusSection() { Status = DumpStatus.TwoOrMoreGreen, Dumpers = new string[] { "Dumper1", "Dumper2" }, OtherDumpers = "Dumper3", }, TracksAndWriteOffsets = new TracksAndWriteOffsetsSection() { ClrMameProData = "Datfile", Cuesheet = "Cuesheet", CommonWriteOffsets = new int[] { 0, 12, -12 }, OtherWriteOffsets = "-2", }, SizeAndChecksums = new SizeAndChecksumsSection() { Layerbreak = 0, Layerbreak2 = 1, Layerbreak3 = 2, Size = 12345, CRC32 = "CRC32", MD5 = "MD5", SHA1 = "SHA1", }, DumpingInfo = new DumpingInfoSection() { DumpingProgram = "DiscImageCreator 20500101", DumpingDate = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss"), Manufacturer = "ATAPI", Model = "Optical Drive", Firmware = "1.23", ReportedDiscType = "CD-R", }, Artifacts = new Dictionary() { ["Sample Artifact"] = "Sample Data", }, }; var result = ShowDiscInformationWindow(submissionInfo); InfoTool.ProcessSpecialFields(result.Item2); } /// /// Show the Options window /// public void ShowOptionsWindow() { var optionsWindow = new OptionsWindow(this.Options) { Focusable = true, Owner = this.Parent, ShowActivated = true, ShowInTaskbar = true, WindowStartupLocation = WindowStartupLocation.CenterOwner, }; optionsWindow.Closed += OnOptionsUpdated; optionsWindow.Show(); } /// /// Toggle the parameters input box /// public void ToggleParameters() { if (this.Parent.EnableParametersCheckBox.IsChecked == true) { this.Parent.SystemTypeComboBox.IsEnabled = false; this.Parent.MediaTypeComboBox.IsEnabled = false; this.Parent.OutputPathTextBox.IsEnabled = false; this.Parent.OutputPathBrowseButton.IsEnabled = false; this.Parent.MediaScanButton.IsEnabled = false; this.Parent.UpdateVolumeLabel.IsEnabled = false; this.Parent.CopyProtectScanButton.IsEnabled = false; this.Parent.ParametersTextBox.IsEnabled = true; } else { this.Parent.ParametersTextBox.IsEnabled = false; ProcessCustomParameters(); this.Parent.SystemTypeComboBox.IsEnabled = true; this.Parent.MediaTypeComboBox.IsEnabled = true; this.Parent.OutputPathTextBox.IsEnabled = true; this.Parent.OutputPathBrowseButton.IsEnabled = true; this.Parent.MediaScanButton.IsEnabled = true; this.Parent.UpdateVolumeLabel.IsEnabled = true; this.Parent.CopyProtectScanButton.IsEnabled = true; } } /// /// Toggle the Start/Stop button /// public async void ToggleStartStop() { // Dump or stop the dump if ((string)this.Parent.StartStopButton.Content == Interface.StartDumping) { StartDumping(); } else if ((string)this.Parent.StartStopButton.Content == Interface.StopDumping) { if (this.Options.VerboseLogging) this.Logger.VerboseLogLn("Canceling dumping process..."); Env.CancelDumping(); this.Parent.CopyProtectScanButton.IsEnabled = true; if (Env.Options.EjectAfterDump == true) { if (this.Options.VerboseLogging) this.Logger.VerboseLogLn($"Ejecting disc in drive {Env.Drive.Letter}"); await Env.EjectDisc(); } if (this.Options.DICResetDriveAfterDump) { if (this.Options.VerboseLogging) this.Logger.VerboseLogLn($"Resetting drive {Env.Drive.Letter}"); await Env.ResetDrive(); } } // Reset the progress bar this.Logger.ResetProgressBar(); } /// /// Update the internal options from a closed OptionsWindow /// /// OptionsWindow to copy back data from public void UpdateOptions(OptionsWindow optionsWindow) { if (optionsWindow?.OptionsViewModel.SavedSettings == true) { this.Options = new MPF.Core.Data.Options(optionsWindow.OptionsViewModel.Options); InitializeUIValues(removeEventHandlers: true, rescanDrives: true); } } #endregion #region UI Functionality /// /// Performs UI value setup end to end /// /// Whether event handlers need to be removed first /// Whether drives should be rescanned or not public async void InitializeUIValues(bool removeEventHandlers, bool rescanDrives) { // Disable the dumping button this.Parent.StartStopButton.IsEnabled = false; // Safely uncheck the parameters box, just in case if (this.Parent.EnableParametersCheckBox.IsChecked == true) { this.Parent.EnableParametersCheckBox.Checked -= EnableParametersCheckBoxClick; this.Parent.EnableParametersCheckBox.IsChecked = false; this.Parent.ParametersTextBox.IsEnabled = false; this.Parent.EnableParametersCheckBox.Checked += EnableParametersCheckBoxClick; } // Set the UI color scheme according to the options if (this.Options.EnableDarkMode) EnableDarkMode(); else EnableLightMode(); // Force the UI to reload after applying the theme this.Parent.UpdateLayout(); // Remove event handlers to ensure ordering if (removeEventHandlers) DisableEventHandlers(); // Populate the list of drives and determine the system if (rescanDrives) { this.Parent.StatusLabel.Text = "Creating drive list, please wait!"; await this.Parent.Dispatcher.InvokeAsync(() => PopulateDrives()); } else { await this.Parent.Dispatcher.InvokeAsync(() => DetermineSystemType()); } // Determine current media type, if possible await this.Parent.Dispatcher.InvokeAsync(() => PopulateMediaType()); CacheCurrentDiscType(); SetCurrentDiscType(); // Set the dumping program await this.Parent.Dispatcher.InvokeAsync(() => PopulateInternalPrograms()); // Set the initial environment and UI values SetSupportedDriveSpeed(); Env = DetermineEnvironment(); GetOutputNames(true); EnsureDiscInformation(); // Enable event handlers EnableEventHandlers(); // Enable the dumping button, if necessary this.Parent.StartStopButton.IsEnabled = ShouldEnableDumpingButton(); } /// /// Performs a fast update of the output path while skipping disc checks /// /// Whether event handlers need to be removed first private void FastUpdateLabel(bool removeEventHandlers) { // Disable the dumping button this.Parent.StartStopButton.IsEnabled = false; // Safely uncheck the parameters box, just in case if (this.Parent.EnableParametersCheckBox.IsChecked == true) { this.Parent.EnableParametersCheckBox.Checked -= EnableParametersCheckBoxClick; this.Parent.EnableParametersCheckBox.IsChecked = false; this.Parent.ParametersTextBox.IsEnabled = false; this.Parent.EnableParametersCheckBox.Checked += EnableParametersCheckBoxClick; } // Remove event handlers to ensure ordering if (removeEventHandlers) DisableEventHandlers(); // Refresh the drive info (this.Parent.DriveLetterComboBox.SelectedItem as Drive)?.RefreshDrive(); // Set the initial environment and UI values Env = DetermineEnvironment(); GetOutputNames(true); EnsureDiscInformation(); // Enable event handlers EnableEventHandlers(); // Enable the dumping button, if necessary this.Parent.StartStopButton.IsEnabled = ShouldEnableDumpingButton(); } /// /// Add all event handlers /// private void AddEventHandlers() { // Menu Bar Click this.Parent.AboutMenuItem.Click += AboutClick; this.Parent.AppExitMenuItem.Click += AppExitClick; this.Parent.CheckForUpdatesMenuItem.Click += CheckForUpdatesClick; this.Parent.DebugViewMenuItem.Click += DebugViewClick; this.Parent.OptionsMenuItem.Click += OptionsMenuItemClick; // User Area Click this.Parent.CopyProtectScanButton.Click += CopyProtectScanButtonClick; this.Parent.EnableParametersCheckBox.Click += EnableParametersCheckBoxClick; this.Parent.MediaScanButton.Click += MediaScanButtonClick; this.Parent.UpdateVolumeLabel.Click += UpdateVolumeLabelClick; this.Parent.OutputPathBrowseButton.Click += OutputPathBrowseButtonClick; this.Parent.StartStopButton.Click += StartStopButtonClick; // User Area SelectionChanged this.Parent.SystemTypeComboBox.SelectionChanged += SystemTypeComboBoxSelectionChanged; this.Parent.MediaTypeComboBox.SelectionChanged += MediaTypeComboBoxSelectionChanged; this.Parent.DriveLetterComboBox.SelectionChanged += DriveLetterComboBoxSelectionChanged; this.Parent.DriveSpeedComboBox.SelectionChanged += DriveSpeedComboBoxSelectionChanged; this.Parent.DumpingProgramComboBox.SelectionChanged += DumpingProgramComboBoxSelectionChanged; // User Area TextChanged this.Parent.OutputPathTextBox.TextChanged += OutputPathTextBoxTextChanged; } /// /// Enable all textbox and combobox event handlers /// private void EnableEventHandlers() { _canExecuteSelectionChanged = true; } /// /// Disable all textbox and combobox event handlers /// private void DisableEventHandlers() { _canExecuteSelectionChanged = false; } /// /// Disable all UI elements during dumping /// private void DisableAllUIElements() { this.Parent.OptionsMenuItem.IsEnabled = false; this.Parent.SystemTypeComboBox.IsEnabled = false; this.Parent.MediaTypeComboBox.IsEnabled = false; this.Parent.OutputPathTextBox.IsEnabled = false; this.Parent.OutputPathBrowseButton.IsEnabled = false; this.Parent.DriveLetterComboBox.IsEnabled = false; this.Parent.DriveSpeedComboBox.IsEnabled = false; this.Parent.DumpingProgramComboBox.IsEnabled = false; this.Parent.EnableParametersCheckBox.IsEnabled = false; this.Parent.StartStopButton.Content = Interface.StopDumping; this.Parent.MediaScanButton.IsEnabled = false; this.Parent.UpdateVolumeLabel.IsEnabled = false; this.Parent.CopyProtectScanButton.IsEnabled = false; } /// /// Enable all UI elements after dumping /// private void EnableAllUIElements() { this.Parent.OptionsMenuItem.IsEnabled = true; this.Parent.SystemTypeComboBox.IsEnabled = true; this.Parent.MediaTypeComboBox.IsEnabled = true; this.Parent.OutputPathTextBox.IsEnabled = true; this.Parent.OutputPathBrowseButton.IsEnabled = true; this.Parent.DriveLetterComboBox.IsEnabled = true; this.Parent.DriveSpeedComboBox.IsEnabled = true; this.Parent.DumpingProgramComboBox.IsEnabled = true; this.Parent.EnableParametersCheckBox.IsEnabled = true; this.Parent.StartStopButton.Content = Interface.StartDumping; this.Parent.MediaScanButton.IsEnabled = true; this.Parent.UpdateVolumeLabel.IsEnabled = true; this.Parent.CopyProtectScanButton.IsEnabled = true; } /// /// Recolor all UI elements for light mode /// private static void EnableLightMode() { var theme = new LightModeTheme(); theme.Apply(); } /// /// Recolor all UI elements for dark mode /// private static void EnableDarkMode() { var theme = new DarkModeTheme(); theme.Apply(); } #endregion #region Helpers /// /// Browse for an output file path /// private void BrowseFile() { // Get the current path, if possible string currentPath = this.Parent.OutputPathTextBox.Text; if (string.IsNullOrWhiteSpace(currentPath)) currentPath = Path.Combine(this.Options.DefaultOutputPath, "track.bin"); if (string.IsNullOrWhiteSpace(currentPath)) currentPath = Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "track.bin"); // Get the full path currentPath = Path.GetFullPath(currentPath); // Get the directory string directory = Path.GetDirectoryName(currentPath); // Get the filename string filename = Path.GetFileName(currentPath); WinForms.FileDialog fileDialog = new WinForms.SaveFileDialog { FileName = filename, InitialDirectory = directory, }; WinForms.DialogResult result = fileDialog.ShowDialog(); if (result == WinForms.DialogResult.OK) { this.Parent.OutputPathTextBox.Text = fileDialog.FileName; } } /// /// Cache the current disc type to internal variable /// private void CacheCurrentDiscType() { // If the selected item is invalid, we just skip if (!(this.Parent.DriveLetterComboBox.SelectedItem is Drive drive)) return; // Get reasonable default values based on the current system RedumpSystem? currentSystem = Systems[this.Parent.SystemTypeComboBox.SelectedIndex]; MediaType? defaultMediaType = currentSystem.MediaTypes().FirstOrDefault() ?? MediaType.CDROM; if (defaultMediaType == MediaType.NONE) defaultMediaType = MediaType.CDROM; #if NET48 || NETSTANDARD2_1 // If we're skipping detection, set the default value if (this.Options.SkipMediaTypeDetection) { if (this.Options.VerboseLogging) this.Logger.VerboseLogLn($"Media type detection disabled, defaulting to {defaultMediaType.LongName()}."); CurrentMediaType = defaultMediaType; } // If the drive is marked active, try to read from it else if (drive.MarkedActive) { if (this.Options.VerboseLogging) this.Logger.VerboseLog($"Trying to detect media type for drive {drive.Letter} [{drive.DriveFormat}].. "); (MediaType? detectedMediaType, string errorMessage) = drive.GetMediaType(); // If we got an error message, post it to the log if (errorMessage != null && this.Options.VerboseLogging) this.Logger.VerboseLogLn($"Message from detecting media type: {errorMessage}"); // If we got either an error or no media, default to the current System default if (detectedMediaType == null) { if (this.Options.VerboseLogging) this.Logger.VerboseLogLn($"Unable to detect, defaulting to {defaultMediaType.LongName()}."); CurrentMediaType = defaultMediaType; } else { if (this.Options.VerboseLogging) this.Logger.VerboseLogLn($"Detected {CurrentMediaType.LongName()}."); CurrentMediaType = detectedMediaType; } } // All other cases, just use the default else { if (this.Options.VerboseLogging) this.Logger.VerboseLogLn($"Drive marked as empty, defaulting to {defaultMediaType.LongName()}."); CurrentMediaType = defaultMediaType; } #else // Media type detection on initialize is always disabled if (this.Options.VerboseLogging) this.Logger.VerboseLogLn($"Media type detection not available, defaulting to {defaultMediaType.LongName()}."); CurrentMediaType = defaultMediaType; #endif // Ensure the UI gets updated this.Parent.UpdateLayout(); } /// /// Create a DumpEnvironment with all current settings /// /// Filled DumpEnvironment this.Parent private DumpEnvironment DetermineEnvironment() { return new DumpEnvironment(this.Options, this.Parent.OutputPathTextBox.Text, this.Parent.DriveLetterComboBox.SelectedItem as Drive, this.Parent.SystemTypeComboBox.SelectedItem as RedumpSystemComboBoxItem, this.Parent.MediaTypeComboBox.SelectedItem as Element, this.Parent.DumpingProgramComboBox.SelectedItem as Element, this.Parent.ParametersTextBox.Text); } /// /// Determine and set the current system type, if allowed /// private void DetermineSystemType() { if (Drives == null || Drives.Count == 0 || this.Parent.DriveLetterComboBox.SelectedIndex == -1) { if (this.Options.VerboseLogging) this.Logger.VerboseLogLn("Skipping system type detection because no valid drives found!"); } else if ((this.Parent.DriveLetterComboBox.SelectedItem as Drive)?.MarkedActive != true) { if (this.Options.VerboseLogging) this.Logger.VerboseLogLn("Skipping system type detection because drive not marked as active!"); } else if (!this.Options.SkipSystemDetection) { if (this.Options.VerboseLogging) this.Logger.VerboseLog($"Trying to detect system for drive {Drives[this.Parent.DriveLetterComboBox.SelectedIndex].Letter}.. "); var currentSystem = Drives[this.Parent.DriveLetterComboBox.SelectedIndex]?.GetRedumpSystem(this.Options.DefaultSystem) ?? this.Options.DefaultSystem; if (this.Options.VerboseLogging) this.Logger.VerboseLogLn(currentSystem == null ? "unable to detect." : ($"detected {currentSystem.LongName()}.")); if (currentSystem != null) { int sysIndex = Systems.FindIndex(s => s == currentSystem); this.Parent.SystemTypeComboBox.SelectedIndex = sysIndex; } } else if (this.Options.SkipSystemDetection && this.Options.DefaultSystem != null) { var currentSystem = this.Options.DefaultSystem; if (this.Options.VerboseLogging) this.Logger.VerboseLogLn($"System detection disabled, setting to default of {currentSystem.LongName()}."); int sysIndex = Systems.FindIndex(s => s == currentSystem); this.Parent.SystemTypeComboBox.SelectedIndex = sysIndex; } // Ensure the UI gets updated this.Parent.UpdateLayout(); } /// /// Ensure information is consistent with the currently selected disc type /// public void EnsureDiscInformation() { // Get the current environment information Env = DetermineEnvironment(); // Get the status to write out Result result = Tools.GetSupportStatus(Env.System, Env.Type); this.Parent.StatusLabel.Text = result.Message; // Set the index for the current disc type SetCurrentDiscType(); // Enable or disable the button this.Parent.StartStopButton.IsEnabled = result && ShouldEnableDumpingButton(); // If we're in a type that doesn't support drive speeds this.Parent.DriveSpeedComboBox.IsEnabled = Env.Type.DoesSupportDriveSpeed(); // If input params are not enabled, generate the full parameters from the environment if (!this.Parent.ParametersTextBox.IsEnabled) { string generated = Env.GetFullParameters((int?)this.Parent.DriveSpeedComboBox.SelectedItem); if (generated != null) this.Parent.ParametersTextBox.Text = generated; } // Ensure the UI gets updated this.Parent.UpdateLayout(); } /// /// Get the default output directory name from the currently selected drive /// /// Force an updated name if the drive letter changes public void GetOutputNames(bool driveChanged) { if (Drives == null || Drives.Count == 0 || this.Parent.DriveLetterComboBox.SelectedIndex == -1) { if (this.Options.VerboseLogging) this.Logger.VerboseLog("Skipping output name building because no valid drives found!"); return; } Drive drive = this.Parent.DriveLetterComboBox.SelectedItem as Drive; RedumpSystem? systemType = this.Parent.SystemTypeComboBox.SelectedItem as RedumpSystemComboBoxItem; MediaType? mediaType = this.Parent.MediaTypeComboBox.SelectedItem as Element; // Get the extension for the file for the next two statements string extension = Env.Parameters?.GetDefaultExtension(mediaType); // Set the output filename, if it's not already if (string.IsNullOrEmpty(this.Parent.OutputPathTextBox.Text)) { string label = drive?.FormattedVolumeLabel ?? systemType.LongName(); string directory = this.Options.DefaultOutputPath; string filename = $"{label}{extension ?? ".bin"}"; // If the path ends with the label already if (directory.EndsWith(label, StringComparison.OrdinalIgnoreCase)) directory = Path.GetDirectoryName(directory); this.Parent.OutputPathTextBox.Text = Path.Combine(directory, label, filename); } // Set the output filename, if we changed drives else if (driveChanged) { string label = drive?.FormattedVolumeLabel ?? systemType.LongName(); string oldPath = InfoTool.NormalizeOutputPaths(this.Parent.OutputPathTextBox.Text, false); string oldFilename = Path.GetFileNameWithoutExtension(oldPath); string directory = Path.GetDirectoryName(oldPath); string filename = $"{label}{extension ?? ".bin"}"; // If the previous path included the label if (directory.EndsWith(oldFilename, StringComparison.OrdinalIgnoreCase)) directory = Path.GetDirectoryName(directory); // If the path ends with the label already if (directory.EndsWith(label, StringComparison.OrdinalIgnoreCase)) directory = Path.GetDirectoryName(directory); this.Parent.OutputPathTextBox.Text = Path.Combine(directory, label, filename); } // Otherwise, reset the extension of the currently set path else { string oldPath = InfoTool.NormalizeOutputPaths(this.Parent.OutputPathTextBox.Text, false); string filename = Path.GetFileNameWithoutExtension(oldPath); string directory = Path.GetDirectoryName(oldPath); filename = $"{filename}{extension ?? ".bin"}"; this.Parent.OutputPathTextBox.Text = Path.Combine(directory, filename); } // Ensure the UI gets updated this.Parent.UpdateLayout(); } /// /// Process the current custom parameters back into UI values /// public void ProcessCustomParameters() { Env.SetParameters(this.Parent.ParametersTextBox.Text); if (Env.Parameters == null) return; // Catch this in case there's an input path issue try { int driveIndex = Drives.Select(d => d.Letter).ToList().IndexOf(Env.Parameters.InputPath[0]); if (driveIndex > -1) this.Parent.DriveLetterComboBox.SelectedIndex = driveIndex; } catch { } int driveSpeed = Env.Parameters.Speed ?? -1; if (driveSpeed > 0) this.Parent.DriveSpeedComboBox.SelectedValue = driveSpeed; else Env.Parameters.Speed = this.Parent.DriveSpeedComboBox.SelectedValue as int?; // Disable change handling DisableEventHandlers(); this.Parent.OutputPathTextBox.Text = InfoTool.NormalizeOutputPaths(Env.Parameters.OutputPath, true); MediaType? mediaType = Env.Parameters.GetMediaType(); int mediaTypeIndex = MediaTypes.FindIndex(m => m == mediaType); if (mediaTypeIndex > -1) this.Parent.MediaTypeComboBox.SelectedIndex = mediaTypeIndex; // Reenable change handling EnableEventHandlers(); } /// /// Scan and show copy protection for the current disc /// public async void ScanAndShowProtection() { // Determine current environment, just in case if (Env == null) Env = DetermineEnvironment(); // Pull the drive letter from the UI directly, just in case var drive = this.Parent.DriveLetterComboBox.SelectedItem as Drive; if (drive != null && drive.Letter != default(char)) { if (this.Options.VerboseLogging) this.Logger.VerboseLogLn($"Scanning for copy protection in {drive.Letter}"); var tempContent = this.Parent.StatusLabel.Text; this.Parent.StatusLabel.Text = "Scanning for copy protection... this might take a while!"; this.Parent.StartStopButton.IsEnabled = false; this.Parent.MediaScanButton.IsEnabled = false; this.Parent.UpdateVolumeLabel.IsEnabled = false; this.Parent.CopyProtectScanButton.IsEnabled = false; var progress = new Progress(); progress.ProgressChanged += ProgressUpdated; (var protections, string error) = await Protection.RunProtectionScanOnPath(drive.Letter + ":\\", this.Options, progress); string output = Protection.FormatProtections(protections); // If SmartE is detected on the current disc, remove `/sf` from the flags for DIC only -- Disabled until further notice //if (Env.InternalProgram == InternalProgram.DiscImageCreator && output.Contains("SmartE")) //{ // ((Modules.DiscImageCreator.Parameters)Env.Parameters)[Modules.DiscImageCreator.FlagStrings.ScanFileProtect] = false; // if (this.Options.VerboseLogging) // this.Logger.VerboseLogLn($"SmartE detected, removing {Modules.DiscImageCreator.FlagStrings.ScanFileProtect} from parameters"); //} if (!this.Parent.LogPanel.IsExpanded) { if (string.IsNullOrEmpty(error)) CustomMessageBox.Show(output, "Detected Protection(s)", MessageBoxButton.OK, MessageBoxImage.Information); else CustomMessageBox.Show("An exception occurred, see the log for details", "Error!", MessageBoxButton.OK, MessageBoxImage.Error); } if (string.IsNullOrEmpty(error)) this.Logger.LogLn($"Detected the following protections in {drive.Letter}:\r\n\r\n{output}"); else this.Logger.ErrorLogLn($"Path could not be scanned! Exception information:\r\n\r\n{error}"); this.Parent.StatusLabel.Text = tempContent; this.Parent.StartStopButton.IsEnabled = ShouldEnableDumpingButton(); this.Parent.MediaScanButton.IsEnabled = true; this.Parent.UpdateVolumeLabel.IsEnabled = true; this.Parent.CopyProtectScanButton.IsEnabled = true; } } /// /// Set the current disc type in the combo box /// private void SetCurrentDiscType() { // If we have an invalid current type, we don't care and return if (CurrentMediaType == null || CurrentMediaType == MediaType.NONE) return; // Now set the selected item, if possible int index = MediaTypes.FindIndex(kvp => kvp.Value == CurrentMediaType); if (index != -1) this.Parent.MediaTypeComboBox.SelectedIndex = index; else this.Parent.StatusLabel.Text = $"Disc of type '{CurrentMediaType.LongName()}' found, but the current system does not support it!"; // Ensure the UI gets updated this.Parent.UpdateLayout(); } /// /// Set the drive speed based on reported maximum and user-defined option /// public void SetSupportedDriveSpeed() { // Set the drive speed list that's appropriate var values = Interface.GetSpeedsForMediaType(CurrentMediaType); this.Parent.DriveSpeedComboBox.ItemsSource = values; if (this.Options.VerboseLogging) this.Logger.VerboseLogLn($"Supported media speeds: {string.Join(", ", values)}"); // Set the selected speed int speed; switch (CurrentMediaType) { case MediaType.CDROM: case MediaType.GDROM: speed = this.Options.PreferredDumpSpeedCD; break; case MediaType.DVD: case MediaType.NintendoGameCubeGameDisc: case MediaType.NintendoWiiOpticalDisc: speed = this.Options.PreferredDumpSpeedDVD; break; case MediaType.HDDVD: speed = this.Options.PreferredDumpSpeedHDDVD; break; case MediaType.BluRay: speed = this.Options.PreferredDumpSpeedBD; break; default: speed = this.Options.PreferredDumpSpeedCD; break; } if (this.Options.VerboseLogging) this.Logger.VerboseLogLn($"Setting drive speed to: {speed}"); this.Parent.DriveSpeedComboBox.SelectedValue = speed; // Ensure the UI gets updated this.Parent.UpdateLayout(); } /// /// Determine if the dumping button should be enabled /// private bool ShouldEnableDumpingButton() { return Drives != null && Drives.Count > 0 && this.Parent.SystemTypeComboBox.SelectedIndex > -1 && this.Parent.SystemTypeComboBox.SelectedItem as RedumpSystemComboBoxItem != null && !string.IsNullOrEmpty(this.Parent.ParametersTextBox.Text); } /// /// Show the disc information window /// /// SubmissionInfo object to display and possibly change /// Dialog open result private (bool?, SubmissionInfo) ShowDiscInformationWindow(SubmissionInfo submissionInfo) { if (this.Options.ShowDiscEjectReminder) CustomMessageBox.Show("It is now safe to eject the disc", "Eject", MessageBoxButton.OK, MessageBoxImage.Information); var discInformationWindow = new DiscInformationWindow(this.Options, submissionInfo) { Focusable = true, Owner = this.Parent, ShowActivated = true, ShowInTaskbar = true, WindowStartupLocation = WindowStartupLocation.CenterOwner, }; bool? result = discInformationWindow.ShowDialog(); // Copy back the submission info changes, if necessary if (result == true) submissionInfo = discInformationWindow.DiscInformationViewModel.SubmissionInfo.Clone() as SubmissionInfo; return (result, submissionInfo); } /// /// Begin the dumping process using the given inputs /// public async void StartDumping() { // One last check to determine environment, just in case Env = DetermineEnvironment(); // Force an internal drive refresh in case the user entered things manually Env.Drive.RefreshDrive(); // If still in custom parameter mode, check that users meant to continue or not if (this.Parent.EnableParametersCheckBox.IsChecked == true) { MessageBoxResult result = CustomMessageBox.Show("It looks like you have custom parameters that have not been saved. Would you like to apply those changes before starting to dump?", "Custom Changes", MessageBoxButton.YesNoCancel, MessageBoxImage.Question); if (result == MessageBoxResult.Yes) { this.Parent.EnableParametersCheckBox.IsChecked = false; this.Parent.ParametersTextBox.IsEnabled = false; ProcessCustomParameters(); } else if (result == MessageBoxResult.Cancel) { return; } // If "No", then we continue with the current known environment } // Run path adjustments for DiscImageCreator -- Disabled until further notice //Env.AdjustPathsForDiscImageCreator(); try { // Run pre-dumping validation checks if (!ValidateBeforeDumping()) return; // Disable all UI elements apart from dumping button DisableAllUIElements(); // Refresh the drive, if it wasn't null Env.Drive?.RefreshDrive(); // Output to the label and log this.Parent.StatusLabel.Text = "Starting dumping process... Please wait!"; this.Logger.LogLn("Starting dumping process... Please wait!"); if (this.Options.ToolsInSeparateWindow) this.Logger.LogLn("Look for the separate command window for more details"); else this.Logger.LogLn("Program outputs may be slow to populate in the log window"); // Get progress indicators var resultProgress = new Progress(); resultProgress.ProgressChanged += ProgressUpdated; var protectionProgress = new Progress(); protectionProgress.ProgressChanged += ProgressUpdated; Env.ReportStatus += ProgressUpdated; // Run the program with the parameters Result result = await Env.Run(resultProgress); this.Logger.ResetProgressBar(); // If we didn't execute a dumping command we cannot get submission output if (!Env.Parameters.IsDumpingCommand()) { this.Logger.LogLn("No dumping command was run, submission information will not be gathered."); this.Parent.StatusLabel.Text = "Execution complete!"; // Reset all UI elements EnableAllUIElements(); return; } // Verify dump output and save it if (result) { result = await Env.VerifyAndSaveDumpOutput(resultProgress, protectionProgress, ShowDiscInformationWindow); } else { this.Logger.ErrorLogLn(result.Message); this.Parent.StatusLabel.Text = "Execution failed!"; } } catch (Exception ex) { this.Logger.ErrorLogLn(ex.ToString()); this.Parent.StatusLabel.Text = "An exception occurred!"; } finally { // Reset all UI elements EnableAllUIElements(); } } /// /// Perform validation, including user input, before attempting to start dumping /// /// 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)) { _ = CustomMessageBox.Show("No output path was provided so dumping cannot continue.", "Missing Path", MessageBoxButton.OK, MessageBoxImage.Exclamation); this.Logger.LogLn("Dumping aborted!"); return false; } // Validate that the user explicitly wants an inactive drive to be considered for dumping if (!Env.Drive.MarkedActive) { string message = "The currently selected drive does not appear to contain a disc! " + (!Env.System.DetectedByWindows() ? $"This is normal for {Env.System.LongName()} as the discs may not be readable on Windows. " : string.Empty) + "Do you want to continue?"; MessageBoxResult mbresult = CustomMessageBox.Show(message, "No Disc Detected", MessageBoxButton.YesNo, MessageBoxImage.Exclamation); if (mbresult == MessageBoxResult.No || mbresult == MessageBoxResult.Cancel || mbresult == MessageBoxResult.None) { this.Logger.LogLn("Dumping aborted!"); return false; } } // 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(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); if (mbresult == MessageBoxResult.No || mbresult == MessageBoxResult.Cancel || mbresult == MessageBoxResult.None) { this.Logger.LogLn("Dumping aborted!"); return false; } } // Validate that at least some space exists // TODO: Tie this to the size of the disc, type of disc, etc. string fullPath = Path.GetFullPath(outputDirectory); var driveInfo = new DriveInfo(Path.GetPathRoot(fullPath)); if (driveInfo.AvailableFreeSpace < Math.Pow(2, 30)) { MessageBoxResult mbresult = CustomMessageBox.Show("There is less than 1gb of space left on the target drive. Are you sure you want to continue?", "Low Space", MessageBoxButton.YesNo, MessageBoxImage.Exclamation); if (mbresult == MessageBoxResult.No || mbresult == MessageBoxResult.Cancel || mbresult == MessageBoxResult.None) { this.Logger.LogLn("Dumping aborted!"); return false; } } // If nothing above fails, we want to continue return true; } #endregion #region Event Handlers /// /// Handler for OptionsWindow OnUpdated event /// private void OnOptionsUpdated(object sender, EventArgs e) => UpdateOptions(sender as OptionsWindow); #region Progress Reporting /// /// Handler for Result ProgressChanged event /// private void ProgressUpdated(object sender, string value) { try { value = value ?? string.Empty; this.Logger.LogLn(value); } catch { } } /// /// Handler for Result ProgressChanged event /// private void ProgressUpdated(object sender, Result value) { string message = value?.Message; // Update the label with only the first line of output if (message.Contains("\n")) this.Parent.StatusLabel.Text = value.Message.Split('\n')[0] + " (See log output)"; else this.Parent.StatusLabel.Text = value.Message; // Log based on success or failure if (value && this.Options.VerboseLogging) this.Logger.VerboseLogLn(message); else if (!value) this.Logger.ErrorLogLn(message); } /// /// Handler for ProtectionProgress ProgressChanged event /// private void ProgressUpdated(object sender, ProtectionProgress value) { string message = $"{value.Percentage * 100:N2}%: {value.Filename} - {value.Protection}"; this.Parent.StatusLabel.Text = message; if (this.Options.VerboseLogging) this.Logger.VerboseLogLn(message); } #endregion #region Menu Bar /// /// Handler for AboutMenuItem Click event /// private void AboutClick(object sender, RoutedEventArgs e) => ShowAboutText(); /// /// Handler for AppExitMenuItem Click event /// private void AppExitClick(object sender, RoutedEventArgs e) => ExitApplication(); /// /// Handler for CheckForUpdatesMenuItem Click event /// private void CheckForUpdatesClick(object sender, RoutedEventArgs e) => CheckForUpdates(showIfSame: true); /// /// Handler for DebugViewMenuItem Click event /// private void DebugViewClick(object sender, RoutedEventArgs e) => ShowDebugDiscInfoWindow(); /// /// Handler for OptionsMenuItem Click event /// private void OptionsMenuItemClick(object sender, RoutedEventArgs e) => ShowOptionsWindow(); #endregion #region User Area /// /// Handler for CopyProtectScanButton Click event /// private void CopyProtectScanButtonClick(object sender, RoutedEventArgs e) => ScanAndShowProtection(); /// /// Handler for DriveLetterComboBox SelectionChanged event /// private void DriveLetterComboBoxSelectionChanged(object sender, SelectionChangedEventArgs e) { if (_canExecuteSelectionChanged) InitializeUIValues(removeEventHandlers: true, rescanDrives: false); } /// /// Handler for DriveSpeedComboBox SelectionChanged event /// private void DriveSpeedComboBoxSelectionChanged(object sender, SelectionChangedEventArgs e) { if (_canExecuteSelectionChanged) EnsureDiscInformation(); } /// /// Handler for DumpingProgramComboBox SelectionChanged event /// private void DumpingProgramComboBoxSelectionChanged(object sender, SelectionChangedEventArgs e) { if (_canExecuteSelectionChanged) ChangeDumpingProgram(); } /// /// Handler for EnableParametersCheckBox Click event /// private void EnableParametersCheckBoxClick(object sender, RoutedEventArgs e) => ToggleParameters(); /// /// Handler for MediaScanButton Click event /// private void MediaScanButtonClick(object sender, RoutedEventArgs e) => InitializeUIValues(removeEventHandlers: true, rescanDrives: true); /// /// Handler for MediaTypeComboBox SelectionChanged event /// private void MediaTypeComboBoxSelectionChanged(object sender, SelectionChangedEventArgs e) { if (_canExecuteSelectionChanged) ChangeMediaType(e); } /// /// Handler for OutputPathBrowseButton Click event /// private void OutputPathBrowseButtonClick(object sender, RoutedEventArgs e) => SetOutputPath(); /// /// Handler for OutputPathTextBox TextChanged event /// private void OutputPathTextBoxTextChanged(object sender, TextChangedEventArgs e) { if (_canExecuteSelectionChanged) EnsureDiscInformation(); } /// /// Handler for StartStopButton Click event /// private void StartStopButtonClick(object sender, RoutedEventArgs e) => ToggleStartStop(); /// /// Handler for SystemTypeComboBox SelectionChanged event /// private void SystemTypeComboBoxSelectionChanged(object sender, SelectionChangedEventArgs e) { if (_canExecuteSelectionChanged) ChangeSystem(); } /// /// Handler for UpdateVolumeLabel Click event /// private void UpdateVolumeLabelClick(object sender, RoutedEventArgs e) { if (_canExecuteSelectionChanged) { if (this.Options.FastUpdateLabel) FastUpdateLabel(removeEventHandlers: true); else InitializeUIValues(removeEventHandlers: true, rescanDrives: false); } } #endregion #endregion // Event Handlers } }