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
This commit is contained in:
2026-07-08 02:39:27 +02:00
parent 3cb4132727
commit beec38a899
4 changed files with 221 additions and 0 deletions
@@ -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."
@@ -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.
@@ -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)."
}
}
@@ -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 <id> » : 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"