Private/SourceParsing.ps1
|
# Source parsers live separately so upstream Microsoft HTML changes can be isolated and tested. function Get-SurfaceWinPEImportCatalog { [CmdletBinding()] param([string]$Uri = $script:WinPEDocumentationUrl) $response = Invoke-SurfaceWebRequest -Uri $Uri $html = [string]$response.Content $sectionPattern = '(?is)<h3\b[^>]*>(?<heading>.*?)</h3>(?<body>.*?)(?=<h3\b|<h2\b|$)' $sections = [regex]::Matches($html, $sectionPattern) $results = @() foreach ($section in $sections) { $heading = ConvertFrom-SurfaceHtmlText $section.Groups['heading'].Value if ($heading -notmatch '^Surface\s') { continue } $body = $section.Groups['body'].Value $codeMatch = [regex]::Match($body, '(?is)<pre\b[^>]*>\s*<code\b[^>]*>(?<code>.*?)</code>\s*</pre>') if (-not $codeMatch.Success) { continue } $code = ConvertFrom-SurfaceHtmlText $codeMatch.Groups['code'].Value $folders = @( $code -split "`r?`n" | ForEach-Object { $_.Trim().Trim('`') } | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | ForEach-Object { $parts = $_ -split '[\\/]' $parts[-1].Trim() } | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | Select-Object -Unique ) $importFolderSource = 'PrimaryImportFolders' # Older Surface sections can document legacy SurfacePlatformInstaller paths in the # primary code block and then explicitly provide the correct folder names for newer # MSI packages beginning with "SurfaceUpdate". Prefer Microsoft's newer-MSI guidance # whenever it is present instead of maintaining a hard-coded alias table. $surfaceUpdateGuidance = [regex]::Match( $body, '(?is)For\s+newer\s+\.msi\s+files\s+beginning\s+with.*?SurfaceUpdate.*?use:\s*(?<list>.*?)(?=</p>|<h[1-6]\b|$)' ) if ($surfaceUpdateGuidance.Success) { $surfaceUpdateFolders = @( [regex]::Matches( $surfaceUpdateGuidance.Groups['list'].Value, '(?i)SurfaceUpdate\\(?<folder>[A-Za-z0-9._-]+)' ) | ForEach-Object { $_.Groups['folder'].Value } | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | Select-Object -Unique ) if ($surfaceUpdateFolders.Count -gt 0) { $folders = $surfaceUpdateFolders $importFolderSource = 'SurfaceUpdateGuidance' } } $requiredPackages = @() $requiredNames = @( [regex]::Matches($body, '(?i)SurfaceHidMini_WinPE_(?:Intel|ARM)') | ForEach-Object { $_.Value } | Select-Object -Unique ) foreach ($requiredName in $requiredNames) { $link = [regex]::Match( $body, '(?is)href=["''](?<url>https://download\.microsoft\.com/[^"'']*SurfaceHidMini[^"'']*\.zip(?:\?[^"'']*)?)["'']' ) if (-not $link.Success) { $link = [regex]::Match( $html, '(?is)href=["''](?<url>https://download\.microsoft\.com/[^"'']*SurfaceHidMini[^"'']*\.zip(?:\?[^"'']*)?)["'']' ) } $requiredPackages += [pscustomobject]@{ Name = $requiredName Folder = $requiredName DownloadUrl = if ($link.Success) { [System.Net.WebUtility]::HtmlDecode($link.Groups['url'].Value) } else { $null } ArchiveType = 'Zip' Required = $true } } $results += [pscustomobject]@{ Model = $heading ModelKey = Get-SurfaceModelKey -Name $heading Architecture = Get-SurfaceArchitectureFromName -Name $heading CpuVendor = Get-SurfaceCpuVendorFromName -Name $heading ImportFolders = $folders ImportFolderCount = $folders.Count ImportFolderSource = $importFolderSource RequiredPackages = $requiredPackages RequiredPackageCount = $requiredPackages.Count SourceUrl = $Uri } } return $results } function Get-SurfaceDriverDownloadCatalog { [CmdletBinding()] param([string]$Uri = $script:DriverCatalogUrl) $response = Invoke-SurfaceWebRequest -Uri $Uri $results = @() $links = @() if ($response.PSObject.Properties['Links']) { $links = @($response.Links) } foreach ($link in $links) { if (-not $link -or -not $link.PSObject.Properties['href']) { continue } $href = [string]$link.PSObject.Properties['href'].Value if ([string]::IsNullOrWhiteSpace($href)) { continue } $href = [System.Net.WebUtility]::HtmlDecode($href) if ($href -notmatch '(?i)download/details\.aspx\?[^#]*\bid=(?<id>\d+)') { continue } $downloadCenterId = [int]$Matches.id if (-not [uri]::IsWellFormedUriString($href, [UriKind]::Absolute)) { $href = ([uri]::new([uri]$Uri, $href)).AbsoluteUri } $name = '' if ($link.PSObject.Properties['innerText']) { $name = [string]$link.PSObject.Properties['innerText'].Value } elseif ($link.PSObject.Properties['innerHTML']) { $name = ConvertFrom-SurfaceHtmlText ([string]$link.PSObject.Properties['innerHTML'].Value) } elseif ($link.PSObject.Properties['outerHTML']) { $name = ConvertFrom-SurfaceHtmlText ([string]$link.PSObject.Properties['outerHTML'].Value) } $name = [System.Net.WebUtility]::HtmlDecode($name).Trim() if ($name -notmatch '^Surface\s') { continue } $results += [pscustomobject]@{ Model = $name ModelKey = Get-SurfaceModelKey -Name $name DownloadCenterId = $downloadCenterId DetailsUrl = $href Architecture = Get-SurfaceArchitectureFromName -Name $name CpuVendor = Get-SurfaceCpuVendorFromName -Name $name SourceUrl = $Uri } } $html = [string]$response.Content $pattern = '(?is)<a\b[^>]*href=["''](?<url>[^"'']*download/details\.aspx\?[^"'']*\bid=(?<id>\d+)[^"'']*)["''][^>]*>(?<name>.*?)</a>' foreach ($match in [regex]::Matches($html, $pattern)) { $name = ConvertFrom-SurfaceHtmlText $match.Groups['name'].Value if ($name -notmatch '^Surface\s') { continue } $href = [System.Net.WebUtility]::HtmlDecode($match.Groups['url'].Value) if (-not [uri]::IsWellFormedUriString($href, [UriKind]::Absolute)) { $href = ([uri]::new([uri]$Uri, $href)).AbsoluteUri } $results += [pscustomobject]@{ Model = $name ModelKey = Get-SurfaceModelKey -Name $name DownloadCenterId = [int]$match.Groups['id'].Value DetailsUrl = $href Architecture = Get-SurfaceArchitectureFromName -Name $name CpuVendor = Get-SurfaceCpuVendorFromName -Name $name SourceUrl = $Uri } } $unique = @{} foreach ($result in $results) { $key = [string]$result.DownloadCenterId if (-not $unique.ContainsKey($key)) { $unique[$key] = $result } } return @($unique.Values | Sort-Object Model) } |