mirror of
https://github.com/rbalsleyMSFT/FFU.git
synced 2026-06-14 10:19:36 -06:00
6df7b16cdf
Adds standard PowerShell comment-based help blocks (synopsis and description) to all UI and common library script modules (`.psm1`) and the main UI entry point script (`.ps1`). This improves maintainability and discoverability by documenting the purpose of each script file. Also removes various redundant or commented-out code blocks.
256 lines
11 KiB
PowerShell
256 lines
11 KiB
PowerShell
<#
|
|
.SYNOPSIS
|
|
Provides common functions for driver management, including compression, mapping, and existence checks.
|
|
|
|
.DESCRIPTION
|
|
The FFU.Common.Drivers module contains a set of shared functions used across the FFU project for handling driver packages.
|
|
This includes compressing driver folders into WIM files for efficient storage and deployment, maintaining a JSON-based mapping
|
|
of downloaded drivers to their respective makes and models, and checking for the pre-existence of driver packages to avoid
|
|
redundant downloads.
|
|
#>
|
|
function Compress-DriverFolderToWim {
|
|
[CmdletBinding(SupportsShouldProcess)]
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[ValidateScript({ Test-Path -Path $_ -PathType Container })]
|
|
[string]$SourceFolderPath,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$DestinationWimPath,
|
|
|
|
[Parameter()]
|
|
[string]$WimName, # Optional, defaults to folder name
|
|
|
|
[Parameter()]
|
|
[string]$WimDescription # Optional, defaults to folder name
|
|
)
|
|
|
|
WriteLog "Starting compression of folder '$SourceFolderPath' to '$DestinationWimPath'."
|
|
|
|
# Default WIM Name and Description to the source folder name if not provided
|
|
$sourceFolderName = Split-Path -Path $SourceFolderPath -Leaf
|
|
if ([string]::IsNullOrWhiteSpace($WimName)) {
|
|
$WimName = $sourceFolderName
|
|
WriteLog "WIM Name not provided, defaulting to source folder name: '$WimName'."
|
|
}
|
|
if ([string]::IsNullOrWhiteSpace($WimDescription)) {
|
|
$WimDescription = $sourceFolderName
|
|
WriteLog "WIM Description not provided, defaulting to source folder name: '$WimDescription'."
|
|
}
|
|
|
|
# Ensure destination directory exists
|
|
$destinationDir = Split-Path -Path $DestinationWimPath -Parent
|
|
if (-not (Test-Path -Path $destinationDir -PathType Container)) {
|
|
WriteLog "Creating destination directory: $destinationDir"
|
|
try {
|
|
New-Item -Path $destinationDir -ItemType Directory -Force -ErrorAction Stop | Out-Null
|
|
}
|
|
catch {
|
|
WriteLog "Failed to create destination directory '$destinationDir': $($_.Exception.Message)"
|
|
return $false # Indicate failure
|
|
}
|
|
}
|
|
|
|
if ($PSCmdlet.ShouldProcess("Folder '$SourceFolderPath'", "Compress to WIM '$DestinationWimPath'")) {
|
|
try {
|
|
# Construct arguments for dism.exe
|
|
$dismArgs = "/Capture-Image /ImageFile:`"$DestinationWimPath`" /CaptureDir:`"$SourceFolderPath`" /Name:`"$WimName`" /Description:`"$WimDescription`" /Compress:Max /CheckIntegrity /Quiet"
|
|
|
|
WriteLog "Executing dism.exe via Invoke-Process with arguments:"
|
|
WriteLog "dism.exe $dismArgs"
|
|
|
|
# Call Invoke-Process (assumed to be available from FFUUI.Core.psm1 or another imported module)
|
|
# Invoke-Process is expected to throw an exception for non-zero exit codes.
|
|
Invoke-Process -FilePath "dism.exe" -ArgumentList $dismArgs -Wait $true
|
|
|
|
WriteLog "Successfully compressed '$SourceFolderPath' to '$DestinationWimPath' using dism.exe."
|
|
|
|
# Remove the source folder after successful compression
|
|
WriteLog "Removing source driver folder: $SourceFolderPath"
|
|
try {
|
|
Remove-Item -Path $SourceFolderPath -Recurse -Force -ErrorAction Stop
|
|
WriteLog "Successfully removed source folder '$SourceFolderPath'."
|
|
}
|
|
catch {
|
|
WriteLog "Warning: Failed to remove source folder '$SourceFolderPath'. Error: $($_.Exception.Message)"
|
|
# Do not fail the whole operation, just log a warning.
|
|
}
|
|
|
|
return $true # Indicate success
|
|
}
|
|
catch {
|
|
WriteLog "Failed to compress folder '$SourceFolderPath' to WIM '$DestinationWimPath' using dism.exe."
|
|
WriteLog "Error details: $($_.Exception.Message)"
|
|
# Check if the error message contains details about the DISM log (dism.exe output might be in the exception)
|
|
if ($_.Exception.Message -match 'DISM log file can be found at (.*)') {
|
|
$dismLogPath = $matches[1].Trim()
|
|
WriteLog "Check the DISM log for more details: $dismLogPath"
|
|
}
|
|
return $false # Indicate failure
|
|
}
|
|
}
|
|
else {
|
|
WriteLog "Compression operation skipped due to -WhatIf."
|
|
return $false # Indicate skipped operation
|
|
}
|
|
}
|
|
|
|
# --------------------------------------------------------------------------
|
|
# SECTION: Driver Mapping Function
|
|
# --------------------------------------------------------------------------
|
|
|
|
function Update-DriverMappingJson {
|
|
[CmdletBinding()]
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[array]$DownloadedDrivers, # Array of PSCustomObjects with Make, Model, DriverPath
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$DriversFolder # Base drivers folder (e.g., C:\FFUDevelopment\Drivers)
|
|
)
|
|
|
|
$mappingFilePath = Join-Path -Path $DriversFolder -ChildPath "DriverMapping.json"
|
|
WriteLog "Updating driver mapping file at: $mappingFilePath"
|
|
|
|
# Load existing mapping file or create a new list
|
|
$mappingList = [System.Collections.Generic.List[PSCustomObject]]::new()
|
|
if (Test-Path -Path $mappingFilePath -PathType Leaf) {
|
|
try {
|
|
$existingJson = Get-Content -Path $mappingFilePath -Raw | ConvertFrom-Json
|
|
# Ensure it's a collection before adding to the list
|
|
if ($existingJson -is [array]) {
|
|
# Iterate through the array to avoid type conversion issues with AddRange
|
|
foreach ($item in $existingJson) {
|
|
$mappingList.Add($item)
|
|
}
|
|
}
|
|
else {
|
|
$mappingList.Add($existingJson)
|
|
}
|
|
WriteLog "Loaded $($mappingList.Count) existing entries from $mappingFilePath"
|
|
}
|
|
catch {
|
|
WriteLog "Warning: Could not read or parse existing DriverMapping.json. A new file will be created. Error: $($_.Exception.Message)"
|
|
}
|
|
}
|
|
|
|
$updatedCount = 0
|
|
$addedCount = 0
|
|
|
|
foreach ($driver in $DownloadedDrivers) {
|
|
# Skip if any required property is missing or null
|
|
if (-not $driver.PSObject.Properties['Make'] -or -not $driver.PSObject.Properties['Model'] -or -not $driver.PSObject.Properties['DriverPath'] -or [string]::IsNullOrWhiteSpace($driver.DriverPath)) {
|
|
WriteLog "Skipping driver entry due to missing or empty Make, Model, or DriverPath. Details: $(($driver | ConvertTo-Json -Compress -Depth 3))"
|
|
continue
|
|
}
|
|
|
|
# Find existing entry
|
|
$existingEntry = $mappingList | Where-Object { $_.Manufacturer -eq $driver.Make -and $_.Model -eq $driver.Model } | Select-Object -First 1
|
|
|
|
if ($null -ne $existingEntry) {
|
|
# Update existing entry if the path is different
|
|
if ($existingEntry.DriverPath -ne $driver.DriverPath) {
|
|
WriteLog "Updating driver path for '$($driver.Make) - $($driver.Model)' from '$($existingEntry.DriverPath)' to '$($driver.DriverPath)'."
|
|
$existingEntry.DriverPath = $driver.DriverPath
|
|
$updatedCount++
|
|
}
|
|
}
|
|
else {
|
|
# Add new entry
|
|
$newEntry = [PSCustomObject]@{
|
|
Manufacturer = $driver.Make
|
|
Model = $driver.Model
|
|
DriverPath = $driver.DriverPath
|
|
}
|
|
$mappingList.Add($newEntry)
|
|
WriteLog "Adding new mapping for '$($driver.Make) - $($driver.Model)' with path '$($driver.DriverPath)'."
|
|
$addedCount++
|
|
}
|
|
}
|
|
|
|
if ($updatedCount -gt 0 -or $addedCount -gt 0) {
|
|
try {
|
|
# Sort the list for consistency before saving
|
|
$sortedList = $mappingList | Sort-Object -Property Manufacturer, Model
|
|
$sortedList | ConvertTo-Json -Depth 5 | Set-Content -Path $mappingFilePath -Encoding UTF8
|
|
WriteLog "Successfully saved DriverMapping.json with $addedCount new entries and $updatedCount updated entries."
|
|
}
|
|
catch {
|
|
WriteLog "Error saving updated DriverMapping.json: $($_.Exception.Message)"
|
|
throw "Failed to save driver mapping file."
|
|
}
|
|
}
|
|
else {
|
|
WriteLog "No changes needed for DriverMapping.json."
|
|
}
|
|
}
|
|
|
|
# --------------------------------------------------------------------------
|
|
# SECTION: Driver Existence Check Function
|
|
# --------------------------------------------------------------------------
|
|
function Test-ExistingDriver {
|
|
[CmdletBinding()]
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Make,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Model,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$DriversFolder,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Identifier,
|
|
|
|
[Parameter()]
|
|
[System.Collections.Concurrent.ConcurrentQueue[hashtable]]$ProgressQueue = $null
|
|
)
|
|
|
|
$makeDriversPath = Join-Path -Path $DriversFolder -ChildPath $Make
|
|
$modelPath = Join-Path -Path $makeDriversPath -ChildPath $Model
|
|
$driverRelativePath = Join-Path -Path $Make -ChildPath $Model
|
|
|
|
# Check for WIM file first
|
|
$wimFilePath = Join-Path -Path $makeDriversPath -ChildPath "$($Model).wim"
|
|
if (Test-Path -Path $wimFilePath -PathType Leaf) {
|
|
$status = "Already downloaded (WIM)"
|
|
WriteLog "Driver WIM for '$Identifier' already exists at '$wimFilePath'."
|
|
if ($null -ne $ProgressQueue) { Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $Identifier -Status $status }
|
|
$wimRelativePath = Join-Path -Path $Make -ChildPath "$($Model).wim"
|
|
return [PSCustomObject]@{
|
|
Model = $Identifier # Return original identifier
|
|
Status = $status
|
|
Success = $true
|
|
DriverPath = $wimRelativePath
|
|
}
|
|
}
|
|
|
|
# Check for existing driver folder
|
|
if (Test-Path -Path $modelPath -PathType Container) {
|
|
$folderSize = (Get-ChildItem -Path $modelPath -Recurse | Measure-Object -Property Length -Sum -ErrorAction SilentlyContinue).Sum
|
|
if ($folderSize -gt 1MB) {
|
|
$status = "Already downloaded"
|
|
WriteLog "Drivers for '$Identifier' already exist in '$modelPath'."
|
|
if ($null -ne $ProgressQueue) { Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $Identifier -Status $status }
|
|
return [PSCustomObject]@{
|
|
Model = $Identifier # Return original identifier
|
|
Status = $status
|
|
Success = $true
|
|
DriverPath = $driverRelativePath
|
|
}
|
|
}
|
|
else {
|
|
WriteLog "Driver folder '$modelPath' for '$Identifier' exists but is empty or very small. Re-downloading."
|
|
}
|
|
}
|
|
|
|
# If neither WIM nor a valid folder exists, return null
|
|
return $null
|
|
}
|
|
|
|
# --------------------------------------------------------------------------
|
|
# SECTION: Module Export
|
|
# --------------------------------------------------------------------------
|
|
|
|
Export-ModuleMember -Function Compress-DriverFolderToWim, Update-DriverMappingJson, Test-ExistingDriver |