# kb_bootpack_inline_json_patch_v1.1.ps1 # Purpose: Update the [BUG_KB_JSON_POINTER] section inside a BootPack file # Mode: PowerShell 5.1 strict-friendly (no ternary, no here-strings, no PS7-only tokens) # Encoding: UTF-8 with BOM; ASCII-safe comments/strings only param( [string]$Root, [string]$BootPackPath, [string]$KBPath, [switch]$Preview, [switch]$Write ) # ---- helpers ---- function Get-FileSha256([string]$Path){ if(-not (Test-Path -LiteralPath $Path)){ throw "Path not found: $Path" } $sha256 = [System.Security.Cryptography.SHA256]::Create() $fs = [System.IO.File]::OpenRead($Path) try{ $hash = $sha256.ComputeHash($fs) } finally { $fs.Dispose() $sha256.Dispose() } $sb = New-Object System.Text.StringBuilder foreach($b in $hash){ [void]$sb.Append($b.ToString("X2")) } return $sb.ToString() } function Get-JsonCore([string]$t){ if($null -eq $t){ return $null } $start = $t.IndexOf('{') $end = $t.LastIndexOf('}') if(($start -ge 0) -and ($end -ge $start)){ return $t.Substring($start, $end - $start + 1) } return $null } # ---- defaults resolution ---- if([string]::IsNullOrWhiteSpace($BootPackPath) -and -not [string]::IsNullOrWhiteSpace($Root)){ $BootPackPath = Join-Path $Root "bootpack\\bootpack.txt" } if([string]::IsNullOrWhiteSpace($KBPath) -and -not [string]::IsNullOrWhiteSpace($Root)){ $KBPath = Join-Path $Root "bug_kb\\BUG_KB.json.txt" } if([string]::IsNullOrWhiteSpace($BootPackPath)){ throw "BootPackPath is required." } if([string]::IsNullOrWhiteSpace($KBPath)){ throw "KBPath is required." } if(-not (Test-Path -LiteralPath $BootPackPath)){ throw "BootPack not found: $BootPackPath" } if(-not (Test-Path -LiteralPath $KBPath)){ throw "KB file not found: $KBPath" } # ---- read files (raw) ---- $bpText = Get-Content -LiteralPath $BootPackPath -Raw -Encoding UTF8 $kbText = Get-Content -LiteralPath $KBPath -Raw -Encoding UTF8 # ---- parse KB: robust ConvertFrom-Json ---- $kbJsonText = Get-JsonCore $kbText if([string]::IsNullOrWhiteSpace($kbJsonText)){ throw "KB JSON core not found in $KBPath" } try{ $kbObj = $kbJsonText | ConvertFrom-Json }catch{ throw "KB JSON parse failed for $KBPath : $($_.Exception.Message)" } $kbEntries = 0 if($null -ne $kbObj){ # entries should be an array $prop = $kbObj.PSObject.Properties["entries"] if($null -ne $prop){ $val = $prop.Value if($val -is [System.Array]){ $kbEntries = $val.Count } elseif($val -ne $null){ # tolerate non-array with count $tmp = @($val) $kbEntries = $tmp.Count } } } $kbSha = Get-FileSha256 -Path $KBPath # ---- locate pointer section in BootPack ---- $lines = $bpText -split "(\r\n|\n|\r)" # We will reconstruct with Windows newlines later (\r\n) # Find exact index of the section header line $sectIdx = -1 for($i=0; $i -lt $lines.Length; $i++){ $line = $lines[$i] if($null -ne $line){ $trim = $line.Trim() if($trim -eq "[BUG_KB_JSON_POINTER]"){ $sectIdx = $i; break } } } if($sectIdx -lt 0){ throw "Section [BUG_KB_JSON_POINTER] not found in BootPack." } # Find the end of section (before next [ ... ] or end of file) $endIdx = $lines.Length - 1 for($j = $sectIdx + 1; $j -lt $lines.Length; $j++){ $t = $lines[$j] if($null -ne $t){ $tt = $t.Trim() if($tt -like "[*]" -and -not $tt.StartsWith("[" + "BUG_KB_JSON_POINTER" + "]")){ $endIdx = $j - 1 break } } } # Extract old values $oldPath = $null $oldSha = $null $oldEnt = $null for($k=$sectIdx+1; $k -le $endIdx; $k++){ $l = $lines[$k] if($null -eq $l){ continue } $trim = $l.Trim() if($trim -like "Path=*"){ $idx = $l.IndexOf("=") if($idx -ge 0){ $oldPath = $l.Substring($idx+1).Trim() } } elseif($trim -like "SHA256=*"){ $idx = $l.IndexOf("=") if($idx -ge 0){ $oldSha = $l.Substring($idx+1).Trim() } } elseif($trim -like "Entries=*"){ $idx = $l.IndexOf("=") if($idx -ge 0){ $oldEnt = $l.Substring($idx+1).Trim() } } } # Prepare new values $NewPath = $KBPath $NewSha = $kbSha $NewEnt = [string]$kbEntries # Build a patched copy of lines $patched = New-Object System.Collections.Generic.List[string] for($m=0; $m -lt $lines.Length; $m++){ if($m -ge $sectIdx+1 -and $m -le $endIdx){ $line = $lines[$m] if($null -eq $line){ $patched.Add($line); continue } $trim = $line.Trim() if($trim -like "Path=*"){ $patched.Add("Path="+$NewPath) continue } if($trim -like "SHA256=*"){ $patched.Add("SHA256="+$NewSha) continue } if($trim -like "Entries=*"){ $patched.Add("Entries="+$NewEnt) continue } # unchanged line inside the section $patched.Add($line) } else { $patched.Add($lines[$m]) } } # join with CRLF $patchedText = [string]::Join("`r`n", $patched) # ---- PREVIEW REPORT ---- Write-Host "== PREVIEW :: INLINE JSON PATCH KB POINTER ==" Write-Host ("BootPack : {0}" -f $BootPackPath) Write-Host ("KB Path : {0}" -f $KBPath) Write-Host ("Old Path : {0}" -f ($oldPath)) Write-Host ("New Path : {0}" -f ($NewPath)) Write-Host ("Old SHA : {0}" -f ($oldSha)) Write-Host ("New SHA : {0}" -f ($NewSha)) Write-Host ("Old Ent : {0}" -f ($oldEnt)) Write-Host ("New Ent : {0}" -f ($NewEnt)) if(-not $Write -or $Preview){ Write-Host "Mode PREVIEW : no write performed." return } # ---- WRITE (SAFE-CREATE style) ---- $dst = $BootPackPath $tmp = $dst + ".__tmp__" $parent = Split-Path -Parent $dst if(-not (Test-Path -LiteralPath $parent)){ New-Item -ItemType Directory -Path $parent -Force | Out-Null } # Write temp with UTF-8 BOM $enc = New-Object System.Text.UTF8Encoding($true) [System.IO.File]::WriteAllText($tmp, $patchedText, $enc) # Backup original if(Test-Path -LiteralPath $dst){ $bak = $dst + "." + (Get-Date).ToString("yyyyMMdd_HHmmss") + ".bak" Copy-Item -LiteralPath $dst -Destination $bak -Force Write-Host ("Backup: {0}" -f $bak) } # Atomic move Move-Item -LiteralPath $tmp -Destination $dst -Force Write-Host ("[OK] BootPack patched -> {0}" -f $dst)