├── .editorconfig ├── Fido.ps1 ├── LICENSE.txt └── README.md /.editorconfig: -------------------------------------------------------------------------------- 1 | # indicate this is the root of the project 2 | root = true 3 | 4 | [*] 5 | # Must use a BOM else Unicode strings will not display 6 | charset = utf-8-bom 7 | insert_final_newline = true 8 | indent_style = tab 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /Fido.ps1: -------------------------------------------------------------------------------- 1 | # 2 | # Fido v1.64 - ISO Downloader, for Microsoft Windows and UEFI Shell 3 | # Copyright © 2019-2024 Pete Batard 4 | # Command line support: Copyright © 2021 flx5 5 | # ConvertTo-ImageSource: Copyright © 2016 Chris Carter 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | # 20 | 21 | # NB: You must have a BOM on your .ps1 if you want Powershell to actually 22 | # realise it should use Unicode for the UI rather than ISO-8859-1. 23 | 24 | #region Parameters 25 | param( 26 | # (Optional) The title to display on the application window. 27 | [string]$AppTitle = "Fido - ISO Downloader", 28 | # (Optional) '|' separated UI localization strings. 29 | [string]$LocData, 30 | # (Optional) Forced locale 31 | [string]$Locale = "en-US", 32 | # (Optional) Path to a file that should be used for the UI icon. 33 | [string]$Icon, 34 | # (Optional) Name of a pipe the download URL should be sent to. 35 | # If not provided, a browser window is opened instead. 36 | [string]$PipeName, 37 | # (Optional) Specify Windows version (e.g. "Windows 10") [Toggles commandline mode] 38 | [string]$Win, 39 | # (Optional) Specify Windows release (e.g. "21H1") [Toggles commandline mode] 40 | [string]$Rel, 41 | # (Optional) Specify Windows edition (e.g. "Pro") [Toggles commandline mode] 42 | [string]$Ed, 43 | # (Optional) Specify Windows language [Toggles commandline mode] 44 | [string]$Lang, 45 | # (Optional) Specify Windows architecture [Toggles commandline mode] 46 | [string]$Arch, 47 | # (Optional) Only display the download URL [Toggles commandline mode] 48 | [switch]$GetUrl = $false, 49 | # (Optional) Specify the architecture of the underlying CPU. 50 | # This avoids a VERY TIME CONSUMING call to WMI to autodetect the arch. 51 | [string]$PlatformArch, 52 | # (Optional) Increase verbosity 53 | [switch]$Verbose = $false, 54 | # (Optional) Produce debugging information 55 | [switch]$Debug = $false 56 | ) 57 | #endregion 58 | 59 | try { 60 | [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 61 | } catch {} 62 | 63 | $Cmd = $false 64 | if ($Win -or $Rel -or $Ed -or $Lang -or $Arch -or $GetUrl) { 65 | $Cmd = $true 66 | } 67 | 68 | # Return a decimal Windows version that we can then check for platform support. 69 | # Note that because we don't want to have to support this script on anything 70 | # other than Windows, this call returns 0.0 for PowerShell running on Linux/Mac. 71 | function Get-Platform-Version() 72 | { 73 | $version = 0.0 74 | $platform = [string][System.Environment]::OSVersion.Platform 75 | # This will filter out non Windows platforms 76 | if ($platform.StartsWith("Win")) { 77 | # Craft a decimal numeric version of Windows 78 | $version = [System.Environment]::OSVersion.Version.Major * 1.0 + [System.Environment]::OSVersion.Version.Minor * 0.1 79 | } 80 | return $version 81 | } 82 | 83 | $winver = Get-Platform-Version 84 | 85 | # The default TLS for Windows 8.x doesn't work with Microsoft's servers so we must force it 86 | if ($winver -lt 10.0) { 87 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls -bor [Net.SecurityProtocolType]::Tls11 -bor [Net.SecurityProtocolType]::Tls12 88 | } 89 | 90 | #region Assembly Types 91 | $Drawing_Assembly = "System.Drawing" 92 | # PowerShell 7 altered the name of the Drawing assembly... 93 | if ($host.version -ge "7.0") { 94 | $Drawing_Assembly += ".Common" 95 | } 96 | 97 | $Signature = @{ 98 | Namespace = "WinAPI" 99 | Name = "Utils" 100 | Language = "CSharp" 101 | UsingNamespace = "System.Runtime", "System.IO", "System.Text", "System.Drawing", "System.Globalization" 102 | ReferencedAssemblies = $Drawing_Assembly 103 | ErrorAction = "Stop" 104 | WarningAction = "Ignore" 105 | IgnoreWarnings = $true 106 | MemberDefinition = @" 107 | [DllImport("shell32.dll", CharSet = CharSet.Auto, SetLastError = true, BestFitMapping = false, ThrowOnUnmappableChar = true)] 108 | internal static extern int ExtractIconEx(string sFile, int iIndex, out IntPtr piLargeVersion, out IntPtr piSmallVersion, int amountIcons); 109 | 110 | [DllImport("user32.dll")] 111 | public static extern bool ShowWindow(IntPtr handle, int state); 112 | // Extract an icon from a DLL 113 | public static Icon ExtractIcon(string file, int number, bool largeIcon) { 114 | IntPtr large, small; 115 | ExtractIconEx(file, number, out large, out small, 1); 116 | try { 117 | return Icon.FromHandle(largeIcon ? large : small); 118 | } catch { 119 | return null; 120 | } 121 | } 122 | "@ 123 | } 124 | 125 | if (!$Cmd) { 126 | Write-Host Please Wait... 127 | 128 | if (!("WinAPI.Utils" -as [type])) 129 | { 130 | Add-Type @Signature 131 | } 132 | Add-Type -AssemblyName PresentationFramework 133 | 134 | # Hide the powershell window: https://stackoverflow.com/a/27992426/1069307 135 | [WinAPI.Utils]::ShowWindow(([System.Diagnostics.Process]::GetCurrentProcess() | Get-Process).MainWindowHandle, 0) | Out-Null 136 | } 137 | #endregion 138 | 139 | #region Data 140 | $WindowsVersions = @( 141 | @( 142 | @("Windows 11", "windows11"), 143 | @( 144 | "24H2 (Build 26100.1742 - 2024.10)", 145 | # Thanks to Microsoft's hare-brained decision not to treat ARM64 as a CPU arch, 146 | # like they did for x86 and x64, we have to handle multiple IDs for each release... 147 | @("Windows 11 Home/Pro/Edu", @(3113, 3131)), 148 | @("Windows 11 Home China ", @(3115, 3132)), 149 | @("Windows 11 Pro China ", @(3114, 3133)) 150 | ) 151 | ), 152 | @( 153 | @("Windows 10", "Windows10ISO"), 154 | @( 155 | "22H2 v1 (Build 19045.2965 - 2023.05)", 156 | @("Windows 10 Home/Pro/Edu", 2618), 157 | @("Windows 10 Home China ", 2378) 158 | ) 159 | ) 160 | @( 161 | @("UEFI Shell 2.2", "UEFI_SHELL 2.2"), 162 | @( 163 | "24H2 (edk2-stable202411)", 164 | @("Release", 0), 165 | @("Debug", 1) 166 | ), 167 | @( 168 | "24H1 (edk2-stable202405)", 169 | @("Release", 0), 170 | @("Debug", 1) 171 | ), 172 | @( 173 | "23H2 (edk2-stable202311)", 174 | @("Release", 0), 175 | @("Debug", 1) 176 | ), 177 | @( 178 | "23H1 (edk2-stable202305)", 179 | @("Release", 0), 180 | @("Debug", 1) 181 | ), 182 | @( 183 | "22H2 (edk2-stable202211)", 184 | @("Release", 0), 185 | @("Debug", 1) 186 | ), 187 | @( 188 | "22H1 (edk2-stable202205)", 189 | @("Release", 0), 190 | @("Debug", 1) 191 | ), 192 | @( 193 | "21H2 (edk2-stable202108)", 194 | @("Release", 0), 195 | @("Debug", 1) 196 | ), 197 | @( 198 | "21H1 (edk2-stable202105)", 199 | @("Release", 0), 200 | @("Debug", 1) 201 | ), 202 | @( 203 | "20H2 (edk2-stable202011)", 204 | @("Release", 0), 205 | @("Debug", 1) 206 | ) 207 | ), 208 | @( 209 | @("UEFI Shell 2.0", "UEFI_SHELL 2.0"), 210 | @( 211 | "4.632 [20100426]", 212 | @("Release", 0) 213 | ) 214 | ) 215 | ) 216 | #endregion 217 | 218 | #region Functions 219 | function Select-Language([string]$LangName) 220 | { 221 | # Use the system locale to try select the most appropriate language 222 | [string]$SysLocale = [System.Globalization.CultureInfo]::CurrentUICulture.Name 223 | if (($SysLocale.StartsWith("ar") -and $LangName -like "*Arabic*") -or ` 224 | ($SysLocale -eq "pt-BR" -and $LangName -like "*Brazil*") -or ` 225 | ($SysLocale.StartsWith("ar") -and $LangName -like "*Bulgar*") -or ` 226 | ($SysLocale -eq "zh-CN" -and $LangName -like "*Chinese*" -and $LangName -like "*simp*") -or ` 227 | ($SysLocale -eq "zh-TW" -and $LangName -like "*Chinese*" -and $LangName -like "*trad*") -or ` 228 | ($SysLocale.StartsWith("hr") -and $LangName -like "*Croat*") -or ` 229 | ($SysLocale.StartsWith("cz") -and $LangName -like "*Czech*") -or ` 230 | ($SysLocale.StartsWith("da") -and $LangName -like "*Danish*") -or ` 231 | ($SysLocale.StartsWith("nl") -and $LangName -like "*Dutch*") -or ` 232 | ($SysLocale -eq "en-US" -and $LangName -eq "English") -or ` 233 | ($SysLocale.StartsWith("en") -and $LangName -like "*English*" -and ($LangName -like "*inter*" -or $LangName -like "*ingdom*")) -or ` 234 | ($SysLocale.StartsWith("et") -and $LangName -like "*Eston*") -or ` 235 | ($SysLocale.StartsWith("fi") -and $LangName -like "*Finn*") -or ` 236 | ($SysLocale -eq "fr-CA" -and $LangName -like "*French*" -and $LangName -like "*Canad*") -or ` 237 | ($SysLocale.StartsWith("fr") -and $LangName -eq "French") -or ` 238 | ($SysLocale.StartsWith("de") -and $LangName -like "*German*") -or ` 239 | ($SysLocale.StartsWith("el") -and $LangName -like "*Greek*") -or ` 240 | ($SysLocale.StartsWith("he") -and $LangName -like "*Hebrew*") -or ` 241 | ($SysLocale.StartsWith("hu") -and $LangName -like "*Hungar*") -or ` 242 | ($SysLocale.StartsWith("id") -and $LangName -like "*Indones*") -or ` 243 | ($SysLocale.StartsWith("it") -and $LangName -like "*Italia*") -or ` 244 | ($SysLocale.StartsWith("ja") -and $LangName -like "*Japan*") -or ` 245 | ($SysLocale.StartsWith("ko") -and $LangName -like "*Korea*") -or ` 246 | ($SysLocale.StartsWith("lv") -and $LangName -like "*Latvia*") -or ` 247 | ($SysLocale.StartsWith("lt") -and $LangName -like "*Lithuania*") -or ` 248 | ($SysLocale.StartsWith("ms") -and $LangName -like "*Malay*") -or ` 249 | ($SysLocale.StartsWith("nb") -and $LangName -like "*Norw*") -or ` 250 | ($SysLocale.StartsWith("fa") -and $LangName -like "*Persia*") -or ` 251 | ($SysLocale.StartsWith("pl") -and $LangName -like "*Polish*") -or ` 252 | ($SysLocale -eq "pt-PT" -and $LangName -eq "Portuguese") -or ` 253 | ($SysLocale.StartsWith("ro") -and $LangName -like "*Romania*") -or ` 254 | ($SysLocale.StartsWith("ru") -and $LangName -like "*Russia*") -or ` 255 | ($SysLocale.StartsWith("sr") -and $LangName -like "*Serbia*") -or ` 256 | ($SysLocale.StartsWith("sk") -and $LangName -like "*Slovak*") -or ` 257 | ($SysLocale.StartsWith("sl") -and $LangName -like "*Slovenia*") -or ` 258 | ($SysLocale -eq "es-ES" -and $LangName -eq "Spanish") -or ` 259 | ($SysLocale.StartsWith("es") -and $Locale -ne "es-ES" -and $LangName -like "*Spanish*") -or ` 260 | ($SysLocale.StartsWith("sv") -and $LangName -like "*Swed*") -or ` 261 | ($SysLocale.StartsWith("th") -and $LangName -like "*Thai*") -or ` 262 | ($SysLocale.StartsWith("tr") -and $LangName -like "*Turk*") -or ` 263 | ($SysLocale.StartsWith("uk") -and $LangName -like "*Ukrain*") -or ` 264 | ($SysLocale.StartsWith("vi") -and $LangName -like "*Vietnam*")) { 265 | return $true 266 | } 267 | return $false 268 | } 269 | 270 | function Add-Entry([int]$pos, [string]$Name, [array]$Items, [string]$DisplayName) 271 | { 272 | $Title = New-Object System.Windows.Controls.TextBlock 273 | $Title.FontSize = $WindowsVersionTitle.FontSize 274 | $Title.Height = $WindowsVersionTitle.Height; 275 | $Title.Width = $WindowsVersionTitle.Width; 276 | $Title.HorizontalAlignment = "Left" 277 | $Title.VerticalAlignment = "Top" 278 | $Margin = $WindowsVersionTitle.Margin 279 | $Margin.Top += $pos * $dh 280 | $Title.Margin = $Margin 281 | $Title.Text = Get-Translation($Name) 282 | $XMLGrid.Children.Insert(2 * $Stage + 2, $Title) 283 | 284 | $Combo = New-Object System.Windows.Controls.ComboBox 285 | $Combo.FontSize = $WindowsVersion.FontSize 286 | $Combo.Height = $WindowsVersion.Height; 287 | $Combo.Width = $WindowsVersion.Width; 288 | $Combo.HorizontalAlignment = "Left" 289 | $Combo.VerticalAlignment = "Top" 290 | $Margin = $WindowsVersion.Margin 291 | $Margin.Top += $pos * $script:dh 292 | $Combo.Margin = $Margin 293 | $Combo.SelectedIndex = 0 294 | if ($Items) { 295 | $Combo.ItemsSource = $Items 296 | if ($DisplayName) { 297 | $Combo.DisplayMemberPath = $DisplayName 298 | } else { 299 | $Combo.DisplayMemberPath = $Name 300 | } 301 | } 302 | $XMLGrid.Children.Insert(2 * $Stage + 3, $Combo) 303 | 304 | $XMLForm.Height += $dh; 305 | $Margin = $Continue.Margin 306 | $Margin.Top += $dh 307 | $Continue.Margin = $Margin 308 | $Margin = $Back.Margin 309 | $Margin.Top += $dh 310 | $Back.Margin = $Margin 311 | 312 | return $Combo 313 | } 314 | 315 | function Refresh-Control([object]$Control) 316 | { 317 | $Control.Dispatcher.Invoke("Render", [Windows.Input.InputEventHandler] { $Continue.UpdateLayout() }, $null, $null) | Out-Null 318 | } 319 | 320 | function Send-Message([string]$PipeName, [string]$Message) 321 | { 322 | [System.Text.Encoding]$Encoding = [System.Text.Encoding]::UTF8 323 | $Pipe = New-Object -TypeName System.IO.Pipes.NamedPipeClientStream -ArgumentList ".", $PipeName, ([System.IO.Pipes.PipeDirection]::Out), ([System.IO.Pipes.PipeOptions]::None), ([System.Security.Principal.TokenImpersonationLevel]::Impersonation) 324 | try { 325 | $Pipe.Connect(1000) 326 | } catch { 327 | Write-Host $_.Exception.Message 328 | } 329 | $bRequest = $Encoding.GetBytes($Message) 330 | $cbRequest = $bRequest.Length; 331 | $Pipe.Write($bRequest, 0, $cbRequest); 332 | $Pipe.Dispose() 333 | } 334 | 335 | # From https://www.powershellgallery.com/packages/IconForGUI/1.5.2 336 | # Copyright © 2016 Chris Carter. All rights reserved. 337 | # License: https://creativecommons.org/licenses/by-sa/4.0/ 338 | function ConvertTo-ImageSource 339 | { 340 | [CmdletBinding()] 341 | Param( 342 | [Parameter(Mandatory=$true,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)] 343 | [System.Drawing.Icon]$Icon 344 | ) 345 | 346 | Process { 347 | foreach ($i in $Icon) { 348 | [System.Windows.Interop.Imaging]::CreateBitmapSourceFromHIcon( 349 | $i.Handle, 350 | (New-Object System.Windows.Int32Rect -Args 0,0,$i.Width, $i.Height), 351 | [System.Windows.Media.Imaging.BitmapSizeOptions]::FromEmptyOptions() 352 | ) 353 | } 354 | } 355 | } 356 | 357 | # Translate a message string 358 | function Get-Translation([string]$Text) 359 | { 360 | if (!($English -contains $Text)) { 361 | Write-Host "Error: '$Text' is not a translatable string" 362 | return "(Untranslated)" 363 | } 364 | if ($Localized) { 365 | if ($Localized.Length -ne $English.Length) { 366 | Write-Host "Error: '$Text' is not a translatable string" 367 | } 368 | for ($i = 0; $i -lt $English.Length; $i++) { 369 | if ($English[$i] -eq $Text) { 370 | if ($Localized[$i]) { 371 | return $Localized[$i] 372 | } else { 373 | return $Text 374 | } 375 | } 376 | } 377 | } 378 | return $Text 379 | } 380 | 381 | # Get the underlying *native* CPU architecture 382 | function Get-Arch 383 | { 384 | $Arch = Get-CimInstance -ClassName Win32_Processor | Select-Object -ExpandProperty Architecture 385 | switch($Arch) { 386 | 0 { return "x86" } 387 | 1 { return "MIPS" } 388 | 2 { return "Alpha" } 389 | 3 { return "PowerPC" } 390 | 5 { return "ARM32" } 391 | 6 { return "IA64" } 392 | 9 { return "x64" } 393 | 12 { return "ARM64" } 394 | default { return "Unknown"} 395 | } 396 | } 397 | 398 | # Convert a Microsoft arch type code to a formal architecture name 399 | function Get-Arch-From-Type([int]$Type) 400 | { 401 | switch($Type) { 402 | 0 { return "x86" } 403 | 1 { return "x64" } 404 | 2 { return "ARM64" } 405 | default { return "Unknown"} 406 | } 407 | } 408 | 409 | function Error([string]$ErrorMessage) 410 | { 411 | Write-Host Error: $ErrorMessage 412 | if (!$Cmd) { 413 | $XMLForm.Title = $(Get-Translation("Error")) + ": " + $ErrorMessage 414 | Refresh-Control($XMLForm) 415 | $XMLGrid.Children[2 * $script:Stage + 1].IsEnabled = $true 416 | $UserInput = [System.Windows.MessageBox]::Show($XMLForm.Title, $(Get-Translation("Error")), "OK", "Error") 417 | $script:ExitCode = $script:Stage-- 418 | } else { 419 | $script:ExitCode = 2 420 | } 421 | } 422 | #endregion 423 | 424 | #region Form 425 | [xml]$XAML = @" 426 | 427 | 428 |