From beec38a8993057d7c9288b58759167c5b1172b7c Mon Sep 17 00:00:00 2001 From: nocode Date: Wed, 8 Jul 2026 02:39:27 +0200 Subject: [PATCH] feat(gameboot-windows): add Windows dual-boot orchestration scripts for FWS gaming Add PowerShell scripts to manage dual-boot transitions between FWS and Windows 11 for gaming sessions with Valorant. Includes: - fws-return.ps1: Arms firmware boot-next (one-shot) to target OS via bcdedit - fws-play.ps1: Orchestrates Valorant session with robust crash recovery - Install-FwsGameboot.ps1: Deploys scripts, disables hibernation, creates ONSTART task - README.md: Documentation on dual-boot workflow and prerequisites Design principles: - Minimal footprint, no Vanguard interference (no injection/kernel hooks) - Permanent fallback: BootOrder[0]=FWS catches unmanaged reboots - Robust locale-independent bcdedit parsing via EFI paths and GUIDs - Crash-proof game session: ONSTART task disabled during play to prevent mid-match boot changes --- .../gameboot-windows/Install-FwsGameboot.ps1 | 44 +++++++++++++ .../share/fws/gameboot-windows/README.md | 50 ++++++++++++++ .../share/fws/gameboot-windows/fws-play.ps1 | 61 +++++++++++++++++ .../share/fws/gameboot-windows/fws-return.ps1 | 66 +++++++++++++++++++ 4 files changed, 221 insertions(+) create mode 100644 configs/releng/airootfs/usr/local/share/fws/gameboot-windows/Install-FwsGameboot.ps1 create mode 100644 configs/releng/airootfs/usr/local/share/fws/gameboot-windows/README.md create mode 100644 configs/releng/airootfs/usr/local/share/fws/gameboot-windows/fws-play.ps1 create mode 100644 configs/releng/airootfs/usr/local/share/fws/gameboot-windows/fws-return.ps1 diff --git a/configs/releng/airootfs/usr/local/share/fws/gameboot-windows/Install-FwsGameboot.ps1 b/configs/releng/airootfs/usr/local/share/fws/gameboot-windows/Install-FwsGameboot.ps1 new file mode 100644 index 0000000..83b9392 --- /dev/null +++ b/configs/releng/airootfs/usr/local/share/fws/gameboot-windows/Install-FwsGameboot.ps1 @@ -0,0 +1,44 @@ +<# + Install-FwsGameboot.ps1 — installe le composant de RETOUR FWS dans Windows. + + A lancer UNE FOIS, EN ADMIN, dans le Windows gaming. Deploie les scripts, + desactive l'hibernation/Fast Startup de Windows (pour un reboot PLEIN → NTFS + jamais "sale"), et cree une tache ONSTART (SYSTEM) qui arme le boot suivant + vers FWS des le demarrage — filet anti-crash : si Windows redemarre tout seul + (Windows Update, plantage) sans passer par fws-play, le boot suivant repart + vers FWS. + + Empreinte minimale et SANS interference avec Vanguard : aucune injection, + aucun pilote/hook noyau — juste des scripts, une tache planifiee et un reglage + d'alimentation. +#> +#Requires -RunAsAdministrator +$ErrorActionPreference = 'Stop' + +$dest = Join-Path $Env:ProgramData 'FWS' +New-Item -ItemType Directory -Force -Path $dest | Out-Null +Copy-Item (Join-Path $PSScriptRoot 'fws-return.ps1') $dest -Force +Copy-Item (Join-Path $PSScriptRoot 'fws-play.ps1') $dest -Force + +# (1) Pas d'hibernation Windows / Fast Startup : reboot PLEIN → volumes propres. +& powercfg /h off 2>$null + +# (2) Tache ONSTART (SYSTEM) : armer le boot suivant vers FWS au demarrage. +$action = New-ScheduledTaskAction -Execute 'powershell.exe' ` + -Argument ('-NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File "{0}\fws-return.ps1" -Target FWS' -f $dest) +$trigger = New-ScheduledTaskTrigger -AtStartup +$principal = New-ScheduledTaskPrincipal -UserId 'SYSTEM' -LogonType ServiceAccount -RunLevel Highest +$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries +Register-ScheduledTask -TaskName 'FWS-Return-OnStart' -Action $action -Trigger $trigger ` + -Principal $principal -Settings $settings -Force | Out-Null + +Write-Host "" +Write-Host "Composant FWS installe dans $dest et tache 'FWS-Return-OnStart' creee." -ForegroundColor Green +Write-Host "" +Write-Host "RAPPELS IMPORTANTS :" -ForegroundColor Yellow +Write-Host " - NE PAS activer BitLocker (Device Encryption) sans sauvegarder la cle :" +Write-Host " le passage de Secure Boot OFF->ON cote FWS change PCR7 et declencherait" +Write-Host " l'ecran de recuperation BitLocker. (Verifie : manage-bde -status)" +Write-Host " - Pour un lancement AUTO de Valorant : ajouter fws-play.ps1 au demarrage" +Write-Host " de session du compte gaming (autologin), sinon lance-le a la main." +Write-Host " - Vanguard exige Secure Boot + TPM 2.0 + VBS/HVCI : ne les desactive pas." diff --git a/configs/releng/airootfs/usr/local/share/fws/gameboot-windows/README.md b/configs/releng/airootfs/usr/local/share/fws/gameboot-windows/README.md new file mode 100644 index 0000000..22005cc --- /dev/null +++ b/configs/releng/airootfs/usr/local/share/fws/gameboot-windows/README.md @@ -0,0 +1,50 @@ +# FWS Gameboot — composant Windows + +Composant à déployer **dans le Windows 11 gaming** (partition bare-metal dédiée) +pour le dual-boot orchestré « hibernate-swap » de FWS. Il gère le **retour vers +FWS** après une session de jeu. + +> ⚠️ **Statut : v1 non validée sur matériel réel.** La sémantique +> `bcdedit /set {fwbootmgr} bootsequence` et le retour au boot dépendent du +> firmware de la carte mère. À tester avant de s'y fier. Voir le plan complet : +> `docs/hibernate-swap-dualboot.md`. + +## Fichiers + +| Fichier | Rôle | +|---|---| +| `fws-return.ps1` | Arme le boot suivant (one-shot) vers `FWS` ou `Windows`. | +| `fws-play.ps1` | Lance Valorant, attend sa fin/crash, puis rebascule vers FWS. | +| `Install-FwsGameboot.ps1` | Installe le tout + tâche ONSTART + `powercfg /h off`. | + +## Installation (dans Windows, PowerShell **admin**) + +```powershell +Set-ExecutionPolicy -Scope Process Bypass -Force +.\Install-FwsGameboot.ps1 +``` + +Ces fichiers sont livrés par FWS dans +`/usr/local/share/fws/gameboot-windows/` ; l'outillage d'installation Windows de +FWS (à venir, cf. plan §6) les copiera sur la partition Windows. En attendant, +copie ce dossier à la main sur le Windows cible. + +## Comment ça marche + +1. Depuis FWS : `fws-gameboot to-windows` hiberne FWS et arme le boot suivant + vers Windows. +2. Windows démarre en **bare-metal** → Vanguard voit un vrai Windows natif. +3. Au démarrage, la tâche **ONSTART** arme le boot suivant vers FWS (filet + anti-crash). +4. `fws-play.ps1` (auto au login gaming, ou manuel) ré-arme Windows le temps du + jeu, puis, à la fermeture/crash de Valorant, arme FWS et redémarre. +5. FWS reprend sa session hibernée via `resume=`. + +Filet permanent : côté FWS, `BootOrder[0] = FWS` est ré-affirmé à chaque +démarrage — tout reboot non géré retombe sur FWS. + +## Rappels + +- **BitLocker** : ne pas l'activer sans sauvegarder la clé (le flip Secure Boot + côté FWS change PCR7 → écran de récupération). `manage-bde -status` pour vérifier. +- Ne pas désactiver **Secure Boot / TPM / VBS-HVCI** : Vanguard les exige. diff --git a/configs/releng/airootfs/usr/local/share/fws/gameboot-windows/fws-play.ps1 b/configs/releng/airootfs/usr/local/share/fws/gameboot-windows/fws-play.ps1 new file mode 100644 index 0000000..b56e12c --- /dev/null +++ b/configs/releng/airootfs/usr/local/share/fws/gameboot-windows/fws-play.ps1 @@ -0,0 +1,61 @@ +<# + fws-play.ps1 — session de jeu Windows orchestree pour le retour vers FWS. + + Flux robuste : + - on desactive la tache ONSTART (evite qu'un declenchement tardif ne rearme + FWS EN PLEIN MATCH) ; + - on arme le boot suivant vers WINDOWS (un crash/reboot pendant le jeu revient + sur Windows, pas ejecte vers FWS au milieu d'un match) ; + - on lance Valorant et on attend sa fermeture OU son crash ; + - dans un FINALLY (donc quoi qu'il arrive) : on rearme le boot suivant vers + FWS, on reactive la tache ONSTART, et on reboote — SEULEMENT si le jeu a + bien ete vu (pas de reboot force en plein 1er telechargement de patch). + + Le filet permanent BootOrder[0]=FWS couvre tout ce que ce script raterait. + A lancer a l'ouverture de session du compte gaming (autologin) ou a la main. + Doit tourner EN ADMIN. #> +$ErrorActionPreference = 'Stop' +$here = Split-Path -Parent $MyInvocation.MyCommand.Path +$return = Join-Path $here 'fws-return.ps1' +$taskName = 'FWS-Return-OnStart' +$seen = $false + +try { + # Neutraliser la course avec la tache ONSTART pendant la session. + try { Disable-ScheduledTask -TaskName $taskName -ErrorAction Stop | Out-Null } catch { } + + # Pendant la session, un crash/reboot doit rester sur WINDOWS. + try { & $return -Target Windows } catch { Write-Warning "Arm Windows a echoue : $_" } + + # Lancer Valorant via le Riot Client (adapter le chemin si necessaire). + $riot = Join-Path $Env:ProgramFiles 'Riot Games\Riot Client\RiotClientServices.exe' + if (Test-Path $riot) { + Start-Process $riot -ArgumentList '--launch-product=valorant','--launch-patchline=live' + } else { + Write-Warning "Riot Client introuvable : $riot — lance Valorant manuellement." + } + + # Attendre l'apparition du process (SANS plafond serre : un 1er patch Riot / + # une MAJ Vanguard peuvent etre longs), puis attendre sa fermeture/crash. + $proc = 'VALORANT-Win64-Shipping' + for ($i = 0; $i -lt 1440; $i++) { # ~2 h max d'attente d'apparition + if (Get-Process $proc -ErrorAction SilentlyContinue) { $seen = $true; break } + Start-Sleep -Seconds 5 + } + if ($seen) { + Get-Process $proc -ErrorAction SilentlyContinue | ForEach-Object { $_.WaitForExit() } + } else { + Write-Warning "Jeu jamais detecte — pas de reboot automatique." + } +} +finally { + # Rearmer FWS et reactiver la tache, QUOI QU'IL ARRIVE. + try { & $return -Target FWS } catch { Write-Warning "Arm FWS a echoue : $_ — BootOrder[0]=FWS reste le filet." } + try { Enable-ScheduledTask -TaskName $taskName -ErrorAction Stop | Out-Null } catch { } + if ($seen) { + Start-Sleep -Seconds 2 + Restart-Computer -Force + } else { + Write-Warning "Session sans jeu detecte : redemarre manuellement pour revenir a FWS (BootNext deja arme vers FWS)." + } +} diff --git a/configs/releng/airootfs/usr/local/share/fws/gameboot-windows/fws-return.ps1 b/configs/releng/airootfs/usr/local/share/fws/gameboot-windows/fws-return.ps1 new file mode 100644 index 0000000..5f1e923 --- /dev/null +++ b/configs/releng/airootfs/usr/local/share/fws/gameboot-windows/fws-return.ps1 @@ -0,0 +1,66 @@ +<# + fws-return.ps1 — arme le "boot suivant" du firmware (one-shot) vers une cible. + + Cote FWS, l'equivalent est « efibootmgr --bootnext ». Cote Windows, on utilise + « bcdedit /set {fwbootmgr} bootsequence » : la sequence de boot UNE FOIS + du firmware (consommee au prochain demarrage puis oubliee), pendant exact de + BootNext. + + IMPORTANT (robustesse locale) : on NE parse PAS les libelles traduits de + « bcdedit /enum firmware » (sur un Windows francais « identifier » devient + « identificateur », « Windows Boot Manager » -> « Gestionnaire d'amorçage + Windows »). On resout : + - Windows : par l'alias NON localise {bootmgr} (aucun parsing) ; + - FWS : par le CHEMIN EFI (\EFI\FWS\ ou \EFI\GRUB\ ou grubx64.efi), qui + n'est pas traduit, en associant le GUID (motif stable) du bloc. + Si FWS est introuvable, on DESARME la one-shot (deletevalue) pour retomber + proprement sur BootOrder[0]=FWS plutot que de laisser une bascule erronee. + + Doit tourner EN ADMIN. Usage : fws-return.ps1 -Target FWS | -Target Windows +#> +param( + [ValidateSet('FWS','Windows')] + [string]$Target = 'FWS' +) +$ErrorActionPreference = 'Stop' + +function Set-BootSequence([string]$id) { + & bcdedit /set '{fwbootmgr}' bootsequence $id | Out-Null + if ($LASTEXITCODE -ne 0) { throw "bcdedit bootsequence $id a echoue (code $LASTEXITCODE)." } +} +function Clear-BootSequence { + & bcdedit /deletevalue '{fwbootmgr}' bootsequence 2>$null | Out-Null +} + +if ($Target -eq 'Windows') { + # Alias non localise -> aucun parsing, aucune dependance a la langue. + Set-BootSequence '{bootmgr}' + Write-Host "Boot suivant arme vers Windows ({bootmgr})." + return +} + +# --- Target = FWS : resoudre le GUID de l'entree firmware pointant vers FWS --- +# /v force l'affichage des GUID complets. On matche le CHEMIN (non localise), +# pas la description (localisee). +$fw = & bcdedit /enum firmware /v 2>$null +if (-not $fw) { throw "bcdedit /enum firmware n'a rien renvoye (droits admin ?)." } + +$reId = '\{[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\}|\{[a-z]+\}' +$rePath = '(?i)\\EFI\\(FWS|GRUB)\\|grubx64\.efi' + +$curId = $null; $fwsId = $null +foreach ($line in $fw) { + $m = [regex]::Match($line, $reId) + if ($m.Success) { $curId = $m.Value; continue } + if ($line -match $rePath -and $curId) { $fwsId = $curId; break } +} + +if (-not $fwsId) { + # Rien trouve par le chemin : on ne devine PAS. On desarme toute one-shot + # pour retomber sur BootOrder[0]=FWS (filet permanent cote FWS). + Clear-BootSequence + throw "Entree firmware FWS introuvable (chemin \EFI\FWS ou \EFI\GRUB). One-shot desarmee -> BootOrder assure le retour vers FWS." +} + +Set-BootSequence $fwsId +Write-Host "Boot suivant arme vers FWS = $fwsId"