diff --git a/FFUDevelopment/BuildFFUVM.ps1 b/FFUDevelopment/BuildFFUVM.ps1 index 66334e0..8830996 100644 --- a/FFUDevelopment/BuildFFUVM.ps1 +++ b/FFUDevelopment/BuildFFUVM.ps1 @@ -1432,7 +1432,7 @@ function Get-DellDrivers { [Parameter(Mandatory = $true)] [string]$Model, [Parameter(Mandatory = $true)] - [ValidateSet('x64','x86','ARM64')] + [ValidateSet('x64', 'x86', 'ARM64')] [string]$WindowsArch, [Parameter(Mandatory = $true)] [int]$WindowsRelease @@ -1454,9 +1454,18 @@ function Get-DellDrivers { if (-not $target) { throw "Requested Dell model '$Model' not found in index." } $cabUrl = $target.CabUrl + if ([string]::IsNullOrWhiteSpace($cabUrl)) { + WriteLog "CabUrl missing for '$($target.Model)'; resolving via CatalogIndexPC." + $resolved = Resolve-DellCabUrlFromModel -DriversFolder $DriversFolder -ModelDisplay $target.Model + if ($null -eq $resolved -or [string]::IsNullOrWhiteSpace($resolved.CabUrl)) { + throw "Unable to resolve CabUrl for $($target.Model) from CatalogIndexPC." + } + $cabUrl = $resolved.CabUrl + $target.CabUrl = $cabUrl + } $modelCabName = [IO.Path]::GetFileName($cabUrl) $modelCabPath = Join-Path $DriversFolder $modelCabName - $modelXmlPath = Join-Path $DriversFolder ($modelCabName -replace '\.cab$','.xml') + $modelXmlPath = Join-Path $DriversFolder ($modelCabName -replace '\.cab$', '.xml') if (Test-Path $modelCabPath) { Remove-Item $modelCabPath -Force -ErrorAction SilentlyContinue } if (Test-Path $modelXmlPath) { Remove-Item $modelXmlPath -Force -ErrorAction SilentlyContinue } @@ -1473,7 +1482,7 @@ function Get-DellDrivers { } foreach ($pkg in $pkgs) { - $categorySafe = ($pkg.Category -replace '[\\\/\:\*\?\"\<\>\| ]','_') + $categorySafe = ($pkg.Category -replace '[\\\/\:\*\?\"\<\>\| ]', '_') $downloadFolder = Join-Path $DriversFolder (Join-Path $Model $categorySafe) if (-not (Test-Path $downloadFolder)) { New-Item -Path $downloadFolder -ItemType Directory -Force | Out-Null } $driverFilePath = Join-Path $downloadFolder $pkg.DriverFileName @@ -1535,8 +1544,8 @@ function Get-DellDrivers { $driverPath = $component.path $downloadUrl = $baseLocation + $driverPath $driverFileName = [System.IO.Path]::GetFileName($driverPath) - $name = $component.Name.Display.'#cdata-section' -replace '[\\\/\:\*\?\"\<\>\| ]','_' -replace '[\,]','-' - $category = $component.Category.Display.'#cdata-section' -replace '[\\\/\:\*\?\"\<\>\| ]','_' + $name = $component.Name.Display.'#cdata-section' -replace '[\\\/\:\*\?\"\<\>\| ]', '_' -replace '[\,]', '-' + $category = $component.Category.Display.'#cdata-section' -replace '[\\\/\:\*\?\"\<\>\| ]', '_' $version = [version]$component.vendorVersion $namePrefix = ($name -split '-')[0] if (-not $latestDrivers[$category]) { $latestDrivers[$category] = @{} } diff --git a/FFUDevelopment/FFU.Common/FFU.Common.Drivers.Dell.psm1 b/FFUDevelopment/FFU.Common/FFU.Common.Drivers.Dell.psm1 index b676054..2e9284b 100644 --- a/FFUDevelopment/FFU.Common/FFU.Common.Drivers.Dell.psm1 +++ b/FFUDevelopment/FFU.Common/FFU.Common.Drivers.Dell.psm1 @@ -231,4 +231,50 @@ function Get-DellLatestDriverPackages { return $chosen } -Export-ModuleMember -Function Convert-DellVendorVersion,Compare-DellVendorVersion,Get-DellCatalogIndex,Get-DellClientModels,Get-DellLatestDriverPackages \ No newline at end of file +# Resolve a Dell per‑model CabUrl when missing by inspecting CatalogIndexPC +function Resolve-DellCabUrlFromModel { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)][string]$DriversFolder, + [Parameter()][string]$ModelDisplay, + [Parameter()][string]$SystemId + ) + + if ([string]::IsNullOrWhiteSpace($SystemId) -and -not [string]::IsNullOrWhiteSpace($ModelDisplay)) { + # Try to parse the trailing (XXXX) token (SystemId) + if ($ModelDisplay -match '\(([0-9A-Fa-f]{4})\)\s*$') { + $SystemId = $matches[1].ToUpperInvariant() + } + } + + if ([string]::IsNullOrWhiteSpace($SystemId)) { + WriteLog "Resolve-DellCabUrlFromModel: No SystemId could be determined from '$ModelDisplay'." + return $null + } + + try { + $indexXml = Get-DellCatalogIndex -DriversFolder $DriversFolder + # Reuse existing model parsing to avoid duplicating streaming logic + $allModels = Get-DellClientModels -CatalogIndexXmlPath $indexXml + $match = $allModels | Where-Object { $_.SystemId -eq $SystemId } | Select-Object -First 1 + if ($null -eq $match) { + WriteLog "Resolve-DellCabUrlFromModel: SystemId '$SystemId' not found in CatalogIndexPC.xml." + return $null + } + WriteLog "Resolve-DellCabUrlFromModel: Resolved CabUrl for '$($match.ModelDisplay)' -> $($match.CabUrl)" + return [pscustomobject]@{ + Brand = $match.Brand + ModelNumber = $match.ModelNumber + SystemId = $match.SystemId + CabRelativePath = $match.CabRelativePath + CabUrl = $match.CabUrl + ModelDisplay = $match.ModelDisplay + } + } + catch { + WriteLog "Resolve-DellCabUrlFromModel: Failure resolving CabUrl for '$ModelDisplay' / SystemId '$SystemId' : $($_.Exception.Message)" + return $null + } +} + +Export-ModuleMember -Function Convert-DellVendorVersion,Compare-DellVendorVersion,Get-DellCatalogIndex,Get-DellClientModels,Get-DellLatestDriverPackages,Resolve-DellCabUrlFromModel \ No newline at end of file diff --git a/FFUDevelopment/FFUUI.Core/FFUUI.Core.Drivers.Dell.psm1 b/FFUDevelopment/FFUUI.Core/FFUUI.Core.Drivers.Dell.psm1 index 0b055e3..1e5bfb0 100644 --- a/FFUDevelopment/FFUUI.Core/FFUUI.Core.Drivers.Dell.psm1 +++ b/FFUDevelopment/FFUUI.Core/FFUUI.Core.Drivers.Dell.psm1 @@ -182,75 +182,35 @@ function Save-DellDriversTask { if ($WindowsRelease -le 11) { $cabUrl = $DriverItemData.CabUrl if ([string]::IsNullOrWhiteSpace($cabUrl)) { - WriteLog "CabUrl missing for '$modelDisplay' – falling back to legacy CatalogPC parsing." - # Fallback legacy client method - $catalogCab = Join-Path $makeDriversPath 'CatalogPC.cab' - $catalogXml = Join-Path $makeDriversPath 'CatalogPC.xml' - $catalogUrl = 'http://downloads.dell.com/catalog/CatalogPC.cab' - $need = $true - if (Test-Path $catalogXml) { - if (((Get-Date) - (Get-Item $catalogXml).CreationTime).TotalDays -lt 7) { $need = $false } + WriteLog "CabUrl missing for '$modelDisplay' – resolving via CatalogIndexPC." + $resolved = Resolve-DellCabUrlFromModel -DriversFolder $DriversFolder -ModelDisplay $modelDisplay + if ($null -eq $resolved -or [string]::IsNullOrWhiteSpace($resolved.CabUrl)) { + throw "Unable to resolve CabUrl for $modelDisplay from CatalogIndexPC." } - if ($need) { - if (Test-Path $catalogCab) { Remove-SafeFolder $catalogCab } - if (Test-Path $catalogXml) { Remove-SafeFolder $catalogXml } - Start-BitsTransferWithRetry -Source $catalogUrl -Destination $catalogCab - Invoke-Process -FilePath Expand.exe -ArgumentList """$catalogCab"" ""$catalogXml""" | Out-Null - Remove-Item $catalogCab -Force -ErrorAction SilentlyContinue + $cabUrl = $resolved.CabUrl + # Optionally persist back into the incoming object if property exists + if ($DriverItemData.PSObject.Properties['CabUrl']) { + $DriverItemData.CabUrl = $cabUrl } - if (-not (Test-Path $catalogXml)) { throw "Legacy fallback failed; missing $catalogXml" } - [xml]$xmlContent = Get-Content -Path $catalogXml -Raw - $baseLocation = "https://$($xmlContent.manifest.baseLocation)/" - $softwareComponents = $xmlContent.Manifest.SoftwareComponent | Where-Object { $_.ComponentType.value -eq 'DRVR' } - - $latestDrivers = @{} - foreach ($component in $softwareComponents) { - $models = $component.SupportedSystems.Brand.Model - foreach ($m in $models) { - if ($m.Display.'#cdata-section' -eq $modelDisplay) { - $validOS = $component.SupportedOperatingSystems.OperatingSystem | Where-Object { $_.osArch -eq $WindowsArch } - if (-not $validOS) { continue } - $driverPath = $component.path - $downloadUrl = $baseLocation + $driverPath - $fileName = [IO.Path]::GetFileName($driverPath) - $name = $component.Name.Display.'#cdata-section' -replace '[\\\/\:\*\?\"\<\>\| ]','_' -replace '[\,]','-' - $category = $component.Category.Display.'#cdata-section' -replace '[\\\/\:\*\?\"\<\>\| ]','_' - $version = [version]$component.vendorVersion - $namePrefix = ($name -split '-')[0] - if (-not $latestDrivers[$category]) { $latestDrivers[$category] = @{} } - if (-not $latestDrivers[$category][$namePrefix] -or $latestDrivers[$category][$namePrefix].Version -lt $version) { - $latestDrivers[$category][$namePrefix] = [pscustomobject]@{ - Name = $name - DownloadUrl = $downloadUrl - DriverFileName = $fileName - Version = $version - Category = $category - } - } - } - } - } - foreach ($cat in $latestDrivers.Keys) { foreach ($drv in $latestDrivers[$cat].Values) { $packages += $drv } } - } - else { - # Normal new model-based workflow - $modelCabName = [IO.Path]::GetFileName($cabUrl) - if ([string]::IsNullOrWhiteSpace($modelCabName)) { throw "Derived model cab name empty for $modelDisplay" } - $modelCabPath = Join-Path $makeDriversPath $modelCabName - $modelXmlPath = Join-Path $makeDriversPath ([IO.Path]::GetFileNameWithoutExtension($modelCabName) + '.xml') - - if ($null -ne $ProgressQueue) { Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $modelDisplay -Status 'Downloading catalog...' } - if (Test-Path $modelCabPath) { Remove-SafeFolder $modelCabPath } - if (Test-Path $modelXmlPath) { Remove-SafeFolder $modelXmlPath } - - Start-BitsTransferWithRetry -Source $cabUrl -Destination $modelCabPath - Invoke-Process -FilePath Expand.exe -ArgumentList """$modelCabPath"" ""$modelXmlPath""" | Out-Null - Remove-Item $modelCabPath -Force -ErrorAction SilentlyContinue - if (-not (Test-Path $modelXmlPath)) { throw "Model XML not found after extraction: $modelXmlPath" } - - if ($null -ne $ProgressQueue) { Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $modelDisplay -Status 'Selecting latest drivers...' } - $packages = Get-DellLatestDriverPackages -ModelXmlPath $modelXmlPath -WindowsArch $WindowsArch -WindowsRelease $WindowsRelease } + + # Model-based workflow (always used for client pathway now) + $modelCabName = [IO.Path]::GetFileName($cabUrl) + if ([string]::IsNullOrWhiteSpace($modelCabName)) { throw "Derived model cab name empty for $modelDisplay" } + $modelCabPath = Join-Path $makeDriversPath $modelCabName + $modelXmlPath = Join-Path $makeDriversPath ([IO.Path]::GetFileNameWithoutExtension($modelCabName) + '.xml') + + if ($null -ne $ProgressQueue) { Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $modelDisplay -Status 'Downloading catalog...' } + if (Test-Path $modelCabPath) { Remove-SafeFolder $modelCabPath } + if (Test-Path $modelXmlPath) { Remove-SafeFolder $modelXmlPath } + + Start-BitsTransferWithRetry -Source $cabUrl -Destination $modelCabPath + Invoke-Process -FilePath Expand.exe -ArgumentList """$modelCabPath"" ""$modelXmlPath""" | Out-Null + Remove-Item $modelCabPath -Force -ErrorAction SilentlyContinue + if (-not (Test-Path $modelXmlPath)) { throw "Model XML not found after extraction: $modelXmlPath" } + + if ($null -ne $ProgressQueue) { Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $modelDisplay -Status 'Selecting latest drivers...' } + $packages = Get-DellLatestDriverPackages -ModelXmlPath $modelXmlPath -WindowsArch $WindowsArch -WindowsRelease $WindowsRelease } else { # Server legacy logic unchanged (kept as before) diff --git a/FFUDevelopment/FFUUI.Core/FFUUI.Core.Drivers.psm1 b/FFUDevelopment/FFUUI.Core/FFUUI.Core.Drivers.psm1 index 52833f2..7587996 100644 --- a/FFUDevelopment/FFUUI.Core/FFUUI.Core.Drivers.psm1 +++ b/FFUDevelopment/FFUUI.Core/FFUUI.Core.Drivers.psm1 @@ -225,6 +225,15 @@ function Save-DriversJson { $modelObject = @{ Name = $driverItem.Model } + if ($driverItem.PSObject.Properties['SystemId'] -and -not [string]::IsNullOrWhiteSpace($driverItem.SystemId)) { + $modelObject.SystemId = $driverItem.SystemId + } + if ($driverItem.PSObject.Properties['CabUrl'] -and -not [string]::IsNullOrWhiteSpace($driverItem.CabUrl)) { + $modelObject.CabUrl = $driverItem.CabUrl + } + if ($driverItem.PSObject.Properties['CabRelativePath'] -and -not [string]::IsNullOrWhiteSpace($driverItem.CabRelativePath)) { + $modelObject.CabRelativePath = $driverItem.CabRelativePath + } } 'HP' { $modelObject = @{ @@ -337,7 +346,7 @@ function Import-DriversJson { if ($null -ne $existingModel) { $existingModel.IsSelected = $true $existingModel.DownloadStatus = "Imported" - + if ($makeName -eq 'Microsoft' -and $importedModelObject.PSObject.Properties['Link']) { if ($existingModel.Link -ne $importedModelObject.Link) { $existingModel.Link = $importedModelObject.Link @@ -352,13 +361,35 @@ function Import-DriversJson { } if ($importedModelObject.PSObject.Properties['MachineType'] -and $existingModel.PSObject.Properties['MachineType'] -and $existingModel.MachineType -ne $importedModelObject.MachineType) { $existingModel.MachineType = $importedModelObject.MachineType - $existingModel.Id = $importedModelObject.MachineType # Update Id as well + $existingModel.Id = $importedModelObject.MachineType $updateExistingLenovo = $true } if ($updateExistingLenovo) { WriteLog "Import-DriversJson: Updated ProductName/MachineType/Id for existing Lenovo model '$($existingModel.Model)'." } } + elseif ($makeName -eq 'Dell') { + # Update Dell extended fields if provided + if ($importedModelObject.PSObject.Properties['SystemId'] -and $existingModel.PSObject.Properties['SystemId'] -and -not [string]::IsNullOrWhiteSpace($importedModelObject.SystemId)) { + if ($existingModel.SystemId -ne $importedModelObject.SystemId) { + $existingModel.SystemId = $importedModelObject.SystemId + WriteLog "Import-DriversJson: Updated SystemId for existing Dell model '$($existingModel.Model)'." + } + } + if ($importedModelObject.PSObject.Properties['CabUrl'] -and $existingModel.PSObject.Properties['CabUrl'] -and -not [string]::IsNullOrWhiteSpace($importedModelObject.CabUrl)) { + if ($existingModel.CabUrl -ne $importedModelObject.CabUrl) { + $existingModel.CabUrl = $importedModelObject.CabUrl + WriteLog "Import-DriversJson: Updated CabUrl for existing Dell model '$($existingModel.Model)'." + } + } + if ($importedModelObject.PSObject.Properties['CabRelativePath'] -and $existingModel.PSObject.Properties['CabRelativePath'] -and -not [string]::IsNullOrWhiteSpace($importedModelObject.CabRelativePath)) { + if ($existingModel.CabRelativePath -ne $importedModelObject.CabRelativePath) { + $existingModel.CabRelativePath = $importedModelObject.CabRelativePath + WriteLog "Import-DriversJson: Updated CabRelativePath for existing Dell model '$($existingModel.Model)'." + } + } + } + $existingModelsUpdated++ WriteLog "Import-DriversJson: Marked existing model '$($existingModel.Make) - $($existingModel.Model)' as imported." } @@ -395,7 +426,7 @@ function Import-DriversJson { $newDriverModel = [PSCustomObject]@{ IsSelected = $true Make = $makeName - Model = $importedModelNameFromObject # Full display name + Model = $importedModelNameFromObject Link = $importedLink Id = $importedId ProductName = $importedProductName @@ -406,6 +437,18 @@ function Import-DriversJson { Arch = "" DownloadStatus = "Imported" } + if ($makeName -eq 'Dell') { + # Attach optional Dell extended fields if present + if ($importedModelObject.PSObject.Properties['SystemId'] -and -not [string]::IsNullOrWhiteSpace($importedModelObject.SystemId)) { + $newDriverModel | Add-Member -NotePropertyName SystemId -NotePropertyValue $importedModelObject.SystemId + } + if ($importedModelObject.PSObject.Properties['CabUrl'] -and -not [string]::IsNullOrWhiteSpace($importedModelObject.CabUrl)) { + $newDriverModel | Add-Member -NotePropertyName CabUrl -NotePropertyValue $importedModelObject.CabUrl + } + if ($importedModelObject.PSObject.Properties['CabRelativePath'] -and -not [string]::IsNullOrWhiteSpace($importedModelObject.CabRelativePath)) { + $newDriverModel | Add-Member -NotePropertyName CabRelativePath -NotePropertyValue $importedModelObject.CabRelativePath + } + } $State.Data.allDriverModels.Add($newDriverModel) $newModelsAdded++ WriteLog "Import-DriversJson: Added new model '$($newDriverModel.Make) - $($newDriverModel.Model)' from import. ID: $($newDriverModel.Id), Link: $($newDriverModel.Link)" @@ -753,7 +796,16 @@ function Invoke-DownloadSelectedDrivers { } 'Dell' { $modelObject = @{ - Name = $driverItem.Model # Model is the display name + Name = $driverItem.Model + } + if ($driverItem.PSObject.Properties['SystemId'] -and -not [string]::IsNullOrWhiteSpace($driverItem.SystemId)) { + $modelObject.SystemId = $driverItem.SystemId + } + if ($driverItem.PSObject.Properties['CabUrl'] -and -not [string]::IsNullOrWhiteSpace($driverItem.CabUrl)) { + $modelObject.CabUrl = $driverItem.CabUrl + } + if ($driverItem.PSObject.Properties['CabRelativePath'] -and -not [string]::IsNullOrWhiteSpace($driverItem.CabRelativePath)) { + $modelObject.CabRelativePath = $driverItem.CabRelativePath } } 'HP' {