# .SYNOPSIS Common Microsoft/Surface driver helpers (cache index, SKU mapping). .DESCRIPTION This module contains Microsoft/Surface-specific functions used by the UI and scripts to map Surface driver packs to System SKU values using: - Source A: Surface System SKU reference (Learn) - Source B: Support page model list - Source C: Download Center details (window.__DLCDetails__) #> # -------------------------------------------------------------------------- # SECTION: Microsoft Surface Driver Index Cache (Sources A/B/C) # -------------------------------------------------------------------------- function Get-SurfaceDriverIndexCachePath { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [string]$DriversFolder ) # Store the cache under Drivers\Microsoft so it travels with the driver content $microsoftDriversFolder = Join-Path -Path $DriversFolder -ChildPath 'Microsoft' if (-not (Test-Path -Path $microsoftDriversFolder -PathType Container)) { New-Item -Path $microsoftDriversFolder -ItemType Directory -Force | Out-Null } return (Join-Path -Path $microsoftDriversFolder -ChildPath 'SurfaceDriverIndex.json') } function Import-SurfaceDriverIndexCache { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [string]$DriversFolder ) $cachePath = Get-SurfaceDriverIndexCachePath -DriversFolder $DriversFolder if (-not (Test-Path -Path $cachePath -PathType Leaf)) { return [pscustomobject]@{ ModelIndex = @() SkuIndex = @() DownloadCenterDetails = @() } } try { $cache = Get-Content -Path $cachePath -Raw | ConvertFrom-Json -ErrorAction Stop } catch { WriteLog "Warning: Could not read Surface driver cache '$cachePath'. Creating a new cache. Error: $($_.Exception.Message)" return [pscustomobject]@{ ModelIndex = @() SkuIndex = @() DownloadCenterDetails = @() } } if ($null -eq $cache) { return [pscustomobject]@{ ModelIndex = @() SkuIndex = @() DownloadCenterDetails = @() } } # Ensure expected properties exist (backward compatible with earlier cache shapes) if (-not $cache.PSObject.Properties['ModelIndex']) { $cache | Add-Member -NotePropertyName ModelIndex -NotePropertyValue @() } if (-not $cache.PSObject.Properties['SkuIndex']) { $cache | Add-Member -NotePropertyName SkuIndex -NotePropertyValue @() } if (-not $cache.PSObject.Properties['DownloadCenterDetails']) { $cache | Add-Member -NotePropertyName DownloadCenterDetails -NotePropertyValue @() } return $cache } function Save-SurfaceDriverIndexCache { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [psobject]$Cache, [Parameter(Mandatory = $true)] [string]$DriversFolder ) $cachePath = Get-SurfaceDriverIndexCachePath -DriversFolder $DriversFolder $Cache | ConvertTo-Json -Depth 10 | Set-Content -Path $cachePath -Encoding UTF8 } function ConvertTo-SurfaceComparableName { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [string]$Text ) # Normalize Surface marketing strings into a comparable family key. # This intentionally strips consumer/commercial/processor qualifiers so we can join Sources A/B/C. $value = [System.Net.WebUtility]::HtmlDecode($Text) if ([string]::IsNullOrWhiteSpace($value)) { return $null } $value = $value.Trim() $value = $value -replace '\(', ' ' $value = $value -replace '\)', ' ' $value = $value -replace ',', ' ' # Normalize punctuation that frequently differs between Support/Learn pages # (e.g. Wi‑Fi unicode hyphen, AT&T, Y!mobile) $value = $value -replace '[-\u2010\u2011\u2012\u2013\u2014\u2212]', ' ' $value = $value -replace '&', ' ' $value = $value -replace '!', ' ' $value = $value -replace '™', ' ' $value = $value -replace '(?i)\bMicrosoft\b', '' $value = $value -replace '(?i)\bfor\s+Business\b', '' $value = $value -replace '(?i)\bConsumer\b', '' $value = $value -replace '(?i)\bCommercial\b', '' # Strip processor/connection qualifiers that cause mismatches between WMI, Learn, and Support naming. $value = $value -replace '(?i)\bwith\s+Intel\b', '' $value = $value -replace '(?i)\bIntel\s+processor\b', '' $value = $value -replace '(?i)\bIntel\b', '' $value = $value -replace '(?i)\bSnapdragon\s+processor\b', '' $value = $value -replace '(?i)\bSnapdragon\b', '' $value = $value -replace '(?i)\bwith\s+5G\b', '' $value = $value -replace '(?i)\bLTE\b', '' $value = $value -replace '(?i)\b4G\b', '' $value = $value -replace '(?i)\bprocessor\b', '' # Cleanup: remove orphaned "with" left behind by earlier removals (e.g., "Surface Pro 9 with Intel Processor") $value = $value -replace '(?i)\bwith\b', '' $value = $value -replace '\s+', ' ' return $value.Trim().ToUpperInvariant() } function Get-SurfaceSystemSkuReferenceIndex { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [string]$DriversFolder ) # Source A: Learn page with authoritative Device / System Model / System SKU table $cache = Import-SurfaceDriverIndexCache -DriversFolder $DriversFolder if ($cache.SkuIndex -and $cache.SkuIndex.Count -gt 0) { return @($cache.SkuIndex) } $url = 'https://learn.microsoft.com/en-us/surface/surface-system-sku-reference' WriteLog "Surface cache: Downloading System SKU reference table from $url" $headers = @{ 'User-Agent' = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)' } $webContent = Invoke-WebRequest -Uri $url -UseBasicParsing -Headers $headers $html = $webContent.Content $skuRows = [System.Collections.Generic.List[pscustomobject]]::new() $rowMatches = [regex]::Matches($html, '
]*>)?(.*?)(?:
)?\s*