??Param( [Parameter(Mandatory=$true)][string]$RegistryRoot ) try { [Console]::OutputEncoding = New-Object System.Text.UTF8Encoding($false) } catch {} function Read-Text([string]$p){ if(-not (Test-Path -LiteralPath $p)){ return '' } try { return Get-Content -LiteralPath $p -Raw -Encoding UTF8 } catch { return (Get-Content -LiteralPath $p -Raw) } } function NormalizeSpaces([string]$s){ if($null -eq $s){ return '' } $s = $s -replace ([char]0x00A0), ' ' # NBSP $s = $s -replace ([char]0x2007), ' ' # Figure space $s = $s -replace ([char]0x202F), ' ' # Narrow NBSP return $s } # --- parseur 1 : section multi-lignes [BUG_KB_JSON_POINTER] --- function Parse-PointerFromSection([string]$txt){ $t = NormalizeSpaces $txt $lines = $t -split "`r?`n" $i = 0; $n = $lines.Length while($i -lt $n){ if($lines[$i] -match '^\s*\[BUG_KB_JSON_POINTER\]\s*$'){ $i++ $path=''; $sha=''; $ent=-1; $size=-1 while($i -lt $n -and ($lines[$i] -notmatch '^\s*\[')){ $L = $lines[$i] if($L -match '^\s*Path\s*:\s*(.+)\s*$'){ $path = $Matches[1].Trim() } if($L -match '^\s*SHA256\s*:\s*([0-9a-fA-F]{64})\s*$'){ $sha = $Matches[1].ToUpperInvariant() } if($L -match '^\s*Entries\s*:\s*(\d+)\s*$'){ $ent = [int]$Matches[1] } if($L -match '^\s*Size(Bytes)?\s*:\s*(\d+)\s*$'){ $size = [long]$Matches[2] ; if(-not $size){ $size=[long]$Matches[1] } } if($L -match '^\s*$'){ } # on tol?re les lignes vides $i++ } if(-not [string]::IsNullOrWhiteSpace($path)){ return [pscustomobject]@{Path=$path; SHA=$sha; Entries=$ent; Size=$size} } break } $i++ } return $null } # --- parseur 2 : ligne inline "KB Pointer : (SHA=..., Entries=..., Size=...)" --- function Parse-PointerFromInline([string]$txt){ $t = NormalizeSpaces $txt foreach($line in ($t -split "`r?`n")){ if($line -match '^\s*(KB\W*Pointer|Pointer)\W*:\W*(?[^()]+?)\s*\((?.*)\)\s*$'){ $path = $Matches['Path'].Value.Trim() $attrs= $Matches['Attrs'].Value $sha = ''; $ent = -1; $size = -1 $m=[regex]::Match($attrs,'(?i)\bSHA\s*=\s*([0-9a-f]{64})'); if($m.Success){ $sha = $m.Groups[1].Value.ToUpperInvariant() } $m=[regex]::Match($attrs,'(?i)\bEntries\s*=\s*(\d+)'); if($m.Success){ $ent = [int]$m.Groups[1].Value } $m=[regex]::Match($attrs,'(?i)\bSize(Bytes)?\s*=\s*(\d+)'); if($m.Success){ $size = [long]$m.Groups[$m.Groups.Count-1].Value } return [pscustomobject]@{Path=$path; SHA=$sha; Entries=$ent; Size=$size} } } return $null } function Count-EntriesFromJson([string]$jsonText){ if([string]::IsNullOrWhiteSpace($jsonText)){ return -1 } if($jsonText.Length -gt 0 -and ([int]$jsonText[0] -eq 0xFEFF)){ $jsonText = $jsonText.Substring(1) } try{ $obj = $jsonText | ConvertFrom-Json if($null -ne $obj -and $obj.PSObject.Properties.Name -contains 'entries'){ return @($obj.entries).Count } }catch{} try{ $m = [regex]::Match($jsonText,'(?ms)"entries"\s*:\s*\[(.*?)\]') if($m.Success){ return ([regex]::Matches($m.Groups[1].Value,'(?i)"id"\s*:')).Count } }catch{} return -1 } $bp = Join-Path $RegistryRoot 'bootpack\bootpack.txt' if(-not (Test-Path -LiteralPath $bp)){ Write-Host "[ERROR] bootpack.txt not found: $bp"; exit 2 } $raw = Read-Text $bp $ptr = Parse-PointerFromSection $raw if($null -eq $ptr){ $ptr = Parse-PointerFromInline $raw } if($null -eq $ptr){ Write-Host "[ERROR] KB Pointer line not found in bootpack."; exit 2 } if([string]::IsNullOrWhiteSpace($ptr.Path) -or -not (Test-Path -LiteralPath $ptr.Path)){ Write-Host "[ERROR] Pointer Path invalid or missing: $($ptr.Path)"; exit 2 } try{ $psSha = (Get-FileHash -Algorithm SHA256 -LiteralPath $ptr.Path).Hash.ToUpperInvariant() }catch{ Write-Host "[ERROR] Cannot compute SHA256 on: $($ptr.Path)"; exit 2 } try{ $psSize = (Get-Item -LiteralPath $ptr.Path).Length }catch{ $psSize = -1 } $psEnt = Count-EntriesFromJson (Read-Text $ptr.Path) $okSha = ($ptr.SHA -eq $psSha) $okEnt = ($ptr.Entries -eq $psEnt) $okSize = ($ptr.Size -eq [long]$psSize) Write-Host "[KB POINTER] $($ptr.Path)" Write-Host ("Expect : SHA={0} Entries={1} Size={2}" -f $ptr.SHA, $ptr.Entries, $ptr.Size) Write-Host ("Actual : SHA={0} Entries={1} Size={2}" -f $psSha, $psEnt, $psSize) if($okSha -and $okEnt -and $okSize){ Write-Host "STATUS=OK"; exit 0 } if(-not $okSha ){ Write-Host "MISMATCH: SHA" } if(-not $okEnt ){ Write-Host "MISMATCH: Entries" } if(-not $okSize){ Write-Host "MISMATCH: SizeBytes" } exit 2