Driver update script thats called using Autounattended xml file

Driver update from Networkshare

Execution flow

flowchart TB
    START([🚀 Script Start]) --> INIT[Initialize-PersistentScript<br/>Copy script to C:\Temp\]
    INIT -->|Failed| FAIL_INIT([❌ Throw Error])
    INIT -->|Success| TRANSCRIPT[Start-Transcript<br/>C:\Temp\DriverUpdateScript.log]
    TRANSCRIPT --> HEADER[Display Header<br/>User, Computer, Model, Config]
    HEADER --> ORPHAN[Stop Orphan Driver Processes<br/>drvinst, pnputil, DPInst, etc.]
    ORPHAN --> LOADSTATE[Get-ScriptState<br/>Load or create state JSON]
    LOADSTATE --> SHOWSTATE[Display Current State<br/>All properties]
    SHOWSTATE --> CHECKPHASE{state.Phase?}

    %% ============================================================
    %% PHASE 0
    %% ============================================================
    CHECKPHASE -->|Phase 0| USB_CHECK{USBCompleted?}

    subgraph PHASE0A ["🔌 PHASE-0A: USB Network Drivers"]
        USB_CHECK -->|No| USB_FIND[Scan drives D: - Z:<br/>Look for DriversOS / Drivers folder]
        USB_FIND -->|Not Found| USB_SKIP[No USB folder found<br/>Skip USB phase]
        USB_FIND -->|Found| USB_INSTALL[Install-DriversFromFolder<br/>SkipDeviceMatching = TRUE<br/>Install ALL drivers from USB]
        USB_INSTALL --> USB_EACH[For each INF file:<br/>Install-SingleDriver<br/>pnputil /add-driver /install]
        USB_EACH --> USB_RESULT{NeedsReboot?}
        USB_RESULT -->|Yes| USB_REBOOT[Schedule-RebootAndContinue<br/>Set RunOnce registry<br/>Reboot in 15 seconds]
        USB_REBOOT --> REBOOT_USB([🔄 REBOOT])
        REBOOT_USB -.->|After reboot| USB_CHECK
        USB_RESULT -->|No| USB_DONE[USBCompleted = true<br/>USBRebootDone = true]
        USB_SKIP --> USB_DONE
    end

    USB_CHECK -->|Yes, not rebooted| USB_FINALIZE[Post-reboot:<br/>USB drivers finalized]
    USB_FINALIZE --> NET_CHECK
    USB_CHECK -->|Yes, rebooted| NET_CHECK
    USB_DONE --> NET_CHECK

    subgraph PHASE0B ["📡 PHASE-0B: Network Share Drivers"]
        NET_CHECK{NetworkShare<br/>Completed?}
        NET_CHECK -->|No| GUEST_CHECK{GuestAuth<br/>Configured?}

        subgraph GUESTAUTH ["🔓 SMB Guest Access Fix"]
            GUEST_CHECK -->|No| GUEST_POLICY[Create policy key if missing<br/>HKLM:\SOFTWARE\Policies\<br/>Microsoft\Windows\LanmanWorkstation]
            GUEST_POLICY --> GUEST_INSECURE[AllowInsecureGuestAuth = 1<br/>Policy + Service level]
            GUEST_INSECURE --> GUEST_SIGN1[RequireSecuritySignature = 0<br/>Disable 'always sign']
            GUEST_SIGN1 --> GUEST_SIGN2[EnableSecuritySignature = 0<br/>Disable 'sign if server agrees']
            GUEST_SIGN2 --> GUEST_RESTART[Restart LanmanWorkstation<br/>+ dependent services]
            GUEST_RESTART --> GUEST_VERIFY[Verify: Read back all 4 values<br/>Display OK/FAIL table]
            GUEST_VERIFY --> SYSINFO
        end

        GUEST_CHECK -->|Yes| SYSINFO

        subgraph MODELPATH ["🏷️ Model Detection & Path Building"]
            SYSINFO[Get-SystemInfo<br/>Manufacturer + Model]
            SYSINFO --> CLEANMODEL["Clean model name<br/>'Latitude 5400' → 'latitude5400'"]
            CLEANMODEL --> MFGFOLDER["Map manufacturer → folder<br/>'Dell Inc.' → 'dell'"]
            MFGFOLDER --> BUILDPATH["Build UNC path<br/>\\networkshare\dell\driver\latitude5400"]
        end

        BUILDPATH --> TESTSHARE[Test-NetworkShareAccess<br/>10 retries × 10s delay]
        TESTSHARE -->|Accessible| DEVSCAN
        TESTSHARE -->|Failed| TRYALT["Try alternative names:<br/>latitude-5400<br/>latitude_5400<br/>latitude 5400"]
        TRYALT -->|Found| DEVSCAN
        TRYALT -->|All failed| NET_NOTFOUND[No share folder found<br/>Skip network share phase]

        subgraph DEVMATCH ["🔍 Device Matching"]
            DEVSCAN[Get-SystemDeviceIds<br/>WMI Win32_PnPEntity<br/>Fallback: Get-PnpDevice]
            DEVSCAN --> MATCHINFS["For each INF:<br/>Parse [Manufacturer] + [Models]<br/>Extract hardware IDs"]
            MATCHINFS --> COMPARE["Compare INF IDs vs System IDs<br/>HashSet O(1) lookup"]
            COMPARE --> MATCHRESULT["Display results:<br/>Matched / Skipped / Mode"]
        end

        MATCHRESULT --> NETINSTALL[Install-DriversFromFolder<br/>Only MATCHED drivers]
        NETINSTALL --> NET_EACH["For each matched INF:<br/>Install-SingleDriver<br/>pnputil /add-driver /install<br/>Timeout monitoring<br/>Spinner animation"]
        NET_EACH --> NET_RESULT{NeedsReboot?}
        NET_RESULT -->|Yes| NET_REBOOT[Schedule-RebootAndContinue]
        NET_REBOOT --> REBOOT_NET([🔄 REBOOT])
        REBOOT_NET -.->|After reboot| NET_CHECK
        NET_RESULT -->|No| NET_DONE[NetworkShareCompleted = true<br/>Phase = 1]
        NET_NOTFOUND --> NET_DONE
    end

    NET_CHECK -->|Yes, not rebooted| NET_FINALIZE[Post-reboot:<br/>Share drivers finalized<br/>Phase = 1]
    NET_CHECK -->|Yes, rebooted| PHASE1_START
    NET_FINALIZE --> PHASE1_START
    NET_DONE --> PHASE1_START

    %% ============================================================
    %% PHASE 1
    %% ============================================================
    CHECKPHASE -->|Phase ≥ 1| PHASE1_START

    subgraph PHASE1 ["🌐 PHASE-1: Online OEM Updates"]
        PHASE1_START[PHASE 1: ONLINE OEM UPDATES]
        PHASE1_START --> INET[Test-InternetConnectivity<br/>15 retries × 10s delay<br/>HEAD https://google.com]
        INET -->|No Internet| INET_FAIL([❌ Exit 1<br/>No internet])
        INET -->|Connected| CHOCO

        subgraph CHOCO_SETUP ["🍫 Chocolatey Setup"]
            CHOCO[Install-ChocolateyIfNeeded]
            CHOCO --> CHOCO_SRC["Set-ChocolateySource<br/>Add: chocosia → choco.local.xyz.com<br/>Disable: default community source"]
        end

        CHOCO_SRC --> MFG_DETECT[Get-SystemManufacturer<br/>Win32_ComputerSystem]
        MFG_DETECT --> MFG_CHECK{Manufacturer?}

        subgraph DELL ["🖥️ Dell Updates"]
            MFG_CHECK -->|Dell| DELL_CLI[Locate dcu-cli.exe<br/>or install via Chocolatey]
            DELL_CLI -->|Not found| DELL_CHOCO["Install-ChocoPackage<br/>'dellcommandupdate'<br/>Invoke-CommandVerbose"]
            DELL_CLI -->|Found| DELL_PHASE
            DELL_CHOCO --> DELL_PHASE

            DELL_PHASE{ManufacturerPhase?}
            DELL_PHASE -->|0| DELL_ADR["Dell Phase-0: ADR<br/>Configure ADR enable<br/>dcu-cli /driverinstall<br/>Verbose output streaming"]
            DELL_ADR --> DELL_ADR_EXIT{Exit code?}
            DELL_ADR_EXIT -->|0, 3| DELL_P2
            DELL_ADR_EXIT -->|1, 5| DELL_ADR_REBOOT[Reboot required]
            DELL_ADR_REBOOT --> REBOOT_DELL([🔄 REBOOT])
            REBOOT_DELL -.-> DELL_PHASE
            DELL_ADR_EXIT -->|2, other| DELL_ADR_FAIL[ADR failed<br/>Skip to Phase-2]
            DELL_ADR_FAIL --> DELL_P2

            DELL_PHASE -->|1| DELL_SCAN["Dell Phase-1: Scan<br/>dcu-cli /scan"]
            DELL_SCAN --> DELL_P2

            DELL_P2["Dell Phase-2: Apply Updates<br/>dcu-cli /applyupdates<br/>-forceUpdate=enable<br/>-autoSuspendBitLocker=enable<br/>Verbose: each driver shown"]
            DELL_PHASE -->|2| DELL_P2
            DELL_P2 --> DELL_P2_EXIT{Exit code?}
            DELL_P2_EXIT -->|0, 3| DELL_DONE[Dell updates complete]
            DELL_P2_EXIT -->|1, 5| DELL_P2_REBOOT[Reboot required]
            DELL_P2_REBOOT --> REBOOT_DELL2([🔄 REBOOT])
            REBOOT_DELL2 -.-> DELL_PHASE
            DELL_P2_EXIT -->|other| DELL_DONE

            DELL_PHASE -->|3+| DELL_DONE
        end

        subgraph HP ["🖨️ HP Updates"]
            MFG_CHECK -->|HP| HP_CLI[Locate HPImageAssistant.exe<br/>or install via Chocolatey]
            HP_CLI -->|Not found| HP_CHOCO["Install-ChocoPackage<br/>'hpimageassistant'<br/>Invoke-CommandVerbose"]
            HP_CLI -->|Found| HP_RUN
            HP_CHOCO --> HP_RUN

            HP_RUN["HP Image Assistant<br/>/Operation:Analyze<br/>/Action:Install<br/>/Selection:All<br/>Verbose output streaming"]
            HP_RUN --> HP_EXIT{Exit code?}
            HP_EXIT -->|0| HP_DONE[HP updates complete]
            HP_EXIT -->|256, 3010| HP_REBOOT_WARN[Reboot recommended]
            HP_EXIT -->|other| HP_CONTINUE[Continue anyway]
            HP_REBOOT_WARN --> HP_DONE
            HP_CONTINUE --> HP_DONE
        end

        MFG_CHECK -->|Other| MFG_UNSUPPORTED[Unsupported manufacturer<br/>Only Dell & HP handled]
    end

    %% ============================================================
    %% FINAL
    %% ============================================================
    DELL_DONE --> FINAL
    HP_DONE --> FINAL
    MFG_UNSUPPORTED --> FINAL

    subgraph CLEANUP ["✅ Final Cleanup"]
        FINAL[Stop remaining driver processes]
        FINAL --> SUMMARY["Display Final Summary<br/>System, Model, Share path<br/>Reboots, Added, Installed<br/>Skipped, Failed, Timed out<br/>Total time"]
        SUMMARY --> REMOVESTATE["Remove-ScriptState<br/>Delete state JSON<br/>Delete RunOnce registry<br/>Delete scheduled task"]
    end

    REMOVESTATE --> DONE([✅ Script Complete])

    %% ============================================================
    %% ERROR HANDLING
    %% ============================================================
    FAIL_INIT --> CATCH
    subgraph ERRORHANDLER ["❌ Error Handler"]
        CATCH[Catch block<br/>Display error message + line<br/>Display stack trace]
        CATCH --> EMERGENCY[Emergency cleanup<br/>Stop all driver processes<br/>ErrorAction = SilentlyContinue]
        EMERGENCY --> KEEPSTATE[State file kept<br/>Re-run script to continue]
    end
    KEEPSTATE --> FINALLY

    DONE --> FINALLY
    FINALLY[Finally block<br/>Stop-Transcript]

    %% ============================================================
    %% INSTALL-SINGLEDRIVER DETAIL
    %% ============================================================
    subgraph SINGLEDRIVER ["⚙️ Install-SingleDriver (called per INF)"]
        direction TB
        SD_START[Write driver header<br/>Progress bar + name] --> SD_CLEAN[Clear-DriverInstallEnvironment<br/>Kill leftover processes]
        SD_CLEAN --> SD_WRAPPER["Create wrapper .ps1<br/>Runs pnputil in child process"]
        SD_WRAPPER --> SD_SPAWN["Start-Process powershell<br/>-WindowStyle Hidden"]
        SD_SPAWN --> SD_MONITOR["Monitor loop:<br/>Spinner animation<br/>Elapsed/Remaining timer<br/>Check timeout"]
        SD_MONITOR --> SD_TIMEOUT{Timed out?}
        SD_TIMEOUT -->|Yes| SD_KILL["Kill process<br/>Stop-DriverInstallProcesses<br/>Return TimedOut result"]
        SD_TIMEOUT -->|No| SD_READ["Read output file<br/>Read exit code file"]
        SD_READ --> SD_PARSE["Parse pnputil output:<br/>added / installed /<br/>already exists / reboot"]
        SD_PARSE --> SD_EXIT["Evaluate exit code:<br/>0=OK, 1=warn, 259=staged<br/>3010=reboot, 482=partial"]
        SD_EXIT --> SD_RETURN[Return result object]
    end

    %% ============================================================
    %% INVOKE-COMMANDVERBOSE DETAIL
    %% ============================================================
    subgraph VERBOSE ["📺 Invoke-CommandVerbose (used by Dell/HP/Choco)"]
        direction TB
        V_START[Start process with<br/>RedirectStdout + Stderr] --> V_LOOP["Async ReadLineAsync loop:<br/>stdout → White text<br/>stderr → Yellow text<br/>Each line timestamped"]
        V_LOOP --> V_HEARTBEAT["If silent >30s:<br/>Print heartbeat message<br/>'...still running (120s)'"]
        V_HEARTBEAT --> V_DONE["Process exits<br/>Save full log to C:\Temp\<br/>Display duration + exit code"]
    end

    %% ============================================================
    %% STYLING
    %% ============================================================
    classDef phase fill:#c9f1f5,stroke:#e94560,color:#fff,stroke-width:2px
    classDef success fill:#0bd979,stroke:#00ff88,color:#fff,stroke-width:2px
    classDef reboot fill:#533483,stroke:#ffcc00,color:#fff,stroke-width:2px
    classDef error fill:#4a0000,stroke:#ff0000,color:#fff,stroke-width:2px
    classDef decision fill:#26948b,stroke:#0f3460,color:#fff,stroke-width:1px
    classDef process fill:#c9f1f5,stroke:#e2e2e2,color:#fff,stroke-width:1px

    class START,DONE success
    class REBOOT_USB,REBOOT_NET,REBOOT_DELL,REBOOT_DELL2 reboot
    class FAIL_INIT,INET_FAIL,CATCH error
    class CHECKPHASE,USB_CHECK,NET_CHECK,GUEST_CHECK,MFG_CHECK,DELL_PHASE,USB_RESULT,NET_RESULT,DELL_ADR_EXIT,DELL_P2_EXIT,HP_EXIT,SD_TIMEOUT decision

Reboot Flow

sequenceDiagram
    participant S as Script
    participant R as Registry RunOnce
    participant W as Windows

    S->>S: Save state to JSON
    S->>R: Write RunOnce entry (64-bit)
    alt RunOnce failed
        S->>W: Create Scheduled Task (fallback)
    end
    S->>W: Restart-Computer -Force
    W->>W: Reboot
    W->>R: Read RunOnce entry
    R->>S: Launch script automatically
    S->>S: Load state from JSON
    S->>S: Resume from saved Phase

Network Share Folder Structure

graph LR
    SHARE["\\networkshare"] --> DELL[dell/]
    SHARE --> HP[hp/]
    DELL --> DDRIVER[driver/]
    HP --> HDRIVER[driver/]
    DDRIVER --> LAT5400[latitude5400/]
    DDRIVER --> PREC3500[precision3500/]
    DDRIVER --> OPT7080[optiplex7080/]
    HDRIVER --> ELITE840[elitebook840g7/]
    HDRIVER --> PRO400[prodesk400g6/]
    LAT5400 --> AUDIO[Audio/]
    LAT5400 --> VIDEO[Video/]
    LAT5400 --> CHIP[Chipset/]
    AUDIO --> INF1[realtek.inf]
    VIDEO --> INF2[igdlh64.inf]
    CHIP --> INF3[SetupChipset.inf]

    style SHARE fill:#e94560,color:#fff
    style DELL fill:#0f3460,color:#fff
    style HP fill:#0f3460,color:#fff
    style LAT5400 fill:#533483,color:#fff

USB Stick Structure (Minimal)

graph LR
    USB["USB Drive E:\"] --> DOS[DriversOS/]
    DOS --> INTEL_LAN["Intel-I219LM/"]
    DOS --> INTEL_WIFI["Intel-AX211/"]
    DOS --> REALTEK["Realtek-RTL8111/"]
    INTEL_LAN --> INF1["e1d68x64.inf"]
    INTEL_WIFI --> INF2["netwtw10.inf"]
    REALTEK --> INF3["rt640x64.inf"]

    style USB fill:#e94560,color:#fff
    style DOS fill:#533483,color:#fff

Powershell Script That searches the network share for drivers

#Requires -RunAsAdministrator
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

#===================================================================
#  CONFIGURATION
#===================================================================
$Script:TempFolder       = "C:\Temp"
$Script:PersistentScript = "C:\Temp\DriverUpdateScript.ps1"
$Script:StateFile        = "C:\Temp\DriverUpdateState.json"
$Script:LogFile          = "C:\Temp\DriverUpdateScript.log"
$Script:RunOnceKey       = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce'
$Script:RunOnceName      = 'DriverUpdateScript_RunOnce'
$Script:DriverTimeout    = 300
$Script:CooldownSeconds  = 5
$Script:DriverProcesses  = @('drvinst','pnputil','DPInst','DPInst64','SetupAPI')
$Script:SpinnerFrames    = @('[ ]','[= ]','[== ]','[=== ]','[====]','[ ===]','[ ==]','[ =]')
$Script:ChocoSourceName  = 'chocosia'
$Script:ChocoSourceUrl   = 'http://choco.local.iut-troyes.univ-reims.fr/repository/chocolatey-group'
$Script:NetworkShareBase    = '\\synosia.local.iut-troyes.univ-reims.fr\batchs'
$Script:ShareDriverFolder   = 'driver'
$Script:ManufacturerShareMap = @{
    'Dell'             = 'dell'
    'Dell Inc.'        = 'dell'
    'HP'               = 'hp'
    'Hewlett-Packard'  = 'hp'
}
$Script:USBDriverFolders = @('DriversOS','Drivers')

#===================================================================
#  LOGGING HELPERS
#===================================================================
function Append-ToLog {
    param([Parameter(Mandatory=$false)][AllowEmptyString()][string]$Message = "")
    # No-op: Start-Transcript captures all console output automatically
}

function Write-Status {
    param(
        [Parameter(Mandatory)]
        [ValidateSet('ERROR','WARN','INFO','STEP','OK','PROGRESS','DEBUG','DRIVER','TIME','SKIP','KILL')]
        [string]$Level,
        [Parameter(Mandatory)][string]$Message
    )
    $timestamp = Get-Date -Format "HH:mm:ss"
    $color = switch ($Level.ToUpper()) {
        'ERROR'    { 'Red' }
        'WARN'     { 'Yellow' }
        'INFO'     { 'Cyan' }
        'STEP'     { 'Magenta' }
        'OK'       { 'Green' }
        'PROGRESS' { 'White' }
        'DEBUG'    { 'DarkGray' }
        'DRIVER'   { 'Blue' }
        'TIME'     { 'DarkYellow' }
        'SKIP'     { 'DarkMagenta' }
        'KILL'     { 'Red' }
        default    { 'White' }
    }
    $prefix = switch ($Level.ToUpper()) {
        'ERROR'    { '[X]' }
        'WARN'     { '[!]' }
        'INFO'     { '[i]' }
        'STEP'     { '[>]' }
        'OK'       { '[+]' }
        'PROGRESS' { '[o]' }
        'DEBUG'    { '[.]' }
        'DRIVER'   { '[D]' }
        'TIME'     { '[T]' }
        'SKIP'     { '[S]' }
        'KILL'     { '[K]' }
        default    { '[ ]' }
    }
    $line = "[$timestamp] $prefix [$Level] $Message"
    Write-Host $line -ForegroundColor $color
}

function Write-Banner {
    param([string]$Text)
    $sep = "=" * 70
    Write-Host ""
    Write-Host $sep -ForegroundColor Magenta
    Write-Host "  $Text" -ForegroundColor Magenta
    Write-Host $sep -ForegroundColor Magenta
    Write-Host ""
}

function Write-SubBanner {
    param([string]$Text)
    Write-Host ""
    Write-Host "--- $Text ---" -ForegroundColor Yellow
    Write-Host ""
}

function Write-LoggedHost {
    param(
        [Parameter(Mandatory=$false)][AllowEmptyString()][string]$Message = "",
        [string]$ForegroundColor = 'White'
    )
    if ($Message) {
        Write-Host $Message -ForegroundColor $ForegroundColor
    }
    else {
        Write-Host ""
    }
}

function Format-Duration {
    param([double]$Seconds)
    if ($Seconds -lt 60) { return "{0:N1}s" -f $Seconds }
    elseif ($Seconds -lt 3600) {
        $m = [math]::Floor($Seconds / 60); $s = $Seconds % 60
        return "{0}m {1:N0}s" -f $m, $s
    }
    else {
        $h = [math]::Floor($Seconds / 3600); $m = [math]::Floor(($Seconds % 3600) / 60)
        return "{0}h {1}m" -f $h, $m
    }
}

function Get-ConsoleWidth {
    $w = 100
    try { $w = [Console]::WindowWidth - 5; if ($w -lt 50) { $w = 100 } } catch { }
    return $w
}

function Write-DriverHeader {
    param([string]$DriverName, [int]$Current, [int]$Total)
    if ($Total -le 0) { $Total = 1 }
    $pct = [math]::Round(($Current / $Total) * 100)
    $filled = [math]::Floor($pct / 5)
    $empty = 20 - $filled
    $bar = "[" + ("#" * $filled) + ("-" * $empty) + "]"
    $lines = @(
        "  ======================================================================"
        "  $bar $pct% ($Current of $Total drivers)"
        "  Driver: $DriverName"
        "  Timeout: $Script:DriverTimeout seconds"
        "  ======================================================================"
    )
    Write-Host ""
    foreach ($l in $lines) {
        Write-Host $l -ForegroundColor DarkCyan
    }
}

#===================================================================
#  INITIALISATION
#===================================================================
function Initialize-PersistentScript {
    if (-not (Test-Path $Script:TempFolder)) {
        New-Item -Path $Script:TempFolder -ItemType Directory -Force | Out-Null
    }
    $sessionHeader = @(
        ""
        "=================================================================="
        "  NEW SESSION: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')"
        "  PID: $PID | User: $env:USERNAME | Computer: $env:COMPUTERNAME"
        "=================================================================="
        ""
    )
    foreach ($line in $sessionHeader) {
        Write-Host $line -ForegroundColor DarkGray
    }
    $currentScript = $MyInvocation.ScriptName
    if (-not $currentScript) { $currentScript = $PSCommandPath }
    if ($currentScript -and $currentScript -ne $Script:PersistentScript) {
        if (Test-Path $currentScript) {
            Write-LoggedHost "[INIT] Copying script to $Script:PersistentScript" -ForegroundColor Yellow
            Copy-Item -Path $currentScript -Destination $Script:PersistentScript -Force
            Write-LoggedHost "[INIT] Script copied successfully." -ForegroundColor Green
        }
    }
    if (-not (Test-Path $Script:PersistentScript)) {
        Write-LoggedHost "[ERROR] Persistent script could not be created." -ForegroundColor Red
        return $false
    }
    return $true
}

#===================================================================
#  PROCESS-HANDLING HELPERS
#===================================================================
function Get-DriverInstallProcesses {
    [System.Collections.ArrayList]$collector = @()
    foreach ($name in $Script:DriverProcesses) {
        $found = $null
        try { $found = Get-Process -Name $name -ErrorAction SilentlyContinue } catch { }
        if ($null -ne $found) {
            foreach ($p in @($found)) { [void]$collector.Add($p) }
        }
    }
    $arr = [object[]]$collector.ToArray()
    return ,$arr
}

function Stop-DriverInstallProcesses {
    param([switch]$Silent)
    $killed = 0
    foreach ($name in $Script:DriverProcesses) {
        $found = $null
        try { $found = Get-Process -Name $name -ErrorAction SilentlyContinue } catch { }
        if ($null -ne $found) {
            foreach ($proc in @($found)) {
                if (-not $Silent) {
                    Write-Status KILL "Terminating: $($proc.ProcessName) (PID $($proc.Id))"
                }
                try { $proc.CloseMainWindow() | Out-Null } catch { }
                Start-Sleep -Milliseconds 300
                try {
                    if (-not $proc.HasExited) { Stop-Process -Id $proc.Id -Force -ErrorAction SilentlyContinue }
                } catch { }
                $killed++
            }
        }
    }
    foreach ($name in $Script:DriverProcesses) {
        try {
            $saveEA = $ErrorActionPreference
            $ErrorActionPreference = 'SilentlyContinue'
            $null = cmd.exe /c "taskkill /F /IM `"$name.exe`" >nul 2>&1"
            $ErrorActionPreference = $saveEA
        }
        catch { try { $ErrorActionPreference = $saveEA } catch { } }
    }
    return $killed
}

function Wait-ForDriverProcesses {
    param([int]$TimeoutSeconds = 30)
    $sw = [System.Diagnostics.Stopwatch]::StartNew()
    while ($sw.Elapsed.TotalSeconds -lt $TimeoutSeconds) {
        $procs = Get-DriverInstallProcesses
        $procCount = ($procs | Measure-Object).Count
        if ($procCount -eq 0) { return $true }
        Start-Sleep -Milliseconds 500
    }
    return $false
}

function Clear-DriverInstallEnvironment {
    $procs = Get-DriverInstallProcesses
    $procCount = ($procs | Measure-Object).Count
    if ($procCount -gt 0) {
        Write-Status WARN "Found $procCount leftover driver process(es) - cleaning up..."
        $k = Stop-DriverInstallProcesses
        Write-Status INFO "Terminated $k process(es)"
        Start-Sleep -Seconds 2
    }
}

#===================================================================
#  EXECUTABLE FINDER (validates file, not directory)
#===================================================================
function Find-Executable {
    param(
        [Parameter(Mandatory)][string]$FileName,
        [Parameter(Mandatory)][string[]]$KnownPaths,
        [string[]]$SearchRoots = @(),
        [string]$Label = "executable"
    )

    # 1 - Check known paths (MUST be a file, not a directory)
    foreach ($p in $KnownPaths) {
        if (Test-Path $p -PathType Leaf) {
            Write-Status OK "Found $Label at known path: $p"
            return $p
        }
    }
    Write-Status INFO "$Label not found at known paths."

    # 2 - Search Chocolatey lib folder
    $chocoLib = "$env:ProgramData\chocolatey\lib"
    if (Test-Path $chocoLib) {
        Write-Status INFO "Searching Chocolatey lib for $FileName..."
        $found = Get-ChildItem -Path $chocoLib -Filter $FileName -Recurse -File -ErrorAction SilentlyContinue | Select-Object -First 1
        if ($found) {
            Write-Status OK "Found $Label in Chocolatey: $($found.FullName)"
            return $found.FullName
        }
    }

    # 3 - Search Chocolatey bin for shims
    $chocoBin = "$env:ProgramData\chocolatey\bin"
    if (Test-Path $chocoBin) {
        $shimPath = Join-Path $chocoBin $FileName
        if (Test-Path $shimPath -PathType Leaf) {
            Write-Status OK "Found $Label shim: $shimPath"
            return $shimPath
        }
    }

    # 4 - Search provided root directories
    $defaultRoots = @(
        'C:\Program Files'
        'C:\Program Files (x86)'
        $env:ProgramData
        'C:\HP'
        'C:\SWSetup'
        'C:\Dell'
    )
    $allRoots = @($SearchRoots) + $defaultRoots | Select-Object -Unique
    foreach ($root in $allRoots) {
        if (-not (Test-Path $root)) { continue }
        Write-Status DEBUG "Searching $root for $FileName..."
        $found = Get-ChildItem -Path $root -Filter $FileName -Recurse -File -ErrorAction SilentlyContinue | Select-Object -First 1
        if ($found) {
            Write-Status OK "Found $Label via search: $($found.FullName)"
            return $found.FullName
        }
    }

    Write-Status WARN "$Label ($FileName) not found anywhere."
    return $null
}

#===================================================================
#  VERBOSE COMMAND EXECUTION
#===================================================================
function Invoke-CommandVerbose {
    param(
        [Parameter(Mandatory)][string]$FilePath,
        [Parameter(Mandatory)][string]$Arguments,
        [Parameter(Mandatory)][string]$Label
    )

    # Validate: must exist AND be a file (not a directory)
    if (-not (Test-Path $FilePath -PathType Leaf)) {
        Write-Status ERROR "Executable not found or is a directory: $FilePath"
        return 999
    }

    $exeName = Split-Path $FilePath -Leaf
    Write-Status INFO "Executing: $exeName $Arguments"
    Write-Status DEBUG "Full path: $FilePath"

    $psi = [System.Diagnostics.ProcessStartInfo]::new()
    $psi.FileName = $FilePath
    $psi.Arguments = $Arguments
    $psi.UseShellExecute = $false
    $psi.RedirectStandardOutput = $true
    $psi.RedirectStandardError = $true
    $psi.CreateNoWindow = $true
    $process = [System.Diagnostics.Process]::new()
    $process.StartInfo = $psi
    try { $null = $process.Start() }
    catch {
        Write-Status ERROR "Failed to start $exeName : $($_.Exception.Message)"
        return 999
    }
    $sw = [System.Diagnostics.Stopwatch]::StartNew()
    $lastActivity = [System.Diagnostics.Stopwatch]::StartNew()
    $heartbeatSec = 30
    $logLines = [System.Collections.ArrayList]::new()
    $stdoutTask = $process.StandardOutput.ReadLineAsync()
    $stderrTask = $process.StandardError.ReadLineAsync()
    $stdoutDone = $false
    $stderrDone = $false
    while ((-not $stdoutDone) -or (-not $stderrDone)) {
        $activity = $false
        if ((-not $stdoutDone) -and $stdoutTask.IsCompleted) {
            $line = $null
            try { $line = $stdoutTask.Result } catch { $stdoutDone = $true; continue }
            if ($null -ne $line) {
                $trimmed = $line.Trim()
                if ($trimmed) {
                    $ts = Get-Date -Format 'HH:mm:ss'
                    $formatted = "  [$ts] [$Label] $trimmed"
                    Write-Host $formatted -ForegroundColor White
                    [void]$logLines.Add("[$ts] $trimmed")
                }
                $stdoutTask = $process.StandardOutput.ReadLineAsync()
                $activity = $true; $lastActivity.Restart()
            }
            else { $stdoutDone = $true }
        }
        if ((-not $stderrDone) -and $stderrTask.IsCompleted) {
            $line = $null
            try { $line = $stderrTask.Result } catch { $stderrDone = $true; continue }
            if ($null -ne $line) {
                $trimmed = $line.Trim()
                if ($trimmed) {
                    $ts = Get-Date -Format 'HH:mm:ss'
                    $formatted = "  [$ts] [$Label] [STDERR] $trimmed"
                    Write-Host $formatted -ForegroundColor Yellow
                    [void]$logLines.Add("[$ts] [STDERR] $trimmed")
                }
                $stderrTask = $process.StandardError.ReadLineAsync()
                $activity = $true; $lastActivity.Restart()
            }
            else { $stderrDone = $true }
        }
        if ((-not $activity) -and ($lastActivity.Elapsed.TotalSeconds -ge $heartbeatSec)) {
            $ts = Get-Date -Format 'HH:mm:ss'
            $elapsed = [math]::Round($sw.Elapsed.TotalSeconds)
            $heartbeatMsg = "  [$ts] [$Label] ... still running (${elapsed}s elapsed)"
            Write-Host $heartbeatMsg -ForegroundColor DarkGray
            [void]$logLines.Add("[$ts] [heartbeat] ${elapsed}s elapsed")
            $lastActivity.Restart()
        }
        if (-not $activity) { Start-Sleep -Milliseconds 100 }
    }
    $process.WaitForExit()
    $sw.Stop()
    $exitCode = $process.ExitCode
    $safeLabel = $Label -replace '[^a-zA-Z0-9]', '_'
    $cmdLogFile = Join-Path $Script:TempFolder "${safeLabel}_$(Get-Date -Format 'yyyyMMdd_HHmmss').log"
    try {
        $header = "Command : $FilePath $Arguments`nExit    : $exitCode`nDuration: $(Format-Duration $sw.Elapsed.TotalSeconds)`n$('=' * 60)`n"
        $body = ($logLines | ForEach-Object { $_ }) -join "`n"
        "$header$body" | Out-File $cmdLogFile -Encoding UTF8 -Force
    } catch { }
    Write-Status TIME "Completed in $(Format-Duration $sw.Elapsed.TotalSeconds)"
    Write-Status INFO "Exit code: $exitCode"
    Write-Status DEBUG "Per-command log: $cmdLogFile"
    try { $process.Dispose() } catch { }
    return $exitCode
}

function Get-ChocoExePath {
    $cmd = Get-Command choco -ErrorAction SilentlyContinue
    if ($null -ne $cmd) { return $cmd.Source }
    $default = "$env:ProgramData\chocolatey\bin\choco.exe"
    if (Test-Path $default) { return $default }
    return $null
}

#===================================================================
#  SMB GUEST AUTH & SIGNING FIX
#===================================================================
function Enable-GuestNetworkAccess {
    Write-SubBanner "Configuring SMB Guest Access & Signing"
    Write-Status INFO "Windows 11 blocks guest SMB access by default."
    Write-Status INFO "Applying registry fixes..."
    $policyPath = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\LanmanWorkstation'
    $servicePath = 'HKLM:\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters'
    $changes = 0
    if (-not (Test-Path $policyPath)) {
        Write-Status INFO "Creating policy key: $policyPath"
        try {
            New-Item -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows' -Name 'LanmanWorkstation' -Force | Out-Null
            Write-Status OK "Policy key created."
            $changes++
        }
        catch { Write-Status ERROR "Failed to create policy key: $($_.Exception.Message)" }
    }
    else { Write-Status OK "Policy key exists." }
    try {
        $currentVal = $null
        try { $currentVal = Get-ItemPropertyValue -Path $policyPath -Name 'AllowInsecureGuestAuth' -ErrorAction SilentlyContinue } catch { }
        if ($currentVal -eq 1) { Write-Status OK "AllowInsecureGuestAuth (policy) already 1." }
        else {
            Set-ItemProperty -Path $policyPath -Name 'AllowInsecureGuestAuth' -Value 1 -Type DWord -Force
            Write-Status OK "AllowInsecureGuestAuth (policy) set to 1."
            $changes++
        }
    }
    catch { Write-Status ERROR "Failed: $($_.Exception.Message)" }
    try {
        $currentVal = $null
        try { $currentVal = Get-ItemPropertyValue -Path $servicePath -Name 'AllowInsecureGuestAuth' -ErrorAction SilentlyContinue } catch { }
        if ($currentVal -eq 1) { Write-Status OK "AllowInsecureGuestAuth (service) already 1." }
        else {
            Set-ItemProperty -Path $servicePath -Name 'AllowInsecureGuestAuth' -Value 1 -Type DWord -Force
            Write-Status OK "AllowInsecureGuestAuth (service) set to 1."
            $changes++
        }
    }
    catch { Write-Status ERROR "Failed: $($_.Exception.Message)" }
    try {
        $currentVal = $null
        try { $currentVal = Get-ItemPropertyValue -Path $servicePath -Name 'RequireSecuritySignature' -ErrorAction SilentlyContinue } catch { }
        if ($currentVal -eq 0) { Write-Status OK "RequireSecuritySignature already 0." }
        else {
            Set-ItemProperty -Path $servicePath -Name 'RequireSecuritySignature' -Value 0 -Type DWord -Force
            Write-Status OK "RequireSecuritySignature set to 0."
            $changes++
        }
    }
    catch { Write-Status ERROR "Failed: $($_.Exception.Message)" }
    try {
        $currentVal = $null
        try { $currentVal = Get-ItemPropertyValue -Path $servicePath -Name 'EnableSecuritySignature' -ErrorAction SilentlyContinue } catch { }
        if ($currentVal -eq 0) { Write-Status OK "EnableSecuritySignature already 0." }
        else {
            Set-ItemProperty -Path $servicePath -Name 'EnableSecuritySignature' -Value 0 -Type DWord -Force
            Write-Status OK "EnableSecuritySignature set to 0."
            $changes++
        }
    }
    catch { Write-Status ERROR "Failed: $($_.Exception.Message)" }
    if ($changes -gt 0) {
        Write-Status INFO "Restarting LanmanWorkstation service ($changes changes)..."
        try {
            $saveEA = $ErrorActionPreference
            $ErrorActionPreference = 'SilentlyContinue'
            $null = cmd.exe /c "net stop LanmanWorkstation /y >nul 2>&1"
            Start-Sleep -Seconds 2
            $null = cmd.exe /c "net start LanmanWorkstation >nul 2>&1"
            $null = cmd.exe /c "net start LanmanServer >nul 2>&1"
            $null = cmd.exe /c "net start Netlogon >nul 2>&1"
            $ErrorActionPreference = $saveEA
            Start-Sleep -Seconds 3
            Write-Status OK "LanmanWorkstation restarted."
        }
        catch {
            try { $ErrorActionPreference = $saveEA } catch { }
            Write-Status WARN "Service restart may need reboot."
        }
    }
    else { Write-Status OK "No changes needed." }
    $readBack = @(
        @{ Name = 'AllowInsecureGuestAuth (policy)';  Path = $policyPath;  Key = 'AllowInsecureGuestAuth'; Expected = 1 }
        @{ Name = 'AllowInsecureGuestAuth (service)'; Path = $servicePath; Key = 'AllowInsecureGuestAuth'; Expected = 1 }
        @{ Name = 'RequireSecuritySignature';         Path = $servicePath; Key = 'RequireSecuritySignature'; Expected = 0 }
        @{ Name = 'EnableSecuritySignature';          Path = $servicePath; Key = 'EnableSecuritySignature'; Expected = 0 }
    )
    Write-LoggedHost
    Write-LoggedHost "  ======================================================================" -ForegroundColor DarkCyan
    Write-LoggedHost "              SMB GUEST ACCESS VERIFICATION" -ForegroundColor Cyan
    Write-LoggedHost "  ======================================================================" -ForegroundColor DarkCyan
    foreach ($item in $readBack) {
        $val = $null
        try { $val = Get-ItemPropertyValue -Path $item.Path -Name $item.Key -ErrorAction SilentlyContinue } catch { }
        $valStr = if ($null -ne $val) { $val.ToString() } else { 'NOT SET' }
        $match = ($val -eq $item.Expected)
        $statusColor = if ($match) { 'Green' } else { 'Red' }
        $statusIcon = if ($match) { 'OK' } else { 'FAIL' }
        Write-LoggedHost "  [$statusIcon] $($item.Name): $valStr (expected: $($item.Expected))" -ForegroundColor $statusColor
    }
    Write-LoggedHost "  Changes applied: $changes" -ForegroundColor White
    Write-LoggedHost "  ======================================================================" -ForegroundColor DarkCyan
    Write-LoggedHost
    return ($changes -ge 0)
}

#===================================================================
#  SYSTEM MODEL & NETWORK SHARE PATH HELPERS
#===================================================================
function Get-SystemInfo {
    $cs = Get-CimInstance Win32_ComputerSystem
    $rawManufacturer = $cs.Manufacturer.Trim()
    $rawModel = $cs.Model.Trim()
    $cleanModel = ($rawModel -replace '\s', '').ToLower()
    return [PSCustomObject]@{
        RawManufacturer = $rawManufacturer
        RawModel        = $rawModel
        CleanModel      = $cleanModel
    }
}

function Get-ManufacturerShareFolder {
    param([Parameter(Mandatory)][string]$Manufacturer)
    foreach ($key in $Script:ManufacturerShareMap.Keys) {
        if ($Manufacturer -like "*$key*") { return $Script:ManufacturerShareMap[$key] }
    }
    return ($Manufacturer -replace '\s', '').ToLower()
}

function Build-NetworkDriverPath {
    param([Parameter(Mandatory)][string]$ManufacturerFolder, [Parameter(Mandatory)][string]$CleanModel)
    $path = Join-Path $Script:NetworkShareBase $ManufacturerFolder
    $path = Join-Path $path $Script:ShareDriverFolder
    $path = Join-Path $path $CleanModel
    return $path
}

function Test-NetworkShareAccess {
    param([Parameter(Mandatory)][string]$SharePath, [int]$MaxRetries = 10, [int]$RetryDelaySec = 10)
    Write-Status INFO "Testing access to: $SharePath"
    for ($i = 1; $i -le $MaxRetries; $i++) {
        try {
            if (Test-Path $SharePath -ErrorAction Stop) {
                Write-Status OK "Network share accessible: $SharePath"
                return $true
            }
        }
        catch { }
        if ($i -lt $MaxRetries) {
            Write-Status WARN "Attempt $i/$MaxRetries failed - waiting ${RetryDelaySec}s..."
            Start-Sleep -Seconds $RetryDelaySec
        }
    }
    Write-Status ERROR "Share not accessible after $MaxRetries attempts: $SharePath"
    return $false
}

#===================================================================
#  DEVICE SCANNING & INF MATCHING
#===================================================================
function Get-SystemDeviceIds {
    $ids = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase)
    $deviceCount = 0
    $problemDeviceNames = [System.Collections.ArrayList]::new()
    try {
        $devices = @(Get-CimInstance Win32_PnPEntity -ErrorAction Stop)
        $deviceCount = ($devices | Measure-Object).Count
        foreach ($dev in $devices) {
            if ($null -ne $dev.HardwareID) { foreach ($hwid in @($dev.HardwareID)) { if ($hwid) { [void]$ids.Add($hwid) } } }
            if ($null -ne $dev.CompatibleID) { foreach ($cid in @($dev.CompatibleID)) { if ($cid) { [void]$ids.Add($cid) } } }
            if ($null -ne $dev.ConfigManagerErrorCode -and $dev.ConfigManagerErrorCode -ne 0) {
                $devName = $dev.Name; if (-not $devName) { $devName = $dev.DeviceID }
                [void]$problemDeviceNames.Add("$devName (error $($dev.ConfigManagerErrorCode))")
            }
        }
    }
    catch { Write-Status WARN "WMI scan failed: $($_.Exception.Message)" }
    if ($ids.Count -eq 0) {
        try {
            $pnpDevs = @(Get-PnpDevice -ErrorAction SilentlyContinue)
            $deviceCount = ($pnpDevs | Measure-Object).Count
            foreach ($dev in $pnpDevs) {
                try {
                    $hwProp = $dev | Get-PnpDeviceProperty -KeyName 'DEVPKEY_Device_HardwareIds' -ErrorAction SilentlyContinue
                    if ($null -ne $hwProp -and $null -ne $hwProp.Data) { foreach ($id in @($hwProp.Data)) { if ($id) { [void]$ids.Add($id) } } }
                } catch { }
                try {
                    $cProp = $dev | Get-PnpDeviceProperty -KeyName 'DEVPKEY_Device_CompatibleIds' -ErrorAction SilentlyContinue
                    if ($null -ne $cProp -and $null -ne $cProp.Data) { foreach ($id in @($cProp.Data)) { if ($id) { [void]$ids.Add($id) } } }
                } catch { }
            }
        } catch { }
    }
    Write-Status OK "Scanned $deviceCount device(s), $($ids.Count) unique ID(s)."
    $probCount = ($problemDeviceNames | Measure-Object).Count
    if ($probCount -gt 0) {
        Write-Status WARN "$probCount device(s) with problems:"
        foreach ($pd in $problemDeviceNames) { Write-Status INFO "  - $pd" }
    }
    return $ids
}

function Get-InfHardwareIds {
    param([Parameter(Mandatory)][string]$InfPath)
    $hardwareIds = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase)
    try { $lines = @(Get-Content $InfPath -ErrorAction Stop) } catch { return @($hardwareIds) }
    $lineCount = ($lines | Measure-Object).Count
    if ($lineCount -eq 0) { return @($hardwareIds) }
    $modelSections = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase)
    $inManufacturer = $false
    foreach ($line in $lines) {
        $trimmed = $line.Trim()
        if ($trimmed.StartsWith(';')) { continue }
        if ($trimmed -match '^\[Manufacturer\]\s*$') { $inManufacturer = $true; continue }
        if ($inManufacturer) {
            if ($trimmed -match '^\[') { $inManufacturer = $false; continue }
            if (-not $trimmed) { continue }
            if ($trimmed -match '=') {
                $rightSide = ($trimmed -split '=', 2)[1]
                if ($null -eq $rightSide) { continue }
                $parts = $rightSide -split ','
                $baseName = $parts[0].Trim()
                if (-not $baseName) { continue }
                [void]$modelSections.Add($baseName)
                $partCount = ($parts | Measure-Object).Count
                for ($j = 1; $j -lt $partCount; $j++) {
                    $decoration = $parts[$j].Trim()
                    if ($decoration) { [void]$modelSections.Add("$baseName.$decoration") }
                }
            }
        }
    }
    foreach ($section in @($modelSections)) {
        $sectionHeader = "[$section]"
        $inSection = $false
        foreach ($line in $lines) {
            $trimmed = $line.Trim()
            if ($trimmed.StartsWith(';')) { continue }
            if ($trimmed -ieq $sectionHeader) { $inSection = $true; continue }
            if ($inSection) {
                if ($trimmed -match '^\[') { $inSection = $false; continue }
                if (-not $trimmed) { continue }
                if ($trimmed -match '=') {
                    $rightSide = ($trimmed -split '=', 2)[1]
                    if ($null -eq $rightSide) { continue }
                    $idParts = $rightSide -split ','
                    $idPartCount = ($idParts | Measure-Object).Count
                    for ($j = 1; $j -lt $idPartCount; $j++) {
                        $hwid = $idParts[$j].Trim().Trim('"').Trim()
                        if ($hwid -and $hwid -match '\\') { [void]$hardwareIds.Add($hwid) }
                    }
                }
            }
        }
    }
    return @($hardwareIds)
}

function Test-InfMatchesSystem {
    param([Parameter(Mandatory)][string]$InfPath, [Parameter(Mandatory)][System.Collections.Generic.HashSet[string]]$SystemIds)
    $infIds = Get-InfHardwareIds -InfPath $InfPath
    $infIdCount = ($infIds | Measure-Object).Count
    if ($infIdCount -eq 0) { return $true }
    foreach ($infId in $infIds) { if ($SystemIds.Contains($infId)) { return $true } }
    return $false
}

#===================================================================
#  STATE-MANAGEMENT
#===================================================================
function Get-ScriptState {
    if (Test-Path $Script:StateFile) {
        try {
            $json = Get-Content $Script:StateFile -Raw -ErrorAction Stop | ConvertFrom-Json
            $defaults = @{
                Phase = 0; USBCompleted = $false; USBRebootDone = $false
                NetworkShareCompleted = $false; NetworkShareRebootDone = $false
                NetworkSharePath = ""; NetworkShareProcessedDrivers = @()
                USBProcessedDrivers = @(); USBDriverRoot = ""
                ManufacturerPhase = 0; DellADRFailed = $false; LastExitCode = 0
                RebootCount = 0; TotalDriversAdded = 0; TotalDriversInstalled = 0
                TotalDriversFailed = 0; TotalDriversSkipped = 0
                TotalDriversTimedOut = 0; TotalTimeSpent = 0; TotalProcessesKilled = 0
                SystemManufacturer = ""; SystemModel = ""; CleanModel = ""
                GuestAuthConfigured = $false
            }
            foreach ($k in $defaults.Keys) {
                if (-not $json.PSObject.Properties[$k]) {
                    $json | Add-Member -NotePropertyName $k -NotePropertyValue $defaults[$k] -Force
                }
            }
            return $json
        }
        catch { Write-Status WARN "State file error - fresh start: $($_.Exception.Message)" }
    }
    return [PSCustomObject]@{
        Phase = 0; USBCompleted = $false; USBRebootDone = $false
        NetworkShareCompleted = $false; NetworkShareRebootDone = $false
        NetworkSharePath = ""; NetworkShareProcessedDrivers = @()
        USBProcessedDrivers = @(); USBDriverRoot = ""
        ManufacturerPhase = 0; DellADRFailed = $false; LastExitCode = 0
        RebootCount = 0; TotalDriversAdded = 0; TotalDriversInstalled = 0
        TotalDriversFailed = 0; TotalDriversSkipped = 0
        TotalDriversTimedOut = 0; TotalTimeSpent = 0; TotalProcessesKilled = 0
        SystemManufacturer = ""; SystemModel = ""; CleanModel = ""
        GuestAuthConfigured = $false
    }
}

function Set-ScriptState {
    param([pscustomobject]$State)
    $State | ConvertTo-Json -Depth 10 | Out-File $Script:StateFile -Encoding UTF8 -Force
}

function Remove-ScriptState {
    Write-Status INFO "Removing state and RunOnce..."
    if (Test-Path $Script:StateFile) { Remove-Item $Script:StateFile -Force -ErrorAction SilentlyContinue }
    try {
        $rk64 = [Microsoft.Win32.RegistryKey]::OpenBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, [Microsoft.Win32.RegistryView]::Registry64).OpenSubKey('SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce', $true)
        if ($null -ne $rk64) { $rk64.DeleteValue($Script:RunOnceName, $false); $rk64.Close() }
    } catch { }
    $fallback = "$($Script:RunOnceName)_Task"
    try {
        $t = Get-ScheduledTask -TaskName $fallback -ErrorAction SilentlyContinue
        if ($null -ne $t) { Unregister-ScheduledTask -TaskName $fallback -Confirm:$false -ErrorAction SilentlyContinue }
    } catch { }
    Write-Status OK "Cleanup finished."
}

#===================================================================
#  RUNONCE & REBOOT
#===================================================================
function Set-RunOnceEntry {
    param([Parameter(Mandatory)][string]$Name, [Parameter(Mandatory)][string]$Command)
    try {
        $rk64 = [Microsoft.Win32.RegistryKey]::OpenBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, [Microsoft.Win32.RegistryView]::Registry64).OpenSubKey('SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce', $true)
        $rk64.SetValue($Name, $Command, [Microsoft.Win32.RegistryValueKind]::String); $rk64.Close()
        Write-Status OK "RunOnce: $Name"; return $true
    }
    catch { Write-Status WARN "RunOnce failed: $($_.Exception.Message)"; return $false }
}

function Set-RunOnceTask {
    param([Parameter(Mandatory)][string]$Name, [Parameter(Mandatory)][string]$ScriptPath)
    try {
        $a = New-ScheduledTaskAction -Execute 'powershell.exe' -Argument "-NoLogo -NoProfile -ExecutionPolicy Bypass -File `"$ScriptPath`""
        $tr = New-ScheduledTaskTrigger -AtStartup
        $p = New-ScheduledTaskPrincipal -UserId 'SYSTEM' -RunLevel Highest -LogonType ServiceAccount
        Register-ScheduledTask -TaskName $Name -Action $a -Trigger $tr -Principal $p -Force -ErrorAction Stop
        Write-Status OK "Fallback task: $Name"; return $true
    }
    catch { Write-Status ERROR "Task failed: $($_.Exception.Message)"; return $false }
}

function Schedule-RebootAndContinue {
    param([pscustomobject]$State, [string]$Reason = "Reboot required")
    Write-LoggedHost
    Write-LoggedHost "======================================================================" -ForegroundColor Yellow
    Write-LoggedHost "  REBOOT REQUIRED: $Reason" -ForegroundColor Yellow
    Write-LoggedHost "======================================================================" -ForegroundColor Yellow
    Write-LoggedHost
    Stop-DriverInstallProcesses -Silent | Out-Null
    $State.RebootCount++; Set-ScriptState -State $State
    $cmd = "powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File `"$Script:PersistentScript`""
    $ok = Set-RunOnceEntry -Name $Script:RunOnceName -Command $cmd
    if (-not $ok) { Set-RunOnceTask -Name "$($Script:RunOnceName)_Task" -ScriptPath $Script:PersistentScript | Out-Null }
    for ($i = 15; $i -ge 1; $i--) {
        Write-LoggedHost "  Rebooting in $i seconds..." -ForegroundColor Yellow
        Start-Sleep -Seconds 1
    }
    Write-LoggedHost "  Rebooting NOW!" -ForegroundColor Red
    try { Stop-Transcript } catch { }
    Restart-Computer -Force; exit 0
}

#===================================================================
#  SINGLE DRIVER INSTALL
#===================================================================
function Install-SingleDriver {
    param(
        [Parameter(Mandatory)][string]$InfPath,
        [Parameter(Mandatory)][string]$DriverName,
        [Parameter(Mandatory)][int]$CurrentNumber,
        [Parameter(Mandatory)][int]$TotalCount,
        [int]$TimeoutSeconds = $Script:DriverTimeout
    )
    $result = [PSCustomObject]@{
        InfPath = $InfPath; DriverName = $DriverName; Success = $false
        ExitCode = -1; Added = $false; Installed = $false; AlreadyExists = $false
        NeedsReboot = $false; TimedOut = $false; ProcessesKilled = 0
        ErrorMessage = ""; Output = ""; Duration = 0
    }
    Write-DriverHeader -DriverName $DriverName -Current $CurrentNumber -Total $TotalCount
    Write-Status DRIVER "Path: $InfPath"
    Clear-DriverInstallEnvironment
    Write-Status INFO "Starting pnputil installation..."
    $stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
    $consoleWidth = Get-ConsoleWidth
    $tempScript = [IO.Path]::GetTempFileName() + ".ps1"
    $outFile = [IO.Path]::GetTempFileName()
    $exitFile = [IO.Path]::GetTempFileName()
    $wrapperContent = @"
try {
    `$out = & pnputil.exe /add-driver "$InfPath" /install 2>&1 | Out-String
    `$out | Out-File -FilePath "$outFile" -Encoding UTF8
    `$LASTEXITCODE | Out-File -FilePath "$exitFile" -Encoding UTF8
} catch {
    `$_.Exception.Message | Out-File -FilePath "$outFile" -Encoding UTF8
    "999" | Out-File -FilePath "$exitFile" -Encoding UTF8
}
"@
    $wrapperContent | Out-File -FilePath $tempScript -Encoding UTF8
    $proc = Start-Process -FilePath "powershell.exe" -ArgumentList "-ExecutionPolicy Bypass -WindowStyle Hidden -File `"$tempScript`"" -PassThru -WindowStyle Hidden
    $frameIdx = 0; $frameCount = $Script:SpinnerFrames.Length
    $animLine = 0; try { $animLine = [Console]::CursorTop } catch { }
    Write-Host "[PNPUTIL] Installing: $DriverName ($CurrentNumber/$TotalCount)" -ForegroundColor Cyan
    while ((-not $proc.HasExited) -and ($stopwatch.Elapsed.TotalSeconds -lt $TimeoutSeconds)) {
        $elapsed = $stopwatch.Elapsed.TotalSeconds
        $remaining = [math]::Max(0, $TimeoutSeconds - $elapsed)
        $pct = [math]::Round(($elapsed / $TimeoutSeconds) * 100)
        $spinner = $Script:SpinnerFrames[$frameIdx % $frameCount]; $frameIdx++
        $line = "  $spinner $pct% | Elapsed: $([math]::Round($elapsed))s | Remaining: $([math]::Round($remaining))s"
        $padded = $line.PadRight($consoleWidth)
        try {
            [Console]::SetCursorPosition(0, $animLine)
            [Console]::ForegroundColor = [ConsoleColor]::Cyan
            [Console]::Write($padded); [Console]::ResetColor()
        }
        catch { Write-Host "`r$padded" -NoNewline -ForegroundColor Cyan }
        Start-Sleep -Milliseconds 150
    }
    try { [Console]::SetCursorPosition(0, $animLine); [Console]::Write(' ' * $consoleWidth); [Console]::SetCursorPosition(0, $animLine) }
    catch { Write-Host "`r$(' ' * $consoleWidth)`r" -NoNewline }
    $stopwatch.Stop()
    $result.Duration = $stopwatch.Elapsed.TotalSeconds
    if (-not $proc.HasExited) {
        Write-Status SKIP "TIMEOUT after $(Format-Duration $TimeoutSeconds)!"
        try { $proc.Kill() } catch { }
        $result.TimedOut = $true; $result.ErrorMessage = "Timeout"
        $killed = Stop-DriverInstallProcesses; $result.ProcessesKilled = $killed
        $null = Wait-ForDriverProcesses -TimeoutSeconds 10
        Remove-Item $tempScript, $outFile, $exitFile -Force -ErrorAction SilentlyContinue
        Write-Host "[PNPUTIL] TIMEOUT for $DriverName after $TimeoutSeconds seconds" -ForegroundColor Yellow
        if ($Script:CooldownSeconds -gt 0) { Start-Sleep -Seconds $Script:CooldownSeconds }
        return $result
    }
    $out = ""; $exit = -1
    if (Test-Path $outFile) { $out = Get-Content $outFile -Raw -ErrorAction SilentlyContinue }
    if (Test-Path $exitFile) {
        $raw = Get-Content $exitFile -Raw -ErrorAction SilentlyContinue
        if ($null -ne $raw) { [int]::TryParse($raw.Trim(), [ref]$exit) | Out-Null }
    }
    Remove-Item $tempScript, $outFile, $exitFile -Force -ErrorAction SilentlyContinue
    $result.ExitCode = $exit; $result.Output = $out
    if ($null -ne $out -and $out.Trim()) {
        Write-Host "[PNPUTIL] --- Output for $DriverName ---" -ForegroundColor DarkGray
        foreach ($outLine in ($out -split "`n")) {
            $trimLine = $outLine.Trim()
            if ($trimLine) { Write-Host "[PNPUTIL] $trimLine" -ForegroundColor DarkGray }
        }
        Write-Host "[PNPUTIL] --- End output ---" -ForegroundColor DarkGray
    }
    if ($null -ne $out) {
        if ($out -match 'added|successfully added|driver package added') { $result.Added = $true; Write-Status OK "Driver added to store" }
        if ($out -match 'install.*device|installed to device') { $result.Installed = $true; Write-Status OK "Driver installed on device" }
        if ($out -match 'already exists|up to date') { $result.AlreadyExists = $true; Write-Status INFO "Already up-to-date" }
        if ($out -match 'reboot|restart') { $result.NeedsReboot = $true; Write-Status WARN "Reboot required" }
    }
    switch ($exit) {
        0    { $result.Success = $true; Write-Status OK "Exit 0 - Success" }
        1    { $result.Success = $true; Write-Status INFO "Exit 1 - Warnings" }
        259  { $result.Success = $true; Write-Status INFO "Exit 259 - Staged" }
        3010 { $result.Success = $true; $result.NeedsReboot = $true; Write-Status WARN "Exit 3010 - Reboot" }
        482  { $result.Success = $true; $result.NeedsReboot = $true; Write-Status WARN "Exit 482 - Partial" }
        default {
            if ($exit -ge 0) {
                if ($result.Added -or $result.Installed -or $result.AlreadyExists) { $result.Success = $true }
                else { $result.Success = $false; $result.ErrorMessage = "Exit $exit"; Write-Status ERROR "Exit $exit" }
            } else { $result.Success = $true }
        }
    }
    Write-Status TIME "Time: $(Format-Duration $result.Duration)"
    if ($Script:CooldownSeconds -gt 0) { Start-Sleep -Seconds $Script:CooldownSeconds }
    return $result
}

#===================================================================
#  GENERIC FOLDER INSTALLER
#===================================================================
function Install-DriversFromFolder {
    param(
        [Parameter(Mandatory)][string]$DriverRoot,
        [Parameter(Mandatory)][string]$Label,
        [string[]]$ProcessedList = @(),
        [switch]$SkipDeviceMatching,
        [System.Collections.Generic.HashSet[string]]$SystemIds = $null
    )
    $result = [PSCustomObject]@{
        Success = $false; NeedsReboot = $false; NotFound = $false
        TotalFound = 0; TotalMatched = 0; TotalSkippedNoMatch = 0
        TotalAdded = 0; TotalInstalled = 0; TotalFailed = 0
        TotalAlreadyExist = 0; TotalTimedOut = 0; TotalTime = 0; TotalKilled = 0
        ProcessedDrivers = @($ProcessedList)
    }
    $allInfFiles = @(Get-ChildItem -Path $DriverRoot -Filter "*.inf" -Recurse -File -ErrorAction SilentlyContinue | Sort-Object FullName)
    $totalFound = ($allInfFiles | Measure-Object).Count
    if ($totalFound -eq 0) {
        Write-Status WARN "No .inf files in: $DriverRoot"
        $result.NotFound = $true; $result.Success = $true; return $result
    }
    $result.TotalFound = $totalFound
    Write-Status OK "Found $totalFound INF(s) in: $DriverRoot"
    $processed = @($ProcessedList)
    $processedCount = ($processed | Measure-Object).Count
    if ($processedCount -gt 0) { Write-Status INFO "$processedCount already processed." }
    $matchedInfs = [System.Collections.ArrayList]::new()
    $skippedNoMatch = [System.Collections.ArrayList]::new()
    if ($SkipDeviceMatching) {
        Write-Status INFO "Device matching DISABLED - installing ALL."
        foreach ($inf in $allInfFiles) { [void]$matchedInfs.Add($inf) }
    }
    else {
        if ($null -eq $SystemIds -or $SystemIds.Count -eq 0) {
            Write-SubBanner "Scanning System Devices"
            $SystemIds = Get-SystemDeviceIds
        }
        if ($SystemIds.Count -eq 0) {
            Write-Status WARN "No device IDs - installing ALL as fallback."
            foreach ($inf in $allInfFiles) { [void]$matchedInfs.Add($inf) }
        }
        else {
            Write-SubBanner "Matching $Label Drivers to Devices"
            foreach ($inf in $allInfFiles) {
                $rel = $inf.FullName.Substring($DriverRoot.Length + 1)
                if ($rel -in $processed) { continue }
                if (Test-InfMatchesSystem -InfPath $inf.FullName -SystemIds $SystemIds) { [void]$matchedInfs.Add($inf) }
                else { [void]$skippedNoMatch.Add($inf); Write-Status SKIP "No match: $rel" }
            }
        }
    }
    $matchedCount = ($matchedInfs | Measure-Object).Count
    $skippedNoMatchCount = ($skippedNoMatch | Measure-Object).Count
    $result.TotalMatched = $matchedCount; $result.TotalSkippedNoMatch = $skippedNoMatchCount
    $modeLabel = if ($SkipDeviceMatching) { "ALL (no filtering)" } else { "TARGETED" }
    $modeColor = if ($SkipDeviceMatching) { 'Yellow' } else { 'Green' }
    Write-LoggedHost
    Write-LoggedHost "  ======================================================================" -ForegroundColor DarkCyan
    Write-LoggedHost "              $Label - MATCHING RESULTS" -ForegroundColor Cyan
    Write-LoggedHost "  ======================================================================" -ForegroundColor DarkCyan
    Write-LoggedHost "  Total INFs:       $totalFound" -ForegroundColor White
    Write-LoggedHost "  Processed:        $processedCount" -ForegroundColor DarkGray
    Write-LoggedHost "  Matched:          $matchedCount" -ForegroundColor Green
    Write-LoggedHost "  Skipped:          $skippedNoMatchCount" -ForegroundColor Yellow
    Write-LoggedHost "  Mode:             $modeLabel" -ForegroundColor $modeColor
    Write-LoggedHost "  ======================================================================" -ForegroundColor DarkCyan
    Write-LoggedHost
    if ($matchedCount -eq 0) {
        Write-Status OK "No new drivers to install."; $result.Success = $true; return $result
    }
    $overallSw = [System.Diagnostics.Stopwatch]::StartNew()
    $globalReboot = $false; $i = 0
    Write-SubBanner "Installing $matchedCount $Label Driver(s)"
    foreach ($inf in $matchedInfs) {
        $i++
        $rel = $inf.FullName.Substring($DriverRoot.Length + 1)
        if ($rel -in $processed) { continue }
        $drvResult = Install-SingleDriver -InfPath $inf.FullName -DriverName $rel -CurrentNumber $i -TotalCount $matchedCount
        $result.TotalTime += $drvResult.Duration; $result.TotalKilled += $drvResult.ProcessesKilled
        if ($drvResult.TimedOut) { $result.TotalTimedOut++ } elseif ($drvResult.Added) { $result.TotalAdded++ }
        if ($drvResult.Installed) { $result.TotalInstalled++ }
        if ($drvResult.AlreadyExists) { $result.TotalAlreadyExist++ }
        if ((-not $drvResult.Success) -and (-not $drvResult.TimedOut)) { $result.TotalFailed++ }
        if ($drvResult.NeedsReboot) { $globalReboot = $true }
        $processed += $rel; $result.ProcessedDrivers = @($processed)
    }
    $overallSw.Stop()
    $failColor = if ($result.TotalFailed -gt 0) { 'Red' } else { 'Green' }
    $timeoutColor = if ($result.TotalTimedOut -gt 0) { 'Yellow' } else { 'Green' }
    $killColor = if ($result.TotalKilled -gt 0) { 'Yellow' } else { 'Green' }
    $rebootColor = if ($globalReboot) { 'Yellow' } else { 'Green' }
    Write-LoggedHost
    Write-LoggedHost "======================================================================" -ForegroundColor Green
    Write-LoggedHost "              $Label DRIVER SUMMARY" -ForegroundColor Green
    Write-LoggedHost "======================================================================" -ForegroundColor Green
    Write-LoggedHost "  Found:      $($result.TotalFound)" -ForegroundColor White
    Write-LoggedHost "  Matched:    $($result.TotalMatched)" -ForegroundColor Green
    Write-LoggedHost "  Skipped:    $($result.TotalSkippedNoMatch)" -ForegroundColor Yellow
    Write-LoggedHost "  Added:      $($result.TotalAdded)" -ForegroundColor Green
    Write-LoggedHost "  Installed:  $($result.TotalInstalled)" -ForegroundColor Green
    Write-LoggedHost "  Up-to-date: $($result.TotalAlreadyExist)" -ForegroundColor Cyan
    Write-LoggedHost "  Failed:     $($result.TotalFailed)" -ForegroundColor $failColor
    Write-LoggedHost "  Timed out:  $($result.TotalTimedOut)" -ForegroundColor $timeoutColor
    Write-LoggedHost "  Killed:     $($result.TotalKilled)" -ForegroundColor $killColor
    Write-LoggedHost "  Time:       $(Format-Duration $overallSw.Elapsed.TotalSeconds)" -ForegroundColor Magenta
    Write-LoggedHost "  Reboot:     $globalReboot" -ForegroundColor $rebootColor
    Write-LoggedHost "======================================================================" -ForegroundColor Green
    Write-LoggedHost
    $result.Success = $true; $result.NeedsReboot = $globalReboot
    return $result
}

#===================================================================
#  PHASE-0A: USB DRIVERS (TARGETED)
#===================================================================
function Install-USBNetworkDrivers {
    param([pscustomobject]$State)
    Write-Banner "PHASE-0A: USB DRIVER INSTALLATION (TARGETED)"
    Write-Status STEP "Installing ONLY drivers matching this PC's devices from USB."
    Clear-DriverInstallEnvironment
    $root = $null
    if ($State.USBDriverRoot -and (Test-Path $State.USBDriverRoot)) {
        $root = $State.USBDriverRoot
    }
    else {
        foreach ($drive in @('D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z')) {
            foreach ($folder in $Script:USBDriverFolders) {
                $candidate = "${drive}:\$folder"
                if (Test-Path $candidate) { $root = $candidate; Write-Status OK "Found USB driver folder: $root"; break }
            }
            if ($null -ne $root) { break }
        }
    }
    if ($null -eq $root) {
        Write-Status INFO "No USB driver folder found - skipping USB phase."
        return [PSCustomObject]@{ Success = $true; NeedsReboot = $false; NotFound = $true }
    }
    $State.USBDriverRoot = $root; Set-ScriptState -State $State
    Write-SubBanner "Scanning System Devices for USB Driver Matching"
    $systemIds = Get-SystemDeviceIds
    Write-Status INFO "Will match USB INF files against $($systemIds.Count) hardware ID(s)."
    $processed = @(); if ($State.USBProcessedDrivers) { $processed = @($State.USBProcessedDrivers) }
    $usbResult = Install-DriversFromFolder -DriverRoot $root -Label "USB" -ProcessedList $processed -SystemIds $systemIds
    $State.USBProcessedDrivers     = $usbResult.ProcessedDrivers
    $State.TotalDriversAdded      += $usbResult.TotalAdded
    $State.TotalDriversInstalled  += $usbResult.TotalInstalled
    $State.TotalDriversFailed     += $usbResult.TotalFailed
    $State.TotalDriversSkipped    += $usbResult.TotalSkippedNoMatch
    $State.TotalDriversTimedOut   += $usbResult.TotalTimedOut
    $State.TotalTimeSpent         += $usbResult.TotalTime
    $State.TotalProcessesKilled   += $usbResult.TotalKilled
    Set-ScriptState -State $State
    return $usbResult
}

#===================================================================
#  PHASE-0B: NETWORK SHARE DRIVERS
#===================================================================
function Install-NetworkShareDrivers {
    param([pscustomobject]$State)
    Write-Banner "PHASE-0B: NETWORK SHARE DRIVER INSTALLATION"
    if (-not $State.GuestAuthConfigured) {
        Enable-GuestNetworkAccess | Out-Null
        $State.GuestAuthConfigured = $true; Set-ScriptState -State $State
    }
    else { Write-Status OK "SMB guest access already configured." }
    Write-SubBanner "Identifying System Model"
    $sysInfo = Get-SystemInfo
    $State.SystemManufacturer = $sysInfo.RawManufacturer
    $State.SystemModel = $sysInfo.RawModel
    $State.CleanModel = $sysInfo.CleanModel
    Set-ScriptState -State $State
    Write-Status INFO "Manufacturer: $($sysInfo.RawManufacturer)"
    Write-Status INFO "Model:        $($sysInfo.RawModel)"
    Write-Status INFO "Clean model:  $($sysInfo.CleanModel)"
    $mfgFolder = Get-ManufacturerShareFolder -Manufacturer $sysInfo.RawManufacturer
    $sharePath = ""
    if ($State.NetworkSharePath -and (Test-Path $State.NetworkSharePath -ErrorAction SilentlyContinue)) {
        $sharePath = $State.NetworkSharePath
    }
    else { $sharePath = Build-NetworkDriverPath -ManufacturerFolder $mfgFolder -CleanModel $sysInfo.CleanModel }
    Write-LoggedHost
    Write-LoggedHost "  ======================================================================" -ForegroundColor DarkCyan
    Write-LoggedHost "              NETWORK SHARE PATH" -ForegroundColor Cyan
    Write-LoggedHost "  ======================================================================" -ForegroundColor DarkCyan
    Write-LoggedHost "  Base:    $($Script:NetworkShareBase)" -ForegroundColor White
    Write-LoggedHost "  Mfg:     $mfgFolder" -ForegroundColor White
    Write-LoggedHost "  Model:   $($sysInfo.CleanModel)" -ForegroundColor White
    Write-LoggedHost "  Path:    $sharePath" -ForegroundColor Green
    Write-LoggedHost "  ======================================================================" -ForegroundColor DarkCyan
    Write-LoggedHost
    Write-SubBanner "Testing Network Share Access"
    $shareOk = Test-NetworkShareAccess -SharePath $sharePath -MaxRetries 10 -RetryDelaySec 10
    if (-not $shareOk) {
        $alternatives = @(($sysInfo.RawModel -replace '\s', '-').ToLower(), ($sysInfo.RawModel -replace '\s', '_').ToLower(), $sysInfo.RawModel.ToLower())
        $altFound = $false
        foreach ($alt in $alternatives) {
            $altPath = Build-NetworkDriverPath -ManufacturerFolder $mfgFolder -CleanModel $alt
            Write-Status INFO "Trying: $altPath"
            if (Test-Path $altPath -ErrorAction SilentlyContinue) {
                $sharePath = $altPath; Write-Status OK "Found: $sharePath"; $altFound = $true; break
            }
        }
        if (-not $altFound) {
            Write-Status ERROR "No share folder found. Expected: $sharePath"
            return [PSCustomObject]@{ Success = $true; NeedsReboot = $false; NotFound = $true }
        }
    }
    $State.NetworkSharePath = $sharePath; Set-ScriptState -State $State
    $processed = @(); if ($State.NetworkShareProcessedDrivers) { $processed = @($State.NetworkShareProcessedDrivers) }
    Write-SubBanner "Scanning Devices"
    $systemIds = Get-SystemDeviceIds
    $netResult = Install-DriversFromFolder -DriverRoot $sharePath -Label "NETWORK SHARE" -ProcessedList $processed -SystemIds $systemIds
    $State.NetworkShareProcessedDrivers = $netResult.ProcessedDrivers
    $State.TotalDriversAdded      += $netResult.TotalAdded
    $State.TotalDriversInstalled  += $netResult.TotalInstalled
    $State.TotalDriversFailed     += $netResult.TotalFailed
    $State.TotalDriversSkipped    += $netResult.TotalSkippedNoMatch
    $State.TotalDriversTimedOut   += $netResult.TotalTimedOut
    $State.TotalTimeSpent         += $netResult.TotalTime
    $State.TotalProcessesKilled   += $netResult.TotalKilled
    Set-ScriptState -State $State
    return $netResult
}

#===================================================================
#  INTERNET & MANUFACTURER
#===================================================================
function Test-InternetConnectivity {
    param([int]$MaxRetries = 15, [int]$RetryDelay = 10)
    Write-Banner "CHECKING INTERNET CONNECTIVITY"
    $w = Get-ConsoleWidth; $totalFrames = $Script:SpinnerFrames.Length
    for ($i = 1; $i -le $MaxRetries; $i++) {
        Write-Status PROGRESS "Attempt $i of $MaxRetries..."
        try {
            $r = Invoke-WebRequest -Uri "https://www.google.com" -Method Head -UseBasicParsing -TimeoutSec 10 -ErrorAction Stop
            if ($r.StatusCode -eq 200) { Write-Status OK "Internet reachable."; return $true }
        }
        catch {
            Write-Status WARN "Attempt $i failed: $($_.Exception.Message)"
            if ($i -lt $MaxRetries) {
                for ($s = $RetryDelay; $s -ge 1; $s--) {
                    $spinner = $Script:SpinnerFrames[($RetryDelay - $s) % $totalFrames]
                    $pad = "  $spinner Waiting $s sec...".PadRight($w)
                    try { [Console]::SetCursorPosition(0, [Console]::CursorTop); [Console]::ForegroundColor = [ConsoleColor]::DarkGray; [Console]::Write($pad); [Console]::ResetColor() }
                    catch { Write-Host "`r$pad" -NoNewline -ForegroundColor DarkGray }
                    Start-Sleep -Seconds 1
                }
            }
        }
    }
    Write-Status ERROR "No internet after $MaxRetries attempts."
    return $false
}

function Get-SystemManufacturer {
    Write-SubBanner "Detecting System Manufacturer"
    $cs = Get-CimInstance Win32_ComputerSystem
    Write-Status INFO "Manufacturer: $($cs.Manufacturer.Trim())"
    Write-Status INFO "Model:        $($cs.Model.Trim())"
    return $cs.Manufacturer.Trim()
}

#===================================================================
#  CHOCOLATEY
#===================================================================
function Set-ChocolateySource {
    Write-SubBanner "Configuring Chocolatey Sources"
    $chocoExe = Get-ChocoExePath; if (-not $chocoExe) { return }
    $null = Invoke-CommandVerbose -FilePath $chocoExe -Arguments "source add -n `"$($Script:ChocoSourceName)`" -s `"$($Script:ChocoSourceUrl)`" --priority=1 --allow-self-service" -Label "CHOCO"
    $null = Invoke-CommandVerbose -FilePath $chocoExe -Arguments "source disable -n `"chocolatey`"" -Label "CHOCO"
    $null = Invoke-CommandVerbose -FilePath $chocoExe -Arguments "source list" -Label "CHOCO"
}

function Install-ChocolateyIfNeeded {
    Write-SubBanner "Ensuring Chocolatey"
    if (-not (Get-Command choco -ErrorAction SilentlyContinue)) {
        Write-Status INFO "Installing Chocolatey..."
        Write-Host "[CHOCO] Installing Chocolatey from community.chocolatey.org..." -ForegroundColor Cyan
        Set-ExecutionPolicy Bypass -Scope Process -Force
        [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor 3072
        Invoke-Expression (Invoke-RestMethod https://community.chocolatey.org/install.ps1)
        $env:Path = [System.Environment]::GetEnvironmentVariable('Path', 'Machine') + ';' + [System.Environment]::GetEnvironmentVariable('Path', 'User')
        Write-Status OK "Chocolatey installed."
    }
    else { Write-Status OK "Chocolatey present." }
    Set-ChocolateySource
}

function Install-ChocoPackage {
    param([Parameter(Mandatory)][string]$PackageName, [string]$DisplayName)
    if (-not $DisplayName) { $DisplayName = $PackageName }
    $chocoExe = Get-ChocoExePath; if (-not $chocoExe) { return $false }
    Write-SubBanner "Installing $DisplayName via Chocolatey"
    $exitCode = Invoke-CommandVerbose -FilePath $chocoExe -Arguments "install $PackageName -y --source=`"$($Script:ChocoSourceName)`"" -Label "CHOCO"
    if ($exitCode -ne 0) {
        Write-Status WARN "Custom source failed - trying default..."
        $exitCode = Invoke-CommandVerbose -FilePath $chocoExe -Arguments "install $PackageName -y" -Label "CHOCO"
    }
    if ($exitCode -eq 0) { Write-Status OK "$DisplayName installed."; return $true }
    else { Write-Status ERROR "$DisplayName failed."; return $false }
}

#===================================================================
#  DELL UPDATES
#===================================================================
function Find-DellCLI {
    $knownPaths = @(
        'C:\Program Files\Dell\CommandUpdate\dcu-cli.exe'
        'C:\Program Files (x86)\Dell\CommandUpdate\dcu-cli.exe'
        'C:\Program Files\Dell\Dell Command Update\dcu-cli.exe'
        'C:\Program Files (x86)\Dell\Dell Command Update\dcu-cli.exe'
    )
    return Find-Executable -FileName 'dcu-cli.exe' -KnownPaths $knownPaths -SearchRoots @('C:\Dell') -Label "Dell Command Update CLI"
}

function Invoke-DellUpdates {
    param([pscustomobject]$State)
    Write-Banner "DELL SYSTEM UPDATE"

    $cli = Find-DellCLI
    if (-not $cli) {
        Install-ChocoPackage -PackageName "dellcommandupdate" -DisplayName "Dell Command Update" | Out-Null
        Start-Sleep -Seconds 5
        $cli = Find-DellCLI
    }
    if (-not $cli) { Write-Status ERROR "Dell CLI not found anywhere."; return $false }
    Write-Status OK "Dell CLI: $cli"

    switch ($State.ManufacturerPhase) {
        0 {
            if ($State.DellADRFailed) { $State.ManufacturerPhase = 2; Set-ScriptState -State $State; return (Invoke-DellUpdates -State $State) }
            Write-SubBanner "Dell Phase-0: Configure ADR"
            $null = Invoke-CommandVerbose -FilePath $cli -Arguments "/configure -advancedDriverRestore=enable" -Label "DCU-CFG"
            Write-SubBanner "Dell Phase-0: ADR Driver Install"
            Write-Status STEP "All driver names and progress will appear below."
            $dellExit = Invoke-CommandVerbose -FilePath $cli -Arguments "/driverinstall" -Label "DCU"
            switch ($dellExit) {
                0 { $State.ManufacturerPhase = 2; Set-ScriptState -State $State; return (Invoke-DellUpdates -State $State) }
                1 { $State.ManufacturerPhase = 1; Schedule-RebootAndContinue -State $State -Reason "Dell ADR (exit 1)" }
                5 { $State.ManufacturerPhase = 1; Schedule-RebootAndContinue -State $State -Reason "Dell ADR (exit 5)" }
                2 { $State.DellADRFailed = $true; $State.ManufacturerPhase = 2; Set-ScriptState -State $State; return (Invoke-DellUpdates -State $State) }
                3 { $State.ManufacturerPhase = 2; Set-ScriptState -State $State; return (Invoke-DellUpdates -State $State) }
                default { $State.DellADRFailed = $true; $State.ManufacturerPhase = 2; Set-ScriptState -State $State; return (Invoke-DellUpdates -State $State) }
            }
        }
        1 {
            Write-SubBanner "Dell Phase-1: Post-Reboot Scan"
            $null = Invoke-CommandVerbose -FilePath $cli -Arguments "/scan" -Label "DCU"
            $State.ManufacturerPhase = 2; Set-ScriptState -State $State; return (Invoke-DellUpdates -State $State)
        }
        2 {
            Write-SubBanner "Dell Phase-2: Apply All Updates"
            Write-Status STEP "Each update name, download, and install status shown below."
            $dellExit = Invoke-CommandVerbose -FilePath $cli -Arguments "/applyupdates -forceUpdate=enable -autoSuspendBitLocker=enable" -Label "DCU"
            switch ($dellExit) {
                0 { Write-Status OK "All updates applied."; $State.ManufacturerPhase = 3; Set-ScriptState -State $State; return $true }
                1 { $State.ManufacturerPhase = 3; Schedule-RebootAndContinue -State $State -Reason "Dell updates (exit 1)" }
                5 { $State.ManufacturerPhase = 3; Schedule-RebootAndContinue -State $State -Reason "Dell updates (exit 5)" }
                3 { Write-Status OK "Already up to date."; $State.ManufacturerPhase = 3; Set-ScriptState -State $State; return $true }
                default { Write-Status WARN "Exit $dellExit - continuing."; $State.ManufacturerPhase = 3; Set-ScriptState -State $State; return $true }
            }
        }
        default { Write-Status OK "Dell updates done."; return $true }
    }
    return $true
}

#===================================================================
#  HP UPDATES (FIXED - robust exe discovery)
#===================================================================
function Find-HPIA {
    $knownPaths = @(
        # Chocolatey install location (priority - most common after choco install)
        "$env:ProgramData\chocolatey\lib\hpimageassistant\tools\HPImageAssistant.exe"
        # Standard HP install locations
        'C:\HP\HPIA\HPImageAssistant.exe'
        'C:\Program Files\HP\HPIA\HPImageAssistant.exe'
        'C:\Program Files (x86)\HP\HPIA\HPImageAssistant.exe'
        'C:\SWSetup\HPIA\HPImageAssistant.exe'
        'C:\Program Files\HPImageAssistant\HPImageAssistant.exe'
        'C:\Program Files (x86)\HPImageAssistant\HPImageAssistant.exe'
        "$env:ProgramData\HP\HPIA\HPImageAssistant.exe"
        'C:\HP\HPImageAssistant\HPImageAssistant.exe'
    )
    return Find-Executable -FileName 'HPImageAssistant.exe' -KnownPaths $knownPaths -SearchRoots @('C:\HP', 'C:\SWSetup') -Label "HP Image Assistant"
}

function Invoke-HPUpdates {
    param([pscustomobject]$State)
    Write-Banner "HP SYSTEM UPDATE"

    # ── Find HPIA before Chocolatey ──
    Write-SubBanner "Locating HP Image Assistant"
    $hpia = Find-HPIA

    # ── Try Chocolatey install if not found ──
    if (-not $hpia) {
        Write-Status WARN "HPIA not found - attempting Chocolatey install..."

        # Try multiple known package names
        $chocoPackages = @('hpimageassistant', 'hp-hpia', 'hp-image-assistant')
        $installed = $false
        foreach ($pkg in $chocoPackages) {
            Write-Status INFO "Trying Chocolatey package: $pkg"
            $result = Install-ChocoPackage -PackageName $pkg -DisplayName "HP Image Assistant ($pkg)"
            if ($result) { $installed = $true; break }
        }

        if ($installed) {
            Start-Sleep -Seconds 5
            # Refresh PATH
            $env:Path = [System.Environment]::GetEnvironmentVariable('Path', 'Machine') + ';' + [System.Environment]::GetEnvironmentVariable('Path', 'User')
            $hpia = Find-HPIA
        }
    }

    # ── Final validation ──
    if (-not $hpia) {
        Write-Status ERROR "HP Image Assistant (HPImageAssistant.exe) could not be found anywhere."
        Write-Status INFO "Searched: known paths, Chocolatey lib, Program Files, C:\HP, C:\SWSetup"
        Write-Status INFO "Please install HPIA manually and re-run the script."
        return $false
    }

    # Verify it is a file, not a directory
    $hpiaItem = Get-Item $hpia -ErrorAction SilentlyContinue
    if ($null -eq $hpiaItem -or $hpiaItem.PSIsContainer) {
        Write-Status ERROR "Path is a directory, not an executable: $hpia"
        return $false
    }

    Write-Status OK  "HPIA executable: $hpia"
    Write-Status DEBUG "HPIA size: $([math]::Round($hpiaItem.Length / 1MB, 1)) MB"
    Write-Status DEBUG "HPIA version: $($hpiaItem.VersionInfo.FileVersion)"

    # ── Create report folder ──
    $hpiaReport = 'C:\Temp\HPIA'
    if (-not (Test-Path $hpiaReport)) {
        New-Item -Path $hpiaReport -ItemType Directory -Force | Out-Null
    }

    # ── Run HPIA ──
    Write-SubBanner "HP Image Assistant: Analyze & Install"
    Write-Status STEP "All update names and progress will appear below."

    $hpArgs = "/Operation:Analyze /Action:Install /Selection:All /Silent /Noninteractive /ReportFolder:`"$hpiaReport`""
    $hpExit = Invoke-CommandVerbose -FilePath $hpia -Arguments $hpArgs -Label "HPIA"

    # ── Report results ──
    Write-LoggedHost
    Write-LoggedHost "  ======================================================================" -ForegroundColor DarkCyan
    Write-LoggedHost "              HP IMAGE ASSISTANT RESULTS" -ForegroundColor Cyan
    Write-LoggedHost "  ======================================================================" -ForegroundColor DarkCyan
    Write-LoggedHost "  Executable:  $hpia" -ForegroundColor White
    Write-LoggedHost "  Exit code:   $hpExit" -ForegroundColor White
    Write-LoggedHost "  Report:      $hpiaReport" -ForegroundColor White

    # Parse report if available
    $jsonReport = Get-ChildItem -Path $hpiaReport -Filter '*.json' -File -ErrorAction SilentlyContinue | Sort-Object LastWriteTime -Descending | Select-Object -First 1
    if ($jsonReport) {
        Write-LoggedHost "  Report file: $($jsonReport.FullName)" -ForegroundColor Green
        try {
            $reportData = Get-Content $jsonReport.FullName -Raw -ErrorAction SilentlyContinue | ConvertFrom-Json -ErrorAction SilentlyContinue
            if ($null -ne $reportData -and $null -ne $reportData.HPIA -and $null -ne $reportData.HPIA.Recommendations) {
                $recs = @($reportData.HPIA.Recommendations)
                $recCount = ($recs | Measure-Object).Count
                Write-LoggedHost "  Updates:     $recCount recommendation(s)" -ForegroundColor White
                foreach ($rec in $recs) {
                    $name = if ($rec.Name) { $rec.Name } else { $rec.SoftPaqId }
                    $status = if ($rec.RecommendationValue) { $rec.RecommendationValue } else { 'Unknown' }
                    Write-LoggedHost "    - $name ($status)" -ForegroundColor DarkGray
                }
            }
        }
        catch { Write-Status DEBUG "Could not parse HPIA report: $($_.Exception.Message)" }
    }
    else {
        Write-LoggedHost "  Report file: (none found)" -ForegroundColor Yellow
    }
    Write-LoggedHost "  ======================================================================" -ForegroundColor DarkCyan
    Write-LoggedHost

    switch ($hpExit) {
        0    { Write-Status OK "HP updates completed successfully."; return $true }
        256  { Write-Status WARN "HP: reboot needed for some updates."; return $true }
        3010 { Write-Status WARN "HP: reboot needed (exit 3010)."; return $true }
        1    { Write-Status WARN "HP: completed with warnings."; return $true }
        default { Write-Status WARN "HP exit $hpExit - check report at $hpiaReport"; return $true }
    }
}

#===================================================================
#  MAIN EXECUTION
#===================================================================
try {
    if (-not (Initialize-PersistentScript)) { throw "Failed to initialise." }

    Start-Transcript -Path $Script:LogFile -Append -Force

    $sysInfoHeader = Get-SystemInfo
    $mfg = Get-ManufacturerShareFolder -Manufacturer $sysInfoHeader.RawManufacturer
    $expectedPath = Build-NetworkDriverPath -ManufacturerFolder $mfg -CleanModel $sysInfoHeader.CleanModel

    $headerLines = @(
        "======================================================================"
        "       DRIVER UPDATE SCRIPT - PowerShell $($PSVersionTable.PSVersion)"
        "======================================================================"
        "  User:          $env:USERNAME"
        "  Computer:      $env:COMPUTERNAME"
        "  Manufacturer:  $($sysInfoHeader.RawManufacturer)"
        "  Model:         $($sysInfoHeader.RawModel)"
        "  Clean model:   $($sysInfoHeader.CleanModel)"
        "  Driver share:  $expectedPath"
        "  Timeout:       $Script:DriverTimeout sec/driver"
        "  Choco source:  $Script:ChocoSourceName"
        "  Flow:          USB(targeted) -> GuestAuth -> Share(targeted) -> OEM(online)"
        "======================================================================"
    )
    Write-LoggedHost
    foreach ($hl in $headerLines) {
        Write-LoggedHost $hl -ForegroundColor Cyan
    }
    Write-LoggedHost

    Write-Status INFO "Checking for orphan processes..."
    $orphanKilled = Stop-DriverInstallProcesses -Silent
    if ($orphanKilled -gt 0) { Write-Status WARN "Terminated $orphanKilled."; Start-Sleep -Seconds 2 }

    $state = Get-ScriptState
    Write-Host "--- Current State ---" -ForegroundColor DarkGray
    foreach ($prop in $state.PSObject.Properties) {
        $stLine = "  {0,-35}: {1}" -f $prop.Name, $prop.Value
        Write-Host $stLine -ForegroundColor DarkGray
    }
    Write-Host "--- End State ---" -ForegroundColor DarkGray
    Write-Host ""

    # PHASE-0A: USB drivers (TARGETED)
    if ($state.Phase -eq 0) {
        if (-not $state.USBCompleted) {
            $usbResult = Install-USBNetworkDrivers -State $state
            if ($usbResult.NeedsReboot) {
                $state.USBCompleted = $true; $state.USBRebootDone = $false; Set-ScriptState -State $state
                Schedule-RebootAndContinue -State $state -Reason "USB drivers - reboot"
            }
            else { $state.USBCompleted = $true; $state.USBRebootDone = $true; Set-ScriptState -State $state }
        }
        else {
            if (-not $state.USBRebootDone) {
                Write-Status OK "Post-reboot: USB drivers finalized."
                $state.USBRebootDone = $true; Set-ScriptState -State $state
            }
        }
        # PHASE-0B: Network share drivers (TARGETED)
        if (-not $state.NetworkShareCompleted) {
            $netResult = Install-NetworkShareDrivers -State $state
            if ($netResult.NeedsReboot) {
                $state.NetworkShareCompleted = $true; $state.NetworkShareRebootDone = $false; Set-ScriptState -State $state
                Schedule-RebootAndContinue -State $state -Reason "Network share drivers - reboot"
            }
            else { $state.NetworkShareCompleted = $true; $state.NetworkShareRebootDone = $true; $state.Phase = 1; Set-ScriptState -State $state }
        }
        else {
            if (-not $state.NetworkShareRebootDone) {
                Write-Status OK "Post-reboot: Share drivers finalized."
                $state.NetworkShareRebootDone = $true
            }
            $state.Phase = 1; Set-ScriptState -State $state
        }
    }

    # PHASE-1: Online OEM updates
    if ($state.Phase -ge 1) {
        Write-Banner "PHASE 1: ONLINE OEM UPDATES"
        if (-not (Test-InternetConnectivity -MaxRetries 15 -RetryDelay 10)) {
            Write-Status ERROR "No internet."; try { Stop-Transcript } catch { }; exit 1
        }
        Install-ChocolateyIfNeeded
        $manufacturer = Get-SystemManufacturer
        if ($manufacturer -like '*Dell*') { Invoke-DellUpdates -State $state | Out-Null }
        elseif ($manufacturer -match 'HP|Hewlett[-\s]?Packard') { Invoke-HPUpdates -State $state | Out-Null }
        else { Write-Status WARN "Unsupported: $manufacturer" }
    }

    # FINAL SUMMARY
    Stop-DriverInstallProcesses -Silent | Out-Null
    $summaryLines = @(
        "======================================================================"
        "      DRIVER UPDATE SCRIPT COMPLETED SUCCESSFULLY!"
        "======================================================================"
        "  System:          $($state.SystemManufacturer) $($state.SystemModel)"
        "  Clean model:     $($state.CleanModel)"
        "  Share path:      $($state.NetworkSharePath)"
        "  Guest auth:      $($state.GuestAuthConfigured)"
        "  Reboots:         $($state.RebootCount)"
        "  Drivers added:   $($state.TotalDriversAdded)"
        "  Installed:       $($state.TotalDriversInstalled)"
        "  Skipped:         $($state.TotalDriversSkipped)"
        "  Failed:          $($state.TotalDriversFailed)"
        "  Timed out:       $($state.TotalDriversTimedOut)"
        "  Killed:          $($state.TotalProcessesKilled)"
        "  Total time:      $(Format-Duration $state.TotalTimeSpent)"
        "======================================================================"
    )
    Write-LoggedHost
    foreach ($sl in $summaryLines) {
        Write-LoggedHost $sl -ForegroundColor Green
    }
    Write-LoggedHost
    Remove-ScriptState
}
catch {
    Write-LoggedHost
    Write-LoggedHost "======================================================================" -ForegroundColor Red
    Write-LoggedHost "  SCRIPT ERROR" -ForegroundColor Red
    Write-LoggedHost "======================================================================" -ForegroundColor Red
    Write-Status ERROR "Message: $($_.Exception.Message)"
    Write-Status ERROR "Line:    $($_.InvocationInfo.ScriptLineNumber)"
    $stackStr = $_.ScriptStackTrace
    Write-Host $stackStr -ForegroundColor Red
    Write-LoggedHost
    Write-Status INFO "Emergency clean-up..."
    try {
        $saveEA = $ErrorActionPreference; $ErrorActionPreference = 'SilentlyContinue'
        Stop-DriverInstallProcesses -Silent | Out-Null
        $ErrorActionPreference = $saveEA
    }
    catch { try { $ErrorActionPreference = $saveEA } catch { } }
    Write-Status INFO "State file kept - re-run to continue."
}
finally {
    try { Stop-Transcript } catch { }
}

Visual example at runtime

======================================================================
       DRIVER UPDATE SCRIPT - PowerShell 5.1.19041.4291
======================================================================
  Manufacturer:  Dell Inc.
  Model:         Latitude 5400
  Clean model:   latitude5400
  Driver share:  \\networkshare\dell\driver\latitude5400
  Mode:          USB(NIC) -> NetworkShare(all) -> OEM(online)
======================================================================

======================================================================
  PHASE-0A: USB NETWORK DRIVER INSTALLATION
======================================================================
[>] Installing NIC/WiFi drivers from USB to enable network connectivity.
[+] Found USB driver folder: E:\DriversOS
[+] Found 3 INF file(s) in: E:\DriversOS
[i] Device matching DISABLED for USB NETWORK - installing ALL drivers.

  ======================================================================
              USB NETWORK - DRIVER MATCHING RESULTS
  ======================================================================
  Total INF files:              3
  Matched to system devices:    3
  Mode:                         ALL (no filtering)
  ======================================================================

  [####################] 100% (3 of 3 drivers)
  Driver: Intel-I219LM\e1d68x64.inf
  [+] Exit code 0 - Success

======================================================================
  PHASE-0B: NETWORK SHARE DRIVER INSTALLATION
======================================================================

--- Identifying System Model ---
[i] Manufacturer:  Dell Inc.
[i] Model:         Latitude 5400
[i] Clean model:   latitude5400
[i] Share folder:  dell

  ======================================================================
              NETWORK SHARE PATH RESOLUTION
  ======================================================================
  Base:           \\networkshare
  Manufacturer:   dell
  Driver folder:  driver
  Model:          latitude5400
  Full path:      \\networkshare\dell\driver\latitude5400
  ======================================================================

--- Testing Network Share Access ---
[i] NIC drivers should be installed from USB - testing network...
[+] Network share accessible: \\networkshare\dell\driver\latitude5400

--- Matching NETWORK SHARE Drivers to System Devices ---
[+] Scanned 45 device(s), found 312 unique hardware/compatible ID(s).
[S] No matching device: Chipset\IntelMEI\heci.inf
[S] No matching device: Chipset\AMT\LMS.inf
... (only unmatched shown)

  ======================================================================
              NETWORK SHARE - DRIVER MATCHING RESULTS
  ======================================================================
  Total INF files:              85
  Matched to system devices:    22
  Skipped (no matching device): 63
  Mode:                         TARGETED (matched only)
  ======================================================================

--- Installing 22 NETWORK SHARE Driver(s) ---
  [#---------] 5% (1 of 22 drivers)
  Driver: Video\Intel-UHD620\igdlh64.inf
  ...

Powershell script that searches for the drivers in the USB

#Requires -RunAsAdministrator
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

#===================================================================
#  CONFIGURATION
#===================================================================
$Script:TempFolder       = "C:\Temp"
$Script:PersistentScript = "C:\Temp\DriverUpdateScript.ps1"
$Script:StateFile        = "C:\Temp\DriverUpdateState.json"
$Script:LogFile          = "C:\Temp\DriverUpdateScript.log"
$Script:RunOnceKey       = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce'
$Script:RunOnceName      = 'DriverUpdateScript_RunOnce'
$Script:DriverTimeout    = 300
$Script:CooldownSeconds  = 5
$Script:DriverProcesses  = @('drvinst','pnputil','DPInst','DPInst64','SetupAPI')
$Script:SpinnerFrames    = @('[ ]','[= ]','[== ]','[=== ]','[====]','[ ===]','[ ==]','[ =]')
$Script:ChocoSourceName  = 'chocosia'
$Script:ChocoSourceUrl   = 'http://choco.local.xyz.com/repository/chocolatey-group'

#===================================================================
#  INITIALISATION
#===================================================================
function Initialize-PersistentScript {
    if (-not (Test-Path $Script:TempFolder)) {
        New-Item -Path $Script:TempFolder -ItemType Directory -Force | Out-Null
    }
    $currentScript = $MyInvocation.ScriptName
    if (-not $currentScript) { $currentScript = $PSCommandPath }
    if ($currentScript -and $currentScript -ne $Script:PersistentScript) {
        if (Test-Path $currentScript) {
            Write-Host "[INIT] Copying script to $Script:PersistentScript" -ForegroundColor Yellow
            Copy-Item -Path $currentScript -Destination $Script:PersistentScript -Force
            Write-Host "[INIT] Script copied successfully." -ForegroundColor Green
        }
    }
    if (-not (Test-Path $Script:PersistentScript)) {
        Write-Host "[ERROR] Persistent script could not be created." -ForegroundColor Red
        return $false
    }
    return $true
}

#===================================================================
#  LOGGING HELPERS
#===================================================================
function Write-Status {
    param(
        [Parameter(Mandatory)]
        [ValidateSet('ERROR','WARN','INFO','STEP','OK','PROGRESS','DEBUG','DRIVER','TIME','SKIP','KILL')]
        [string]$Level,
        [Parameter(Mandatory)][string]$Message
    )
    $timestamp = Get-Date -Format "HH:mm:ss"
    $color = switch ($Level.ToUpper()) {
        'ERROR'    { 'Red' }
        'WARN'     { 'Yellow' }
        'INFO'     { 'Cyan' }
        'STEP'     { 'Magenta' }
        'OK'       { 'Green' }
        'PROGRESS' { 'White' }
        'DEBUG'    { 'DarkGray' }
        'DRIVER'   { 'Blue' }
        'TIME'     { 'DarkYellow' }
        'SKIP'     { 'DarkMagenta' }
        'KILL'     { 'Red' }
        default    { 'White' }
    }
    $prefix = switch ($Level.ToUpper()) {
        'ERROR'    { '[X]' }
        'WARN'     { '[!]' }
        'INFO'     { '[i]' }
        'STEP'     { '[>]' }
        'OK'       { '[+]' }
        'PROGRESS' { '[o]' }
        'DEBUG'    { '[.]' }
        'DRIVER'   { '[D]' }
        'TIME'     { '[T]' }
        'SKIP'     { '[S]' }
        'KILL'     { '[K]' }
        default    { '[ ]' }
    }
    Write-Host "[$timestamp] $prefix $Message" -ForegroundColor $color
}

function Write-Banner {
    param([string]$Text)
    Write-Host ""
    Write-Host ("=" * 70) -ForegroundColor Magenta
    Write-Host "  $Text" -ForegroundColor Magenta
    Write-Host ("=" * 70) -ForegroundColor Magenta
    Write-Host ""
}

function Write-SubBanner {
    param([string]$Text)
    Write-Host ""
    Write-Host "--- $Text ---" -ForegroundColor Yellow
    Write-Host ""
}

function Format-Duration {
    param([double]$Seconds)
    if ($Seconds -lt 60) {
        return "{0:N1}s" -f $Seconds
    }
    elseif ($Seconds -lt 3600) {
        $m = [math]::Floor($Seconds / 60)
        $s = $Seconds % 60
        return "{0}m {1:N0}s" -f $m, $s
    }
    else {
        $h = [math]::Floor($Seconds / 3600)
        $m = [math]::Floor(($Seconds % 3600) / 60)
        return "{0}h {1}m" -f $h, $m
    }
}

function Get-ConsoleWidth {
    $w = 100
    try {
        $w = [Console]::WindowWidth - 5
        if ($w -lt 50) { $w = 100 }
    }
    catch { }
    return $w
}

function Write-DriverHeader {
    param([string]$DriverName, [int]$Current, [int]$Total)
    if ($Total -le 0) { $Total = 1 }
    $pct = [math]::Round(($Current / $Total) * 100)
    $filled = [math]::Floor($pct / 5)
    $empty = 20 - $filled
    $bar = "[" + ("#" * $filled) + ("-" * $empty) + "]"
    Write-Host ""
    Write-Host "  ======================================================================" -ForegroundColor DarkCyan
    Write-Host "  $bar $pct% ($Current of $Total drivers)" -ForegroundColor Cyan
    Write-Host "  Driver: $DriverName" -ForegroundColor White
    Write-Host "  Timeout: $Script:DriverTimeout seconds | Monitoring: drvinst.exe" -ForegroundColor DarkGray
    Write-Host "  ======================================================================" -ForegroundColor DarkCyan
}

#===================================================================
#  PROCESS-HANDLING HELPERS
#===================================================================
function Get-DriverInstallProcesses {
    [System.Collections.ArrayList]$collector = @()
    foreach ($name in $Script:DriverProcesses) {
        $found = $null
        try { $found = Get-Process -Name $name -ErrorAction SilentlyContinue } catch { }
        if ($null -ne $found) {
            foreach ($p in @($found)) { [void]$collector.Add($p) }
        }
    }
    $arr = [object[]]$collector.ToArray()
    return ,$arr
}

function Stop-DriverInstallProcesses {
    param([switch]$Silent)
    $killed = 0
    foreach ($name in $Script:DriverProcesses) {
        $found = $null
        try { $found = Get-Process -Name $name -ErrorAction SilentlyContinue } catch { }
        if ($null -ne $found) {
            foreach ($proc in @($found)) {
                if (-not $Silent) {
                    Write-Status KILL "Terminating: $($proc.ProcessName) (PID $($proc.Id))"
                }
                try { $proc.CloseMainWindow() | Out-Null } catch { }
                Start-Sleep -Milliseconds 300
                try {
                    if (-not $proc.HasExited) {
                        Stop-Process -Id $proc.Id -Force -ErrorAction SilentlyContinue
                    }
                }
                catch { }
                $killed++
            }
        }
    }
    foreach ($name in $Script:DriverProcesses) {
        try {
            $saveEA = $ErrorActionPreference
            $ErrorActionPreference = 'SilentlyContinue'
            $null = cmd.exe /c "taskkill /F /IM `"$name.exe`" >nul 2>&1"
            $ErrorActionPreference = $saveEA
        }
        catch {
            try { $ErrorActionPreference = $saveEA } catch { }
        }
    }
    return $killed
}

function Wait-ForDriverProcesses {
    param([int]$TimeoutSeconds = 30)
    $sw = [System.Diagnostics.Stopwatch]::StartNew()
    while ($sw.Elapsed.TotalSeconds -lt $TimeoutSeconds) {
        $procs = Get-DriverInstallProcesses
        $procCount = ($procs | Measure-Object).Count
        if ($procCount -eq 0) { return $true }
        Start-Sleep -Milliseconds 500
    }
    return $false
}

function Clear-DriverInstallEnvironment {
    $procs = Get-DriverInstallProcesses
    $procCount = ($procs | Measure-Object).Count
    if ($procCount -gt 0) {
        Write-Status WARN "Found $procCount leftover driver process(es) - cleaning up..."
        $k = Stop-DriverInstallProcesses
        Write-Status INFO "Terminated $k process(es)"
        Start-Sleep -Seconds 2
    }
}

#===================================================================
#  VERBOSE COMMAND EXECUTION (real-time stdout/stderr streaming)
#===================================================================
function Invoke-CommandVerbose {
    <#
    .SYNOPSIS
        Starts a process, streams stdout/stderr line-by-line with timestamps,
        shows a heartbeat if silent for 30s, saves a full log, returns exit code.
    #>
    param(
        [Parameter(Mandatory)][string]$FilePath,
        [Parameter(Mandatory)][string]$Arguments,
        [Parameter(Mandatory)][string]$Label
    )

    if (-not (Test-Path $FilePath)) {
        Write-Status ERROR "Executable not found: $FilePath"
        return 999
    }

    $exeName = Split-Path $FilePath -Leaf
    Write-Status INFO "Executing: $exeName $Arguments"
    Write-Host ""

    $psi = [System.Diagnostics.ProcessStartInfo]::new()
    $psi.FileName = $FilePath
    $psi.Arguments = $Arguments
    $psi.UseShellExecute = $false
    $psi.RedirectStandardOutput = $true
    $psi.RedirectStandardError = $true
    $psi.CreateNoWindow = $true

    $process = [System.Diagnostics.Process]::new()
    $process.StartInfo = $psi

    try {
        $null = $process.Start()
    }
    catch {
        Write-Status ERROR "Failed to start $exeName : $($_.Exception.Message)"
        return 999
    }

    $sw = [System.Diagnostics.Stopwatch]::StartNew()
    $lastActivity = [System.Diagnostics.Stopwatch]::StartNew()
    $heartbeatSec = 30
    $logLines = [System.Collections.ArrayList]::new()

    # Async line readers for stdout and stderr
    $stdoutTask = $process.StandardOutput.ReadLineAsync()
    $stderrTask = $process.StandardError.ReadLineAsync()
    $stdoutDone = $false
    $stderrDone = $false

    while ((-not $stdoutDone) -or (-not $stderrDone)) {
        $activity = $false

        # --- stdout ---
        if ((-not $stdoutDone) -and $stdoutTask.IsCompleted) {
            $line = $null
            try { $line = $stdoutTask.Result } catch { $stdoutDone = $true; continue }
            if ($null -ne $line) {
                $trimmed = $line.Trim()
                if ($trimmed) {
                    $ts = Get-Date -Format 'HH:mm:ss'
                    Write-Host "  [$ts] [$Label] $trimmed" -ForegroundColor White
                    [void]$logLines.Add("[$ts] $trimmed")
                }
                $stdoutTask = $process.StandardOutput.ReadLineAsync()
                $activity = $true
                $lastActivity.Restart()
            }
            else {
                $stdoutDone = $true
            }
        }

        # --- stderr ---
        if ((-not $stderrDone) -and $stderrTask.IsCompleted) {
            $line = $null
            try { $line = $stderrTask.Result } catch { $stderrDone = $true; continue }
            if ($null -ne $line) {
                $trimmed = $line.Trim()
                if ($trimmed) {
                    $ts = Get-Date -Format 'HH:mm:ss'
                    Write-Host "  [$ts] [$Label] $trimmed" -ForegroundColor Yellow
                    [void]$logLines.Add("[$ts] [WARN] $trimmed")
                }
                $stderrTask = $process.StandardError.ReadLineAsync()
                $activity = $true
                $lastActivity.Restart()
            }
            else {
                $stderrDone = $true
            }
        }

        # --- heartbeat when process is silent ---
        if ((-not $activity) -and ($lastActivity.Elapsed.TotalSeconds -ge $heartbeatSec)) {
            $ts = Get-Date -Format 'HH:mm:ss'
            $elapsed = [math]::Round($sw.Elapsed.TotalSeconds)
            Write-Host "  [$ts] [$Label] ... still running (${elapsed}s elapsed)" -ForegroundColor DarkGray
            [void]$logLines.Add("[$ts] [heartbeat] ${elapsed}s elapsed")
            $lastActivity.Restart()
        }

        if (-not $activity) {
            Start-Sleep -Milliseconds 100
        }
    }

    $process.WaitForExit()
    $sw.Stop()
    $exitCode = $process.ExitCode

    # Save detailed log
    $safeLabel = $Label -replace '[^a-zA-Z0-9]', '_'
    $logFile = Join-Path $Script:TempFolder "${safeLabel}_$(Get-Date -Format 'yyyyMMdd_HHmmss').log"
    try {
        $header = "Command : $FilePath $Arguments`nExit    : $exitCode`nDuration: $(Format-Duration $sw.Elapsed.TotalSeconds)`n$('=' * 60)`n"
        $body = ($logLines | ForEach-Object { $_ }) -join "`n"
        "$header$body" | Out-File $logFile -Encoding UTF8 -Force
    }
    catch { }

    Write-Host ""
    Write-Status TIME "Completed in $(Format-Duration $sw.Elapsed.TotalSeconds)"
    Write-Status INFO "Exit code: $exitCode"
    Write-Status DEBUG "Log: $logFile"

    try { $process.Dispose() } catch { }
    return $exitCode
}

#===================================================================
#  CHOCOLATEY PATH HELPER
#===================================================================
function Get-ChocoExePath {
    $cmd = Get-Command choco -ErrorAction SilentlyContinue
    if ($null -ne $cmd) { return $cmd.Source }
    $default = "$env:ProgramData\chocolatey\bin\choco.exe"
    if (Test-Path $default) { return $default }
    return $null
}

#===================================================================
#  DEVICE SCANNING & INF MATCHING
#===================================================================
function Get-SystemDeviceIds {
    $ids = [System.Collections.Generic.HashSet[string]]::new(
        [System.StringComparer]::OrdinalIgnoreCase
    )
    $deviceCount = 0
    $problemDeviceNames = [System.Collections.ArrayList]::new()
    try {
        $devices = @(Get-CimInstance Win32_PnPEntity -ErrorAction Stop)
        $deviceCount = ($devices | Measure-Object).Count
        foreach ($dev in $devices) {
            if ($null -ne $dev.HardwareID) {
                foreach ($hwid in @($dev.HardwareID)) {
                    if ($hwid) { [void]$ids.Add($hwid) }
                }
            }
            if ($null -ne $dev.CompatibleID) {
                foreach ($cid in @($dev.CompatibleID)) {
                    if ($cid) { [void]$ids.Add($cid) }
                }
            }
            if ($null -ne $dev.ConfigManagerErrorCode -and $dev.ConfigManagerErrorCode -ne 0) {
                $devName = $dev.Name
                if (-not $devName) { $devName = $dev.DeviceID }
                [void]$problemDeviceNames.Add("$devName (error $($dev.ConfigManagerErrorCode))")
            }
        }
    }
    catch {
        Write-Status WARN "WMI device scan failed: $($_.Exception.Message)"
    }
    if ($ids.Count -eq 0) {
        Write-Status INFO "Trying Get-PnpDevice as fallback..."
        try {
            $pnpDevs = @(Get-PnpDevice -ErrorAction SilentlyContinue)
            $deviceCount = ($pnpDevs | Measure-Object).Count
            foreach ($dev in $pnpDevs) {
                try {
                    $hwProp = $dev | Get-PnpDeviceProperty -KeyName 'DEVPKEY_Device_HardwareIds' -ErrorAction SilentlyContinue
                    if ($null -ne $hwProp -and $null -ne $hwProp.Data) {
                        foreach ($id in @($hwProp.Data)) { if ($id) { [void]$ids.Add($id) } }
                    }
                }
                catch { }
                try {
                    $cProp = $dev | Get-PnpDeviceProperty -KeyName 'DEVPKEY_Device_CompatibleIds' -ErrorAction SilentlyContinue
                    if ($null -ne $cProp -and $null -ne $cProp.Data) {
                        foreach ($id in @($cProp.Data)) { if ($id) { [void]$ids.Add($id) } }
                    }
                }
                catch { }
            }
        }
        catch {
            Write-Status WARN "Get-PnpDevice fallback also failed: $($_.Exception.Message)"
        }
    }
    Write-Status OK "Scanned $deviceCount device(s), found $($ids.Count) unique hardware/compatible ID(s)."
    $probCount = ($problemDeviceNames | Measure-Object).Count
    if ($probCount -gt 0) {
        Write-Status WARN "$probCount device(s) with problems (may need drivers):"
        foreach ($pd in $problemDeviceNames) {
            Write-Status INFO "  - $pd"
        }
    }
    else {
        Write-Status OK "No devices with driver problems detected."
    }
    return $ids
}

function Get-InfHardwareIds {
    param([Parameter(Mandatory)][string]$InfPath)
    $hardwareIds = [System.Collections.Generic.HashSet[string]]::new(
        [System.StringComparer]::OrdinalIgnoreCase
    )
    try { $lines = @(Get-Content $InfPath -ErrorAction Stop) }
    catch { return @($hardwareIds) }
    $lineCount = ($lines | Measure-Object).Count
    if ($lineCount -eq 0) { return @($hardwareIds) }
    $modelSections = [System.Collections.Generic.HashSet[string]]::new(
        [System.StringComparer]::OrdinalIgnoreCase
    )
    $inManufacturer = $false
    foreach ($line in $lines) {
        $trimmed = $line.Trim()
        if ($trimmed.StartsWith(';')) { continue }
        if ($trimmed -match '^\[Manufacturer\]\s*$') { $inManufacturer = $true; continue }
        if ($inManufacturer) {
            if ($trimmed -match '^\[') { $inManufacturer = $false; continue }
            if (-not $trimmed) { continue }
            if ($trimmed -match '=') {
                $rightSide = ($trimmed -split '=', 2)[1]
                if ($null -eq $rightSide) { continue }
                $parts = $rightSide -split ','
                $baseName = $parts[0].Trim()
                if (-not $baseName) { continue }
                [void]$modelSections.Add($baseName)
                $partCount = ($parts | Measure-Object).Count
                for ($j = 1; $j -lt $partCount; $j++) {
                    $decoration = $parts[$j].Trim()
                    if ($decoration) { [void]$modelSections.Add("$baseName.$decoration") }
                }
            }
        }
    }
    foreach ($section in @($modelSections)) {
        $sectionHeader = "[$section]"
        $inSection = $false
        foreach ($line in $lines) {
            $trimmed = $line.Trim()
            if ($trimmed.StartsWith(';')) { continue }
            if ($trimmed -ieq $sectionHeader) { $inSection = $true; continue }
            if ($inSection) {
                if ($trimmed -match '^\[') { $inSection = $false; continue }
                if (-not $trimmed) { continue }
                if ($trimmed -match '=') {
                    $rightSide = ($trimmed -split '=', 2)[1]
                    if ($null -eq $rightSide) { continue }
                    $idParts = $rightSide -split ','
                    $idPartCount = ($idParts | Measure-Object).Count
                    for ($j = 1; $j -lt $idPartCount; $j++) {
                        $hwid = $idParts[$j].Trim().Trim('"').Trim()
                        if ($hwid -and $hwid -match '\\') { [void]$hardwareIds.Add($hwid) }
                    }
                }
            }
        }
    }
    return @($hardwareIds)
}

function Test-InfMatchesSystem {
    param(
        [Parameter(Mandatory)][string]$InfPath,
        [Parameter(Mandatory)][System.Collections.Generic.HashSet[string]]$SystemIds
    )
    $infIds = Get-InfHardwareIds -InfPath $InfPath
    $infIdCount = ($infIds | Measure-Object).Count
    if ($infIdCount -eq 0) { return $true }
    foreach ($infId in $infIds) {
        if ($SystemIds.Contains($infId)) { return $true }
    }
    return $false
}

#===================================================================
#  STATE-MANAGEMENT HELPERS
#===================================================================
function Get-ScriptState {
    if (Test-Path $Script:StateFile) {
        try {
            $json = Get-Content $Script:StateFile -Raw -ErrorAction Stop | ConvertFrom-Json
            $defaults = @{
                Phase = 0; VentoyCompleted = $false; VentoyRebootDone = $false
                VentoyProcessedDrivers = @(); VentoyDriverRoot = ""
                ManufacturerPhase = 0; DellADRFailed = $false; LastExitCode = 0
                RebootCount = 0; TotalDriversAdded = 0; TotalDriversInstalled = 0
                TotalDriversFailed = 0; TotalDriversSkipped = 0
                TotalDriversTimedOut = 0; TotalTimeSpent = 0; TotalProcessesKilled = 0
            }
            foreach ($k in $defaults.Keys) {
                if (-not $json.PSObject.Properties[$k]) {
                    $json | Add-Member -NotePropertyName $k -NotePropertyValue $defaults[$k] -Force
                }
            }
            return $json
        }
        catch {
            Write-Status WARN "Failed reading state file - starting fresh: $($_.Exception.Message)"
        }
    }
    return [PSCustomObject]@{
        Phase = 0; VentoyCompleted = $false; VentoyRebootDone = $false
        VentoyProcessedDrivers = @(); VentoyDriverRoot = ""
        ManufacturerPhase = 0; DellADRFailed = $false; LastExitCode = 0
        RebootCount = 0; TotalDriversAdded = 0; TotalDriversInstalled = 0
        TotalDriversFailed = 0; TotalDriversSkipped = 0
        TotalDriversTimedOut = 0; TotalTimeSpent = 0; TotalProcessesKilled = 0
    }
}

function Set-ScriptState {
    param([pscustomobject]$State)
    $State | ConvertTo-Json -Depth 10 | Out-File $Script:StateFile -Encoding UTF8 -Force
}

function Remove-ScriptState {
    Write-Status INFO "Removing state file and RunOnce entry..."
    if (Test-Path $Script:StateFile) { Remove-Item $Script:StateFile -Force -ErrorAction SilentlyContinue }
    try {
        $rk64 = [Microsoft.Win32.RegistryKey]::OpenBaseKey(
            [Microsoft.Win32.RegistryHive]::LocalMachine,
            [Microsoft.Win32.RegistryView]::Registry64
        ).OpenSubKey('SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce', $true)
        if ($null -ne $rk64) { $rk64.DeleteValue($Script:RunOnceName, $false); $rk64.Close() }
    }
    catch { }
    $fallback = "$($Script:RunOnceName)_Task"
    try {
        $existingTask = Get-ScheduledTask -TaskName $fallback -ErrorAction SilentlyContinue
        if ($null -ne $existingTask) {
            Unregister-ScheduledTask -TaskName $fallback -Confirm:$false -ErrorAction SilentlyContinue
            Write-Status INFO "Deleted fallback Scheduled-Task: $fallback"
        }
    }
    catch { }
    Write-Status OK "Cleanup finished."
}

#===================================================================
#  RUNONCE HELPERS
#===================================================================
function Set-RunOnceEntry {
    param([Parameter(Mandatory)][string]$Name, [Parameter(Mandatory)][string]$Command)
    try {
        $rk64 = [Microsoft.Win32.RegistryKey]::OpenBaseKey(
            [Microsoft.Win32.RegistryHive]::LocalMachine,
            [Microsoft.Win32.RegistryView]::Registry64
        ).OpenSubKey('SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce', $true)
        $rk64.SetValue($Name, $Command, [Microsoft.Win32.RegistryValueKind]::String)
        $rk64.Close()
        Write-Status OK "RunOnce entry created (64-bit): $Name"
        return $true
    }
    catch {
        Write-Status WARN "Failed to write RunOnce (64-bit): $($_.Exception.Message)"
        return $false
    }
}

function Set-RunOnceTask {
    param([Parameter(Mandatory)][string]$Name, [Parameter(Mandatory)][string]$ScriptPath)
    $taskAction = New-ScheduledTaskAction -Execute 'powershell.exe' -Argument "-NoLogo -NoProfile -ExecutionPolicy Bypass -File `"$ScriptPath`""
    $taskTrigger = New-ScheduledTaskTrigger -AtStartup
    $principal = New-ScheduledTaskPrincipal -UserId 'SYSTEM' -RunLevel Highest -LogonType ServiceAccount
    try {
        Register-ScheduledTask -TaskName $Name -Action $taskAction -Trigger $taskTrigger -Principal $principal -Force -ErrorAction Stop
        Write-Status OK "Fallback Scheduled-Task created: $Name"
        return $true
    }
    catch {
        Write-Status ERROR "Could not create fallback Scheduled-Task: $($_.Exception.Message)"
        return $false
    }
}

function Schedule-RebootAndContinue {
    param([pscustomobject]$State, [string]$Reason = "Script requires reboot to continue")
    Write-Host ""
    Write-Host "======================================================================" -ForegroundColor Yellow
    Write-Host "  REBOOT REQUIRED" -ForegroundColor Yellow
    Write-Host "======================================================================" -ForegroundColor Yellow
    Write-Host "  Reason: $Reason" -ForegroundColor Yellow
    Write-Host "  The script will automatically resume after reboot." -ForegroundColor Yellow
    Write-Host "======================================================================" -ForegroundColor Yellow
    Write-Host ""
    Write-Status INFO "Cleaning up driver processes before reboot..."
    Stop-DriverInstallProcesses -Silent | Out-Null
    $State.RebootCount++
    Set-ScriptState -State $State
    $cmd = "powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File `"$Script:PersistentScript`""
    $runOnceCreated = Set-RunOnceEntry -Name $Script:RunOnceName -Command $cmd
    if (-not $runOnceCreated) {
        $fallbackName = "$($Script:RunOnceName)_Task"
        $taskCreated = Set-RunOnceTask -Name $fallbackName -ScriptPath $Script:PersistentScript
        if (-not $taskCreated) {
            Write-Status ERROR "Unable to schedule script after reboot - manual restart required."
        }
    }
    Write-Status INFO "Reboot scheduled - system will restart in 15 seconds..."
    for ($i = 15; $i -ge 1; $i--) {
        Write-Host "  Rebooting in $i seconds..." -ForegroundColor Yellow
        Start-Sleep -Seconds 1
    }
    Write-Host ""
    Write-Host "  Rebooting NOW!" -ForegroundColor Red
    Write-Host ""
    try { Stop-Transcript } catch { }
    Restart-Computer -Force
    exit 0
}

#===================================================================
#  DRIVER INSTALLATION (single driver with timeout & monitoring)
#===================================================================
function Install-SingleDriver {
    param(
        [Parameter(Mandatory)][string]$InfPath,
        [Parameter(Mandatory)][string]$DriverName,
        [Parameter(Mandatory)][int]$CurrentNumber,
        [Parameter(Mandatory)][int]$TotalCount,
        [int]$TimeoutSeconds = $Script:DriverTimeout
    )
    $result = [PSCustomObject]@{
        InfPath = $InfPath; DriverName = $DriverName; Success = $false
        ExitCode = -1; Added = $false; Installed = $false; AlreadyExists = $false
        NeedsReboot = $false; TimedOut = $false; ProcessesKilled = 0
        ErrorMessage = ""; Output = ""; Duration = 0
    }
    Write-DriverHeader -DriverName $DriverName -Current $CurrentNumber -Total $TotalCount
    Write-Status DRIVER "Path: $InfPath"
    Clear-DriverInstallEnvironment
    Write-Status INFO "Starting installation with process monitoring..."
    Write-Host ""
    $stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
    $consoleWidth = Get-ConsoleWidth
    $tempScript = [IO.Path]::GetTempFileName() + ".ps1"
    $outFile = [IO.Path]::GetTempFileName()
    $exitFile = [IO.Path]::GetTempFileName()
    $wrapperContent = @"
try {
    `$out = & pnputil.exe /add-driver "$InfPath" /install 2>&1 | Out-String
    `$out | Out-File -FilePath "$outFile" -Encoding UTF8
    `$LASTEXITCODE | Out-File -FilePath "$exitFile" -Encoding UTF8
} catch {
    `$_.Exception.Message | Out-File -FilePath "$outFile" -Encoding UTF8
    "999" | Out-File -FilePath "$exitFile" -Encoding UTF8
}
"@
    $wrapperContent | Out-File -FilePath $tempScript -Encoding UTF8
    $proc = Start-Process -FilePath "powershell.exe" -ArgumentList "-ExecutionPolicy Bypass -WindowStyle Hidden -File `"$tempScript`"" -PassThru -WindowStyle Hidden
    $frameIdx = 0
    $frameCount = $Script:SpinnerFrames.Length
    $animLine = 0
    try { $animLine = [Console]::CursorTop } catch { }
    while ((-not $proc.HasExited) -and ($stopwatch.Elapsed.TotalSeconds -lt $TimeoutSeconds)) {
        $elapsed = $stopwatch.Elapsed.TotalSeconds
        $remaining = [math]::Max(0, $TimeoutSeconds - $elapsed)
        $pct = [math]::Round(($elapsed / $TimeoutSeconds) * 100)
        $spinner = $Script:SpinnerFrames[$frameIdx % $frameCount]
        $frameIdx++
        $line = "  $spinner $pct% | Elapsed: $([math]::Round($elapsed))s | Remaining: $([math]::Round($remaining))s"
        $padded = $line.PadRight($consoleWidth)
        try {
            [Console]::SetCursorPosition(0, $animLine)
            [Console]::ForegroundColor = [ConsoleColor]::Cyan
            [Console]::Write($padded)
            [Console]::ResetColor()
        }
        catch { Write-Host "`r$padded" -NoNewline -ForegroundColor Cyan }
        Start-Sleep -Milliseconds 150
    }
    try {
        [Console]::SetCursorPosition(0, $animLine)
        [Console]::Write(' ' * $consoleWidth)
        [Console]::SetCursorPosition(0, $animLine)
    }
    catch { Write-Host "`r$(' ' * $consoleWidth)`r" -NoNewline }
    $stopwatch.Stop()
    $result.Duration = $stopwatch.Elapsed.TotalSeconds
    if (-not $proc.HasExited) {
        Write-Host ""
        Write-Status SKIP "TIMEOUT after $(Format-Duration $TimeoutSeconds)!"
        try { $proc.Kill() } catch { }
        $result.TimedOut = $true
        $result.ErrorMessage = "Timeout after $TimeoutSeconds seconds"
        $killed = Stop-DriverInstallProcesses
        $result.ProcessesKilled = $killed
        Write-Status KILL "Terminated $killed driver-process(es)"
        $null = Wait-ForDriverProcesses -TimeoutSeconds 10
        Remove-Item $tempScript, $outFile, $exitFile -Force -ErrorAction SilentlyContinue
        Write-Status TIME "Time spent: $(Format-Duration $result.Duration) (TIMEOUT)"
        if ($Script:CooldownSeconds -gt 0) { Start-Sleep -Seconds $Script:CooldownSeconds }
        Write-Host ""
        return $result
    }
    $out = ""
    $exit = -1
    if (Test-Path $outFile) { $out = Get-Content $outFile -Raw -ErrorAction SilentlyContinue }
    if (Test-Path $exitFile) {
        $raw = Get-Content $exitFile -Raw -ErrorAction SilentlyContinue
        if ($null -ne $raw) { [int]::TryParse($raw.Trim(), [ref]$exit) | Out-Null }
    }
    Remove-Item $tempScript, $outFile, $exitFile -Force -ErrorAction SilentlyContinue
    $result.ExitCode = $exit
    $result.Output = $out
    if ($null -ne $out) {
        if ($out -match 'added|successfully added|driver package added') {
            $result.Added = $true; Write-Status OK "Driver added to store"
        }
        if ($out -match 'install.*device|installed to device') {
            $result.Installed = $true; Write-Status OK "Driver installed on device"
        }
        if ($out -match 'already exists|up to date') {
            $result.AlreadyExists = $true; Write-Status INFO "Driver already up-to-date"
        }
        if ($out -match 'reboot|restart') {
            $result.NeedsReboot = $true; Write-Status WARN "Reboot required (output)"
        }
    }
    switch ($exit) {
        0    { $result.Success = $true; Write-Status OK "Exit code 0 - Success" }
        1    { $result.Success = $true; Write-Status INFO "Exit code 1 - Success with warnings" }
        259  { $result.Success = $true; Write-Status INFO "Exit code 259 - staged (no matching device)" }
        3010 { $result.Success = $true; $result.NeedsReboot = $true; Write-Status WARN "Exit code 3010 - Reboot required" }
        482  { $result.Success = $true; $result.NeedsReboot = $true; Write-Status WARN "Exit code 482 - Partial success, reboot needed" }
        default {
            if ($exit -ge 0) {
                if ($result.Added -or $result.Installed -or $result.AlreadyExists) {
                    $result.Success = $true; Write-Status INFO "Exit code $exit - driver processed"
                }
                else {
                    $result.Success = $false; $result.ErrorMessage = "Exit code $exit"
                    Write-Status ERROR "Exit code $exit - failure"
                }
            }
            else { $result.Success = $true; Write-Status WARN "Unable to determine exit code" }
        }
    }
    Write-Status TIME "Time taken: $(Format-Duration $result.Duration)"
    if ($Script:CooldownSeconds -gt 0) { Start-Sleep -Seconds $Script:CooldownSeconds }
    return $result
}

#===================================================================
#  PHASE-0: VENTOY OFFLINE DRIVER INSTALLATION
#===================================================================
function Install-VentoyDrivers {
    param([string[]]$FolderCandidates = @('DriversOS','Drivers'), [pscustomobject]$State)
    Write-Banner "PHASE-0: VENTOY OFFLINE DRIVER INSTALLATION"
    Write-Status INFO "Performing initial cleanup..."
    Clear-DriverInstallEnvironment
    $log = Join-Path $env:WINDIR 'Temp\InstallVentoyDrivers.log'
    "=== Install-VentoyDrivers start $(Get-Date) ===" | Out-File $log -Encoding UTF8 -Append
    $result = [PSCustomObject]@{
        Success = $false; NeedsReboot = $false; NotFound = $false
        TotalFound = 0; TotalMatched = 0; TotalSkippedNoMatch = 0
        TotalAdded = 0; TotalInstalled = 0; TotalFailed = 0; TotalSkipped = 0
        TotalAlreadyExist = 0; TotalTimedOut = 0; TotalTime = 0; TotalKilled = 0
    }
    $root = $null
    if ($State.VentoyDriverRoot -and (Test-Path $State.VentoyDriverRoot)) {
        $root = $State.VentoyDriverRoot
        Write-Status OK "Using saved driver root: $root"
    }
    else {
        $driveLetters = @('D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z')
        foreach ($drive in $driveLetters) {
            foreach ($folder in $FolderCandidates) {
                $candidate = "${drive}:\$folder"
                if (Test-Path $candidate) { $root = $candidate; Write-Status OK "Found driver folder: $root"; break }
            }
            if ($null -ne $root) { break }
        }
    }
    if ($null -eq $root) {
        Write-Status INFO "No Ventoy driver folder found."
        $result.NotFound = $true; $result.Success = $true; return $result
    }
    $State.VentoyDriverRoot = $root; Set-ScriptState -State $State
    $allInfFiles = @(Get-ChildItem -Path $root -Filter "*.inf" -Recurse -File -ErrorAction SilentlyContinue | Sort-Object FullName)
    $totalFound = ($allInfFiles | Measure-Object).Count
    if ($totalFound -eq 0) {
        Write-Status WARN "No .inf files discovered."
        $result.NotFound = $true; $result.Success = $true; return $result
    }
    $result.TotalFound = $totalFound
    Write-Status OK "Found $totalFound INF file(s) in driver folder."
    $processed = @()
    if ($State.VentoyProcessedDrivers) { $processed = @($State.VentoyProcessedDrivers) }
    $processedCount = ($processed | Measure-Object).Count
    if ($processedCount -gt 0) { Write-Status INFO "$processedCount driver(s) already processed." }
    Write-SubBanner "Scanning System Devices"
    $scanSw = [System.Diagnostics.Stopwatch]::StartNew()
    $systemIds = Get-SystemDeviceIds
    $scanSw.Stop()
    Write-Status TIME "Device scan took $(Format-Duration $scanSw.Elapsed.TotalSeconds)"
    Write-SubBanner "Matching Drivers to System Devices"
    $matchSw = [System.Diagnostics.Stopwatch]::StartNew()
    $matchedInfs = [System.Collections.ArrayList]::new()
    $skippedNoMatch = [System.Collections.ArrayList]::new()
    $fallbackMode = $false
    if ($systemIds.Count -eq 0) {
        Write-Status WARN "Could not enumerate any device IDs - installing ALL drivers as fallback."
        $fallbackMode = $true
        foreach ($inf in $allInfFiles) { [void]$matchedInfs.Add($inf) }
    }
    else {
        foreach ($inf in $allInfFiles) {
            $rel = $inf.FullName.Substring($root.Length + 1)
            if ($rel -in $processed) { continue }
            $isMatch = Test-InfMatchesSystem -InfPath $inf.FullName -SystemIds $systemIds
            if ($isMatch) { [void]$matchedInfs.Add($inf) }
            else { [void]$skippedNoMatch.Add($inf); Write-Status SKIP "No matching device: $rel" }
        }
    }
    $matchSw.Stop()
    $matchedCount = ($matchedInfs | Measure-Object).Count
    $skippedNoMatchCount = ($skippedNoMatch | Measure-Object).Count
    $result.TotalMatched = $matchedCount
    $result.TotalSkippedNoMatch = $skippedNoMatchCount
    Write-Host ""
    Write-Host "  ======================================================================" -ForegroundColor DarkCyan
    Write-Host "                     DRIVER MATCHING RESULTS" -ForegroundColor Cyan
    Write-Host "  ======================================================================" -ForegroundColor DarkCyan
    Write-Host "  Total INF files found:        $totalFound" -ForegroundColor White
    Write-Host "  Already processed:            $processedCount" -ForegroundColor DarkGray
    Write-Host "  Matched to system devices:    $matchedCount" -ForegroundColor Green
    Write-Host "  Skipped (no matching device): $skippedNoMatchCount" -ForegroundColor Yellow
    Write-Host "  Matching time:                $(Format-Duration $matchSw.Elapsed.TotalSeconds)" -ForegroundColor DarkGray
    $modeLabel = if ($fallbackMode) { "FALLBACK (all drivers)" } else { "TARGETED (matched only)" }
    $modeColor = if ($fallbackMode) { 'Red' } else { 'Green' }
    Write-Host "  Mode:                         $modeLabel" -ForegroundColor $modeColor
    Write-Host "  ======================================================================" -ForegroundColor DarkCyan
    Write-Host ""
    if ($matchedCount -eq 0) {
        Write-Status OK "No new drivers to install."
        $result.Success = $true; return $result
    }
    $overallSw = [System.Diagnostics.Stopwatch]::StartNew()
    $globalReboot = $false
    $i = 0
    Write-SubBanner "Installing $matchedCount Matched Driver(s)"
    foreach ($inf in $matchedInfs) {
        $i++
        $rel = $inf.FullName.Substring($root.Length + 1)
        if ($rel -in $processed) { continue }
        $drvResult = Install-SingleDriver -InfPath $inf.FullName -DriverName $rel -CurrentNumber $i -TotalCount $matchedCount
        $result.TotalTime += $drvResult.Duration
        $result.TotalKilled += $drvResult.ProcessesKilled
        if ($drvResult.TimedOut) { $result.TotalTimedOut++ }
        elseif ($drvResult.Added) { $result.TotalAdded++ }
        if ($drvResult.Installed) { $result.TotalInstalled++ }
        if ($drvResult.AlreadyExists) { $result.TotalAlreadyExist++ }
        if ((-not $drvResult.Success) -and (-not $drvResult.TimedOut)) { $result.TotalFailed++ }
        if ($drvResult.NeedsReboot) { $globalReboot = $true }
        $processed += $rel
        $State.VentoyProcessedDrivers = $processed
        $State.TotalDriversAdded = $result.TotalAdded
        $State.TotalDriversInstalled = $result.TotalInstalled
        $State.TotalDriversFailed = $result.TotalFailed
        $State.TotalDriversSkipped = $result.TotalSkippedNoMatch
        $State.TotalDriversTimedOut = $result.TotalTimedOut
        $State.TotalTimeSpent = $result.TotalTime
        $State.TotalProcessesKilled = $result.TotalKilled
        Set-ScriptState -State $State
    }
    $overallSw.Stop()
    $failColor = if ($result.TotalFailed -gt 0) { 'Red' } else { 'Green' }
    $timeoutColor = if ($result.TotalTimedOut -gt 0) { 'Yellow' } else { 'Green' }
    $killColor = if ($result.TotalKilled -gt 0) { 'Yellow' } else { 'Green' }
    $rebootColor = if ($globalReboot) { 'Yellow' } else { 'Green' }
    Write-Host ""
    Write-Host "======================================================================" -ForegroundColor Green
    Write-Host "               VENTOY DRIVER INSTALLATION SUMMARY" -ForegroundColor Green
    Write-Host "======================================================================" -ForegroundColor Green
    Write-Host "  Total INFs found:       $($result.TotalFound)" -ForegroundColor White
    Write-Host "  Matched to devices:     $($result.TotalMatched)" -ForegroundColor Green
    Write-Host "  Skipped (no match):     $($result.TotalSkippedNoMatch)" -ForegroundColor Yellow
    Write-Host "  Added to store:         $($result.TotalAdded)" -ForegroundColor Green
    Write-Host "  Installed on hardware:  $($result.TotalInstalled)" -ForegroundColor Green
    Write-Host "  Already up-to-date:     $($result.TotalAlreadyExist)" -ForegroundColor Cyan
    Write-Host "  Failed:                 $($result.TotalFailed)" -ForegroundColor $failColor
    Write-Host "  Timed out:              $($result.TotalTimedOut)" -ForegroundColor $timeoutColor
    Write-Host "  Processes killed:       $($result.TotalKilled)" -ForegroundColor $killColor
    Write-Host "  Total install time:     $(Format-Duration $overallSw.Elapsed.TotalSeconds)" -ForegroundColor Magenta
    Write-Host "  Reboot required:        $globalReboot" -ForegroundColor $rebootColor
    Write-Host "======================================================================" -ForegroundColor Green
    Write-Host ""
    $result.Success = $true
    $result.NeedsReboot = $globalReboot
    return $result
}

#===================================================================
#  INTERNET CONNECTIVITY CHECK
#===================================================================
function Test-InternetConnectivity {
    param([int]$MaxRetries = 15, [int]$RetryDelay = 10)
    Write-Banner "CHECKING INTERNET CONNECTIVITY"
    $w = Get-ConsoleWidth
    $totalFrames = $Script:SpinnerFrames.Length
    for ($i = 1; $i -le $MaxRetries; $i++) {
        Write-Status PROGRESS "Attempt $i of $MaxRetries..."
        try {
            $r = Invoke-WebRequest -Uri "https://www.google.com" -Method Head -UseBasicParsing -TimeoutSec 10 -ErrorAction Stop
            if ($r.StatusCode -eq 200) { Write-Status OK "Internet reachable."; return $true }
        }
        catch {
            Write-Status WARN "Attempt $i failed: $($_.Exception.Message)"
            if ($i -lt $MaxRetries) {
                Write-Status INFO "Retrying in $RetryDelay seconds..."
                for ($s = $RetryDelay; $s -ge 1; $s--) {
                    $spinner = $Script:SpinnerFrames[($RetryDelay - $s) % $totalFrames]
                    $line = "  $spinner Waiting $s sec..."
                    $pad = $line.PadRight($w)
                    try {
                        [Console]::SetCursorPosition(0, [Console]::CursorTop)
                        [Console]::ForegroundColor = [ConsoleColor]::DarkGray
                        [Console]::Write($pad)
                        [Console]::ResetColor()
                    }
                    catch { Write-Host "`r$pad" -NoNewline -ForegroundColor DarkGray }
                    Start-Sleep -Seconds 1
                }
            }
        }
    }
    Write-Status ERROR "No internet after $MaxRetries attempts."
    return $false
}

#===================================================================
#  MANUFACTURER DETECTION
#===================================================================
function Get-SystemManufacturer {
    Write-SubBanner "Detecting System Manufacturer"
    $cs = Get-CimInstance Win32_ComputerSystem
    $man = $cs.Manufacturer.Trim()
    $mod = $cs.Model.Trim()
    Write-Status INFO "Manufacturer: $man"
    Write-Status INFO "Model:        $mod"
    return $man
}

#===================================================================
#  CHOCOLATEY INSTALL + SOURCE CONFIGURATION
#===================================================================
function Set-ChocolateySource {
    Write-SubBanner "Configuring Chocolatey Sources"
    $chocoExe = Get-ChocoExePath
    if (-not $chocoExe) {
        Write-Status ERROR "Chocolatey executable not found - cannot configure sources."
        return
    }

    # Check if source exists
    Write-Status INFO "Checking existing Chocolatey sources..."
    $listExit = Invoke-CommandVerbose -FilePath $chocoExe -Arguments "source list" -Label "CHOCO"

    # Try adding the custom source
    Write-Status INFO "Adding custom source: $($Script:ChocoSourceName) -> $($Script:ChocoSourceUrl)"
    $addExit = Invoke-CommandVerbose -FilePath $chocoExe -Arguments "source add -n `"$($Script:ChocoSourceName)`" -s `"$($Script:ChocoSourceUrl)`" --priority=1 --allow-self-service" -Label "CHOCO"
    if ($addExit -eq 0) {
        Write-Status OK "Custom source '$($Script:ChocoSourceName)' configured."
    }
    else {
        Write-Status WARN "Source add returned exit code $addExit (may already exist - OK)."
    }

    # Disable default community source
    Write-Status INFO "Disabling default Chocolatey community source..."
    $disableExit = Invoke-CommandVerbose -FilePath $chocoExe -Arguments "source disable -n `"chocolatey`"" -Label "CHOCO"
    if ($disableExit -eq 0) {
        Write-Status OK "Default community source disabled."
    }
    else {
        Write-Status WARN "Could not disable default source (may not exist)."
    }

    # Verify final state
    Write-Status INFO "Final source configuration:"
    $null = Invoke-CommandVerbose -FilePath $chocoExe -Arguments "source list" -Label "CHOCO"
}

function Install-ChocolateyIfNeeded {
    Write-SubBanner "Ensuring Chocolatey"
    if (-not (Get-Command choco -ErrorAction SilentlyContinue)) {
        Write-Status INFO "Chocolatey not present - installing..."
        Write-Host ""
        Set-ExecutionPolicy Bypass -Scope Process -Force
        [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor 3072
        Invoke-Expression (Invoke-RestMethod https://community.chocolatey.org/install.ps1)
        $env:Path = [System.Environment]::GetEnvironmentVariable('Path', 'Machine') + ';' + [System.Environment]::GetEnvironmentVariable('Path', 'User')
        Write-Host ""
        Write-Status OK "Chocolatey installed."
    }
    else {
        Write-Status OK "Chocolatey already installed."
    }
    Set-ChocolateySource
}

function Install-ChocoPackage {
    <#
    .SYNOPSIS
        Installs a chocolatey package using Invoke-CommandVerbose so every line
        of choco output (download progress, install steps, etc.) is visible.
    #>
    param(
        [Parameter(Mandatory)][string]$PackageName,
        [string]$DisplayName
    )
    if (-not $DisplayName) { $DisplayName = $PackageName }
    $chocoExe = Get-ChocoExePath
    if (-not $chocoExe) {
        Write-Status ERROR "Chocolatey executable not found."
        return $false
    }

    Write-SubBanner "Installing $DisplayName via Chocolatey"
    Write-Status INFO "Source: $($Script:ChocoSourceName) ($($Script:ChocoSourceUrl))"
    Write-Host ""

    # Try custom source first
    $chocoArgs = "install $PackageName -y --source=`"$($Script:ChocoSourceName)`""
    $exitCode = Invoke-CommandVerbose -FilePath $chocoExe -Arguments $chocoArgs -Label "CHOCO"

    if ($exitCode -ne 0) {
        Write-Status WARN "Install from custom source returned $exitCode - trying default sources..."
        $chocoArgs = "install $PackageName -y"
        $exitCode = Invoke-CommandVerbose -FilePath $chocoExe -Arguments $chocoArgs -Label "CHOCO"
    }

    if ($exitCode -eq 0) {
        Write-Status OK "$DisplayName installed successfully."
        return $true
    }
    else {
        Write-Status ERROR "$DisplayName installation failed (exit $exitCode)."
        return $false
    }
}

#===================================================================
#  DELL UPDATE LOGIC  (fully verbose - no spinners)
#===================================================================
function Invoke-DellUpdates {
    param([pscustomobject]$State)
    Write-Banner "DELL SYSTEM UPDATE"

    # Locate dcu-cli.exe
    $cli = @(
        'C:\Program Files\Dell\CommandUpdate\dcu-cli.exe',
        'C:\Program Files (x86)\Dell\CommandUpdate\dcu-cli.exe'
    ) | Where-Object { Test-Path $_ } | Select-Object -First 1

    if (-not $cli) {
        $installed = Install-ChocoPackage -PackageName "dellcommandupdate" -DisplayName "Dell Command Update"
        if ($installed) { Start-Sleep -Seconds 5 }
        $cli = @(
            'C:\Program Files\Dell\CommandUpdate\dcu-cli.exe',
            'C:\Program Files (x86)\Dell\CommandUpdate\dcu-cli.exe'
        ) | Where-Object { Test-Path $_ } | Select-Object -First 1
    }

    if (-not $cli) {
        Write-Status ERROR "Dell Command Update CLI still missing - aborting Dell section."
        return $false
    }

    Write-Status OK "Dell CLI located: $cli"
    Write-Host ""

    switch ($State.ManufacturerPhase) {
        0 {
            # --- Phase-0: Advanced Driver Restore ---
            if ($State.DellADRFailed) {
                Write-Status WARN "ADR previously failed - skipping to phase-2."
                $State.ManufacturerPhase = 2
                Set-ScriptState -State $State
                return (Invoke-DellUpdates -State $State)
            }

            Write-SubBanner "Dell Phase-0: Configuring ADR"
            Write-Status INFO "Enabling Advanced Driver Restore..."
            $configExit = Invoke-CommandVerbose -FilePath $cli -Arguments "/configure -advancedDriverRestore=enable" -Label "DCU-CFG"
            Write-Host ""

            Write-SubBanner "Dell Phase-0: ADR Driver Install"
            Write-Status STEP "This will download and install drivers matching your Dell system."
            Write-Status STEP "All progress and driver names will be displayed below."
            Write-Host ""
            $dellExit = Invoke-CommandVerbose -FilePath $cli -Arguments "/driverinstall" -Label "DCU"

            switch ($dellExit) {
                0 {
                    Write-Status OK "ADR driver install succeeded."
                    $State.ManufacturerPhase = 2
                    Set-ScriptState -State $State
                    return (Invoke-DellUpdates -State $State)
                }
                1 {
                    Write-Status WARN "Drivers installed - reboot required."
                    $State.ManufacturerPhase = 1
                    Schedule-RebootAndContinue -State $State -Reason "Dell ADR driver install (exit 1) requires reboot"
                }
                5 {
                    Write-Status WARN "Reboot required to complete operation."
                    $State.ManufacturerPhase = 1
                    Schedule-RebootAndContinue -State $State -Reason "Dell ADR driver install (exit 5) requires reboot"
                }
                2 {
                    Write-Status ERROR "ADR crashed - marking as failed, skipping to updates."
                    $State.DellADRFailed = $true
                    $State.ManufacturerPhase = 2
                    Set-ScriptState -State $State
                    return (Invoke-DellUpdates -State $State)
                }
                3 {
                    Write-Status OK "All drivers already up to date."
                    $State.ManufacturerPhase = 2
                    Set-ScriptState -State $State
                    return (Invoke-DellUpdates -State $State)
                }
                default {
                    Write-Status ERROR "ADR returned $dellExit - skipping to phase-2."
                    $State.DellADRFailed = $true
                    $State.ManufacturerPhase = 2
                    Set-ScriptState -State $State
                    return (Invoke-DellUpdates -State $State)
                }
            }
        }
        1 {
            # --- Phase-1: Post-reboot scan ---
            Write-SubBanner "Dell Phase-1: Post-Reboot Scan"
            Write-Status INFO "Scanning for remaining Dell drivers after reboot..."
            Write-Host ""
            $null = Invoke-CommandVerbose -FilePath $cli -Arguments "/scan" -Label "DCU"
            $State.ManufacturerPhase = 2
            Set-ScriptState -State $State
            return (Invoke-DellUpdates -State $State)
        }
        2 {
            # --- Phase-2: Apply all updates ---
            Write-SubBanner "Dell Phase-2: Apply BIOS / Firmware / Driver Updates"
            Write-Status STEP "Downloading and installing all available Dell updates."
            Write-Status STEP "Each update name, download progress, and install status shown below."
            Write-Host ""
            $dellExit = Invoke-CommandVerbose -FilePath $cli -Arguments "/applyupdates -forceUpdate=enable -autoSuspendBitLocker=enable" -Label "DCU"

            switch ($dellExit) {
                0 {
                    Write-Status OK "All Dell updates applied successfully."
                    $State.ManufacturerPhase = 3
                    Set-ScriptState -State $State
                    return $true
                }
                1 {
                    Write-Status WARN "Updates applied - reboot required."
                    $State.ManufacturerPhase = 3
                    Schedule-RebootAndContinue -State $State -Reason "Dell updates (exit 1) require reboot"
                }
                5 {
                    Write-Status WARN "Reboot required to finish updates."
                    $State.ManufacturerPhase = 3
                    Schedule-RebootAndContinue -State $State -Reason "Dell updates (exit 5) require reboot"
                }
                3 {
                    Write-Status OK "System already up to date."
                    $State.ManufacturerPhase = 3
                    Set-ScriptState -State $State
                    return $true
                }
                default {
                    Write-Status WARN "Apply-updates returned $dellExit - continuing."
                    $State.ManufacturerPhase = 3
                    Set-ScriptState -State $State
                    return $true
                }
            }
        }
        default {
            Write-Status OK "Dell updates already completed."
            return $true
        }
    }
    return $true
}

#===================================================================
#  HP UPDATE LOGIC  (fully verbose - no spinners)
#===================================================================
function Invoke-HPUpdates {
    param([pscustomobject]$State)
    Write-Banner "HP SYSTEM UPDATE"

    $hpiaPaths = @(
        'C:\HP\HPIA\HPImageAssistant.exe',
        'C:\Program Files\HP\HPIA\HPImageAssistant.exe',
        'C:\Program Files (x86)\HP\HPIA\HPImageAssistant.exe'
    )
    $hpia = $hpiaPaths | Where-Object { Test-Path $_ } | Select-Object -First 1

    if (-not $hpia) {
        $installed = Install-ChocoPackage -PackageName "hpimageassistant" -DisplayName "HP Image Assistant"
        if ($installed) { Start-Sleep -Seconds 5 }
        $hpia = $hpiaPaths | Where-Object { Test-Path $_ } | Select-Object -First 1
    }

    if (-not $hpia) {
        Write-Status ERROR "HP Image Assistant still missing - aborting HP section."
        return $false
    }

    Write-Status OK "HP Image Assistant located: $hpia"
    Write-Host ""

    Write-SubBanner "HP Image Assistant: Analyze & Install All Updates"
    Write-Status STEP "Scanning for BIOS, firmware, driver, and software updates."
    Write-Status STEP "Each update name and install progress will be displayed below."
    Write-Host ""

    $hpiaArgs = "/Operation:Analyze /Action:Install /Selection:All /Silent /Noninteractive /ReportFolder:C:\Temp\HPIA"
    $hpExit = Invoke-CommandVerbose -FilePath $hpia -Arguments $hpiaArgs -Label "HPIA"

    switch ($hpExit) {
        0    { Write-Status OK "HP updates completed successfully."; return $true }
        256  { Write-Status WARN "HP updates require reboot (exit $hpExit)."; return $true }
        3010 { Write-Status WARN "HP updates require reboot (exit $hpExit)."; return $true }
        default {
            Write-Status WARN "HP updates returned $hpExit - continuing."
            return $true
        }
    }
}

#===================================================================
#  MAIN EXECUTION
#===================================================================
try {
    if (-not (Initialize-PersistentScript)) {
        throw "Failed to initialise persistent script."
    }
    Start-Transcript -Path $Script:LogFile -Append -Force
    Write-Host ""
    Write-Host "======================================================================" -ForegroundColor Cyan
    Write-Host "       DRIVER UPDATE SCRIPT - PowerShell $($PSVersionTable.PSVersion)" -ForegroundColor Cyan
    Write-Host "======================================================================" -ForegroundColor Cyan
    Write-Host "  User:       $env:USERNAME" -ForegroundColor Cyan
    Write-Host "  Computer:   $env:COMPUTERNAME" -ForegroundColor Cyan
    Write-Host "  PID:        $PID" -ForegroundColor Cyan
    Write-Host "  Time:       $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" -ForegroundColor Cyan
    Write-Host "  Script:     $Script:PersistentScript" -ForegroundColor Cyan
    Write-Host "  Timeout:    $Script:DriverTimeout seconds per driver" -ForegroundColor Cyan
    Write-Host "  ChocoSrc:   $Script:ChocoSourceName -> $Script:ChocoSourceUrl" -ForegroundColor Cyan
    Write-Host "  Mode:       TARGETED (device-matched) + VERBOSE output" -ForegroundColor Cyan
    Write-Host "======================================================================" -ForegroundColor Cyan
    Write-Host ""
    Write-Status INFO "Checking for orphan driver processes..."
    $orphanKilled = Stop-DriverInstallProcesses -Silent
    if ($orphanKilled -gt 0) {
        Write-Status WARN "Terminated $orphanKilled leftover processes."
        Start-Sleep -Seconds 2
    }
    $state = Get-ScriptState
    Write-Host "----------------------------------------------------------------------" -ForegroundColor DarkGray
    Write-Host "  CURRENT STATE" -ForegroundColor DarkGray
    Write-Host "----------------------------------------------------------------------" -ForegroundColor DarkGray
    foreach ($prop in $state.PSObject.Properties) {
        Write-Host ("  {0,-25}: {1}" -f $prop.Name, $prop.Value) -ForegroundColor DarkGray
    }
    Write-Host "----------------------------------------------------------------------" -ForegroundColor DarkGray
    Write-Host ""

    # PHASE-0: Ventoy offline drivers
    if ($state.Phase -eq 0) {
        if (-not $state.VentoyCompleted) {
            $ventoyResult = Install-VentoyDrivers -State $state
            $state.TotalDriversAdded = $ventoyResult.TotalAdded
            $state.TotalDriversInstalled = $ventoyResult.TotalInstalled
            $state.TotalDriversSkipped = $ventoyResult.TotalSkippedNoMatch
            $state.TotalDriversTimedOut = $ventoyResult.TotalTimedOut
            $state.TotalProcessesKilled = $ventoyResult.TotalKilled
            $state.TotalTimeSpent = $ventoyResult.TotalTime
            if ($ventoyResult.NotFound) {
                Write-Status INFO "No Ventoy drivers - moving to online phase."
                $state.VentoyCompleted = $true; $state.Phase = 1
                Set-ScriptState -State $state
            }
            elseif ($ventoyResult.NeedsReboot) {
                Write-Status STEP "Ventoy driver install requires reboot."
                $state.VentoyCompleted = $true; $state.VentoyRebootDone = $false
                Set-ScriptState -State $state
                Schedule-RebootAndContinue -State $state -Reason "Ventoy driver install - reboot required"
            }
            else {
                Write-Status OK "Ventoy drivers installed - no reboot needed."
                $state.VentoyCompleted = $true; $state.Phase = 1
                Set-ScriptState -State $state
            }
        }
        else {
            Write-Status OK "Post-reboot: Ventoy driver installation finalized."
            $state.VentoyRebootDone = $true; $state.Phase = 1
            Set-ScriptState -State $state
        }
    }

    # PHASE-1: Online updates
    if ($state.Phase -ge 1) {
        Write-Banner "PHASE 1: ONLINE UPDATES"
        if (-not (Test-InternetConnectivity -MaxRetries 15 -RetryDelay 10)) {
            Write-Status ERROR "No internet - cannot continue."
            Write-Status INFO "Connect to a network and re-run the script."
            try { Stop-Transcript } catch { }
            exit 1
        }
        Install-ChocolateyIfNeeded
        $manufacturer = Get-SystemManufacturer
        if ($manufacturer -like '*Dell*') {
            Invoke-DellUpdates -State $state | Out-Null
        }
        elseif ($manufacturer -match 'HP|Hewlett[-\s]?Packard') {
            Invoke-HPUpdates -State $state | Out-Null
        }
        else {
            Write-Status WARN "Unsupported manufacturer: $manufacturer - only Dell and HP are handled."
        }
    }

    # FINAL CLEAN-UP
    Write-Status INFO "Final clean-up - terminating any remaining driver processes."
    Stop-DriverInstallProcesses -Silent | Out-Null
    Write-Host ""
    Write-Host "======================================================================" -ForegroundColor Green
    Write-Host "      DRIVER UPDATE SCRIPT COMPLETED SUCCESSFULLY!" -ForegroundColor Green
    Write-Host "======================================================================" -ForegroundColor Green
    Write-Host ""
    Write-Host "  Total reboots:          $($state.RebootCount)" -ForegroundColor White
    Write-Host "  Drivers added:          $($state.TotalDriversAdded)" -ForegroundColor White
    Write-Host "  Drivers installed:      $($state.TotalDriversInstalled)" -ForegroundColor White
    Write-Host "  Drivers skipped:        $($state.TotalDriversSkipped)" -ForegroundColor White
    Write-Host "  Drivers failed:         $($state.TotalDriversFailed)" -ForegroundColor White
    Write-Host "  Drivers timed out:      $($state.TotalDriversTimedOut)" -ForegroundColor White
    Write-Host "  Processes killed:       $($state.TotalProcessesKilled)" -ForegroundColor White
    Write-Host "  Total time spent:       $(Format-Duration $state.TotalTimeSpent)" -ForegroundColor White
    Write-Host "  Dell ADR failed:        $($state.DellADRFailed)" -ForegroundColor White
    Write-Host "  Choco source:           $($Script:ChocoSourceName)" -ForegroundColor White
    Write-Host ""
    Write-Host "======================================================================" -ForegroundColor Green
    Write-Host ""
    Remove-ScriptState
}
catch {
    Write-Host ""
    Write-Host "======================================================================" -ForegroundColor Red
    Write-Host "  SCRIPT ERROR" -ForegroundColor Red
    Write-Host "======================================================================" -ForegroundColor Red
    Write-Status ERROR "Message: $($_.Exception.Message)"
    Write-Status ERROR "Line:    $($_.InvocationInfo.ScriptLineNumber)"
    Write-Host $_.ScriptStackTrace -ForegroundColor Red
    Write-Host ""
    Write-Status INFO "Attempting emergency clean-up..."
    try {
        $saveEA = $ErrorActionPreference
        $ErrorActionPreference = 'SilentlyContinue'
        Stop-DriverInstallProcesses -Silent | Out-Null
        $ErrorActionPreference = $saveEA
    }
    catch { try { $ErrorActionPreference = $saveEA } catch { } }
    Write-Status INFO "State file kept - re-run script to continue."
}
finally {
    try { Stop-Transcript } catch { }
}