From f31eed5616f988376a922feac2783a062ba31ec6 Mon Sep 17 00:00:00 2001
From: Manuel Thalmann <m@nuth.ch>
Date: Wed, 28 Aug 2024 00:03:28 +0200
Subject: [PATCH] Make the install script action agnostic

---
 scripts/Common/Scripts/Config.ps1             |  46 ++++++-
 scripts/Windows/OS/Manage.ps1                 | 114 +++++++++++-------
 .../Windows/Types/WindowsInstallerAction.ps1  |   4 +
 3 files changed, 117 insertions(+), 47 deletions(-)
 create mode 100644 scripts/Windows/Types/WindowsInstallerAction.ps1

diff --git a/scripts/Common/Scripts/Config.ps1 b/scripts/Common/Scripts/Config.ps1
index 9fd6db22..7f7953c6 100644
--- a/scripts/Common/Scripts/Config.ps1
+++ b/scripts/Common/Scripts/Config.ps1
@@ -2,6 +2,12 @@ using namespace Microsoft.Win32;
 using namespace System.Security.AccessControl;
 using namespace System.Security.Principal;
 
+enum WindowsInstallerStage {
+    Initialize
+    Run
+    Completed
+}
+
 enum SetupStage {
     Configure
     Install
@@ -18,6 +24,7 @@ enum UserStage {
 $null = New-Module {
     [string] $configRoot = "HKLM:\Software\PortValhalla";
     [string] $stageOption = "Stage";
+    [string] $setupStageOption = "SetupStage";
     [string] $userOption = "SetupUser";
     [string] $userStageOption = "UserStage";
     [string] $accountOption = "MSAccount";
@@ -272,11 +279,44 @@ $null = New-Module {
 
     <#
         .SYNOPSIS
-        Gets the name of the current setup stage.
+        Gets the name of the current stage of the Windows install script action.
     #>
     function Get-Stage {
         $stage = Get-SetupOption $stageOption;
 
+        if ($null -ne $stage) {
+            $stage = [WindowsInstallerStage]$stage;
+        }
+
+        return $stage;
+    }
+
+    <#
+        .SYNOPSIS
+        Sets the name of the current stage of the Windows install script action.
+
+        .PARAMETER Name
+        The name of the stage to set.
+    #>
+    function Set-Stage {
+        param(
+            $Name
+        )
+
+        if (-not (($null -eq $Name) -or ($Name -is [string]))) {
+            $Name = ([WindowsInstallerStage]$Name).ToString();
+        }
+
+        $null = Set-SetupOption $stageOption $Name;
+    }
+
+    <#
+        .SYNOPSIS
+        Gets the name of the current setup stage.
+    #>
+    function Get-SetupStage {
+        $stage = Get-SetupOption $setupStageOption;
+
         if ($null -ne $stage) {
             $stage = [SetupStage]$stage;
         }
@@ -291,7 +331,7 @@ $null = New-Module {
         .PARAMETER Name
         The name to set the current stage to.
     #>
-    function Set-Stage {
+    function Set-SetupStage {
         param(
             $Name
         )
@@ -300,7 +340,7 @@ $null = New-Module {
             $Name = ([SetupStage]$Name).ToString();
         }
 
-        $null = Set-SetupOption $stageOption $Name;
+        $null = Set-SetupOption $setupStageOption $Name;
     }
 
     <#
diff --git a/scripts/Windows/OS/Manage.ps1 b/scripts/Windows/OS/Manage.ps1
index d228e328..cff16d4c 100644
--- a/scripts/Windows/OS/Manage.ps1
+++ b/scripts/Windows/OS/Manage.ps1
@@ -18,6 +18,7 @@ $null = New-Module {
     . "$PSScriptRoot/../Scripts/Security.ps1";
     . "$PSScriptRoot/../Scripts/Update.ps1";
     . "$PSScriptRoot/../Scripts/Users.ps1";
+    . "$PSScriptRoot/../Types/WindowsInstallerAction.ps1";
     . "$PSScriptRoot/../../Common/Scripts/Config.ps1";
     . "$PSScriptRoot/../../Common/Scripts/Operations.ps1";
     . "$PSScriptRoot/../../Common/Scripts/Software.ps1";
@@ -30,7 +31,7 @@ $null = New-Module {
     #>
     function Start-WindowsInstallation {
         Start-Operation -NoImplicitCleanup {
-            Start-InstallationLoop;
+            Start-InstallationLoop ([WindowsInstallerAction]::Install);
         };
     }
 
@@ -39,60 +40,85 @@ $null = New-Module {
         Starts the installation loop.
     #>
     function Start-InstallationLoop {
-        while (-not (Get-IsFinished)) {
-            if (Test-Admin) {
-                $null = Import-Module PSWindowsUpdate;
-
-                Invoke-Hook "Invoke-WindowsUpdate" -Fallback {
-                    Update-WindowsInstallation;
-                };
-
-                if ((Get-WURebootStatus -Silent)) {
-                    Restart-Intermediate;
-                    return;
-                }
-            }
+        param(
+            [WindowsInstallerAction] $Action
+        )
 
+        while ((Get-Stage) -ne ([WindowsInstallerStage]::Completed)) {
             switch (Get-Stage) {
                 ($null) {
-                    Set-Stage ([SetupStage]::Configure);
+                    Set-Stage ([WindowsInstallerStage]::Initialize);
                     break;
                 }
-                ([SetupStage]::Configure) {
-                    if (Get-Config "valhalla.windows.dualboot.enable") {
-                        if (-not (Test-Qemu)) {
-                            # Fix synchronization between Linux and Windows clocks.
-                            Set-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\TimeZoneInformation" -Name "RealTimeIsUniversal" -Value 1 -Type "DWord";
-                        }
+                ([WindowsInstallerStage]::Initialize) {
+                    Set-Stage ([WindowsInstallerStage]::Run);
+                    break;
+                }
+                ([WindowsInstallerStage]::Run) {
+                    switch ($Action) {
+                        ([WindowsInstallerAction]::Install) {
+                            while (-not (Get-IsFinished)) {
+                                if (Test-Admin) {
+                                    $null = Import-Module PSWindowsUpdate;
 
-                        # Force time resynchronization
-                        $timeZoneOption = "Start";
-                        $timeZoneKey = "HKLM:\SYSTEM\CurrentControlSet\Services\tzautoupdate";
-                        $service = Get-Service W32Time;
-                        $autoUpdate = (Get-Item $timeZoneKey).GetValue($timeZoneOption);
-                        $stopped = ($service.Status -eq "Stopped");
+                                    Invoke-Hook "Invoke-WindowsUpdate" -Fallback {
+                                        Update-WindowsInstallation;
+                                    };
 
-                        $setUpdate = { param([int] $Value) Set-ItemProperty $timeZoneKey -Name $timeZoneOption $Value };
-                        & $setUpdate 3;
-                        Start-Service $service;
-                        w32tm /resync /force;
-                        & $setUpdate $autoUpdate;
+                                    if ((Get-WURebootStatus -Silent)) {
+                                        Restart-Intermediate;
+                                        return;
+                                    }
+                                }
 
-                        if ($stopped) {
-                            Stop-Service $service;
+                                switch (Get-SetupStage) {
+                                    ($null) {
+                                        Set-SetupStage ([SetupStage]::Configure);
+                                        break;
+                                    }
+                                    ([SetupStage]::Configure) {
+                                        if (Get-Config "valhalla.windows.dualboot.enable") {
+                                            if (-not (Test-Qemu)) {
+                                                # Fix synchronization between Linux and Windows clocks.
+                                                Set-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\TimeZoneInformation" -Name "RealTimeIsUniversal" -Value 1 -Type "DWord";
+                                            }
+
+                                            # Force time resynchronization
+                                            $timeZoneOption = "Start";
+                                            $timeZoneKey = "HKLM:\SYSTEM\CurrentControlSet\Services\tzautoupdate";
+                                            $service = Get-Service W32Time;
+                                            $autoUpdate = (Get-Item $timeZoneKey).GetValue($timeZoneOption);
+                                            $stopped = ($service.Status -eq "Stopped");
+
+                                            $setUpdate = { param([int] $Value) Set-ItemProperty $timeZoneKey -Name $timeZoneOption $Value };
+                                            & $setUpdate 3;
+                                            Start-Service $service;
+                                            w32tm /resync /force;
+                                            & $setUpdate $autoUpdate;
+
+                                            if ($stopped) {
+                                                Stop-Service $service;
+                                            }
+                                        }
+
+                                        Set-SetupStage ([SetupStage]::Install);
+                                    }
+                                    ([SetupStage]::Install) {
+                                        Write-Host "Entering install phase";
+                                        Deploy-SoftwareAction;
+                                        Set-SetupStage ([SetupStage]::CreateUser);
+                                    }
+                                    ([SetupStage]::CreateUser) {
+                                        Install-ValhallaUsers;
+                                        Set-IsFinished $true;
+                                    }
+                                }
+                            }
                         }
                     }
 
-                    Set-Stage ([SetupStage]::Install);
-                }
-                ([SetupStage]::Install) {
-                    Write-Host "Entering install phase";
-                    Deploy-SoftwareAction;
-                    Set-Stage ([SetupStage]::CreateUser);
-                }
-                ([SetupStage]::CreateUser) {
-                    Install-ValhallaUsers;
-                    Set-IsFinished $true;
+                    Set-Stage ([WindowsInstallerStage]::Completed);
+                    break;
                 }
             }
         }
diff --git a/scripts/Windows/Types/WindowsInstallerAction.ps1 b/scripts/Windows/Types/WindowsInstallerAction.ps1
new file mode 100644
index 00000000..3cc3b9a0
--- /dev/null
+++ b/scripts/Windows/Types/WindowsInstallerAction.ps1
@@ -0,0 +1,4 @@
+enum WindowsInstallerAction {
+    Backup
+    Install
+}