diff --git a/FFUDevelopment/BuildFFUVM_UI.ps1 b/FFUDevelopment/BuildFFUVM_UI.ps1 index c3a6396..4fd0b7c 100644 --- a/FFUDevelopment/BuildFFUVM_UI.ps1 +++ b/FFUDevelopment/BuildFFUVM_UI.ps1 @@ -8,13 +8,9 @@ if ($PSVersionTable.PSVersion.Major -lt 7) { exit 1 } -# -------------------------------------------------------------------------- -# SECTION: Variables & Constants -# -------------------------------------------------------------------------- -# $FFUDevelopmentPath = $PSScriptRoot +# Creating custom state object to hold UI state and data $FFUDevelopmentPath = 'C:\FFUDevelopment' # hard coded for testing -# --- NEW: Central State Object --- $script:uiState = [PSCustomObject]@{ FFUDevelopmentPath = $FFUDevelopmentPath; Window = $null; @@ -46,12 +42,11 @@ if (Get-Module -Name 'FFU.Common.Core' -ErrorAction SilentlyContinue) { if (Get-Module -Name 'FFUUI.Core' -ErrorAction SilentlyContinue) { Remove-Module -Name 'FFUUI.Core' -Force } -# Import the common core module first for logging +# Import Modules Import-Module "$PSScriptRoot\FFU.Common" -Force -# Import the Core UI Logic Module Import-Module "$PSScriptRoot\FFUUI.Core" -Force -# Set the log path for the common logger (for UI operations) +# Set the log path Set-CommonCoreLogPath -Path $script:uiState.LogFilePath # Setting long path support - this prevents issues where some applications have deep directory structures @@ -99,38 +94,13 @@ $reader = New-Object System.IO.StringReader($xamlString) $xmlReader = [System.Xml.XmlReader]::Create($reader) $window = [Windows.Markup.XamlReader]::Load($xmlReader) -# ----------------------------------------------------------------------------- -# SECTION: Winget UI -# ----------------------------------------------------------------------------- -# Create data context class for version binding -$script:uiState.Data.versionData = [PSCustomObject]@{ - WingetVersion = "Not checked" - ModuleVersion = "Not checked" -} - -# Add observable property support -$script:uiState.Data.versionData | Add-Member -MemberType ScriptMethod -Name NotifyPropertyChanged -Value { - param($PropertyName) - if ($this.PropertyChanged) { - $this.PropertyChanged.Invoke($this, [System.ComponentModel.PropertyChangedEventArgs]::new($PropertyName)) - } -} - -$script:uiState.Data.versionData | Add-Member -MemberType NoteProperty -Name PropertyChanged -Value $null -$script:uiState.Data.versionData | Add-Member -TypeName "System.ComponentModel.INotifyPropertyChanged" - - - $window.Add_Loaded({ # Pass the state object to all initialization functions $script:uiState.Window = $window $window.Tag = $script:uiState Initialize-UIControls -State $script:uiState - Initialize-UIDefaults -State $script:uiState - Initialize-DynamicUIElements -State $script:uiState - Register-EventHandlers -State $script:uiState }) @@ -144,14 +114,14 @@ $btnRun.Add_Click({ $progressBar.Visibility = 'Visible' $txtStatus.Text = "Starting FFU build..." $config = Get-UIConfig -State $script:uiState - $configFilePath = Join-Path $config.FFUDevelopmentPath "FFUConfig.json" + $configFilePath = Join-Path $config.FFUDevelopmentPath "\config\FFUConfig.json" $config | ConvertTo-Json -Depth 10 | Set-Content $configFilePath -Encoding UTF8 - $txtStatus.Text = "Executing BuildFFUVM script with config file..." - & "$PSScriptRoot\BuildFFUVM.ps1" -ConfigFile $configFilePath if ($config.InstallOffice -and $config.OfficeConfigXMLFile) { Copy-Item -Path $config.OfficeConfigXMLFile -Destination $config.OfficePath -Force $txtStatus.Text = "Office Configuration XML file copied successfully." } + $txtStatus.Text = "Executing BuildFFUVM script with config file..." + & "$PSScriptRoot\BuildFFUVM.ps1" -ConfigFile $configFilePath $txtStatus.Text = "FFU build completed successfully." } catch { @@ -163,322 +133,6 @@ $btnRun.Add_Click({ } }) -# Button: Build Config -$btnBuildConfig = $window.FindName('btnBuildConfig') -$btnBuildConfig.Add_Click({ - try { - $config = Get-UIConfig -State $script:uiState - $defaultConfigPath = Join-Path $config.FFUDevelopmentPath "config" - if (-not (Test-Path $defaultConfigPath)) { - New-Item -Path $defaultConfigPath -ItemType Directory -Force | Out-Null - } - $sfd = New-Object System.Windows.Forms.SaveFileDialog - $sfd.Filter = "JSON files (*.json)|*.json|All files (*.*)|*.*" - $sfd.Title = "Save Configuration File" - $sfd.InitialDirectory = $defaultConfigPath - $sfd.FileName = "FFUConfig.json" - if ($sfd.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) { - $savePath = $sfd.FileName - $config | ConvertTo-Json -Depth 10 | Set-Content $savePath -Encoding UTF8 - [System.Windows.MessageBox]::Show("Configuration file saved to:`n$savePath", "Success", "OK", "Information") - } - } - catch { - [System.Windows.MessageBox]::Show("Error saving config file:`n$_", "Error", "OK", "Error") - } - }) - -# Button: Load Config File -$btnLoadConfig = $window.FindName('btnLoadConfig') -$btnLoadConfig.Add_Click({ - try { - $ofd = New-Object System.Windows.Forms.OpenFileDialog - $ofd.Filter = "JSON files (*.json)|*.json|All files (*.*)|*.*" - $ofd.Title = "Load Configuration File" - if ($ofd.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) { - WriteLog "Loading configuration from: $($ofd.FileName)" - $configContent = Get-Content -Path $ofd.FileName -Raw | ConvertFrom-Json - - if ($null -eq $configContent) { - WriteLog "LoadConfig Error: configContent is null after parsing $($ofd.FileName). File might be empty or malformed." - [System.Windows.MessageBox]::Show("Failed to parse the configuration file. It might be empty or not valid JSON.", "Load Error", "OK", "Error") - return - } - WriteLog "LoadConfig: Successfully parsed config file. Top-level keys: $($configContent.PSObject.Properties.Name -join ', ')" - - # Update Build tab values - Set-UIValue -ControlName 'txtFFUDevPath' -PropertyName 'Text' -ConfigObject $configContent -ConfigKey 'FFUDevelopmentPath' -State $script:uiState - Set-UIValue -ControlName 'txtCustomFFUNameTemplate' -PropertyName 'Text' -ConfigObject $configContent -ConfigKey 'CustomFFUNameTemplate' -State $script:uiState - Set-UIValue -ControlName 'txtFFUCaptureLocation' -PropertyName 'Text' -ConfigObject $configContent -ConfigKey 'FFUCaptureLocation' -State $script:uiState - Set-UIValue -ControlName 'txtShareName' -PropertyName 'Text' -ConfigObject $configContent -ConfigKey 'ShareName' -State $script:uiState - Set-UIValue -ControlName 'txtUsername' -PropertyName 'Text' -ConfigObject $configContent -ConfigKey 'Username' -State $script:uiState - Set-UIValue -ControlName 'chkBuildUSBDriveEnable' -PropertyName 'IsChecked' -ConfigObject $configContent -ConfigKey 'BuildUSBDrive' -State $script:uiState - Set-UIValue -ControlName 'chkCompactOS' -PropertyName 'IsChecked' -ConfigObject $configContent -ConfigKey 'CompactOS' -State $script:uiState - Set-UIValue -ControlName 'chkUpdateADK' -PropertyName 'IsChecked' -ConfigObject $configContent -ConfigKey 'UpdateADK' -State $script:uiState - Set-UIValue -ControlName 'chkOptimize' -PropertyName 'IsChecked' -ConfigObject $configContent -ConfigKey 'Optimize' -State $script:uiState - Set-UIValue -ControlName 'chkAllowVHDXCaching' -PropertyName 'IsChecked' -ConfigObject $configContent -ConfigKey 'AllowVHDXCaching' -State $script:uiState - Set-UIValue -ControlName 'chkAllowExternalHardDiskMedia' -PropertyName 'IsChecked' -ConfigObject $configContent -ConfigKey 'AllowExternalHardDiskMedia' -State $script:uiState - Set-UIValue -ControlName 'chkPromptExternalHardDiskMedia' -PropertyName 'IsChecked' -ConfigObject $configContent -ConfigKey 'PromptExternalHardDiskMedia' -State $script:uiState - Set-UIValue -ControlName 'chkCreateCaptureMedia' -PropertyName 'IsChecked' -ConfigObject $configContent -ConfigKey 'CreateCaptureMedia' -State $script:uiState - Set-UIValue -ControlName 'chkCreateDeploymentMedia' -PropertyName 'IsChecked' -ConfigObject $configContent -ConfigKey 'CreateDeploymentMedia' -State $script:uiState - - # USB Drive Modification group (Build Tab) - Set-UIValue -ControlName 'chkCopyAutopilot' -PropertyName 'IsChecked' -ConfigObject $configContent -ConfigKey 'CopyAutopilot' -State $script:uiState - Set-UIValue -ControlName 'chkCopyUnattend' -PropertyName 'IsChecked' -ConfigObject $configContent -ConfigKey 'CopyUnattend' -State $script:uiState - Set-UIValue -ControlName 'chkCopyPPKG' -PropertyName 'IsChecked' -ConfigObject $configContent -ConfigKey 'CopyPPKG' -State $script:uiState - - # Post Build Cleanup group (Build Tab) - Set-UIValue -ControlName 'chkCleanupAppsISO' -PropertyName 'IsChecked' -ConfigObject $configContent -ConfigKey 'CleanupAppsISO' -State $script:uiState - Set-UIValue -ControlName 'chkCleanupCaptureISO' -PropertyName 'IsChecked' -ConfigObject $configContent -ConfigKey 'CleanupCaptureISO' -State $script:uiState - Set-UIValue -ControlName 'chkCleanupDeployISO' -PropertyName 'IsChecked' -ConfigObject $configContent -ConfigKey 'CleanupDeployISO' -State $script:uiState - Set-UIValue -ControlName 'chkCleanupDrivers' -PropertyName 'IsChecked' -ConfigObject $configContent -ConfigKey 'CleanupDrivers' -State $script:uiState - Set-UIValue -ControlName 'chkRemoveFFU' -PropertyName 'IsChecked' -ConfigObject $configContent -ConfigKey 'RemoveFFU' -State $script:uiState - Set-UIValue -ControlName 'chkRemoveApps' -PropertyName 'IsChecked' -ConfigObject $configContent -ConfigKey 'RemoveApps' -State $script:uiState - Set-UIValue -ControlName 'chkRemoveUpdates' -PropertyName 'IsChecked' -ConfigObject $configContent -ConfigKey 'RemoveUpdates' -State $script:uiState - - # Hyper-V Settings - Set-UIValue -ControlName 'cmbVMSwitchName' -PropertyName 'SelectedItem' -ConfigObject $configContent -ConfigKey 'VMSwitchName' -State $script:uiState - Set-UIValue -ControlName 'txtVMHostIPAddress' -PropertyName 'Text' -ConfigObject $configContent -ConfigKey 'VMHostIPAddress' -State $script:uiState - Set-UIValue -ControlName 'txtDiskSize' -PropertyName 'Text' -ConfigObject $configContent -ConfigKey 'Disksize' -TransformValue { param($val) $val / 1GB } -State $script:uiState - Set-UIValue -ControlName 'txtMemory' -PropertyName 'Text' -ConfigObject $configContent -ConfigKey 'Memory' -TransformValue { param($val) $val / 1GB } -State $script:uiState - Set-UIValue -ControlName 'txtProcessors' -PropertyName 'Text' -ConfigObject $configContent -ConfigKey 'Processors' -State $script:uiState - Set-UIValue -ControlName 'txtVMLocation' -PropertyName 'Text' -ConfigObject $configContent -ConfigKey 'VMLocation' -State $script:uiState - Set-UIValue -ControlName 'txtVMNamePrefix' -PropertyName 'Text' -ConfigObject $configContent -ConfigKey 'FFUPrefix' -State $script:uiState - Set-UIValue -ControlName 'cmbLogicalSectorSize' -PropertyName 'SelectedItem' -ConfigObject $configContent -ConfigKey 'LogicalSectorSizeBytes' -TransformValue { param($val) $val.ToString() } -State $script:uiState - - # Windows Settings - Set-UIValue -ControlName 'txtISOPath' -PropertyName 'Text' -ConfigObject $configContent -ConfigKey 'ISOPath' -State $script:uiState - Set-UIValue -ControlName 'cmbWindowsRelease' -PropertyName 'SelectedItem' -ConfigObject $configContent -ConfigKey 'WindowsRelease' -State $script:uiState - Set-UIValue -ControlName 'cmbWindowsVersion' -PropertyName 'SelectedItem' -ConfigObject $configContent -ConfigKey 'WindowsVersion' -State $script:uiState - Set-UIValue -ControlName 'cmbWindowsArch' -PropertyName 'SelectedItem' -ConfigObject $configContent -ConfigKey 'WindowsArch' -State $script:uiState - Set-UIValue -ControlName 'cmbWindowsLang' -PropertyName 'SelectedItem' -ConfigObject $configContent -ConfigKey 'WindowsLang' -State $script:uiState - Set-UIValue -ControlName 'cmbWindowsSKU' -PropertyName 'SelectedItem' -ConfigObject $configContent -ConfigKey 'WindowsSKU' -State $script:uiState - Set-UIValue -ControlName 'cmbMediaType' -PropertyName 'SelectedItem' -ConfigObject $configContent -ConfigKey 'MediaType' -State $script:uiState - Set-UIValue -ControlName 'txtProductKey' -PropertyName 'Text' -ConfigObject $configContent -ConfigKey 'ProductKey' -State $script:uiState - Set-UIValue -ControlName 'txtOptionalFeatures' -PropertyName 'Text' -ConfigObject $configContent -ConfigKey 'OptionalFeatures' -State $script:uiState - - # Update Optional Features checkboxes based on the loaded text - $loadedFeaturesString = $script:uiState.Controls.txtOptionalFeatures.Text - if (-not [string]::IsNullOrWhiteSpace($loadedFeaturesString)) { - $loadedFeaturesArray = $loadedFeaturesString.Split(';') - WriteLog "LoadConfig: Updating Optional Features checkboxes. Loaded features: $($loadedFeaturesArray -join ', ')" - foreach ($featureEntry in $script:uiState.Controls.featureCheckBoxes.GetEnumerator()) { - $featureName = $featureEntry.Key - $featureCheckbox = $featureEntry.Value - if ($loadedFeaturesArray -contains $featureName) { - $featureCheckbox.IsChecked = $true - WriteLog "LoadConfig: Checked checkbox for feature '$featureName'." - } - else { - $featureCheckbox.IsChecked = $false - } - } - } - else { - # If no optional features are loaded, uncheck all - WriteLog "LoadConfig: No optional features string loaded. Unchecking all feature checkboxes." - foreach ($featureEntry in $script:uiState.Controls.featureCheckBoxes.GetEnumerator()) { - $featureEntry.Value.IsChecked = $false - } - } - - # M365 Apps/Office tab - Set-UIValue -ControlName 'chkInstallOffice' -PropertyName 'IsChecked' -ConfigObject $configContent -ConfigKey 'InstallOffice' -State $script:uiState - Set-UIValue -ControlName 'txtOfficePath' -PropertyName 'Text' -ConfigObject $configContent -ConfigKey 'OfficePath' -State $script:uiState - Set-UIValue -ControlName 'chkCopyOfficeConfigXML' -PropertyName 'IsChecked' -ConfigObject $configContent -ConfigKey 'CopyOfficeConfigXML' -State $script:uiState - Set-UIValue -ControlName 'txtOfficeConfigXMLFilePath' -PropertyName 'Text' -ConfigObject $configContent -ConfigKey 'OfficeConfigXMLFile' -State $script:uiState - - # Drivers tab - Set-UIValue -ControlName 'chkInstallDrivers' -PropertyName 'IsChecked' -ConfigObject $configContent -ConfigKey 'InstallDrivers' -State $script:uiState - Set-UIValue -ControlName 'chkDownloadDrivers' -PropertyName 'IsChecked' -ConfigObject $configContent -ConfigKey 'DownloadDrivers' -State $script:uiState - Set-UIValue -ControlName 'chkCopyDrivers' -PropertyName 'IsChecked' -ConfigObject $configContent -ConfigKey 'CopyDrivers' -State $script:uiState - Set-UIValue -ControlName 'cmbMake' -PropertyName 'SelectedItem' -ConfigObject $configContent -ConfigKey 'Make' -State $script:uiState - Set-UIValue -ControlName 'txtDriversFolder' -PropertyName 'Text' -ConfigObject $configContent -ConfigKey 'DriversFolder' -State $script:uiState - Set-UIValue -ControlName 'txtPEDriversFolder' -PropertyName 'Text' -ConfigObject $configContent -ConfigKey 'PEDriversFolder' -State $script:uiState - Set-UIValue -ControlName 'txtDriversJsonPath' -PropertyName 'Text' -ConfigObject $configContent -ConfigKey 'DriversJsonPath' -State $script:uiState - Set-UIValue -ControlName 'chkCopyPEDrivers' -PropertyName 'IsChecked' -ConfigObject $configContent -ConfigKey 'CopyPEDrivers' -State $script:uiState - Set-UIValue -ControlName 'chkCompressDriversToWIM' -PropertyName 'IsChecked' -ConfigObject $configContent -ConfigKey 'CompressDownloadedDriversToWim' -State $script:uiState - - - # Updates tab - Set-UIValue -ControlName 'chkUpdateLatestCU' -PropertyName 'IsChecked' -ConfigObject $configContent -ConfigKey 'UpdateLatestCU' -State $script:uiState - Set-UIValue -ControlName 'chkUpdateLatestNet' -PropertyName 'IsChecked' -ConfigObject $configContent -ConfigKey 'UpdateLatestNet' -State $script:uiState - Set-UIValue -ControlName 'chkUpdateLatestDefender' -PropertyName 'IsChecked' -ConfigObject $configContent -ConfigKey 'UpdateLatestDefender' -State $script:uiState - Set-UIValue -ControlName 'chkUpdateEdge' -PropertyName 'IsChecked' -ConfigObject $configContent -ConfigKey 'UpdateEdge' -State $script:uiState - Set-UIValue -ControlName 'chkUpdateOneDrive' -PropertyName 'IsChecked' -ConfigObject $configContent -ConfigKey 'UpdateOneDrive' -State $script:uiState - Set-UIValue -ControlName 'chkUpdateLatestMSRT' -PropertyName 'IsChecked' -ConfigObject $configContent -ConfigKey 'UpdateLatestMSRT' -State $script:uiState - Set-UIValue -ControlName 'chkUpdateLatestMicrocode' -PropertyName 'IsChecked' -ConfigObject $configContent -ConfigKey 'UpdateLatestMicrocode' -State $script:uiState - Set-UIValue -ControlName 'chkUpdatePreviewCU' -PropertyName 'IsChecked' -ConfigObject $configContent -ConfigKey 'UpdatePreviewCU' -State $script:uiState - - # Applications tab - Set-UIValue -ControlName 'chkInstallApps' -PropertyName 'IsChecked' -ConfigObject $configContent -ConfigKey 'InstallApps' -State $script:uiState - Set-UIValue -ControlName 'chkInstallWingetApps' -PropertyName 'IsChecked' -ConfigObject $configContent -ConfigKey 'InstallWingetApps' -State $script:uiState - Set-UIValue -ControlName 'chkBringYourOwnApps' -PropertyName 'IsChecked' -ConfigObject $configContent -ConfigKey 'BringYourOwnApps' -State $script:uiState - Set-UIValue -ControlName 'txtApplicationPath' -PropertyName 'Text' -ConfigObject $configContent -ConfigKey 'AppsPath' -State $script:uiState - Set-UIValue -ControlName 'txtAppListJsonPath' -PropertyName 'Text' -ConfigObject $configContent -ConfigKey 'AppListPath' -State $script:uiState - - # Handle AppsScriptVariables - $appsScriptVarsKeyExists = $false - if ($configContent -is [System.Management.Automation.PSCustomObject] -and $null -ne $configContent.PSObject.Properties) { - try { - if (($configContent.PSObject.Properties.Match('AppsScriptVariables')).Count -gt 0) { - $appsScriptVarsKeyExists = $true - } - } - catch { WriteLog "ERROR: Exception while trying to Match key 'AppsScriptVariables'. Error: $($_.Exception.Message)" } - } - - $lstAppsScriptVars = $script:uiState.Controls.lstAppsScriptVariables - $chkDefineAppsScriptVars = $script:uiState.Controls.chkDefineAppsScriptVariables - $appsScriptVarsPanel = $script:uiState.Controls.appsScriptVariablesPanel - $script:uiState.Data.appsScriptVariablesDataList.Clear() - - if ($appsScriptVarsKeyExists -and $null -ne $configContent.AppsScriptVariables -and $configContent.AppsScriptVariables -is [System.Management.Automation.PSCustomObject]) { - WriteLog "LoadConfig: Processing AppsScriptVariables from config." - $loadedVars = $configContent.AppsScriptVariables - $hasVars = $false - foreach ($prop in $loadedVars.PSObject.Properties) { - $script:uiState.Data.appsScriptVariablesDataList.Add([PSCustomObject]@{ IsSelected = $false; Key = $prop.Name; Value = $prop.Value }) - $hasVars = $true - } - if ($hasVars) { - $chkDefineAppsScriptVars.IsChecked = $true - $appsScriptVarsPanel.Visibility = 'Visible' - WriteLog "LoadConfig: Loaded AppsScriptVariables and checked 'Define Apps Script Variables'." - } - else { - $chkDefineAppsScriptVars.IsChecked = $false - $appsScriptVarsPanel.Visibility = 'Collapsed' - WriteLog "LoadConfig: AppsScriptVariables key was present but empty. Unchecked 'Define Apps Script Variables'." - } - } - elseif ($appsScriptVarsKeyExists -and $null -ne $configContent.AppsScriptVariables -and $configContent.AppsScriptVariables -is [hashtable]) { - # Handle if it's already a hashtable (e.g., from older config or direct creation) - WriteLog "LoadConfig: Processing AppsScriptVariables (Hashtable) from config." - $loadedVars = $configContent.AppsScriptVariables - $hasVars = $false - foreach ($keyName in $loadedVars.Keys) { - $script:uiState.Data.appsScriptVariablesDataList.Add([PSCustomObject]@{ IsSelected = $false; Key = $keyName; Value = $loadedVars[$keyName] }) - $hasVars = $true - } - if ($hasVars) { - $chkDefineAppsScriptVars.IsChecked = $true - $appsScriptVarsPanel.Visibility = 'Visible' - WriteLog "LoadConfig: Loaded AppsScriptVariables (Hashtable) and checked 'Define Apps Script Variables'." - } - else { - $chkDefineAppsScriptVars.IsChecked = $false - $appsScriptVarsPanel.Visibility = 'Collapsed' - WriteLog "LoadConfig: AppsScriptVariables (Hashtable) key was present but empty. Unchecked 'Define Apps Script Variables'." - } - } - else { - $chkDefineAppsScriptVars.IsChecked = $false - $appsScriptVarsPanel.Visibility = 'Collapsed' - WriteLog "LoadConfig Info: Key 'AppsScriptVariables' not found, is null, or not a PSCustomObject/Hashtable. Unchecked 'Define Apps Script Variables'." - } - # Update the ListView's ItemsSource after populating the data list - $lstAppsScriptVars.ItemsSource = $script:uiState.Data.appsScriptVariablesDataList.ToArray() - # Update the header checkbox state - if ($null -ne $script:uiState.Controls.chkSelectAllAppsScriptVariables) { - Update-SelectAllHeaderCheckBoxState -ListView $lstAppsScriptVars -HeaderCheckBox $script:uiState.Controls.chkSelectAllAppsScriptVariables - } - - # Update USB Drive selection if present in config - $usbDriveListKeyExists = $false - if ($configContent -is [System.Management.Automation.PSCustomObject] -and $null -ne $configContent.PSObject.Properties) { - try { - if (($configContent.PSObject.Properties.Match('USBDriveList')).Count -gt 0) { - $usbDriveListKeyExists = $true - } - } - catch { - WriteLog "ERROR: Exception while trying to Match key 'USBDriveList' on configContent.PSObject.Properties. Error: $($_.Exception.Message)" - } - } - - if ($usbDriveListKeyExists -and $null -ne $configContent.USBDriveList) { - WriteLog "LoadConfig: Processing USBDriveList from config." - # First click the Check USB Drives button to populate the list - $script:uiState.Controls.btnCheckUSBDrives.RaiseEvent( - [System.Windows.RoutedEventArgs]::new( - [System.Windows.Controls.Button]::ClickEvent - ) - ) - - # Then select the drives that match the saved configuration - foreach ($item in $script:uiState.Controls.lstUSBDrives.Items) { - $propertyName = $item.Model - $propertyExists = $false - $propertyValue = $null - - # Ensure USBDriveList is a PSCustomObject before trying to access its properties dynamically - if ($null -ne $configContent.USBDriveList -and $configContent.USBDriveList -is [System.Management.Automation.PSCustomObject]) { - # Check if the property exists on the USBDriveList object - if ($configContent.USBDriveList.PSObject.Properties.Match($propertyName).Count -gt 0) { - $propertyExists = $true - # Access the value dynamically - $propertyValue = $configContent.USBDriveList.$($propertyName) - } - } - - if ($propertyExists -and ($propertyValue -eq $item.SerialNumber)) { - WriteLog "LoadConfig: Selecting USB Drive Model '$($item.Model)' with Serial '$($item.SerialNumber)'." - $item.IsSelected = $true - } - else { - if (-not $propertyExists -and ($null -ne $configContent.USBDriveList)) { - WriteLog "LoadConfig: Property '$($propertyName)' not found on USBDriveList for item Model '$($item.Model)'." - } - $item.IsSelected = $false # Ensure others are deselected if not in config or value mismatch - } - } - $script:uiState.Controls.lstUSBDrives.Items.Refresh() - - # Update the Select All header checkbox state - $headerChk = $script:uiState.Controls.chkSelectAllUSBDrivesHeader - if ($null -ne $headerChk) { - Update-SelectAllHeaderCheckBoxState -ListView $script:uiState.Controls.lstUSBDrives -HeaderCheckBox $headerChk - } - WriteLog "LoadConfig: USBDriveList processing complete." - } - else { - WriteLog "LoadConfig Info: Key 'USBDriveList' not found or is null in configuration file. Skipping USB drive selection." - } - - # If BuildUSBDrive is enabled and USBDriveList was present and not empty in the config, - # ensure "Select Specific USB Drives" is checked to show the list. - $shouldAutoCheckSpecificDrives = $false - if ($window.FindName('chkBuildUSBDriveEnable').IsChecked -and $usbDriveListKeyExists -and ($null -ne $configContent.USBDriveList)) { - if ($configContent.USBDriveList -is [System.Management.Automation.PSCustomObject]) { - if ($configContent.USBDriveList.PSObject.Properties.Count -gt 0) { - $shouldAutoCheckSpecificDrives = $true - } - } - elseif ($configContent.USBDriveList -is [hashtable]) { - # Fallback for older configs - if ($configContent.USBDriveList.Keys.Count -gt 0) { - $shouldAutoCheckSpecificDrives = $true - } - } - } - - if ($shouldAutoCheckSpecificDrives) { - WriteLog "LoadConfig: Auto-checking 'Select Specific USB Drives' due to pre-selected USB drives in config." - $script:uiState.Controls.chkSelectSpecificUSBDrives.IsChecked = $true - } - else { - WriteLog "LoadConfig: Condition to auto-check 'Select Specific USB Drives' was NOT met." - } - WriteLog "LoadConfig: Configuration loading process finished." - } - } - catch { - WriteLog "LoadConfig FATAL Error: $($_.Exception.ToString())" # Log full exception details - [System.Windows.MessageBox]::Show("Error loading config file:`n$($_.Exception.Message)", "Error", "OK", "Error") - } - }) - # Add handler for Remove button clicks $window.Add_SourceInitialized({ $listView = $window.FindName('lstApplications') diff --git a/FFUDevelopment/FFUUI.Core/FFUUI.Core.Config.psm1 b/FFUDevelopment/FFUUI.Core/FFUUI.Core.Config.psm1 index 2777807..beccacc 100644 --- a/FFUDevelopment/FFUUI.Core/FFUUI.Core.Config.psm1 +++ b/FFUDevelopment/FFUUI.Core/FFUUI.Core.Config.psm1 @@ -224,4 +224,340 @@ function Set-UIValue { } } -Export-ModuleMember -Function Get-UIConfig, Set-UIValue \ No newline at end of file +function Invoke-LoadConfiguration { + param( + [Parameter(Mandatory = $true)] + [psobject]$State + ) + try { + $filePath = Invoke-BrowseAction -Type 'OpenFile' -Filter "JSON files (*.json)|*.json|All files (*.*)|*.*" -Title "Load Configuration File" + if (-not $filePath) { + WriteLog "Load configuration cancelled by user." + return + } + + WriteLog "Loading configuration from: $filePath" + $configContent = Get-Content -Path $filePath -Raw | ConvertFrom-Json + + if ($null -eq $configContent) { + WriteLog "LoadConfig Error: configContent is null after parsing $filePath. File might be empty or malformed." + [System.Windows.MessageBox]::Show("Failed to parse the configuration file. It might be empty or not valid JSON.", "Load Error", "OK", "Error") + return + } + WriteLog "LoadConfig: Successfully parsed config file. Top-level keys: $($configContent.PSObject.Properties.Name -join ', ')" + + # Apply the configuration to the UI + Update-UIFromConfig -ConfigContent $configContent -State $State + } + catch { + WriteLog "LoadConfig FATAL Error: $($_.Exception.ToString())" + [System.Windows.MessageBox]::Show("Error loading config file:`n$($_.Exception.Message)", "Error", "OK", "Error") + } +} + +function Update-UIFromConfig { + param( + [Parameter(Mandatory = $true)] + [psobject]$ConfigContent, + [Parameter(Mandatory = $true)] + [psobject]$State + ) + + WriteLog "Applying loaded configuration to the UI." + + # Update Build tab values + Set-UIValue -ControlName 'txtFFUDevPath' -PropertyName 'Text' -ConfigObject $ConfigContent -ConfigKey 'FFUDevelopmentPath' -State $State + Set-UIValue -ControlName 'txtCustomFFUNameTemplate' -PropertyName 'Text' -ConfigObject $ConfigContent -ConfigKey 'CustomFFUNameTemplate' -State $State + Set-UIValue -ControlName 'txtFFUCaptureLocation' -PropertyName 'Text' -ConfigObject $ConfigContent -ConfigKey 'FFUCaptureLocation' -State $State + Set-UIValue -ControlName 'txtShareName' -PropertyName 'Text' -ConfigObject $ConfigContent -ConfigKey 'ShareName' -State $State + Set-UIValue -ControlName 'txtUsername' -PropertyName 'Text' -ConfigObject $ConfigContent -ConfigKey 'Username' -State $State + Set-UIValue -ControlName 'chkBuildUSBDriveEnable' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'BuildUSBDrive' -State $State + Set-UIValue -ControlName 'chkCompactOS' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'CompactOS' -State $State + Set-UIValue -ControlName 'chkUpdateADK' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'UpdateADK' -State $State + Set-UIValue -ControlName 'chkOptimize' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'Optimize' -State $State + Set-UIValue -ControlName 'chkAllowVHDXCaching' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'AllowVHDXCaching' -State $State + Set-UIValue -ControlName 'chkAllowExternalHardDiskMedia' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'AllowExternalHardDiskMedia' -State $State + Set-UIValue -ControlName 'chkPromptExternalHardDiskMedia' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'PromptExternalHardDiskMedia' -State $State + Set-UIValue -ControlName 'chkCreateCaptureMedia' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'CreateCaptureMedia' -State $State + Set-UIValue -ControlName 'chkCreateDeploymentMedia' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'CreateDeploymentMedia' -State $State + + # USB Drive Modification group (Build Tab) + Set-UIValue -ControlName 'chkCopyAutopilot' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'CopyAutopilot' -State $State + Set-UIValue -ControlName 'chkCopyUnattend' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'CopyUnattend' -State $State + Set-UIValue -ControlName 'chkCopyPPKG' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'CopyPPKG' -State $State + + # Post Build Cleanup group (Build Tab) + Set-UIValue -ControlName 'chkCleanupAppsISO' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'CleanupAppsISO' -State $State + Set-UIValue -ControlName 'chkCleanupCaptureISO' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'CleanupCaptureISO' -State $State + Set-UIValue -ControlName 'chkCleanupDeployISO' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'CleanupDeployISO' -State $State + Set-UIValue -ControlName 'chkCleanupDrivers' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'CleanupDrivers' -State $State + Set-UIValue -ControlName 'chkRemoveFFU' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'RemoveFFU' -State $State + Set-UIValue -ControlName 'chkRemoveApps' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'RemoveApps' -State $State + Set-UIValue -ControlName 'chkRemoveUpdates' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'RemoveUpdates' -State $State + + # Hyper-V Settings + Set-UIValue -ControlName 'cmbVMSwitchName' -PropertyName 'SelectedItem' -ConfigObject $ConfigContent -ConfigKey 'VMSwitchName' -State $State + Set-UIValue -ControlName 'txtVMHostIPAddress' -PropertyName 'Text' -ConfigObject $ConfigContent -ConfigKey 'VMHostIPAddress' -State $State + Set-UIValue -ControlName 'txtDiskSize' -PropertyName 'Text' -ConfigObject $ConfigContent -ConfigKey 'Disksize' -TransformValue { param($val) $val / 1GB } -State $State + Set-UIValue -ControlName 'txtMemory' -PropertyName 'Text' -ConfigObject $ConfigContent -ConfigKey 'Memory' -TransformValue { param($val) $val / 1GB } -State $State + Set-UIValue -ControlName 'txtProcessors' -PropertyName 'Text' -ConfigObject $ConfigContent -ConfigKey 'Processors' -State $State + Set-UIValue -ControlName 'txtVMLocation' -PropertyName 'Text' -ConfigObject $ConfigContent -ConfigKey 'VMLocation' -State $State + Set-UIValue -ControlName 'txtVMNamePrefix' -PropertyName 'Text' -ConfigObject $ConfigContent -ConfigKey 'FFUPrefix' -State $State + Set-UIValue -ControlName 'cmbLogicalSectorSize' -PropertyName 'SelectedItem' -ConfigObject $ConfigContent -ConfigKey 'LogicalSectorSizeBytes' -TransformValue { param($val) $val.ToString() } -State $State + + # Windows Settings + Set-UIValue -ControlName 'txtISOPath' -PropertyName 'Text' -ConfigObject $ConfigContent -ConfigKey 'ISOPath' -State $State + Set-UIValue -ControlName 'cmbWindowsRelease' -PropertyName 'SelectedItem' -ConfigObject $ConfigContent -ConfigKey 'WindowsRelease' -State $State + Set-UIValue -ControlName 'cmbWindowsVersion' -PropertyName 'SelectedItem' -ConfigObject $ConfigContent -ConfigKey 'WindowsVersion' -State $State + Set-UIValue -ControlName 'cmbWindowsArch' -PropertyName 'SelectedItem' -ConfigObject $ConfigContent -ConfigKey 'WindowsArch' -State $State + Set-UIValue -ControlName 'cmbWindowsLang' -PropertyName 'SelectedItem' -ConfigObject $ConfigContent -ConfigKey 'WindowsLang' -State $State + Set-UIValue -ControlName 'cmbWindowsSKU' -PropertyName 'SelectedItem' -ConfigObject $ConfigContent -ConfigKey 'WindowsSKU' -State $State + Set-UIValue -ControlName 'cmbMediaType' -PropertyName 'SelectedItem' -ConfigObject $ConfigContent -ConfigKey 'MediaType' -State $State + Set-UIValue -ControlName 'txtProductKey' -PropertyName 'Text' -ConfigObject $ConfigContent -ConfigKey 'ProductKey' -State $State + Set-UIValue -ControlName 'txtOptionalFeatures' -PropertyName 'Text' -ConfigObject $ConfigContent -ConfigKey 'OptionalFeatures' -State $State + + # Update Optional Features checkboxes based on the loaded text + $loadedFeaturesString = $State.Controls.txtOptionalFeatures.Text + if (-not [string]::IsNullOrWhiteSpace($loadedFeaturesString)) { + $loadedFeaturesArray = $loadedFeaturesString.Split(';') + WriteLog "LoadConfig: Updating Optional Features checkboxes. Loaded features: $($loadedFeaturesArray -join ', ')" + foreach ($featureEntry in $State.Controls.featureCheckBoxes.GetEnumerator()) { + $featureName = $featureEntry.Key + $featureCheckbox = $featureEntry.Value + if ($loadedFeaturesArray -contains $featureName) { + $featureCheckbox.IsChecked = $true + WriteLog "LoadConfig: Checked checkbox for feature '$featureName'." + } + else { + $featureCheckbox.IsChecked = $false + } + } + } + else { + # If no optional features are loaded, uncheck all + WriteLog "LoadConfig: No optional features string loaded. Unchecking all feature checkboxes." + foreach ($featureEntry in $State.Controls.featureCheckBoxes.GetEnumerator()) { + $featureEntry.Value.IsChecked = $false + } + } + + # M365 Apps/Office tab + Set-UIValue -ControlName 'chkInstallOffice' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'InstallOffice' -State $State + Set-UIValue -ControlName 'txtOfficePath' -PropertyName 'Text' -ConfigObject $ConfigContent -ConfigKey 'OfficePath' -State $State + Set-UIValue -ControlName 'chkCopyOfficeConfigXML' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'CopyOfficeConfigXML' -State $State + Set-UIValue -ControlName 'txtOfficeConfigXMLFilePath' -PropertyName 'Text' -ConfigObject $ConfigContent -ConfigKey 'OfficeConfigXMLFile' -State $State + + # Drivers tab + Set-UIValue -ControlName 'chkInstallDrivers' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'InstallDrivers' -State $State + Set-UIValue -ControlName 'chkDownloadDrivers' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'DownloadDrivers' -State $State + Set-UIValue -ControlName 'chkCopyDrivers' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'CopyDrivers' -State $State + Set-UIValue -ControlName 'cmbMake' -PropertyName 'SelectedItem' -ConfigObject $ConfigContent -ConfigKey 'Make' -State $State + Set-UIValue -ControlName 'txtDriversFolder' -PropertyName 'Text' -ConfigObject $ConfigContent -ConfigKey 'DriversFolder' -State $State + Set-UIValue -ControlName 'txtPEDriversFolder' -PropertyName 'Text' -ConfigObject $ConfigContent -ConfigKey 'PEDriversFolder' -State $State + Set-UIValue -ControlName 'txtDriversJsonPath' -PropertyName 'Text' -ConfigObject $ConfigContent -ConfigKey 'DriversJsonPath' -State $State + Set-UIValue -ControlName 'chkCopyPEDrivers' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'CopyPEDrivers' -State $State + Set-UIValue -ControlName 'chkCompressDriversToWIM' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'CompressDownloadedDriversToWim' -State $State + + # Updates tab + Set-UIValue -ControlName 'chkUpdateLatestCU' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'UpdateLatestCU' -State $State + Set-UIValue -ControlName 'chkUpdateLatestNet' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'UpdateLatestNet' -State $State + Set-UIValue -ControlName 'chkUpdateLatestDefender' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'UpdateLatestDefender' -State $State + Set-UIValue -ControlName 'chkUpdateEdge' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'UpdateEdge' -State $State + Set-UIValue -ControlName 'chkUpdateOneDrive' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'UpdateOneDrive' -State $State + Set-UIValue -ControlName 'chkUpdateLatestMSRT' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'UpdateLatestMSRT' -State $State + Set-UIValue -ControlName 'chkUpdateLatestMicrocode' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'UpdateLatestMicrocode' -State $State + Set-UIValue -ControlName 'chkUpdatePreviewCU' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'UpdatePreviewCU' -State $State + + # Applications tab + Set-UIValue -ControlName 'chkInstallApps' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'InstallApps' -State $State + Set-UIValue -ControlName 'chkInstallWingetApps' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'InstallWingetApps' -State $State + Set-UIValue -ControlName 'chkBringYourOwnApps' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'BringYourOwnApps' -State $State + Set-UIValue -ControlName 'txtApplicationPath' -PropertyName 'Text' -ConfigObject $ConfigContent -ConfigKey 'AppsPath' -State $State + Set-UIValue -ControlName 'txtAppListJsonPath' -PropertyName 'Text' -ConfigObject $ConfigContent -ConfigKey 'AppListPath' -State $State + + # Handle AppsScriptVariables + $appsScriptVarsKeyExists = $false + if ($ConfigContent -is [System.Management.Automation.PSCustomObject] -and $null -ne $ConfigContent.PSObject.Properties) { + try { + if (($ConfigContent.PSObject.Properties.Match('AppsScriptVariables')).Count -gt 0) { + $appsScriptVarsKeyExists = $true + } + } + catch { WriteLog "ERROR: Exception while trying to Match key 'AppsScriptVariables'. Error: $($_.Exception.Message)" } + } + + $lstAppsScriptVars = $State.Controls.lstAppsScriptVariables + $chkDefineAppsScriptVars = $State.Controls.chkDefineAppsScriptVariables + $appsScriptVarsPanel = $State.Controls.appsScriptVariablesPanel + $State.Data.appsScriptVariablesDataList.Clear() + + if ($appsScriptVarsKeyExists -and $null -ne $ConfigContent.AppsScriptVariables -and $ConfigContent.AppsScriptVariables -is [System.Management.Automation.PSCustomObject]) { + WriteLog "LoadConfig: Processing AppsScriptVariables from config." + $loadedVars = $ConfigContent.AppsScriptVariables + $hasVars = $false + foreach ($prop in $loadedVars.PSObject.Properties) { + $State.Data.appsScriptVariablesDataList.Add([PSCustomObject]@{ IsSelected = $false; Key = $prop.Name; Value = $prop.Value }) + $hasVars = $true + } + if ($hasVars) { + $chkDefineAppsScriptVars.IsChecked = $true + $appsScriptVarsPanel.Visibility = 'Visible' + WriteLog "LoadConfig: Loaded AppsScriptVariables and checked 'Define Apps Script Variables'." + } + else { + $chkDefineAppsScriptVars.IsChecked = $false + $appsScriptVarsPanel.Visibility = 'Collapsed' + WriteLog "LoadConfig: AppsScriptVariables key was present but empty. Unchecked 'Define Apps Script Variables'." + } + } + elseif ($appsScriptVarsKeyExists -and $null -ne $ConfigContent.AppsScriptVariables -and $ConfigContent.AppsScriptVariables -is [hashtable]) { + # Handle if it's already a hashtable (e.g., from older config or direct creation) + WriteLog "LoadConfig: Processing AppsScriptVariables (Hashtable) from config." + $loadedVars = $ConfigContent.AppsScriptVariables + $hasVars = $false + foreach ($keyName in $loadedVars.Keys) { + $State.Data.appsScriptVariablesDataList.Add([PSCustomObject]@{ IsSelected = $false; Key = $keyName; Value = $loadedVars[$keyName] }) + $hasVars = $true + } + if ($hasVars) { + $chkDefineAppsScriptVars.IsChecked = $true + $appsScriptVarsPanel.Visibility = 'Visible' + WriteLog "LoadConfig: Loaded AppsScriptVariables (Hashtable) and checked 'Define Apps Script Variables'." + } + else { + $chkDefineAppsScriptVars.IsChecked = $false + $appsScriptVarsPanel.Visibility = 'Collapsed' + WriteLog "LoadConfig: AppsScriptVariables (Hashtable) key was present but empty. Unchecked 'Define Apps Script Variables'." + } + } + else { + $chkDefineAppsScriptVars.IsChecked = $false + $appsScriptVarsPanel.Visibility = 'Collapsed' + WriteLog "LoadConfig Info: Key 'AppsScriptVariables' not found, is null, or not a PSCustomObject/Hashtable. Unchecked 'Define Apps Script Variables'." + } + # Update the ListView's ItemsSource after populating the data list + $lstAppsScriptVars.ItemsSource = $State.Data.appsScriptVariablesDataList.ToArray() + # Update the header checkbox state + if ($null -ne $State.Controls.chkSelectAllAppsScriptVariables) { + Update-SelectAllHeaderCheckBoxState -ListView $lstAppsScriptVars -HeaderCheckBox $State.Controls.chkSelectAllAppsScriptVariables + } + + # Update USB Drive selection if present in config + $usbDriveListKeyExists = $false + if ($ConfigContent -is [System.Management.Automation.PSCustomObject] -and $null -ne $ConfigContent.PSObject.Properties) { + try { + if (($ConfigContent.PSObject.Properties.Match('USBDriveList')).Count -gt 0) { + $usbDriveListKeyExists = $true + } + } + catch { + WriteLog "ERROR: Exception while trying to Match key 'USBDriveList' on ConfigContent.PSObject.Properties. Error: $($_.Exception.Message)" + } + } + + if ($usbDriveListKeyExists -and $null -ne $ConfigContent.USBDriveList) { + WriteLog "LoadConfig: Processing USBDriveList from config." + # First click the Check USB Drives button to populate the list + $State.Controls.btnCheckUSBDrives.RaiseEvent( + [System.Windows.RoutedEventArgs]::new( + [System.Windows.Controls.Button]::ClickEvent + ) + ) + + # Then select the drives that match the saved configuration + foreach ($item in $State.Controls.lstUSBDrives.Items) { + $propertyName = $item.Model + $propertyExists = $false + $propertyValue = $null + + # Ensure USBDriveList is a PSCustomObject before trying to access its properties dynamically + if ($null -ne $ConfigContent.USBDriveList -and $ConfigContent.USBDriveList -is [System.Management.Automation.PSCustomObject]) { + # Check if the property exists on the USBDriveList object + if ($ConfigContent.USBDriveList.PSObject.Properties.Match($propertyName).Count -gt 0) { + $propertyExists = $true + # Access the value dynamically + $propertyValue = $ConfigContent.USBDriveList.$($propertyName) + } + } + + if ($propertyExists -and ($propertyValue -eq $item.SerialNumber)) { + WriteLog "LoadConfig: Selecting USB Drive Model '$($item.Model)' with Serial '$($item.SerialNumber)'." + $item.IsSelected = $true + } + else { + if (-not $propertyExists -and ($null -ne $ConfigContent.USBDriveList)) { + WriteLog "LoadConfig: Property '$($propertyName)' not found on USBDriveList for item Model '$($item.Model)'." + } + $item.IsSelected = $false # Ensure others are deselected if not in config or value mismatch + } + } + $State.Controls.lstUSBDrives.Items.Refresh() + + # Update the Select All header checkbox state + $headerChk = $State.Controls.chkSelectAllUSBDrivesHeader + if ($null -ne $headerChk) { + Update-SelectAllHeaderCheckBoxState -ListView $State.Controls.lstUSBDrives -HeaderCheckBox $headerChk + } + WriteLog "LoadConfig: USBDriveList processing complete." + } + else { + WriteLog "LoadConfig Info: Key 'USBDriveList' not found or is null in configuration file. Skipping USB drive selection." + } + + # If BuildUSBDrive is enabled and USBDriveList was present and not empty in the config, + # ensure "Select Specific USB Drives" is checked to show the list. + $shouldAutoCheckSpecificDrives = $false + if ($State.Controls.chkBuildUSBDriveEnable.IsChecked -and $usbDriveListKeyExists -and ($null -ne $ConfigContent.USBDriveList)) { + if ($ConfigContent.USBDriveList -is [System.Management.Automation.PSCustomObject]) { + if ($ConfigContent.USBDriveList.PSObject.Properties.Count -gt 0) { + $shouldAutoCheckSpecificDrives = $true + } + } + elseif ($ConfigContent.USBDriveList -is [hashtable]) { + # Fallback for older configs + if ($ConfigContent.USBDriveList.Keys.Count -gt 0) { + $shouldAutoCheckSpecificDrives = $true + } + } + } + + if ($shouldAutoCheckSpecificDrives) { + WriteLog "LoadConfig: Auto-checking 'Select Specific USB Drives' due to pre-selected USB drives in config." + $State.Controls.chkSelectSpecificUSBDrives.IsChecked = $true + } + else { + WriteLog "LoadConfig: Condition to auto-check 'Select Specific USB Drives' was NOT met." + } + WriteLog "LoadConfig: Configuration loading process finished." +} + +function Invoke-SaveConfiguration { + param( + [Parameter(Mandatory = $true)] + [psobject]$State + ) + try { + $config = Get-UIConfig -State $State + $defaultConfigPath = Join-Path $config.FFUDevelopmentPath "config" + if (-not (Test-Path $defaultConfigPath)) { + New-Item -Path $defaultConfigPath -ItemType Directory -Force | Out-Null + } + + $savePath = Invoke-BrowseAction -Type 'SaveFile' ` + -Title "Save Configuration File" ` + -Filter "JSON files (*.json)|*.json|All files (*.*)|*.*" ` + -InitialDirectory $defaultConfigPath ` + -FileName "FFUConfig.json" ` + -DefaultExt ".json" + + if ($savePath) { + $config | ConvertTo-Json -Depth 10 | Set-Content -Path $savePath -Encoding UTF8 + [System.Windows.MessageBox]::Show("Configuration file saved to:`n$savePath", "Success", "OK", "Information") + } + } + catch { + [System.Windows.MessageBox]::Show("Error saving config file:`n$($_.Exception.Message)", "Error", "OK", "Error") + } +} + +Export-ModuleMember -Function * \ No newline at end of file diff --git a/FFUDevelopment/FFUUI.Core/FFUUI.Core.Handlers.psm1 b/FFUDevelopment/FFUUI.Core/FFUUI.Core.Handlers.psm1 index 4c06adc..bd16885 100644 --- a/FFUDevelopment/FFUUI.Core/FFUUI.Core.Handlers.psm1 +++ b/FFUDevelopment/FFUUI.Core/FFUUI.Core.Handlers.psm1 @@ -723,5 +723,18 @@ function Register-EventHandlers { $localState = $window.Tag Import-DriversJson -State $localState }) + + $State.Controls.btnLoadConfig.Add_Click({ + param($eventSource, $routedEventArgs) + $window = [System.Windows.Window]::GetWindow($eventSource) + $localState = $window.Tag + Invoke-LoadConfiguration -State $localState + }) + $State.Controls.btnBuildConfig.Add_Click({ + param($eventSource, $routedEventArgs) + $window = [System.Windows.Window]::GetWindow($eventSource) + $localState = $window.Tag + Invoke-SaveConfiguration -State $localState + }) } Export-ModuleMember -Function * \ No newline at end of file diff --git a/FFUDevelopment/FFUUI.Core/FFUUI.Core.Initialize.psm1 b/FFUDevelopment/FFUUI.Core/FFUUI.Core.Initialize.psm1 index b840362..50d66e5 100644 --- a/FFUDevelopment/FFUUI.Core/FFUUI.Core.Initialize.psm1 +++ b/FFUDevelopment/FFUUI.Core/FFUUI.Core.Initialize.psm1 @@ -144,6 +144,8 @@ function Initialize-UIControls { $State.Controls.txtDriversJsonPath = $window.FindName('txtDriversJsonPath') $State.Controls.btnBrowseDriversJsonPath = $window.FindName('btnBrowseDriversJsonPath') $State.Controls.chkUpdateADK = $window.FindName('chkUpdateADK') + $State.Controls.btnLoadConfig = $window.FindName('btnLoadConfig') + $State.Controls.btnBuildConfig = $window.FindName('btnBuildConfig') } function Initialize-VMSwitchData {