# repair-ico.ps1 - Fix broken ICO files whose frame offsets omit the directory size. # # Background: some generators write ICO frame offsets starting at 0 (not accounting # for the 6 + count*16 directory), which makes .NET Framework System.Drawing.Icon # throw "The operation completed successfully". This script detects that pattern # and adds the directory length to every frame offset. # # Usage: powershell -NoProfile -ExecutionPolicy Bypass -File repair-ico.ps1 [more.ico ...] # Each file is backed up as .bak- before being rewritten. param([Parameter(Mandatory=$true, Position=0)][string]$IcoPath) Add-Type -AssemblyName System.Drawing foreach ($path in @($IcoPath)) { $full = (Resolve-Path $path -ErrorAction SilentlyContinue).Path if (-not $full -or -not (Test-Path $full)) { Write-Host "SKIP (not found): $path"; continue } $bytes = [System.IO.File]::ReadAllBytes($full) if ($bytes.Length -lt 22) { Write-Host "SKIP (too small): $path"; continue } $count = [BitConverter]::ToUInt16($bytes, 4) if ($count -lt 1 -or $count -gt 64) { Write-Host "SKIP (odd frame count $count): $path"; continue } $dirLen = 6 + $count * 16 # Detect the broken pattern: first frame offset is 0 (points at the directory itself) $off0 = [BitConverter]::ToUInt32($bytes, 6 + 12) if ($off0 -ne 0) { Write-Host "OK (first offset=$off0, no fix needed): $path"; continue } $stamp = Get-Date -Format 'yyyyMMdd-HHmmss' $bak = "$full.bak-$stamp" Copy-Item $full $bak -Force $b = [byte[]]$bytes.Clone() for ($i = 0; $i -lt $count; $i++) { $o = 6 + $i * 16 + 12 $v = [BitConverter]::ToUInt32($b, $o) + $dirLen [BitConverter]::GetBytes($v).CopyTo($b, $o) } [System.IO.File]::WriteAllBytes($full, $b) try { $ic = [System.Drawing.Icon]::new($full) $ok = "OK $($ic.Width)x$($ic.Height)" $ic.Dispose() } catch { $ok = "STILL FAILS: $($_.Exception.InnerException.Message)" } Write-Host "FIXED: $full (backup $bak) verify: $ok" }