using namespace System.Management.Automation.Host # Hellbomb Script # Requires -RunAsAdministrator $ErrorActionPreference = 'Continue' Set-StrictMode -Version Latest $script:DetectedOS = $null Function Initialize-OSDetection { # PowerShell 7+ exposes $IsWindows/$IsLinux/$IsMacOS If ( $PSVersionTable.PSVersion.Major -ge 7 ) { If ($IsWindows) { $script:DetectedOS = "Windows"; Return } If ($IsLinux) { $script:DetectedOS = "Linux"; Return } } # Fallback for Windows PowerShell 5.1 If ($Env:OS -eq "Windows_NT") { $script:DetectedOS = "Windows"; Return } If ($Env:OS -eq "Unix") { $script:DetectedOS = "Linux"; Return } $script:DetectedOS = "Unknown" } Initialize-OSDetection $pshost = Get-Host $psWindow = $pshost.UI.RawUI # Set the window size (height and width) If ( $script:DetectedOS -eq 'Windows' ) { $psWindow.WindowSize.Height = $psWindow.BufferSize.Height = 60 } Function Get-HD2ConfigPath { Param([switch]$All) $appId = 553850 $targetRel = "drive_c/users/steamuser/AppData/Roaming/Arrowhead/Helldivers2" $paths = @() If ($script:DetectedOS -eq 'Windows') { $path = Join-Path $env:APPDATA "Arrowhead\Helldivers2" If (Test-Path $path) { $paths += (Get-Item $path).FullName } } ElseIf ($script:DetectedOS -eq 'Linux') { $defaultPrefix = "$HOME/.steam/steam/steamapps/compatdata/$appId/pfx/$targetRel" If (Test-Path $defaultPrefix) { $paths += (Resolve-Path -LiteralPath $defaultPrefix).ProviderPath } $libFile = "$HOME/.steam/steam/steamapps/libraryfolders.vdf" If (Test-Path $libFile) { $libraries = Select-String -Path $libFile -Pattern 'path"\s*"(.+)"' | ForEach-Object { $_.Matches.Groups[1].Value } Foreach ($lib in $libraries) { $candidate = Join-Path $lib "steamapps/compatdata/$appId/pfx/$targetRel" If (Test-Path $candidate) { $resolved = Resolve-Path -LiteralPath $candidate $paths += $resolved.ProviderPath } } } } Else { Write-Host "Unsupported OS detected." -ForegroundColor Red Return $null } [array]$uniquePaths = @( $paths | Sort-Object -Unique ) If ($All) { Return $uniquePaths } ElseIf ($uniquePaths.Count -gt 0) { Return [string]$uniquePaths[0] } Else { Return $null } } $script:SystemInfo = @{ "GPUInfo" = @() } If ( $script:DetectedOS -eq 'Windows') { $downloadsPath = (New-Object -ComObject Shell.Application).Namespace('shell:Downloads').Self.Path } If ( $script:DetectedOS -eq 'Linux' ) { $downloadsPath = Join-Path $HOME "Downloads" } $script:HellbombScriptDirectory = Join-Path $downloadsPath -ChildPath "HellbombScript-2281e8aa-e61f-446d-93d0-49182b519490" $script:Tests = @{ "IntelMicrocodeCheck" = @{ 'TestPassed' = $null 'AffectedModels' = @("13900", "13790", "13700", "13600", "13500", "13490", "13400", "14900", "14790", "14700", "14600", "14500", "14490", "14400") 'LatestMicrocode' = @("0x12F", "0x3A") 'TestFailMsg' = @' Write-Host "$([Environment]::NewLine)[FAIL] " -ForegroundColor Red -NoNewLine Write-Host "CPU model with unpatched microcode detected!! " -ForegroundColor Yellow -NoNewLine; Write-Host "$script:myCPU" -ForegroundColor White Write-Host "$([Environment]::NewLine) WARNING: If you are NOT currently having stability issues, please update $([Environment]::NewLine) your motherboard UEFI (BIOS) ASAP to prevent permanent damage to the CPU." -ForegroundColor Yellow Write-Host "$([Environment]::NewLine) If you ARE experiencing stability issues, your CPU may be unstable$([Environment]::NewLine) and permanently damaged." -ForegroundColor Red Write-Host "$([Environment]::NewLine) For more information, visit: $([Environment]::NewLine) https://www.tomsguide.com/computing/hardware/13th-and-14th-gen-intel-cpu-damage-could-be-permanent-despite-incoming-fix" -ForegroundColor Cyan Pause "$([Environment]::NewLine) Any proposed fixes by this tool may fail to work if your CPU is damaged.$([Environment]::NewLine)Press [SPACEBAR] to continue..." -ForegroundColor Yellow '@ 'TestPassedIntelMsg' = @' Write-Host "Your CPU: " -ForegroundColor Cyan -NoNewLine ; Write-Host "$script:myCPU " -NoNewLine Write-Host "is running the latest " -NoNewLine -ForegroundColor Green Write-Host "$script:runningMicrocode " -NoNewLine -ForegroundColor Cyan Write-Host "microcode." -ForegroundColor Green '@ 'NotApplicableMsg' = @' Write-Host "Your CPU model: " -ForegroundColor Cyan -NoNewLine ; Write-Host "$script:myCPU " -NoNewLine Write-Host "is not affected by the Intel CPU issues." -ForegroundColor Green '@ 'ErrorMsg' = @' Write-Host "Error occurred determining microcode version for CPU model: " -ForegroundColor Red -NoNewLine ; Write-Host "$script:myCPU " '@ } "PendingReboot" = @{ 'TestPassed' = $null 'rebootRequired' = $false 'keys' = @( "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending", "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootInProgress", "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\PackagesPending", "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired", "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\PendingFileRenameOperations") 'TestFailMsg' = @' Write-Host "$([Environment]::NewLine)[FAIL] " -ForegroundColor Red -NoNewLine Write-Host "Windows is reporting a pending reboot is required." -ForegroundColor Yellow -NoNewLine Write-Host "$([Environment]::NewLine) Please exit the script and reboot your machine..." -ForegroundColor Cyan '@ } "BadPrinter" = @{ 'TestPassed' = $null 'TestFailMsg' = @' Write-Host "$([Environment]::NewLine)[FAIL] " -ForegroundColor Red -NoNewLine Write-Host "OneNote for Windows 10 virtual printer detected! This can cause crashes on game startup." -ForegroundColor Yellow -NoNewLine Write-Host "$([Environment]::NewLine) Please remove this printer driver from your computer. Opening Printers window..." -ForegroundColor Cyan Start-Process "explorer.exe" -ArgumentList "shell:PrintersFolder" '@ } "LongSysUptime" = @{ 'TestPassed' = $null 'TestFailMsg' = @' Write-Host "$([Environment]::NewLine)[FAIL] " -ForegroundColor Red -NoNewLine Write-Host "Your computer has not been restarted in $($script:Tests.LongSysUptime.SystemUptime) days." -ForegroundColor Yellow Write-Host " Please restart your computer. Restart only. Do not use 'Shutdown'." -ForegroundColor Cyan '@ } "AVX2" = @{ 'TestPassed' = $null 'TestFailMsg' = @' Write-Host "$([Environment]::NewLine)[FAIL] " -ForegroundColor Red -NoNewLine Write-Host "Your CPU does not support the AVX2 instruction set." -ForegroundColor Yellow '@ } "MultiChannelMemory" = @{ 'TestPassed' = $null 'TestFailMsg' = @' Write-Host "$([Environment]::NewLine)[FAIL] " -ForegroundColor Red -NoNewLine Write-Host "Memory running in single-channel mode. This will hurt performance." -ForegroundColor Yellow '@ } "MatchingMemory" = @{ 'TestPassed' = $null 'RAMInfo' = $null 'NotFound' = $null 'TestFailMsg' = @' Write-Host "$([Environment]::NewLine)[FAIL] " -ForegroundColor Red -NoNewLine Write-Host "You have mixed memory. This can cause performance and stability issues." -ForegroundColor Yellow '@ 'TestPassedMsg' = @' Write-Host "$([Environment]::NewLine)RAM Information:" -ForegroundColor Cyan '@ 'AlwaysDisplayMsg' = @' If ( $script:Tests.MatchingMemory.NotFound ) { Write-Host "$([Environment]::NewLine)[WARN] " -ForegroundColor Yellow -NoNewLine Write-Host 'RAM Information not found.' -ForegroundColor Cyan } Else { $formattedTable = $script:Tests.MatchingMemory.RAMInfo | Format-Table -AutoSize | Out-String $indentedTable = $formattedTable -split "$([Environment]::NewLine)" | ForEach-Object { " $_" } $indentedTable | ForEach-Object { Write-Host $_ -ForegroundColor White } } '@ } "GetMemorySpeed" = @{ 'TestPassed' = $null 'RAMSpeed' = $null 'TestFailMsg' = @' Write-Host "$([Environment]::NewLine) RAM Info not found." -ForegroundColor Yellow '@ 'TestPassedMsg' = @' Write-Host "$([Environment]::NewLine)[INFO] " -NoNewLine Write-Host 'RAM is currently running at ' -NoNewLine -ForegroundColor Cyan Write-Host ("{0} MHz" -f [int]($script:Tests.GetMemorySpeed.RAMSpeed * 2)) -ForegroundColor White '@ } "DomainTest" = @{ 'TestPassed' = $null 'DomainList' = @( [PSCustomObject]@{ RequiredDomains = 'steamcdn-a.akamaihd.net'; PassedTest = $null }, [PSCustomObject]@{ RequiredDomains = 'api.live.prod.thehelldiversgame.com'; PassedTest = $null }, [PSCustomObject]@{ RequiredDomains = 'cluster-a.playfabapi.com'; PassedTest = $null }, [PSCustomObject]@{ RequiredDomains = 'gameguard.co.kr'; PassedTest = $null }, [PSCustomObject]@{ RequiredDomains = 'gameguard.thehelldiversgame.com'; PassedTest = $null }, [PSCustomObject]@{ RequiredDomains = 'mgr.gameguard.co.kr'; PassedTest = $null }, [PSCustomObject]@{ RequiredDomains = 'ocsp.digicert.com'; PassedTest = $null }, [PSCustomObject]@{ RequiredDomains = 'helldivers2.playfabapi.com'; PassedTest = $null }, [PSCustomObject]@{ RequiredDomains = 'pss-cloud.net'; PassedTest = $null }, [PSCustomObject]@{ RequiredDomains = 'steamcommunity.com'; PassedTest = $null }, [PSCustomObject]@{ RequiredDomains = 'steamcontent.com'; PassedTest = $null }, [PSCustomObject]@{ RequiredDomains = 'steamgames.com'; PassedTest = $null }, [PSCustomObject]@{ RequiredDomains = 'api.steampowered.com'; PassedTest = $null }, [PSCustomObject]@{ RequiredDomains = 'steamstatic.com'; PassedTest = $null }, [PSCustomObject]@{ RequiredDomains = 'steamusercontent.com'; PassedTest = $null }, [PSCustomObject]@{ RequiredDomains = 'testament.api.wwsga.me'; PassedTest = $null } ) 'TestFailMsg' = @' Write-Host "$([Environment]::NewLine)[FAIL] " -ForegroundColor Red -NoNewLine Write-Host "The following URLs failed to resolve with DNS" -ForegroundColor Yellow $script:Tests.DomainTest.DomainList | Where-Object { $_.PassedTest -ne $true } | ForEach-Object { " $($_.RequiredDomains)" } | Write-Host -ForegroundColor White '@ } "FirewallRules" = @{ 'TestPassed' = $null 'Rules' = @( [PSCustomObject]@{ RuleName = 'Inbound TCP Rule'; PassedTest = $null; CorrectName = $null } [PSCustomObject]@{ RuleName = 'Inbound UDP Rule'; PassedTest = $null; CorrectName = $null } ) 'TestFailMsg' = @' $rulesPassed = -not ($script:Tests.FirewallRules.Rules | Where-Object {$_.PassedTest -ne $true }) $namesPassed = -not ($script:Tests.FirewallRules.Rules | Where-Object {$_.CorrectName -eq $false }) if($rulesPassed -eq $false) { Write-Host "$([Environment]::NewLine)[FAIL] " -ForegroundColor Red -NoNewLine Write-Host "The Windows Firewall is missing the following required rules: " -ForegroundColor Yellow $script:Tests.FirewallRules.Rules | Where-Object {$_.PassedTest -ne $true } | ForEach-Object { " Helldivers 2 $($_.Rulename)" } | Write-Host -ForegroundColor White Start-Process wf.msc } if($namesPassed -eq $false) { Write-Host "$([Environment]::NewLine)[WARN] " -ForegroundColor Yellow -NoNewLine Write-Host "The following Windows Firewall rules do not have the correct name:" -ForegroundColor Yellow $script:Tests.FirewallRules.Rules | Where-Object {$_.CorrectName -ne $true -and $_.PassedTest -eq $true } | ForEach-Object { " Helldivers 2 $($_.Rulename)" } | Write-Host -ForegroundColor White } '@ } "GameMods" = @{ 'TestPassed' = $null 'TestFailMsg' = @' Write-Host "$([Environment]::NewLine)[FAIL] " -ForegroundColor Red -NoNewLine Write-Host 'Mods were detected!' -ForegroundColor Yellow Write-Host ' Use option ' -ForegroundColor Cyan -NoNewLine Write-Host 'Q'-ForegroundColor White -BackgroundColor Black -NoNewLine Write-Host ' under the Clear Data menu to attempt removal.' -ForegroundColor Cyan '@ } "PageFileEnabled" = @{ 'TestPassed' = $null 'TestFailMsg' = @' Write-Host "$([Environment]::NewLine)[WARN] " -ForegroundColor Yellow -NoNewLine Write-Host 'Your page file is set to zero. This may cause the game to crash on launch.' -ForegroundColor Cyan '@ } "SecureBootEnabled" = @{ 'TestPassed' = $null 'SecureBootNotSupported' = $null 'TestFailMsg' = @' Write-Host "$([Environment]::NewLine)[WARN] " -ForegroundColor Yellow -NoNewLine If ( $script:Tests.SecureBootEnabled.SecureBootNotSupported -eq $true ) { Write-Host 'Secure Boot is not supported on this platform. If you experience constant GameGuard errors, ensure that no unverified drivers are loaded at boot.' } Write-Host 'Secure Boot is disabled! Can cause GameGuard errors & disables Above 4G Decoding/Nvidia Resizable BAR/AMD SAM on Windows 11.' -ForegroundColor Cyan '@ } "SystemClockAccurate" = @{ 'TestPassed' = $null 'TestFailMsg' = @' Write-Host "$([Environment]::NewLine)[FAIL] " -ForegroundColor Red -NoNewLine Write-Host 'Your time and/or date is inaccurate. This will cause connection issues.' -ForegroundColor Yellow '@ } "VSyncDisabled" = @{ 'TestPassed' = $null 'TestFailMsg' = @' Write-Host "$([Environment]::NewLine)[WARN] " -ForegroundColor Yellow -NoNewLine Write-Host 'V-Sync is enabled in game settings. This may cause framerate issues.' -ForegroundColor Cyan '@ } "GameResolution" = @{ 'TestPassed' = $null 'WidthValue' = $null 'HeightValue' = $null 'TestFailMsg' = @' Write-Host "$([Environment]::NewLine)[WARN] " -ForegroundColor Yellow -NoNewLine Write-Host 'Screen resolution unable to be retrieved.' -ForegroundColor Cyan '@ 'TestPassedMsg' = @' Write-Host "$([Environment]::NewLine)[INFO] " -NoNewLine Write-Host "Output resolution is: " -ForegroundColor Cyan -NoNewLine Write-Host "$($script:Tests.GameResolution.WidthValue) x $($script:Tests.GameResolution.HeightValue)" If ( $($script:Tests.GameResolution.HeightValue) -le 1080 ) { Write-Host 'Your resolution is low. With a powerful GPU, this may increase CPU usage & hurt performance' -ForegroundColor Yellow } If ( $($script:Tests.GameResolution.HeightValue) -ge 2160 ) { Write-Host 'Your resolution is high. This may hurt FPS if your GPU isn''t powerful enough' -ForegroundColor Yellow } '@ } "RenderResolution" = @{ 'TestPassed' = $null 'WidthValue' = $null 'HeightValue' = $null 'TestFailMsg' = @' Write-Host "$([Environment]::NewLine)[WARN] " -ForegroundColor Yellow -NoNewLine Write-Host 'Render resolution unable to be retrieved.' -ForegroundColor Cyan '@ 'TestPassedMsg' = @' Write-Host "$([Environment]::NewLine)[INFO] " -NoNewLine Write-Host "Game engine render resolution is: " -ForegroundColor Cyan -NoNewLine Write-Host "$($script:Tests.RenderResolution.WidthValue) x $($script:Tests.RenderResolution.HeightValue)" If ( $($script:Tests.RenderResolution.HeightValue) -le 1080 ) { Write-Host 'Game rendering resolution is low. With a powerful GPU, this may increase CPU usage & hurt performance' -ForegroundColor Yellow } If ( $($script:Tests.RenderResolution.HeightValue) -ge 2160 ) { Write-Host 'Game rendering resolution is high. This may hurt FPS if your GPU isn''t powerful enough' -ForegroundColor Yellow } '@ } "NoVegaGPUs" = @{ 'TestPassed' = $null 'PCIDeviceVendor' = '1002' 'ApprovedDriverVersion' = '22.11.2' 'VegaPCIDevIDs' = @( "6860","6861","6862","6863","6864","6867","6868","6869","686a","686b","686c","686d","686e","687f", "66a0","66a1","66a2","66a3","66a7","66af", "69a0","69a1","69a2","69a3","69af", "15d8","15dd","1636","1638", "aaf8","ab18","ab20" ) 'TestFailMsg' = @' Write-Host "$([Environment]::NewLine)[FAIL] " -ForegroundColor Red -NoNewLine Write-Host "Your system has a Vega-based AMD GPU & is not using driver version " -ForegroundColor Yellow -NoNewLine Write-Host $($script:Tests.NoVegaGPUs.ApprovedDriverVersion) -NoNewLine Write-Host " for HD2 to fully function." -ForegroundColor Yellow Write-Host "$([Environment]::NewLine)Any other driver version will have various issues, from Terminid planets crashing, to the game crashing on the opening screen." -ForegroundColor Cyan '@ } "BTAGSDisabled" = @{ 'TestPassed' = $null 'TestFailMsg' = @' Write-Host "$([Environment]::NewLine)[WARN] " -ForegroundColor Yellow -NoNewLine Write-Host 'Bluetooth Audio Gateway (BTAG) Service is running.' -ForegroundColor Cyan Write-Host ' This will cause audio routing issues with ' -NoNewLine -ForegroundColor Cyan Write-Host 'Bluetooth Headphones.' -NoNewLine -ForegroundColor Yellow Write-Host "$([Environment]::NewLine) Toggle this service ON or OFF from the menu (Select Audio Options > option B)" -ForegroundColor Cyan '@ } "SSDFreeSpace" = @{ 'TestPassed' = $null 'TestFailMsg' = @' Write-Host "$([Environment]::NewLine)[WARN] " -ForegroundColor Yellow -NoNewLine Write-Host "SSD over 75% full - This may slow patching speed on drives without over-provisioning." '@ } "FreeDiskSpace" = @{ 'TestPassed' = $null 'TestFailMsg' = @' Write-Host "$([Environment]::NewLine)[FAIL] " -ForegroundColor Red -NoNewLine Write-Host "Drive has less than 30GB of free space. This may cause updates to fail." '@ } "USBGameDrive" = @{ 'TestPassed' = $null 'TestFailMsg' = @' Write-Host "$([Environment]::NewLine)[FAIL] " -ForegroundColor Red -NoNewLine Write-Host "Game is installed on a USB drive. This may slow game patching & cause audio stuttering." '@ } "FasterDriveAvailable" = @{ 'TestPassed' = $null 'fasterDrives' = $null 'TestFailMsg' = @' Write-Host "$([Environment]::NewLine)[INFO] " -NoNewLine Write-Host "There are faster drives available to install the game to:" $columns = @( @{ Name = 'Disk Name'; Expression = { $_.FriendlyName } } @{ Name = 'BusType'; Expression = { $_.BusType } } @{ Name = 'MediaType'; Expression = { $_.MediaType } } @{ Name = 'Size'; Expression = { Convert-Size $_.Size } } ) $($script:Tests.FasterDriveAvailable.fasterDrives) | Select-Object $columns | Format-Table -AutoSize '@ } "BetaBranchActive" = @{ 'TestPassed' = $null 'selectedBranch' = $null 'TestFailMsg' = @' Write-Host "$([Environment]::NewLine)[FAIL] " -NoNewLine -ForegroundColor Red Write-Host "Beta branch ($($script:Tests.BetaBranchActive.selectedBranch)) is active. All branches but 'public'/default will no longer recieve updates." -ForegroundColor Yellow '@ } } $NvidiaCodenames = @{ "Turing" = @( "1e03","1e04","1e07","1e2d","1e2e", "1e81","1e82","1e84","1e87","1e89", "1e90","1e91","1e93","1ec2","1ec7", "1ed0","1ed1","1ed3", "1f02","1f03","1f06","1f07","1f08", "1f10","1f11","1f12","1f14","1f15", "1f42","1f47","1f50","1f51","1f54","1f55" ) "Ampere" = @( "2203","2204","2205","2206","2207","2208","220a", "2216","222b","222f", "2414","2420","2460", "2482","2484","2486","2487","2488","2489","248c","248d","248e", "249c","249d","24a0","24ac","24ad","24af","24bf","24c7","24c8","24c9", "24dc","24dd","24e0", "2501","2503","2504","2507","2508","2509", "2520","2521","2523","252f","2544", "2560","2561","2563", "2582","2583","2584", "25a0","25a2","25a5","25a9","25ab","25ac","25ad","25af", "25e0","25e2","25e5","25ec","25ed" ) "AdaLovelace" = @( "2684","2685","2689", "2702","2703","2704","2705","2709", "2717","2757", "2782","2783","2786","2788", "27a0","27e0", "2803","2805","2808", "2820","2822","2860", "2882", "28a0","28a1","28a3", "28e0","28e1","28e3" ) "Blackwell" = @( "2b85","2b87", "2c02","2c05", "2c18","2c19","2c58","2c59", "2d04","2d05", "2d18","2d19","2d58","2d59", "2d83", "2d98","2dd8", "2f04", "2f18","2f58" ) } $NvidiaCodenameLookupTable = @{} # Autogenerate LookupTable ForEach ($generation in $NvidiaCodenames.Keys) { ForEach ($id in $NvidiaCodenames[$generation]) { $NvidiaCodenameLookupTable[$id] = $generation } } Function Show-Variables { If ($script:AppIDFound -eq $true) { Clear-Host Write-Host "AppID: $($script:AppID) is located in directory:" -ForegroundColor Green Write-Host $script:AppInstallPath -ForegroundColor White Write-Host "Current build of AppID $($script:AppID) is: $script:BuildID" -ForegroundColor Cyan } Else { Write-Host 'Error. AppID was not found.' -ForegroundColor Red } Return } # Function adapted from: https://stackoverflow.com/questions/20886243/press-any-key-to-continue#:~:text=Function%20pause%20(%24message) Function pause ($message) { # Check if running Powershell ISE If (Test-Path variable:global:psISE) { Add-Type -AssemblyName System.Windows.Forms [System.Windows.Forms.MessageBox]::Show("$message") } Else { Write-Host "$message"$([Environment]::NewLine) -ForegroundColor Yellow # Loop until the space bar is pressed Do { # Read a key press, suppress key display, and include key down events $x = $host.ui.RawUI.ReadKey("NoEcho,IncludeKeyDown") # Check if the Character property of the key press object is a space ' ' } While ($x.Character -ne ' ') Write-Host "" # Print a newline after the pause } } Function Install-EXE { param ( [Parameter(Mandatory = $true, Position = 0)] [ValidateNotNullOrEmpty()] [string] $DownloadURL, [Parameter(Mandatory = $true, Position = 1)] [ValidateNotNullOrEmpty()] [string] $DownloadPath, [Parameter(Mandatory = $true, Position = 2)] [ValidateNotNullOrEmpty()] [string] $FileName, [Parameter(Mandatory = $false, Position = 3)] [ValidateNotNullOrEmpty()] [string] $SignerCertificateThumbprint, [Parameter(Mandatory = $false, Position = 4)] [ValidateNotNullOrEmpty()] [string] $SHA256Hash, [Parameter(Mandatory = $true, Position = 5)] [ValidateNotNullOrEmpty()] [string] $CommonName ) $downloadedFile = Join-Path $DownloadPath $FileName # Logic check to ensure that either a Digital Certificate thumbprint is passed or a hash # to force verification of any binary that is executed $hasThumbprint = $PSBoundParameters.ContainsKey('SignerCertificateThumbprint') $hasHash = $PSBoundParameters.ContainsKey('SHA256Hash') If (-not ($hasThumbprint -xor $hasHash)) { Throw "You must provide either -SignerCertificateThumbprint OR -SHA256Hash (but not both)." } # Turn off progress bar (only affects this function--will revert automatically) to speed up download $ProgressPreference = 'SilentlyContinue' Write-Host "$([Environment]::NewLine)Downloading $CommonName..." -ForegroundColor Cyan Invoke-WebRequest $DownloadURL -OutFile $downloadedFile If ($hasThumbprint) { If (-not $script:DetectedOS -eq "Windows") { Throw "Certificate thumbprint validation requires Windows Authenticode support." } $sig = Get-AuthenticodeSignature -FilePath $downloadedFile If ($sig.Status -ne 'Valid') { Write-Host "Digital signature is invalid. Aborting $CommonName" -ForegroundColor Yellow Remove-Item -Path $downloadedFile Return } If ($sig.SignerCertificate.Thumbprint -ne $SignerCertificateThumbprint) { Write-Host "Digital certificate thumbprint mismatch. Aborting $CommonName" -ForegroundColor Yellow Remove-Item -Path $downloadedFile Return } } If ($hasHash) { $fileHash = (Get-FileHash -Path $downloadedFile -Algorithm SHA256).Hash If ($fileHash -ne $SHA256Hash) { Write-Host "Installer file hash verification failed. Aborting $CommonName" -ForegroundColor Yellow Remove-Item -Path $downloadedFile Return } } Write-Host 'Installing... look for UAC prompts' -ForegroundColor Cyan $Error.Clear() Try { $installProcess = Start-Process $downloadedFile -ArgumentList "/q" -PassThru -Wait If ( $installProcess.ExitCode -ne 0) { Write-Host "$([Environment]::NewLine)UAC prompt was canceled, or another error occurred installing $CommonName$([Environment]::NewLine)" -ForegroundColor Red Remove-Item -Path $downloadedFile Return } } Catch { Write-Host "Error occurred installing $CommonName" -ForegroundColor Red } If (!$Error) { Write-Host "$CommonName installed successfully!" -ForegroundColor Green } Remove-Item -Path $downloadedFile } Function Reset-GameGuard { # Delete GameGuard files $Error.Clear() Try { Remove-Item -Path $script:AppInstallPath\bin\GameGuard\*.* } Catch { Write-Host ("Error occurred deleting GameGuard files in " + $script:AppInstallPath + "\bin\GameGuard") -ForegroundColor Red } If (!$Error) { Write-Host "Helldivers 2\bin\GameGuard cleared successfully!" -ForegroundColor Green } # Uninstall GameGuard $Error.Clear() Try { Start-Process $script:AppInstallPath\tools\gguninst.exe -Wait } Catch { Write-Host "Error occurred uninstalling GameGuard" -ForegroundColor Red } If (!$Error) { Write-Host "GameGuard Uninstalled Successfully" -ForegroundColor Green } # Install GameGuard $Error.Clear() Try { Start-Process $script:AppInstallPath\tools\GGSetup.exe -Wait } Catch { Write-Host "Error occurred installing GameGuard" -ForegroundColor Red } If (!$Error) { Write-Host "GameGuard installed successfully"$([Environment]::NewLine) -ForegroundColor Green } Return } Function Remove-HD2AppData { $paths = @( Get-HD2ConfigPath -All ) If ($paths.Count -gt 1) { Write-Host "Warning: Multiple Helldivers 2 config folders detected. This can be normal on a Linux-based OS" -ForegroundColor Yellow } If ($paths.Count -gt 0) { $oldPref = $ProgressPreference $ProgressPreference = 'SilentlyContinue' Foreach ($path in $paths) { If (-Not (Test-Path -LiteralPath $path)) { Write-Host "Path not found: $path" -ForegroundColor Yellow Continue } Write-Host "Clearing: $path" -ForegroundColor Cyan Try { Get-ChildItem -LiteralPath $path -Force -Recurse | Remove-Item -Force -Recurse Write-Host "Cleared: $path" -ForegroundColor Green } Catch { Write-Host "Error deleting contents of $path" -ForegroundColor Red Write-Host $_.Exception.Message -ForegroundColor DarkRed } } $ProgressPreference = $oldPref Write-Host "Now please use Steam's " -NoNewLine -ForegroundColor Cyan Write-Host 'Verify Integrity of Game Files ' -NoNewLine Write-Host 'function.' -ForegroundColor Cyan } Else { Write-Host "Helldivers 2 AppData path not found." -ForegroundColor Yellow } Return } Function Get-IsProcessRunning { [CmdletBinding()] Param( [Parameter(Mandatory = $True)] [Object[]]$InputObject ) If (Get-Process -ProcessName $InputObject.ProcessName -ErrorAction SilentlyContinue) { Write-Host $InputObject.ErrorMsg -ForegroundColor Red Pause 'Press [SPACEBAR] to Exit...' Exit } } Function Uninstall-VCRedist { # List of Visual C++ Redistributables to uninstall $redistributables = @( 'Microsoft Visual C++ 2012 Redistributable (x64)', 'Microsoft Visual C++ 2013 Redistributable (x64)', 'Microsoft Visual C++ 2015-2022 Redistributable (x64)', 'Microsoft Visual C++ v14 Redistributable (x64)' ) ForEach ($programName in $redistributables) { $programlist = @($script:InstalledProgramsList | Where-Object { $_.DisplayName -like "$programName*" }) Write-Host "$([Environment]::NewLine)⚠️ Please restart the computer once this process completes." -ForegroundColor Yellow If ($programlist.Count -gt 0) { ForEach ( $program in $programlist ) { Write-Host $program.QuietUninstallString -ForegroundColor Cyan Try { Invoke-Expression "& $($program.QuietUninstallString.ToString())" Write-Host "Uninstallation of $programName completed." } Catch { Write-Host "Failed to uninstall $programName $_" -ForegroundColor Red } } } Else { Write-Host "Program $programName not found." } } } Function Install-VCRedist { Pause "$([Environment]::NewLine) ⚠️ Make sure you used Reset/Toggle Components >> Option U to uninstall current VC++ Redists before using this option... [SPACEBAR] to continue" -ForegroundColor Yellow Pause "$([Environment]::NewLine) This function will likely cause your computer to restart. Save any work before continuing... [SPACEBAR] to continue" -ForegroundColor Cyan Install-EXE -DownloadURL 'https://download.microsoft.com/download/1/6/B/16B06F60-3B20-4FF2-B699-5E9B7962F9AE/VSU_4/vcredist_x64.exe' ` -DownloadPath ((New-Object -ComObject Shell.Application).Namespace('shell:Downloads').Self.Path) -FileName 'VisualC++Redist2012.exe' ` -SHA256Hash '681BE3E5BA9FD3DA02C09D7E565ADFA078640ED66A0D58583EFAD2C1E3CC4064' -CommonName '2012 Visual C++ Redistributable' Install-EXE -DownloadURL 'https://download.microsoft.com/download/2/E/6/2E61CFA4-993B-4DD4-91DA-3737CD5CD6E3/vcredist_x64.exe' ` -DownloadPath ((New-Object -ComObject Shell.Application).Namespace('shell:Downloads').Self.Path) -FileName 'VisualC++Redist2013.exe' ` -SHA256Hash 'E554425243E3E8CA1CD5FE550DB41E6FA58A007C74FAD400274B128452F38FB8' -CommonName '2013 Visual C++ Redistributable' Install-EXE -DownloadURL 'https://aka.ms/vc14/vc_redist.x64.exe' ` -DownloadPath ((New-Object -ComObject Shell.Application).Namespace('shell:Downloads').Self.Path) -FileName 'VisualC++v14Redist.exe' ` -SignerCertificateThumbprint '3F56A45111684D454E231CFDC4DA5C8D370F9816' -CommonName 'Microsoft Visual C++ v14 Redistributable' Pause "$([Environment]::NewLine)Please restart the computer before continuing... [SPACEBAR] to continue" -ForegroundColor Yellow Exit } Function Switch-GameInput { Try { $gameInputSvc = Get-Service -Name "GameInputSvc" } Catch { Write-Host "GameInput is not installed." -ForegroundColor Green Return } If ( $gameInputSvc.StartType -ne "Disabled" ) { Write-Host "Disabling GameInput..." -ForegroundColor Cyan Stop-Service "GameInputSvc" Set-Service "GameInputSvc" -StartupType Disabled Write-Host "GameInput now Disabled." -ForegroundColor Green Return } Else { If( $gameInputSvc.StartType -ne "Enabled" ) { Write-Host "Enabling GameInput..." -ForegroundColor Cyan Set-Service "GameInputSvc" -StartupType Manual Start-Service "GameInputSvc" Write-Host "GameInput now Enabled." -ForegroundColor Yellow Return } } } Function Find-BlacklistedDrivers { $BadDeviceList = @('A-Volute', 'Hamachi', 'Nahimic', 'LogMeIn Hamachi', 'Sonic') $FoundBlacklistedDevice = $false Write-Host "$([Environment]::NewLine)Checking for devices that are known to cause issues..." -ForegroundColor Cyan -NoNewLine $DeviceDatabase = Get-PnpDevice # Check for blacklisted devices ForEach ($device in $DeviceDatabase) { ForEach ($badDevice in $BadDeviceList) { If ($device.FriendlyName -like "$badDevice*" -and $device.Status -eq "OK") { Write-Host ("$([Environment]::NewLine)⚠️ " + $device.FriendlyName + " device detected! Known compatibility issues! Please disable using Device Manager.") -ForegroundColor Red -NoNewLine $FoundBlacklistedDevice = $true Break # Exit the inner loop if a bad device is found } } } If (-not $FoundBlacklistedDevice) { Write-Host " no problematic devices found." -ForegroundColor Green } # Check for missing critical drivers (AMD and Intel only) $MissingDriverPresentCounter = ($DeviceDatabase | Where-Object { $_.Present -eq $true -and $_.InstanceId -match "VEN_1022|VEN_8086" -and ( $_.FriendlyName -match "Base System Device|Unknown" -or $_.Status -eq 'Unknown' ) } | Measure-Object).Count $MissingDriverDisconnectedCounter = ($DeviceDatabase | Where-Object { $_.Present -eq $false -and $_.InstanceId -match "VEN_1022|VEN_8086" -and ( $_.FriendlyName -match "Base System Device|Unknown" -or $_.Status -eq 'Unknown' ) } | Measure-Object).Count If ( $MissingDriverPresentCounter -gt 0 ) { Write-Host "$([Environment]::NewLine)⚠️You are missing critical AMD and/or Intel drivers." -ForegroundColor Yellow Write-Host "Please install them from your motherboard manufacturer or OEM system support site." -ForegroundColor Yellow } If ( $MissingDriverDisconnectedCounter -gt 2 ) { Write-Host "$([Environment]::NewLine)ℹ️ It appears your motherboard/CPU was upgraded without re-installing Windows." -ForegroundColor Yellow Write-Host "If this applies to you, recommend using the Reset Windows feature or re-installing Windows." -ForegroundColor Yellow } Return } Function Test-BadPrinters { # Get the Print Spooler service status $spoolerService = Get-Service -Name "Spooler" If ($spoolerService.StartType -ne "Disabled") { If ($spoolerService.Status -ne "Running") { # Restart the Print Spooler service if it's not running and not disabled Start-Service -Name "Spooler" } Get-Printer | ForEach-Object { If ($_.Name -eq 'OneNote for Windows 10') { $script:Tests.BadPrinter.TestPassed = $false } } $script:Tests.BadPrinter.TestPassed = $script:Tests.BadPrinter.TestPassed -ne $false } Else { $script:Tests.BadPrinter.TestPassed = $true } } Function Find-CPUInfo { If ($script:DetectedOS -eq 'Windows') { $script:myCPU = (Get-CimInstance -ClassName Win32_Processor).Name.Trim() } If ($script:DetectedOS -eq 'Linux') { $script:myCPU = (Get-Content /proc/cpuinfo | Where-Object { $_ -match '^model name' } | Select-Object -First 1).Split(':')[1].Trim() } If ( $script:myCPU.Contains('Intel') ) { ForEach ($cpuModel in $script:Tests.IntelMicrocodeCheck.AffectedModels) { If (($script:myCPU).Contains($cpuModel)) { $pattern = "Microcode Revision\s+(0x[0-9A-Fa-f]+)" $script:runningMicrocode = ($script:HardwareInfoText | Select-String -Pattern $pattern | Select-Object -First 1).Matches[0].Groups[1].Value If ( $script:runningMicrocode -match $script:Tests.IntelMicrocodeCheck.LatestMicrocode[0] -or $script:runningMicrocode -match $script:Tests.IntelMicrocodeCheck.LatestMicrocode[1] ) { $script:Tests.IntelMicrocodeCheck.TestPassed = $true Invoke-Expression $script:Tests.IntelMicrocodeCheck.TestPassedIntelMsg Return } Else { $script:Tests.IntelMicrocodeCheck.TestPassed = $false Return } } } } $script:Tests.IntelMicrocodeCheck.TestPassed = $true Invoke-Expression $script:Tests.IntelMicrocodeCheck.NotApplicableMsg Return } Function Show-MotherboardInfo { If ($script:DetectedOS -eq 'Windows') { $baseboard = Get-CimInstance -ClassName Win32_BaseBoard -ErrorAction SilentlyContinue $bios = Get-CimInstance -ClassName Win32_BIOS -ErrorAction SilentlyContinue $motherboardInfo = @() function Get-SafeString { param($Object, $Property) if ($null -eq $Object) { return "Unknown" } $value = $Object.$Property if ([string]::IsNullOrWhiteSpace($value)) { return "Unknown" } $value.Trim() } function Get-SafeDate { param($Object, $Property) if ($null -eq $Object -or $null -eq $Object.$Property) { return "Unknown" } $Object.$Property.ToString("yyyy-MM-dd") } $motherboardInfo = @( [pscustomobject]@{ "Motherboard Info" = "Manufacturer: " + (Get-SafeString $baseBoard Manufacturer) "UEFI Info" = "SMBIOS Version: " + (Get-SafeString $bios SMBIOSBIOSVersion) } [pscustomobject]@{ "Motherboard Info" = "Product: " + (Get-SafeString $baseBoard Product) "UEFI Info" = "Manufacturer: " + (Get-SafeString $bios Manufacturer) } [pscustomobject]@{ "Motherboard Info" = "" "UEFI Info" = "BIOS Version: " + (Get-SafeString $bios Name) } [pscustomobject]@{ "Motherboard Info" = "" "UEFI Info" = "BIOS Release Date: " + (Get-SafeDate $bios ReleaseDate) } ) $motherboardInfo | Format-Table "Motherboard Info", "UEFI Info" -AutoSize } If ($script:DetectedOS -eq 'Linux') { $boardVendor = (Get-Content "/sys/devices/virtual/dmi/id/board_vendor" -ErrorAction SilentlyContinue).Trim() $boardName = (Get-Content "/sys/devices/virtual/dmi/id/board_name" -ErrorAction SilentlyContinue).Trim() $biosVendor = (Get-Content "/sys/devices/virtual/dmi/id/bios_vendor" -ErrorAction SilentlyContinue).Trim() $biosVersion = (Get-Content "/sys/devices/virtual/dmi/id/bios_version" -ErrorAction SilentlyContinue).Trim() $biosDate = (Get-Content "/sys/devices/virtual/dmi/id/bios_date" -ErrorAction SilentlyContinue).Trim() $motherboardInfo = @( [pscustomobject]@{ 'Motherboard Info' = "Manufacturer: $boardVendor" 'UEFI Info' = "SMBIOS Version: $biosVersion" } [pscustomobject]@{ 'Motherboard Info' = "Product: $boardName" 'UEFI Info' = "Manufacturer: $biosVendor" } [pscustomobject]@{ 'Motherboard Info' = "" 'UEFI Info' = "BIOS Version: $biosVersion" } [pscustomobject]@{ 'Motherboard Info' = "" 'UEFI Info' = "BIOS Release Date: $biosDate" } ) $motherboardInfo | Format-Table 'Motherboard Info', 'UEFI Info' -AutoSize } } Function Show-ISPInfo { Try { $ipInfo = Invoke-RestMethod -Uri "http://ip-api.com/json" -ErrorAction Stop -UseBasicParsing } Catch { Write-Host "Error: Could not retrieve ISP." -ForegroundColor Red Return } If ($ipInfo.status -ne "success") { Write-Host "Could not retrieve ISP information. The service returned an error: $($ipInfo.message)" -ForegroundColor Yellow Return } # Extract ASN and ISP $asn = ($ipInfo.as -split " ")[0] -replace "^AS","" $isp = $ipInfo.isp Write-Host "Your ISP is: " -NoNewLine -ForegroundColor Cyan Write-Host $isp Write-Host "Your ASN is: " -NoNewLine -ForegroundColor Cyan Write-Host $asn # --- Spamhaus ASN DROP check --- Try { $raw = Invoke-WebRequest "https://www.spamhaus.org/drop/asndrop.json" -UseBasicParsing # PowerShell Core returns bytes; convert to string safely $content = $raw.Content | ForEach-Object { $_.ToString() } # Parse NDJSON (one JSON object per line) $asnDrop = $content -split "`n" | Where-Object { $_.Trim() -ne "" } | ForEach-Object { $_ | ConvertFrom-Json } } Catch { Write-Host "Warning: Could not retrieve Spamhaus ASN DROP list." -ForegroundColor Yellow Return } $isListed = $asnDrop | Where-Object { (Get-Member -InputObject $_ -Name "asn" -MemberType Properties) -and $_.asn -eq [int]$asn } If ($isListed) { Write-Host "⚠ WARNING: Your ASN ($asn) appears in the Spamhaus ASN DROP list!" -ForegroundColor Red } Else { Write-Host "Your ASN is NOT listed in the Spamhaus ASN DROP list." -ForegroundColor Green } } Function Show-WindowsGPUInfo { $gpus = Get-CimInstance -ClassName Win32_VideoController # Print GPU information ForEach ($gpu in $gpus) { $vendor = 'Generic' $driverVersion = $gpu.DriverVersion $archCodename = 'Not Identified' If ( $gpu.Name.Contains( 'AMD' ) ) { $vendor = 'AMD' If ( [bool]($script:Tests.NoVegaGPUs.VegaPCIDevIDs | Where-Object { $gpu.PNPDeviceID -match "DEV_$_" } | Select-Object -First 1) ) { $archCodename = 'Vega' } Try { $driverVersion = (Get-ItemProperty -Path "HKLM:\SOFTWARE\ATI Technologies\Install" -Name RadeonSoftwareVersion).RadeonSoftwareVersion } Catch { $driverVersion = $gpu.DriverVersion + ' ( Windows Driver Version Format ) ' } } ElseIf ( $gpu.Name.Contains( 'NVIDIA' ) ) { $vendor = 'NVIDIA' If ($gpu.PNPDeviceID -match "DEV_([0-9A-Fa-f]{4})") { $deviceID = $matches[1] } Else { $deviceID = $null } If ($deviceID) { $archCodename = $NvidiaCodenameLookupTable[$deviceID] } Try { $process = New-Object System.Diagnostics.Process $process.StartInfo = New-Object System.Diagnostics.ProcessStartInfo $process.StartInfo.FileName = "nvidia-smi" $process.StartInfo.Arguments = "--query-gpu=driver_version --format=csv,noheader" $process.StartInfo.RedirectStandardOutput = $true $process.StartInfo.UseShellExecute = $false $process.StartInfo.CreateNoWindow = $true # Start the process $process.Start() | Out-Null # Read the output as a string $driverVersion = (New-Object System.IO.StreamReader($process.StandardOutput.BaseStream, [System.Text.Encoding]::UTF8)).ReadToEnd().Trim() $process.WaitForExit() } Catch { $driverVersion = $gpu.DriverVersion + ' ( Windows Driver Version Format ) ' } } ElseIf ( $gpu.Name.Contains( 'INTEL(R)' ) ) { $vendor = "Intel" Try { $driverVersion = (Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\igfx\Parameters" -Name "DriverVersion").DriverVersion } Catch { $driverVersion += " (Windows Format)" } } $script:systemInfo["GPUInfo"] += @{ vendor = $vendor driverVersion = $driverVersion archCodename = $archCodename } Write-Host "-------------------------------------" Write-Host " GPU Model: $($gpu.Name)" Write-Host " Codename: $($script:SystemInfo["GPUInfo"][-1].archCodename)" Write-Host " Drvr Ver.: $($script:SystemInfo["GPUInfo"][-1].DriverVersion)" Write-Host " Status: " -NoNewLine If ( $gpu.Status -ne 'OK' ) { Write-Host $gpu.Status -ForegroundColor Red } Else { Write-Host $gpu.Status -ForegroundColor Green } Write-Host "-------------------------------------" } } Function Show-LinuxGPUInfo { $gpuLines = lspci -mm -nn | Select-String -Pattern 'VGA|3D|Display' $gpus = ForEach ($line in $gpuLines) { $parts = $line.ToString().Split('"') | ForEach-Object { $_.Trim() } [pscustomobject]@{ Name = $parts[5] # GPU model Vendor = $parts[3] # Vendor name RawLine = $line.ToString() } } ForEach ($gpu in $gpus) { $vendor = "Generic" $driverVersion = "Unknown" $archCodename = "Not Identified" $status = "Unknown" # ------------------------- # AMD # ------------------------- If ($gpu.Vendor -match "AMD|Advanced Micro Devices") { $vendor = "AMD" Try { $driverVersion = ( glxinfo -B 2>$null | Select-String "OpenGL version string" ).ToString().Split(":")[1].Trim() } Catch { $driverVersion = "Unknown (No glxinfo)" } # Detect Vega by PCI ID $pciID = $null If ($gpu.RawLine -match " \[(.*?)\] ") { $pciID = $matches[1] } If ($pciID -and $script:Tests.NoVegaGPUs.VegaPCIDevIDs -contains $pciID) { $archCodename = "Vega" } Try { glxinfo -B 2>$null | Out-Null $status = "OK" } Catch { $status = "Driver Missing" } } ElseIf ($gpu.Vendor -match "NVIDIA") { $vendor = "NVIDIA" $deviceID = $null If ($gpu.RawLine -match "Device ([0-9A-Fa-f]{4})") { $deviceID = $matches[1] } If ($deviceID -and $NvidiaCodenameLookupTable.ContainsKey($deviceID)) { $archCodename = $NvidiaCodenameLookupTable[$deviceID] } Try { $driverVersion = ( nvidia-smi --query-gpu=driver_version --format=csv,noheader 2>$null ).Trim() } Catch { $driverVersion = "Unknown (no nvidia-smi)" } Try { nvidia-smi -L 2>$null | Out-Null $status = "OK" } Catch { $status = "Driver Missing" } } ElseIf ($gpu.Vendor -match "Intel") { $vendor = "Intel" Try { $driverVersion = ( glxinfo -B 2>$null | Select-String "OpenGL version string" ).ToString().Split(":")[1].Trim() } Catch { $driverVersion = "Unknown (no glxinfo)" } Try { glxinfo -B 2>$null | Out-Null $status = "OK" } Catch { $status = "Driver Missing" } } $script:SystemInfo["GPUInfo"] += @{ vendor = $vendor driverVersion = $driverVersion archCodename = $archCodename status = $status } Write-Host "-------------------------------------" Write-Host " GPU Model: $($gpu.Name)" Write-Host " Codename: $archCodename" Write-Host " Drvr Ver.: $driverVersion" Write-Host " Status: " -NoNewLine If ($status -eq "OK") { Write-Host $status -ForegroundColor Green } Else { Write-Host $status -ForegroundColor Red } Write-Host "-------------------------------------" } } Function Show-OSInfo { If ( $script:DetectedOS -eq 'Windows') { $script:OSVersion = (Get-CimInstance -ClassName Win32_OperatingSystem).Caption } If ( $script:DetectedOs -eq 'Linux' ) { $osRelease = Get-Content /etc/os-release | ForEach-Object { $parts = $_.Split("=") If ($parts.Count -eq 2) { [pscustomobject]@{ Key = $parts[0]; Value = $parts[1].Trim('"') } } } $script:OSVersion = ($osRelease | Where-Object { $_.Key -eq "PRETTY_NAME" }).Value } Write-Host ($([Environment]::NewLine)+'Operating System:').Trim() -NoNewLine -ForegroundColor Cyan Write-Host '' $script:OSversion } Function Show-GameLaunchOptions { $script:localconfigVDF = Join-Path -Path $script:mostRecentSteamUserProfilePath -ChildPath 'config\localconfig.vdf' If (-not (Test-Path $script:localconfigVDF)) { Write-Host "Error: File not found at $script:localconfigVDF" -ForegroundColor Red Return } $localconfigData = $null try { $localconfigData = Get-Content -Path $script:localconfigVDF -Raw -Encoding UTF8 } catch { Write-Host "[WARN] " -NoNewline -ForegroundColor Yellow Write-Host "Error reading $script:localconfigVDF" Write-Host "Skipping Launch Options check..." return; } $ParsedConfig = Read-VDF $localconfigData $KeyPath = @( "UserLocalConfigStore", "Software", "Valve", "Steam", "apps", $script:AppID.ToString() ) $HD2ConfigData = Get-VDFValue -Root $ParsedConfig -Path $KeyPath if($null -eq $HD2ConfigData) { Write-Host "Could not locate Helldivers 2 data in $script:localconfigVDF." -ForegroundColor Yellow } Else { $HD2LaunchOptions = $HD2ConfigData["LaunchOptions"] if([string]::IsNullOrWhiteSpace($HD2LaunchOptions)) { Write-Host "No launch options currently in use." } Else { Write-Host "HD2 Launch Options: " -NoNewline -ForegroundColor Cyan Write-Host $HD2LaunchOptions -ForegroundColor $(If ($HD2LaunchOptions -match '--use-d3d11') { 'Yellow' } Else { 'White' }) } Write-Host 'Launch options retrieved from LAST USED Steam Profile' } } Function Show-WindowsPowerPlan { Try { $script:PowerPlan = (Get-CimInstance -Namespace root\cimv2\power -ClassName Win32_PowerPlan -Filter "IsActive = True").ElementName } Catch { $script:PowerPlan = 'Error retrieving Power Plan' } Write-Host 'Active Windows Power Plan: ' -NoNewLine -ForegroundColor Cyan Write-Host $script:PowerPlan } Function Show-LinuxPowerPlan { Try { # Governor path $govPath = "/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor" # Detect CPU frequency driver $cpuDriverPath = "/sys/devices/system/cpu/cpu0/cpufreq/scaling_driver" $cpuDriver = If (Test-Path $cpuDriverPath) { (Get-Content $cpuDriverPath).Trim() } Else { "unknown" } # Read governor If (Test-Path $govPath) { $governor = (Get-Content $govPath).Trim() } Else { $governor = "Unavailable" } # Interpret based on driver If ($cpuDriver -eq "amd_pstate" -or "amd_pstate_epp") { Switch ($governor) { "powersave" { # This is the correct mode for amd_pstate $script:PowerPlan = "powersave (correct mode for amd_pstate)" } "performance" { # This is NOT the recommended mode $script:PowerPlan = "performance (not recommended under amd_pstate)" } Default { $script:PowerPlan = "$governor (amd_pstate mode)" } } } Else { # Normal ACPI cpufreq interpretation $script:PowerPlan = $governor } } Catch { $script:PowerPlan = "Error retrieving CPU governor" } Write-Host "Active Linux CPU Governor: " -NoNewLine -ForegroundColor Cyan Write-Host $script:PowerPlan } Function Test-VegaGPUDriver { [OutputType([bool])] $VegaWrongDriver = @($script:SystemInfo.GPUInfo | Where-Object { $_.vendor -eq 'AMD' -and $_.archCodename -eq 'Vega' -and $_.driverVersion -and $_.driverVersion -ne $script:Tests.NoVegaGPUs.ApprovedDriverVersion }) Return ($VegaWrongDriver.Count -eq 0) } Function Test-AVX2 { # Check for AVX2 # Define the pattern to match the line $pattern = "^\tInstructions\ssets\t.*AVX2" $script:Tests.AVX2.TestPassed = [bool]($script:HardwareInfoText | Select-String -Pattern $pattern) } Function Get-MemorySpeed { # RAM Speed $linepattern = '^Memory Frequency.*$' $freqpattern = '(\d{4}(?:\.\d+)?)\s*MHz' # Find and display lines matching the pattern $match = $script:HardwareInfoText | Select-String -Pattern $linepattern If ($match) { $speed = [regex]::Match($match.Line, $freqpattern).Groups[1].Value If ($speed) { $script:Tests.GetMemorySpeed.RAMSpeed = [double]$speed $script:Tests.GetMemorySpeed.TestPassed = $true } Else { $script:Tests.GetMemorySpeed.TestPassed = $false } } Else { $script:Tests.GetMemorySpeed.TestPassed = $false } } Function Get-MemoryPartNumber { [CmdletBinding()] param ( [array]$Lines ) $dimmData = [System.Collections.Generic.List[PSCustomObject]]::new() $currentDimm = $null $index = 0 ForEach ($line in $Lines) { If ( $index -lt $Lines.Count - 1 ) { $index++ $nextLine = $Lines[$index] } # Check for the start of a new DIMM section If ( $line -match '^\s*DIMM\s*#\s*(\d+)' -and -not ($nextLine -match '^\s*SPD Registers')) { # Add previous DIMM object if it exists If ($currentDimm) { $dimmData.Add($currentDimm) } # Create a new, clean object for the DIMM just found $currentDimm = [PSCustomObject]@{ DIMM = $Matches[1] Size = 'N/A' PartNumber = 'N/A' } continue } # Check for an empty line to signal the end of a DIMM block ElseIf ($line -match '^\s*$') { If ($currentDimm) { $dimmData.Add($currentDimm) $currentDimm = $null } continue } # If we are not "inside" a DIMM block, ignore the line If (-not $currentDimm) { continue } # Capture memory details If ($line -match '^\s*Size\s+(.+)') { # Only grab the first Size entry If ($currentDimm.Size -eq 'N/A') { $currentDimm.Size = $Matches[1].Trim() } } ElseIf ($line -match '^\s*Part\s+number\s+(.+)') { $currentDimm.PartNumber = $Matches[1].Trim() } } # Add the very last DIMM that was processed If ($currentDimm) { $dimmData.Add($currentDimm) } # If no DIMMs were found, try parsing DMI data If ($dimmData.Count -eq 0) { For ($i = 0; $i -lt $Lines.Count; $i++) { If ($Lines[$i] -Match "^DMI Memory Device") { $designation = $null $typeFound = $false $sizeFound = $null # Get the designation from the next line If ($i + 1 -lt $Lines.Count) { $designationLine = $Lines[$i + 1] If ($designationLine -Match "designation\s+") { $designation = ($designationLine -Split "\s{1,}" | Select-Object -Last 1).Trim() } } # Look for the size and type in the following lines For ($j = $i + 1; $j -lt $Lines.Count; $j++) { If ($Lines[$j] -Match "^DMI Memory Device") { break } If ($Lines[$j] -Match "type\s+(DDR4|DDR5)") { $typeFound = $True } If ($Lines[$j] -Match "size\s" -and $typeFound) { $sizeFound = (($Lines[$j] -split "\s{1,}" | Select-Object -Last 2) -join ' ') If ($sizeFound -and $sizeFound -ne "NO DIMM") { $dimmData.Add([PSCustomObject]@{ DIMM = $designation Size = $sizeFound PartNumber = $null }) $typeFound = $false break } } } } } } # Validate results If ($dimmData) { $script:Tests.MatchingMemory.RAMInfo = $dimmData If ( ($dimmData.PartNumber | Select-Object -Unique | Measure-Object).Count -Eq 1 -And ($dimmData.Size | Select-Object -Unique | Measure-Object).Count -Eq 1 ) { $script:Tests.MatchingMemory.TestPassed = $True } Else { $script:Tests.MatchingMemory.TestPassed = $False } } Else { # Print RAM info only if all DIMMs have a Size If ($dimmData.Count -gt 0 -and ($dimmData | Where-Object { -not $_.Size }).Count -eq 0) { $script:Tests.MatchingMemory.NotFound = $False } Else { $script:Tests.MatchingMemory.NotFound = $True $script:Tests.MatchingMemory.TestPassed = $True } } } Function Get-HardwareInfo { If (-not (Test-Path $script:HellbombScriptDirectory)) { New-Item -Path $script:HellbombScriptDirectory -ItemType Directory -Force | Out-Null } # Define URLs and paths $timestamp = Get-Date -Format "yyyyMMdd-HHmmss" $CPUZUrl = "https://download.cpuid.com/cpu-z/cpu-z_2.18-en.zip" $CPUZZip = Join-Path -Path $script:HellbombScriptDirectory -ChildPath "cpu-z_2.18-en.zip" $CPUZExe = Join-Path -Path $script:HellbombScriptDirectory -ChildPath "cpuz_x64.exe" $CPUZFile = "cpuz_x64.exe" $HellbombScriptReportName = "CPUZHellbombReport-$timestamp" # Download and extract CPU-Z if it does not exist If (-not (Test-Path $CPUZExe)) { If (-Not (Test-Path $CPUZZip)) { Try { Invoke-WebRequest -Uri $CPUZUrl -OutFile $CPUZZip -ErrorAction Continue } Catch { Return Write-Error "Failed to download ${CPUZZip}: $_" } } If ( (Get-FileHash -Path $CPUZZip -Algorithm SHA256).Hash -ne '5F7175D0CBC692754F596F38EC12D17215EFB95FC911DEBF6F79C65DF1C8E1DC') { Remove-Item $CPUZZip Invoke-WebRequest -Uri $CPUZUrl -OutFile $CPUZZip -ErrorAction Continue } Try { Get-CPUZ -zipPath $CPUZZip -extractTo $script:HellbombScriptDirectory -targetFile $CPUZFile } Catch { Return Write-Error "CPU-Z extraction failed. Download $CPUZZip from https://download.cpuid.com/cpu-z/$CPUZFile and place in your Downloads folder." } } $CPUZSHA256 = (Get-FileHash -Path (Join-Path -Path $script:HellbombScriptDirectory -ChildPath $CPUZFile) -Algorithm SHA256).Hash If ( $CPUZSHA256 -ne 'B0D40B5EB26F053BCAB2A10C86ACD18862E8C3ED1FB715280AE15B1B5C2B652C' ) { Remove-Item $CPUZZip Remove-Item $CPUZFile Invoke-WebRequest -Uri $CPUZUrl -OutFile $CPUZZip -ErrorAction Stop Get-CPUZ -zipPath $CPUZZip -extractTo $script:HellbombScriptDirectory -targetFile $CPUZFile } # Run CPU-Z and dump report to file Write-Host "$([Environment]::NewLine)Scanning hardware using CPU-Z. Please wait..." -ForegroundColor Cyan -NoNewLine $psi = New-Object System.Diagnostics.ProcessStartInfo $psi.CreateNoWindow = $true $psi.UseShellExecute = $false $psi.RedirectStandardOutput = $true $psi.RedirectStandardError = $true $psi.FileName = (Join-Path -Path $script:HellbombScriptDirectory -ChildPath $CPUZFile) $psi.Arguments = @("-accepteula -txt=$HellbombScriptReportName") # Set encoding to UTF8 so that Unicode compilation doesn't break CPU-Z console output $psi.StandardOutputEncoding = [System.Text.Encoding]::UTF8 $process = New-Object System.Diagnostics.Process $process.StartInfo = $psi [void]$process.Start() $process.WaitForExit() $script:HardwareInfoText = Get-Content (Join-Path -Path $script:HellbombScriptDirectory -ChildPath ($HellbombScriptReportName + ".txt")) Write-Host ' complete!' } Function Get-CPUZ { param ($zipPath, $extractTo, $targetFile) Add-Type -AssemblyName System.IO.Compression.FileSystem Try { # Open the zip file $zip = [System.IO.Compression.ZipFile]::OpenRead($zipPath) # Find the target file in the zip archive $entry = $zip.Entries | Where-Object { $_.FullName -eq $targetFile } If ($entry) { # Extract the file manually using streams $cpuzExtractionPath = Join-Path -Path $extractTo -ChildPath $targetFile If (Test-Path $cpuzExtractionPath) { Remove-Item $cpuzExtractionPath -Force } $fileStream = [System.IO.File]::Create($cpuzExtractionPath) $entryStream = $entry.Open() $entryStream.CopyTo($fileStream) $fileStream.Close() $entryStream.Close() } Else { Write-Error "$targetFile not found in the zip file." } } Catch { Write-Error "Failed to extract ${targetFile}: $_" Throw } Finally { If ($null -ne $zip) { Try { $zip.Dispose() } Catch {} } } } Function Get-InstalledPrograms { # This portion modified from: # https://devblogs.microsoft.com/scripting/use-powershell-to-quickly-find-installed-software/ $installedPrograms = @() Write-Host "$([Environment]::NewLine)Gathering installed programs..." -ForegroundColor Cyan $UninstallPaths = @( "SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall", "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" ) $regKeys = @( [Microsoft.Win32.RegistryKey]::OpenBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, [Microsoft.Win32.RegistryView]::Registry64), [Microsoft.Win32.RegistryKey]::OpenBaseKey([Microsoft.Win32.RegistryHive]::CurrentUser, [Microsoft.Win32.RegistryView]::Registry64) ) ForEach ($baseKey in $regKeys) { ForEach ($path in $UninstallPaths) { $regKey = $baseKey.OpenSubKey($path) If ($regKey) { ForEach ($subKeyName in $regKey.GetSubKeyNames()) { $subKey = $regKey.OpenSubKey($subKeyName) If ($subKey) { $displayName = $subKey.GetValue("DisplayName") $displayVersion = $subKey.GetValue("DisplayVersion") -replace '^[a-zA-Z]+|[a-zA-Z]$', '' -replace '\s+', '' $installLocation = $subKey.GetValue("InstallLocation") $publisher = $subKey.GetValue("Publisher") $QuietUninstallString = $subKey.GetValue("QuietUninstallString") $UninstallString = $subKey.GetValue("UninstallString") If ($displayName) { $installedPrograms += [PSCustomObject]@{ DisplayName = $displayName DisplayVersion = If ($displayVersion) { Try { [System.Version]$displayVersion } Catch { '0.0.0' } } Else { '0.0.0' } InstallLocation = $installLocation Publisher = $publisher QuietUninstallString = $QuietUninstallString UninstallString = $UninstallString } } } } } } } Return $installedPrograms | Where-Object { $_.DisplayName } | Sort-Object DisplayName } Function Test-Programs { Write-Host "$([Environment]::NewLine)Checking for programs that interfere with Helldivers 2..." -ForegroundColor Cyan -NoNewLine $ProblematicPrograms = @( [PSCustomObject]@{ProgramName = 'AMD Chipset Software'; RecommendedVersion = '6.05.28.016'; Installed = $false; InstalledVersion = '0.0.0'; Notes = 'Your ver. may be SLIGHTLY older. Latest @ https://www.amd.com/en/support/download/drivers.html.' } [PSCustomObject]@{ProgramName = 'Avast Internet Security'; RecommendedVersion = '100.100'; Installed = $false; InstalledVersion = '0.0.0'; Notes = 'Can cause performance issues. Recommend uninstalling. Disabling when playing MAY resolve issues.' } [PSCustomObject]@{ProgramName = 'Avast Free Antivirus'; RecommendedVersion = '100.100'; Installed = $false; InstalledVersion = '0.0.0'; Notes = 'Can cause performance issues. Recommend uninstalling. Disabling when playing MAY resolve issues.' } [PSCustomObject]@{ProgramName = 'AVG Antivirus'; RecommendedVersion = '100.100'; Installed = $false; InstalledVersion = '0.0.0'; Notes = 'Can cause performance issues. Recommend uninstalling. Disabling when playing MAY resolve issues.' } [PSCustomObject]@{ProgramName = 'Cepstral SwiftTalker'; RecommendedVersion = '100.100'; Installed = $false; InstalledVersion = '0.0.0'; Notes = 'Known to cause crashes in the past.' } [PSCustomObject]@{ProgramName = 'cFosSpeed'; RecommendedVersion = '100.100'; Installed = $false; InstalledVersion = '0.0.0'; Notes = 'Uninstall. Unnecessary networking stack that causes network issues.' } [PSCustomObject]@{ProgramName = 'Cisco Webex'; RecommendedVersion = '100.100'; Installed = $false; InstalledVersion = '0.0.0'; Notes = 'If the process is running, Webex will control certain keyboard shortcuts. Close completely using Task Manager.' } [PSCustomObject]@{ProgramName = 'ESET Endpoint'; RecommendedVersion = '100.100'; Installed = $false; InstalledVersion = '0.0.0'; Notes = 'Can cause crashes. Please disable/add exclusions for *.des files in tools folder.' } [PSCustomObject]@{ProgramName = 'ESET File'; RecommendedVersion = '100.100'; Installed = $false; InstalledVersion = '0.0.0'; Notes = 'Can cause crashes. Please disable/add exclusions for *.des files in tools folder.' } [PSCustomObject]@{ProgramName = 'ESET Management'; RecommendedVersion = '100.100'; Installed = $false; InstalledVersion = '0.0.0'; Notes = 'Can cause crashes. Please disable/add exclusions for *.des files in tools folder.' } [PSCustomObject]@{ProgramName = 'ESET PROTECT'; RecommendedVersion = '100.100'; Installed = $false; InstalledVersion = '0.0.0'; Notes = 'Can cause crashes. Please disable/add exclusions for *.des files in tools folder.' } [PSCustomObject]@{ProgramName = 'ESET Rogue'; RecommendedVersion = '100.100'; Installed = $false; InstalledVersion = '0.0.0'; Notes = 'Can cause crashes. Please disable/add exclusions for *.des files in tools folder.' } [PSCustomObject]@{ProgramName = 'ESET Security'; RecommendedVersion = '100.100'; Installed = $false; InstalledVersion = '0.0.0'; Notes = 'Can cause crashes. Please disable/add exclusions for *.des files in tools folder.' } [PSCustomObject]@{ProgramName = 'EVGA Precision'; RecommendedVersion = '100.100'; Installed = $false; InstalledVersion = '0.0.0'; Notes = 'Reported to cause issues. Disabling the OSD may resolve the issue.' } [PSCustomObject]@{ProgramName = 'ExpressVPN'; RecommendedVersion = '100.100'; Installed = $false; InstalledVersion = '0.0.0'; Notes = 'Can cause networking issues. Open Device Manager and disable the adapter there.' } [PSCustomObject]@{ProgramName = 'GameFirst VI'; RecommendedVersion = '100.100'; Installed = $false; InstalledVersion = '0.0.0'; Notes = 'Uninstall. Unnecessary networking stack that causes network issues.' } [PSCustomObject]@{ProgramName = 'Gigabyte Speed'; RecommendedVersion = '100.100'; Installed = $false; InstalledVersion = '0.0.0'; Notes = 'Uninstall. Unnecessary networking stack that causes network issues.' } [PSCustomObject]@{ProgramName = 'Hamachi'; RecommendedVersion = '100.100'; Installed = $false; InstalledVersion = '0.0.0'; Notes = 'Breaks connectivity. Recommend uninstalling or disable IN DEVICE MANAGER.' } [PSCustomObject]@{ProgramName = 'iCue'; RecommendedVersion = '100.100'; Installed = $false; InstalledVersion = '0.0.0'; Notes = 'Outdated versions are known to cause issues.' } [PSCustomObject]@{ProgramName = 'Lunar Client'; RecommendedVersion = '100.100'; Installed = $false; InstalledVersion = '0.0.0'; Notes = 'Exit Lunar Client before launching HD2 to prevent connectivity issues.' } [PSCustomObject]@{ProgramName = 'Medal'; RecommendedVersion = '100.100'; Installed = $false; InstalledVersion = '0.0.0'; Notes = 'Can cause slowdowns, crashes, etc. Turn off/Disable/uninstall.' } [PSCustomObject]@{ProgramName = 'Microsoft GameInput'; RecommendedVersion = '3.2.138.0'; Installed = $false; InstalledVersion = '0.0.0'; Notes = 'This is an old version of GameInput. Crashes with Wwise audio component of HD2 & other games.' } [PSCustomObject]@{ProgramName = 'MSI Afterburner'; RecommendedVersion = '4.6.5'; Installed = $false; InstalledVersion = '0.0.0'; Notes = 'Outdated versions cause crashing & performance issues.' } [PSCustomObject]@{ProgramName = 'Mullvad VPN'; RecommendedVersion = '100.100'; Installed = $false; InstalledVersion = '0.0.0'; Notes = 'Causes connection issues. Recommend uninstall or disable in DEVICE MANAGER.' } [PSCustomObject]@{ProgramName = 'Nahimic'; RecommendedVersion = '100.100'; Installed = $false; InstalledVersion = '0.0.0'; Notes = 'Myriad of issues. Recommend removing all devices and services.' } [PSCustomObject]@{ProgramName = 'Norton 360'; RecommendedVersion = '100.100'; Installed = $false; InstalledVersion = '0.0.0'; Notes = 'Will destroy FPS if Game Optimizer is enabled. Disable Game Optimizer in Norton 360.' } [PSCustomObject]@{ProgramName = 'Outplayed'; RecommendedVersion = '100.100'; Installed = $false; InstalledVersion = '0.0.0'; Notes = 'Can cause stuttering & VRAM leaks. Disable Outplayed Autoclipping or disable/uninstall.' } [PSCustomObject]@{ProgramName = 'Overwolf'; RecommendedVersion = '100.100'; Installed = $false; InstalledVersion = '0.0.0'; Notes = 'Can cause stuttering & VRAM leaks. Disable Outplayed Autoclipping or disable/uninstall.' } [PSCustomObject]@{ProgramName = 'Process Lasso'; RecommendedVersion = '100.100'; Installed = $false; InstalledVersion = '0.0.0'; Notes = 'Causes threading and stability issues. Please uninstall.' } [PSCustomObject]@{ProgramName = 'Radmin'; RecommendedVersion = '100.100'; Installed = $false; InstalledVersion = '0.0.0'; Notes = 'Will cause network issues. Recommend uninstall or disable in DEVICE MANAGER.' } [PSCustomObject]@{ProgramName = 'Razer Cortex'; RecommendedVersion = '100.100'; Installed = $false; InstalledVersion = '0.0.0'; Notes = 'Causes severe performance issues. Must disable/uninstall.' } [PSCustomObject]@{ProgramName = 'Ryzen Master'; RecommendedVersion = '2.14.2.3341'; Installed = $false; InstalledVersion = '0.0.0'; Notes = 'Known to cause RAM leaks & general issues. Recommend uninstalling.' } [PSCustomObject]@{ProgramName = 'Samsung Magician'; RecommendedVersion = '8.1'; Installed = $false; InstalledVersion = '0.0.0'; Notes = 'Outdated versions break connectivity completely.' } [PSCustomObject]@{ProgramName = 'SmartByte Drivers and Services'; RecommendedVersion = '100.100'; Installed = $false; InstalledVersion = '0.0.0'; Notes = 'Inteferes with network traffic priorities. May cause connection issues.' } [PSCustomObject]@{ProgramName = 'Surfshark'; RecommendedVersion = '100.100'; Installed = $false; InstalledVersion = '0.0.0'; Notes = 'Will prevent connectivity. Recommend uninstall or disable IN DEVICE MANAGER.' } [PSCustomObject]@{ProgramName = 'Wallpaper Engine'; Installed = $false; RecommendedVersion = '100.100'; InstalledVersion = '0.0.0'; Notes = 'Can crash AMD GPUs in some instances. If setup improperly, can limit FPS of games.' } [PSCustomObject]@{ProgramName = 'Wargaming.net Game Center'; Installed = $false; RecommendedVersion = '100.100'; InstalledVersion = '0.0.0'; Notes = 'Reported to cause issues.' } [PSCustomObject]@{ProgramName = 'Webroot'; Installed = $false; RecommendedVersion = '100.100'; InstalledVersion = '0.0.0'; Notes = 'Causes low FPS. Uninstall or launch HD2 & THEN shutdown Webroot.' } [PSCustomObject]@{ProgramName = 'Wemod'; Installed = $false; RecommendedVersion = '100.100'; InstalledVersion = '0.0.0'; Notes = 'Has a kernel-level driver that enables cheats. Disable/Exit/Uninstall if having issues.' } [PSCustomObject]@{ProgramName = 'ZeroTier One'; Installed = $false; RecommendedVersion = '100.100'; InstalledVersion = '0.0.0'; Notes = 'Causes connectivity issues. Recommend uninstalling or disable IN DEVICE MANAGER.'}) # Avast Web Shield checks $regPath = "HKLM:\SOFTWARE\Avast Software\Avast\properties\WebShield\Common" $regName = "ProviderEnabled" If (Test-Path $regPath) { $regProps = Get-ItemProperty -Path $regPath If ($regProps.PSObject.Properties.Name -contains $regName) { If ($regProps.$regName -eq 1) { Write-Host "$([Environment]::NewLine)⚠️ Avast WebShield is enabled!" -ForegroundColor Yellow Write-Host 'Ensure an exception is added for ' -ForegroundColor Cyan -NoNewLine Write-Host 'https://microsoft.com ' -NoNewLine Write-Host 'to prevent HTTPS CRL access issues.' -ForegroundColor Cyan Write-Host 'More information can be found here: https://discord.com/channels/1102970375731691612/1218153537914273802/1273154218022408252' } } } # MSI AI LAN Manager checks $regPath = 'HKLM:\SOFTWARE\WOW6432Node\MSI\MSI Center\Component\LAN Manager' $exePath = (Join-Path "$env:ProgramFiles (x86)" 'MSI\MSI Center\LAN Manager\MSI_LAN_Manager_Tool.exe') If (Test-Path -LiteralPath $regPath) { If (Test-Path -LiteralPath $exePath -PathType Leaf) { Write-Host "$([Environment]::NewLine)⚠️ MSI AI LAN Manager module in MSI Center is installed!" -ForegroundColor Yellow Write-Host 'To prevent connectivity issues, open MSI Center and uninstall AI LAN Manager.' -ForegroundColor Cyan } } $bool = $false ForEach ($program in $ProblematicPrograms) { ForEach ($installedApp in $script:InstalledProgramsList) { $bool = $false If ($installedApp.DisplayName -like "*" + $program.ProgramName + "*" -and ([System.Version]$program.RecommendedVersion -gt [System.Version]$installedApp.DisplayVersion)) { $bool = $true Break } } If ($bool) { $program.Installed = $true $program.InstalledVersion = [System.Version]$installedApp.DisplayVersion } } $result = $null $result = $ProblematicPrograms | Where-Object { $_.Installed -eq $true } If ($null -ne $result) { Write-Host " found the following programs that are known to cause issues:$([Environment]::NewLine)" -ForegroundColor Yellow Write-Host ("{0,-33} {1,-20} {2,-35}" -f "Program Name", "Installed Version", "Notes") -ForegroundColor Cyan Write-Host ("{0,-33} {1,-20} {2,-35}" -f '--------------------------------', '-----------------', '------------------------------------------------------------------------------------------------') ForEach ($row in $result) { Write-Host '[FAIL] ' -ForegroundColor Red -NoNewLine Write-Host ("{0,-26}" -f $row.ProgramName) -ForegroundColor Yellow -NoNewLine Write-Host (" {0,-20} {1,-132}" -f $row.InstalledVersion, $row.Notes) } } Else { Write-Host 'Checks complete. No problematic programs found!' -ForegroundColor Green } Return } Function Get-SystemUptime { If ($script:DetectedOS -eq 'Windows') { # --- Windows logic unchanged --- $lastBoot = (Get-CimInstance -ClassName Win32_OperatingSystem).LastBootUpTime $uptime = [math]::Round(((Get-Date) - $lastBoot).TotalDays, 0) } ElseIf ($script:DetectedOS -eq 'Linux') { Try { $raw = Get-Content -Path "/proc/uptime" -ErrorAction Stop $seconds = [double]($raw.Split(" ")[0]) $uptime = [math]::Round(($seconds / 86400), 0) # convert seconds → days } Catch { Write-Host "Unable to read /proc/uptime" -ForegroundColor Yellow $uptime = $null } } Else { Write-Host "Unsupported OS detected." -ForegroundColor Red Return } If ($uptime -lt 1) { $script:Tests.LongSysUptime.TestPassed = $true } Else { $script:Tests.LongSysUptime.SystemUptime = $uptime $script:Tests.LongSysUptime.TestPassed = $false } } Function Test-SystemClockAccuracy { # Define the NTP server $NtpServer = "time.windows.com" # Query the NTP server for its time offset using a process to support the EXE $process = New-Object System.Diagnostics.Process $process.StartInfo = New-Object System.Diagnostics.ProcessStartInfo $process.StartInfo.FileName = "w32tm" $process.StartInfo.Arguments = "/stripchart /computer:$NtpServer /samples:1 /dataonly" $process.StartInfo.RedirectStandardOutput = $true $process.StartInfo.UseShellExecute = $false $process.StartInfo.CreateNoWindow = $true $process.Start() | Out-Null # Read the time output as a string $NtpQuery = New-Object System.IO.StreamReader($process.StandardOutput.BaseStream, [System.Text.Encoding]::UTF8) $NtpQuery = $NtpQuery.ReadToEnd().Trim() $process.WaitForExit() $OffsetString = $NtpQuery | Select-String "[+-]([\d]+)\.([\d]+)s" If ($OffsetString) { $OffsetValue = [Math]::Abs([double]$OffsetString.Matches[0].Groups[1].Value) If ($OffsetValue -lt 5.0) { $script:Tests.SystemClockAccurate.TestPassed = $true } Else { $script:Tests.SystemClockAccurate.TestPassed = $false } } } Function Test-Firewall { $HD2FirewallRuleName = "Helldivers$([char]0x2122) 2" try { $HD2FirewallApplicationFilters = Get-NetFirewallApplicationFilter -Program (Join-Path -Path $script:AppInstallPath -ChildPath "bin\helldivers2.exe") -ErrorAction SilentlyContinue } catch { $script:Tests.FirewallRules.TestPassed = $false return } foreach ($filter in $HD2FirewallApplicationFilters) { if($null -eq $filter) { continue } $associatedRule = Get-NetFirewallRule -AssociatedNetFirewallApplicationFilter $filter if($null -eq $associatedRule) { continue } if($associatedRule.Action -eq 'Allow' -and $associatedRule.Direction -eq 'Inbound' -and $associatedRule.Enabled -eq $true) { $ruleProtocol = ($associatedRule | Get-NetFirewallPortFilter).Protocol $ruleTestEntry = switch ($ruleProtocol) { "TCP" { $script:Tests.FirewallRules.Rules[0] } "UDP" { $script:Tests.FirewallRules.Rules[1] } Default {} } $ruleTestEntry.PassedTest = $true $ruleTestEntry.CorrectName = $associatedRule.DisplayName -eq $HD2FirewallRuleName } } $allTestsPassed = -not ($script:Tests.FirewallRules.Rules | Where-Object { $_.PassedTest -ne $true }) $allNamesCorrect = -not ($script:Tests.FirewallRules.Rules | Where-Object { $_.CorrectName -ne $true }) $script:Tests.FirewallRules.TestPassed = $allTestsPassed -and $allNamesCorrect } Function Test-CRL { # Adapted from: https://stackoverflow.com/questions/11531068/powershell-capturing-standard-out-and-error-with-process-object # This overly-complicated mess with curl is used to ensure that an HTTP and an HTTPS request are used. Invoke-WebRequest # will return false positives when it's actually broken. Clear-DnsClientCache $psi = New-Object System.Diagnostics.ProcessStartInfo $psi.CreateNoWindow = $true $psi.UseShellExecute = $false $psi.RedirectStandardOutput = $true $psi.RedirectStandardError = $true $psi.FileName = 'curl.exe' $psi.Arguments = @('-X HEAD -I http://www.microsoft.com/pkiops/crl/Microsoft%20Azure%20RSA%20TLS%20Issuing%20CA%2003.crl') # Set encoding to UTF8 so that Unicode compilation doesn't break curl arguments $psi.StandardOutputEncoding = [System.Text.Encoding]::UTF8 $process = New-Object System.Diagnostics.Process $process.StartInfo = $psi [void]$process.Start() $output = $process.StandardOutput.ReadToEnd() $process.WaitForExit() $output = $output.Split("$([Environment]::NewLine)") Write-Host 'HTTP CRL access ' -NoNewLine If ($output[0].Trim() -eq 'HTTP/1.1 200 OK') { Write-Host '[OK]' -ForegroundColor Green } Else { Write-Host '[FAIL]' -ForegroundColor Red Write-Host 'Security software may be blocking the connection.' -ForegroundColor Yellow } $psi.Arguments = @('-X HEAD -I https://www.microsoft.com/pkiops/crl/Microsoft%20Azure%20RSA%20TLS%20Issuing%20CA%2003.crl') $process = New-Object System.Diagnostics.Process $process.StartInfo = $psi [void]$process.Start() $output = $process.StandardOutput.ReadToEnd() $process.WaitForExit() $output = $output.Split("$([Environment]::NewLine)") Write-Host 'HTTPS CRL access ' -NoNewLine If ($output[0].Trim() -eq 'HTTP/1.1 200 OK') { Write-Host '[OK]' -ForegroundColor Green } Else { Write-Host '[FAIL]' -ForegroundColor Red Write-Host 'Anti-Virus WebShields can cause this issue. Please whitelist microsoft.com or disable them.' -ForegroundColor Yellow Write-Host 'Pi-Holes/DNS-blocking software can also cause this issue. Whitelist oneocsp.microsoft.com.' -ForegroundColor Yellow } Write-Host "OCSP Connection " -NoNewLine If ( Test-NetConnection 'oneocsp.microsoft.com' -ErrorAction SilentlyContinue -InformationLevel Quiet ) { Write-Host ' [OK]' -ForegroundColor Green } Else { Write-Host ' [FAIL]' -ForegroundColor Red } Write-Progress -Completed -Activity "make progress bar disappear" Test-ClientDnsConfig Return } Function Test-RequiredURLsWindows { Clear-DnsClientCache ForEach ($domain in $script:Tests.DomainTest.DomainList) { # If not running in ISE or old PowerShell, let's make it pretty If ((Get-Host).Name -ne 'Windows PowerShell ISE Host' -and (Get-Host).Version -ge '7.0.0') { $x, $y = [Console]::GetCursorPosition() -split '\D' -ne '' -as 'int[]' [Console]::SetCursorPosition(46 , $y) } If (Resolve-DnsName -Name $domain.RequiredDomains -DnsOnly -ErrorAction SilentlyContinue) { # Logic to handle intermittent domain connectivity. If it was marked false already, do not set to pass If ( $domain.PassedTest -ne $false ) { $domain.PassedTest = $true } } Else { $domain.PassedTest = $false } } # Filter and print RequiredDomains where PassedTest is false If ($script:Tests.DomainTest.DomainList | Where-Object { $_.PassedTest -ne $true }) { $script:Tests.DomainTest.TestPassed = $false } Else { $script:Tests.DomainTest.TestPassed = $true } } Function Test-RequiredURLsLinux { # Flush DNS cache if systemd-resolved is present If (Test-Path "/usr/bin/resolvectl") { resolvectl flush-caches 2>$null Foreach ($domain In $script:Tests.DomainTest.DomainList) { $resolved = $false If (Get-Command dig -ErrorAction SilentlyContinue) { # Check A, AAAA, and CNAME — NOT NS/TXT $a = dig +short $domain.RequiredDomains A 2>$null $aaaa = dig +short $domain.RequiredDomains AAAA 2>$null $cname = dig +short $domain.RequiredDomains CNAME 2>$null $resolved = @($a, $aaaa, $cname) | Where-Object { -Not [string]::IsNullOrWhiteSpace($_) } | Measure-Object | Select-Object -ExpandProperty Count $resolved = $resolved -Gt 0 } Else { Write-Host "dig is required on Linux for DNS tests" -ForegroundColor Yellow $resolved = $false } If ($resolved) { If ($domain.PassedTest -Ne $false) { $domain.PassedTest = $true } } Else { $domain.PassedTest = $false } } # Final test result If ($script:Tests.DomainTest.DomainList | Where-Object { $_.PassedTest -Ne $true }) { $script:Tests.DomainTest.TestPassed = $false } Else { $script:Tests.DomainTest.TestPassed = $true } } Else { Write-Host "DNS test skipped (Linux only)" -ForegroundColor DarkYellow } } Function Test-DnsResolution { param ( [string]$hostname, [string[]]$dnsServers ) ForEach ($server in $dnsServers) { Try { Resolve-DnsName -Name $hostname -Server $server -ErrorAction Stop | Out-Null Write-Host '[PASS]' -ForegroundColor Green -NoNewLine Write-Host " DNS Server $server successfully resolved $hostname" } Catch { Write-Host '[FAIL]' -ForegroundColor Red -NoNewLine Write-Host " DNS Server $server failed to resolve $hostname" } } } Function Test-ClientDnsConfig { # Define the hostname to test $hostname = "www.google.com" # Get the main network adapter with the default route $mainAdapter = Get-NetRoute -DestinationPrefix '0.0.0.0/0' | Sort-Object -Property { $_.InterfaceMetric + $_.RouteMetric } | Select-Object -First 1 | Get-NetAdapter $IPv6Status = Get-NetAdapterBinding -Name $mainAdapter.Name -ComponentID ms_tcpip6 # Get the DNS servers for IPv4 Try { $dnsServersIPv4 = Get-DnsClientServerAddress -InterfaceIndex $mainAdapter.InterfaceIndex -AddressFamily IPv4 } Catch { # Will check if null or empty in next part of script } Write-Host "$([Environment]::NewLine)CHECKING IPv4 DNS..." -ForegroundColor Cyan # Print and test DNS servers for IPv4 If (-not ([string]::IsNullOrEmpty(($dnsServersIPv4 | Get-Member -Name 'ServerAddresses')))) { Write-Host "[PASS]" -ForegroundColor Green -NoNewLine Write-Host " Detected IPv4 DNS servers:" -ForegroundColor Cyan $dnsServersIPv4.ServerAddresses | ForEach-Object { Write-Host " $_" } Write-Host "$([Environment]::NewLine) Testing IPv4 DNS server(s)..." -ForegroundColor Cyan Test-DnsResolution -hostname $hostname -dnsServers $dnsServersIPv4.ServerAddresses } Else { Write-Host '[FAIL] No IPv4 DNS servers found!' -ForegroundColor Yellow Write-Host ' Your internet is probably down right now.' } # Get the DNS servers for IPv6 If ($IPv6Status.Enabled) { Try { $dnsServersIPv6 = Get-DnsClientServerAddress -InterfaceIndex $mainAdapter.InterfaceIndex -AddressFamily IPv6 } Catch { Write-Host '[FAIL] ' -ForegroundColor Red -NoNewLine Write-Host 'IPv6 issues detected. Please disable IPv6 on your network adapter.' -ForegroundColor Yellow Write-Host 'Opening the Network Adapters screen now...' -ForegroundColor Cyan Start-Process 'ncpa.cpl' } # Print and test DNS servers for IPv6 Write-Host "$([Environment]::NewLine)CHECKING IPv6 DNS..." -ForegroundColor Cyan If (-not ([string]::IsNullOrEmpty(($dnsServersIPv6 | Get-Member -Name 'ServerAddresses')))) { Write-Host "[PASS]" -ForegroundColor Green -NoNewLine Write-Host ' Detected IPv6 DNS server(s):' -ForegroundColor Cyan $dnsServersIPv6.ServerAddresses | ForEach-Object { Write-Host " $_" } Write-Host "$([Environment]::NewLine) Testing IPv6 DNS servers..." -ForegroundColor Cyan Try { Test-DnsResolution -hostname $hostname -dnsServers $dnsServersIPv6.ServerAddresses } Catch { Write-Host '[FAIL] ' -ForegroundColor Yellow -NoNewLine Write-Host 'No IPv6 DNS servers found!' Write-Host 'Consider setting an IPv6 DNS server like' Write-Host '2606:4700:4700::1111' -ForegroundColor Cyan -NoNewLine Write-Host ' on your network adapter.' } } Else { Write-Host '[FAIL] ' -ForegroundColor Yellow -NoNewLine Write-Host 'No IPv6 DNS servers found!' Write-Host 'Consider setting an IPv6 DNS server like' Write-Host '2606:4700:4700::1111' -ForegroundColor Cyan -NoNewLine Write-Host ' on your network adapter.' } } Else { Write-Host "$([Environment]::NewLine)Skipping IPv6 checks because IPv6 is disabled." -ForegroundColor Cyan } } Function Test-Wifi { # Ping the default gateway for 30 seconds and collect statistics $mainAdapter = Get-NetIPConfiguration | Where-Object { $null -ne $_.IPv4DefaultGateway -or $null -ne $_.IPv6DefaultGateway } If ($null -eq $mainAdapter -or $null -eq $mainAdapter.IPv4DefaultGateway) { Write-Host "No default gateway available... returning to menu" -ForegroundColor Yellow Return } Write-Host "$([Environment]::NewLine)Testing the connection to the default gateway for (30 seconds)... please wait for results & do not press any keys..." -ForegroundColor Cyan $NetworkType = (Get-NetAdapter -InterfaceIndex $mainAdapter.InterfaceIndex).PhysicalMediaType $NetworkType = $NetworkType -replace '.*?(802\.\d+).*','$1' If ($NetworkType -ne '802.11') { Write-Host "$([Environment]::NewLine)This is not a wireless connection. Testing anyway..." -ForegroundColor Yellow } $ipAddress = ($mainAdapter.IPv4DefaultGateway).NextHop $endTime = ([datetime]::UtcNow).AddSeconds(30) $pingResults = New-Object System.Collections.Generic.List[Object] While ([datetime]::UtcNow -lt $endTime) { Try { $pingResult = Test-Connection $ipAddress -Count 1 -ErrorAction Stop } Catch { Write-Host 'Error pinging the default gateway... returning to menu' -ForegroundColor Yellow Return } If ($pingResult) { $pingResults.Add($pingResult) } } # Summarize results $sent = $pingResults.Count # If Statements for PowerShell version compatibility If ([bool]($pingResult.PSobject.Properties.name -like 'Status')) { $received = $pingResults | Where-Object { $_.Status -eq 'Success' } | Measure-Object | Select-Object -ExpandProperty Count $responseTimes = $pingResults | Select-Object -ExpandProperty Latency } If ([bool]($pingResult.PSobject.Properties.name -like 'StatusCode')) { $received = $pingResults | Where-Object { $_.StatusCode -eq 0 } | Measure-Object | Select-Object -ExpandProperty Count $responseTimes = $pingResults | Select-Object -ExpandProperty ResponseTime } $lost = $sent - $received $minTime = $responseTimes | Measure-Object -Minimum | Select-Object -ExpandProperty Minimum $maxTime = $responseTimes | Measure-Object -Maximum | Select-Object -ExpandProperty Maximum $avgTime = $responseTimes | Measure-Object -Average | Select-Object -ExpandProperty Average # Calculate standard deviation $mean = $avgTime $squaredDifferences = $responseTimes | ForEach-Object { ($_ - $mean) * ($_ - $mean) } $variance = ($squaredDifferences | Measure-Object -Sum).Sum / $responseTimes.Count $stdDev = [math]::Sqrt($variance) # Format to 3 significant digits $avgTimeFormatted = "{0:N3}" -f $avgTime $stdDevFormatted = "{0:N3}" -f $stdDev $packetLossPercentage = ($lost / $sent) * 100 # Output results $results = [PSCustomObject]@{ Sent = $sent Received = $received Lost = $lost PacketLossPercentage = "$packetLossPercentage %" MinResponseTime = "$minTime ms" MaxResponseTime = "$maxTime ms" AvgResponseTime = "$avgTimeFormatted ms" StdDevResponseTime = "$stdDevFormatted ms" } $results If ($stdDev -gt 5) { Write-Host "Your connection to your default gateway has significant jitter (latency variance).$([Environment]::NewLine)$([Environment]::NewLine)" -ForegroundColor Yellow } If ($packetLossPercentage -gt 1) { Write-Host "Your connection to your default gateway has more than 1% packet loss.$([Environment]::NewLine)$([Environment]::NewLine)" -ForegroundColor Yellow } If ($stdDev -le 5 -and $packetLossPercentage -le 1) { Write-Host "Your connection appears to be operating normally.$([Environment]::NewLine)$([Environment]::NewLine)" -ForegroundColor Green } } Function Test-BTAGService { # Handles Windows installs that are missing BTAGS (Windows Server for example) $service = Get-Service -Name 'BTAGService' -ErrorAction SilentlyContinue $script:Tests.BTAGSDisabled.TestPassed = ($null -eq $service) -or ($service.Status -ne 'Running') } Function Reset-Steam { $SteamProcess = [PSCustomObject]@{ ProcessName = 'steam' ErrorMsg = ' ⚠️ Steam is currently running. ⚠️ Please close Steam first. ' } Get-IsProcessRunning $SteamProcess Pause "You will need to sign into Steam after this process completes.$([Environment]::NewLine)Press [SPACEBAR] to continue..." -ForegroundColor Yellow # Remove CEF Cache Write-Host "$([Environment]::NewLine)Clearing contents of $env:LOCALAPPDATA\Steam\" -ForegroundColor Cyan Remove-Item -Path $env:LOCALAPPDATA\Steam\* -Recurse -ErrorAction Continue Write-Host "Clearing contents of $SteamPath. Keeping \steamapps, \userdata, \logs and \dumps" -ForegroundColor Cyan $PropertyName = "Parent" Get-ChildItem -Path $SteamPath -File -Recurse | Where-Object { (ForEach-Object { If ([bool]$_.PSObject.Properties["PSParentPath"]) { $_.Name -ne "steam.exe" -and $_.PSObject.Properties["PSParentPath"].Value -notlike "*" + $SteamPath + "\steamapps*" -and $_.PSObject.Properties["PSParentPath"].Value -notlike "*" + $SteamPath + "\userdata*" -and $_.PSObject.Properties["PSParentPath"].Value -notlike "*" + $SteamPath + "\logs*" -and $_.PSObject.Properties["PSParentPath"].Value -notlike "*" + $SteamPath + "\dumps*" } }) } | Remove-Item Write-Host 'Steam Data cleared successfully!' -ForegroundColor Green Write-Host 'Launching Steam now...'$([Environment]::NewLine) -ForegroundColor Cyan Start-Process $SteamPath\steam.exe Return } Function Open-AdvancedGraphics { Start-Process ms-settings:display-advancedgraphics Write-Host "$([Environment]::NewLine)Verify Helldivers 2 is set to use the correct GPU.", "$([Environment]::NewLine)If HD2 is not listed, click " -NoNewLine -ForegroundColor Cyan Write-Host "Add desktop app " -NoNewLine -ForegroundColor Yellow Write-Host "and browse to:" -ForegroundColor Cyan Write-Host $script:AppInstallPath, "\bin\helldivers2.exe"$([Environment]::NewLine) -ForegroundColor Yellow Return } Function Test-PrivateIP { <# .SYNOPSIS Use to determine if a given IP address is within the IPv4 private address space ranges. .DESCRIPTION Returns $true or $false for a given IP address string depending on whether or not is is within the private IP address ranges. .PARAMETER IP The IP address to test. .EXAMPLE Test-PrivateIP -IP 172.16.1.2 .EXAMPLE '10.1.2.3' | Test-PrivateIP #> param( [parameter(Mandatory, ValueFromPipeline)] [string] $IP ) $IP -Match '(^127\.)|(^192\.168\.)|(^10\.)|(^172\.1[6-9]\.)|(^172\.2[0-9]\.)|(^172\.3[0-1]\.)' } Function Test-DoubleNAT { Write-Host "$([Environment]::NewLine)Running Double-NAT test... this will take a minute" -ForegroundColor Cyan $server = 'cloudflare.com' $ip = Resolve-DnsName -Type A $server | Select-Object -Expand IPAddress $tracedroute = Test-NetConnection -Hops 10 -TraceRoute $ip[0] -WarningAction:SilentlyContinue Write-Progress -Completed -Activity "make progress bar disappear" $privateIPs = @() ForEach ($hop in $tracedroute.TraceRoute) { If (Test-PrivateIP $hop) { $privateIPs += $hop } } If ($privateIPs.Count -gt 1) { Write-Host '⚠️ Possible Double-NAT connection detected.' -ForegroundColor Yellow Write-Host 'Private IPs detected are:' Write-Host $privateIPs -Separator "$([Environment]::NewLine)" Write-Host "$([Environment]::NewLine)If you're not sure what these results mean, these results are safe to share with others." -ForegroundColor Cyan } Else { Write-Host "$([Environment]::NewLine)No Double-NAT connection detected." -ForegroundColor Green } Pause "$([Environment]::NewLine)Press [SPACEBAR] to continue..." } Function Switch-BTAGService { If ($script:DetectedOS -eq 'Windows') { If (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { Write-Host 'This command requires Administrator privileges.', "$([Environment]::NewLine)To run PowerShell with admin privileges:", "$([Environment]::NewLine) Right-click on PowerShell and click Run as Administrator", "$([Environment]::NewLine) Then run the script again.$([Environment]::NewLine)" -ForegroundColor Cyan } Else { If ((Get-Service -Name BTAGService).Status -eq 'Running') { Set-Service -Name BTAGService -StartupType Disabled Stop-Service -Name BTAGService Start-Sleep -Seconds 1.5 Write-Host "$([Environment]::NewLine)Bluetooth Audio Gateway Service", "is now " -ForegroundColor Cyan Write-Host (Get-Service -Name BTAGService).Status -ForegroundColor Yellow Write-Host 'Please disconnect and re-connect your Bluetooth device.'$([Environment]::NewLine) -ForegroundColor Cyan } Else { If ((Get-Service -Name BTAGService).Status -eq 'Stopped') { Set-Service -Name BTAGService -StartupType Automatic Set-Service -Name BTAGService -Status Running Start-Sleep -Seconds 1.5 Write-Host "$([Environment]::NewLine)Bluetooth Audio Gateway Service", "is now " -ForegroundColor Cyan Write-Host (Get-Service -Name BTAGService).Status$([Environment]::NewLine) -ForegroundColor Green } } } } } Function Test-VisualC++Redists { $VCRedists = @( [PSCustomObject]@{ProgramNames = @('Microsoft Visual C++ 2012 Redistributable (x64)'); Installed = $false}, [PSCustomObject]@{ProgramNames = @('Microsoft Visual C++ 2013 Redistributable (x64)'); Installed = $false}, [PSCustomObject]@{ProgramNames = @( 'Microsoft Visual C++ v14 Redistributable (x64)', 'Microsoft Visual C++ 2015-2022 Redistributable (x64)' ); Installed = $false } ) Write-Host "$([Environment]::NewLine)Checking for required Microsoft Visual C++ Redistributables..." -ForegroundColor Cyan -NoNewLine # Speed up the search by checking if the program name starts with 'Microsoft' before entering nested loop $filteredApps = $script:InstalledProgramsList | Where-Object { $_.DisplayName -like 'Microsoft Visual*' } ForEach ( $vcRedist in $VCRedists ) { Foreach ( $name in $vcRedist.ProgramNames ) { If ( $filteredApps | Where-Object { $_.DisplayName -like "$name*" } ) { $vcRedist.Installed = $true Break } } } $missingRedists = $VCRedists | Where-Object { $_.Installed -eq $false } If ($missingRedists) { Write-Host "$([Environment]::NewLine)You are missing critical Visual C++ Redists. The game will not run.$([Environment]::NewLine)" -ForegroundColor Yellow Write-Host ("{0,-33}" -f "Missing Visual C++ Redistributable(s)") -ForegroundColor Cyan Write-Host ("{0,-33}" -f '-------------------------------------') ForEach ($redist in $missingRedists) { Write-Host '[FAIL] ' -ForegroundColor Red -NoNewLine Write-Host ("{0,-26}" -f $redist.ProgramNames[0]) -ForegroundColor Yellow } Write-Host "$([Environment]::NewLine)Please install them using the [" -ForegroundColor Yellow -NoNewLine Write-Host 'I' -NoNewLine Write-Host '] option on the Reset/Toggle Components menu.' -ForegroundColor Yellow } Else { Write-Host ' all required Visual C++ Redists found!' -ForegroundColor Green } Return } Function Test-MemoryChannels { # Dual-Channel RAM test # Define single-channel pattern to search for $SingleChannelpattern = "^Channels\s*\b((1\s+x\s+\b(32|64)\b-bit)|(Single))$" $script:Tests.MultiChannelMemory.TestPassed = -not ($script:HardwareInfoText -match $SingleChannelPattern) } Function Test-PendingReboot { $script:Tests.PendingReboot.RebootRequired = [bool]($script:Tests.PendingReboot.keys | Where-Object { Test-Path $_ }) $script:Tests.PendingReboot.TestPassed = -not $script:Tests.PendingReboot.RebootRequired } Function Test-SSDFreeSpace { If ($script:DetectedOS -eq 'Windows') { $GameVolume = Get-Volume -DriveLetter (Split-Path $script:AppInstallPath -Qualifier).TrimEnd(":") $GamePhysicalDisk = $GameVolume | Get-Partition | Get-Disk | Get-PhysicalDisk $GameVolumeFreeSpace = ($GameVolume.SizeRemaining / $GameVolume.Size) * 100 $isSSD = ($null -ne $GamePhysicalDisk -and $GamePhysicalDisk.MediaType -eq 'SSD') } ElseIf ($script:DetectedOS -eq 'Linux') { # Determine mount point $mount = ((df -P "$script:AppInstallPath" | Select-Object -Skip 1) -split '\s+')[5] # Free space % $df = df -B1 "$mount" | Select-Object -Skip 1 $parts = $df -split "\s+" $size = [double]$parts[1] $free = [double]$parts[3] $GameVolumeFreeSpace = ($free / $size) * 100 # Determine SSD/HDD via rotational flag $device = (df "$mount" | Select-Object -Skip 1).Split()[0] -replace "^/dev/", "" # Strip partition suffix (nvme0n1p2 → nvme0n1, sda1 → sda) $parentDevice = $device -replace "(p?\d+)$", "" $rotFile = "/sys/block/$parentDevice/queue/rotational" $isSSD = (Test-Path $rotFile -and (Get-Content $rotFile) -eq "0") } $script:Tests.SSDFreeSpace.TestPassed = (($GameVolumeFreeSpace -ge 25 -and $isSSD) -or (-not $isSSD)) } Function Test-FreeDiskSpace { If ($script:DetectedOS -eq 'Windows') { $GameVolume = Get-Volume -DriveLetter (Split-Path $script:AppInstallPath -Qualifier).TrimEnd(":") $free = $GameVolume.SizeRemaining } ElseIf ($script:DetectedOS -eq 'Linux') { $mount = ((df -P "$script:AppInstallPath" | Select-Object -Skip 1) -split '\s+')[5] $df = df -B1 "$mount" | Select-Object -Skip 1 $parts = $df -split "\s+" $free = [double]$parts[3] } $script:Tests.FreeDiskSpace.TestPassed = ($free -gt 30GB) } Function Test-USBGameDrive { $isUSB = switch ($script:DetectedOS) { "Windows" { $GameVolume = Get-Volume -DriveLetter (Split-Path $script:AppInstallPath -Qualifier).TrimEnd(":") -ErrorAction SilentlyContinue $GamePhysicalDisk = $GameVolume | Get-Partition -ErrorAction SilentlyContinue | Get-Disk -ErrorAction SilentlyContinue | Get-PhysicalDisk -ErrorAction SilentlyContinue $GamePhysicalDisk -and ($GamePhysicalDisk.PSObject.Properties.Name -contains 'BusType') -and ($GamePhysicalDisk.BusType -eq "USB") } "Linux" { $mount = (df -P "$script:AppInstallPath" | Select-Object -Skip 1).Split()[5] $device = (df "$mount" | Select-Object -Skip 1).Split()[0] -replace "^/dev/", "" # Check bus type via sysfs $busPath = "/sys/block/$device/device" Test-Path "$busPath/usb" } Default { $false } } $script:Tests.USBGameDrive.TestPassed = (-not $isUSB) } Function Test-FasterDriveAvailable { $script:Tests.FasterDriveAvailable.fasterDrives = @() if ($script:DetectedOS -eq 'Windows') { $GameVolume = Get-Volume -DriveLetter (Split-Path $script:AppInstallPath -Qualifier).TrimEnd(":") $GamePhysicalDisk = $GameVolume | Get-Partition | Get-Disk | Get-PhysicalDisk $GameDiskScore = Get-DiskScore $GamePhysicalDisk foreach ($systemDisk in Get-PhysicalDisk | Where-Object { $_.UniqueId -ne $GamePhysicalDisk.UniqueId }) { $score = Get-DiskScore $systemDisk if ($score -gt $GameDiskScore) { $script:Tests.FasterDriveAvailable.fasterDrives += $systemDisk } } } elseif ($script:DetectedOS -eq 'Linux') { function Get-LinuxDiskScore($dev) { $rot = Get-Content "/sys/block/$dev/queue/rotational" $isSSD = ($rot -eq "0") $isNVMe = ($dev -like "nvme*") # Score: NVMe > SSD > HDD if ($isNVMe) { return 3 } elseif ($isSSD) { return 2 } else { return 1 } } # Determine game device $mount = (df -P "$script:AppInstallPath" | Select-Object -Skip 1).Split()[5] $gameDev = (df "$mount" | Select-Object -Skip 1).Split()[0] -replace "^/dev/", "" $GameDiskScore = Get-LinuxDiskScore $gameDev foreach ($dev in Get-ChildItem /sys/block | Select-Object -Expand Name) { if ($dev -eq $gameDev) { continue } $score = Get-LinuxDiskScore $dev if ($score -gt $GameDiskScore) { $script:Tests.FasterDriveAvailable.fasterDrives += $dev } } } $script:Tests.FasterDriveAvailable.TestPassed = ($script:Tests.FasterDriveAvailable.fasterDrives.Count -eq 0) } Function Test-BetaBranch { if(-Not (Test-Path $script:AppManifestPath)) { $script:Tests.BetaBranchActive.TestPassed = $true return } $AppManifestContent = Get-Content -Path $script:AppManifestPath -Raw $ParsedAppManifest = Read-VDF $AppManifestContent -Encoding UTF8 $SelectedBetaPath = @( "AppState", "UserConfig", "BetaKey" ) $SelectedBetaBranch = Get-VDFValue -Root $ParsedAppManifest -Path $SelectedBetaPath $script:Tests.BetaBranchActive.TestPassed = ($null -eq $SelectedBetaBranch -or $SelectedBetaBranch.ToLower() -eq "public") $script:Tests.BetaBranchActive.selectedBranch = $SelectedBetaBranch } Function Get-DiskScore($disk) { if (-not $disk) { return 0 } $score = 0 $busType = $null if (Get-Member -InputObject $disk -Name 'BusType' -ErrorAction SilentlyContinue) { $busType = $disk.BusType } Switch ($busType) { "NVMe" { $score += 2 } "SATA" { $score += 1 } default { $score += 0 } } if ((Get-Member -InputObject $disk -Name 'BusType') -and $disk.MediaType -eq "SSD") { $score += 1 } Return $score } Function Convert-Size($bytes) { $units = "B","KB","MB","GB","TB" $i = 0 While ($bytes -ge 1024 -and $i -lt $units.Length - 1) { $bytes /= 1024 $i++ } Return ("{0:N2} {1}" -f $bytes, $units[$i]) } Function Reset-HD2SteamCloud { Clear-Host Write-Host "$([Environment]::NewLine)This function will reset your HD2 Steam Cloud saved data." -ForegroundColor Cyan Write-Host 'You will lose any custom key bindings & character customizations will be reset to defaults. ' -NoNewLine Write-Host 'No game progress will be lost.' -ForegroundColor Yellow Write-Host "This can resolve a myriad of input issues, and in some instances,$([Environment]::NewLine)can resolve the game not running at all." Write-Host "If you have multiple Steam user profiles,$([Environment]::NewLine)this function will clear the LAST USED HD2 Steam Cloud profile."-ForegroundColor Yellow Write-Host "If you need to switch Steam profiles before running this script,$([Environment]::NewLine)please close the script or press " -NoNewLine Write-Host 'Ctrl + C' -NoNewLine -ForegroundColor Cyan Write-Host " to stop the script...$([Environment]::NewLine)Open Steam using the correct Steam profile and re-run this script." Write-Host "$([Environment]::NewLine)These are the steps that will be completed:" Write-Host "1.) Script will close Steam if it is running$([Environment]::NewLine)2.) Script will temporarily disable Steam Cloud saves for HD2$([Environment]::NewLine)3.) Script will delete your HD2 Steam Cloud data$([Environment]::NewLine)4.) Script will pause$([Environment]::NewLine)5.) Script will request for you to run Helldivers 2$([Environment]::NewLine) and load into the ship to generate new Steam Cloud files." Write-Host "6.) You will close the game, and continue the script." Write-Host "7.) Script will re-enable Steam Cloud saves for HD2. $([Environment]::NewLine) The new files to be synced to Steam Cloud next time Steam is launched." Pause 'Press [SPACEBAR] to continue.' # Shutdown Steam and disable SteamCloud # Get the Steam process $steamProcess = Get-Process -Name "Steam" -ErrorAction SilentlyContinue # Check if the Steam process is running If ($steamProcess) { # Stop the Steam process Stop-Process -Name "Steam" -Force Write-Host "Steam has been stopped... continuing" } Else { Write-Host "Steam is not running... continuing" } $HD2SteamCloudSaveFolder = Join-Path $script:mostRecentSteamUserProfilePath -ChildPath $script:AppID # Define the path to the sharedconfig.vdf file $sharedConfigPath = Join-Path $script:mostRecentSteamUserProfilePath -ChildPath '\7\remote\sharedconfig.vdf' $configContent = Get-Content -Path $sharedConfigPath $inAppSection = $false $modifiedContent = @() # Parse the sharedconfig.vdf file and modify the cloudenabled value to '0' ForEach ($line in $configContent) { If ($line -match $script:AppID) { $inAppSection = $true } ElseIf ($inAppSection -and $line -match '"cloudenabled"') { $line = $line -replace '("cloudenabled"\s+)"\d+"', '$1"0"' $inAppSection = $false } $modifiedContent += $line } # Write the modified content back to the sharedconfig.vdf file and then clear the modifiedContent array $modifiedContent | Out-File -FilePath $sharedConfigPath -Encoding UTF8 -Force $modifiedContent = @() Write-Host 'Cloud save for HD2 has been disabled.' -ForegroundColor Cyan Remove-Item -Path $HD2SteamCloudSaveFolder\* -Recurse Write-Host "Cleared cloud save folder $HD2SteamCloudSaveFolder" -ForegroundColor Cyan Write-Host "STOP! Please open Helldivers 2 and skip intro/wait until it gets to the menu BEFORE continuing the script..." -ForegroundColor Red pause 'Press [SPACEBAR] to continue...' Write-Host 'Re-enabling Cloud Save for HD2...' -ForegroundColor Cyan $configContent = Get-Content -Path $sharedConfigPath ForEach ($line in $configContent) { If ($line -match $script:AppID) { $inAppSection = $true } ElseIf ($inAppSection -and $line -match '"cloudenabled"') { $line = $line -replace '("cloudenabled"\s+)"\d+"', '$1"1"' $inAppSection = $false } $modifiedContent += $line } $modifiedContent | Out-File -FilePath $sharedConfigPath -Encoding UTF8 -Force $modifiedContent = $null Write-Host 'HD2 Steam Cloud clearing procedures completed!' -ForegroundColor Cyan Return } Function Switch-FullScreenOptimizations { # Define the path to the executable $exePath = "$script:AppInstallPath\bin\helldivers2.exe" # Define the registry path $regPath = "HKCU:\Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers" # Check if the registry key exists, create it if it doesn't If (-not (Test-Path $regPath)) { New-Item -Path $regPath -Force | Out-Null } # Check if the property exists within the registry key $currentProperty = Get-ItemProperty -Path $regPath -Name $exePath -ErrorAction SilentlyContinue If ($null -eq $currentProperty) { # Create the property if it doesn't exist New-ItemProperty -Path $regPath -Name $exePath -Value 'DISABLEDXMAXIMIZEDWINDOWEDMODE' -PropertyType String | Out-Null } Else { # Check the current value of the property $currentValue = ($currentProperty | Select-Object -ExpandProperty $exePath -ErrorAction SilentlyContinue).Trim() If ($currentValue -like "*DISABLEDXMAXIMIZEDWINDOWEDMODE*") { $newValue = ($currentValue -replace "DISABLEDXMAXIMIZEDWINDOWEDMODE", "").Trim() If ($newValue) { Set-ItemProperty -Path $regPath -Name $exePath -Value $newValue } Else { Remove-ItemProperty -Path $regPath -Name $exePath } Return Write-Host "$([Environment]::NewLine)Fullscreen optimizations enabled for $exePath. This is probably not desired." -ForegroundColor Yellow } Else { # Append DISABLEDXMAXIMIZEDWINDOWEDMODE to the current value $newValue = "$currentValue DISABLEDXMAXIMIZEDWINDOWEDMODE" Set-ItemProperty -Path $regPath -Name $exePath -Value $newValue } } Return Write-Host "$([Environment]::NewLine)Fullscreen optimizations disabled for $exePath. This is probably the desired setting." -ForegroundColor Green } Function Reset-HostabilityKey { $basePath = Get-HD2ConfigPath $configPath = if ($basePath) { Join-Path $basePath "user_settings.config" } if(-Not $basePath -Or -Not (Test-Path $configPath)) { Write-Host '[WARN] ' -NoNewLine -ForegroundColor Yellow Write-Host 'Helldivers 2 config file not found.' -ForegroundColor Cyan return } Try { $OriginalHash = Get-FileHash -Path $configPath -Algorithm SHA256} Catch { Write-Host '[WARN] ' -NoNewLine -ForegroundColor Yellow Write-Host 'User_settings.config is missing.' -ForegroundColor Cyan Return } $content = Get-Content $configPath $content = $content -replace 'hostability\s*=.*', 'hostability = ""' Set-Content $configPath -Value $content If ( $OriginalHash -ne (Get-FileHash -Path $configPath -Algorithm SHA256) ) { Write-Host "$([Environment]::NewLine)Hostability key removed successfully!" -ForegroundColor Green } Else { Write-Host '[FAIL] ' -NoNewLine -ForegroundColor Red Write-Host "Hostability key could not be removed.$([Environment]::NewLine)" -ForegroundColor Yellow } } Function Get-VSyncConfig { $basePath = Get-HD2ConfigPath $configPath = if ($basePath) { Join-Path $basePath "user_settings.config" } if(-Not $basePath -Or -Not (Test-Path $configPath)) { $script:Tests.VSyncDisabled.TestPassed = $true Return } Try { If ( Select-String $configPath -Pattern "vsync = false" -Quiet ) { $script:Tests.VSyncDisabled.TestPassed = $true } Else { $script:Tests.VSyncDisabled.TestPassed = $false } } Catch { Return } } Function Get-GameResolution { $basePath = Get-HD2ConfigPath $configPath = if ($basePath) { Join-Path $basePath "user_settings.config" } if(-Not $basePath -Or -Not (Test-Path $configPath)) { $script:Tests.GameResolution.TestPassed = $false $script:Tests.RenderResolution.TestPassed = $false Return } Try { $lines = Get-Content $configPath $screen = Get-ConfigResolutionBlock -SettingName 'screen_resolution' -Lines $lines $render = Get-ConfigResolutionBlock -SettingName 'render_resolution' -Lines $lines If ($screen) { $script:Tests.GameResolution.WidthValue = $screen.Width $script:Tests.GameResolution.HeightValue = $screen.Height $script:Tests.GameResolution.TestPassed = $true } Else { $script:Tests.GameResolution.TestPassed = $false } If ($render) { $script:Tests.RenderResolution.WidthValue = $render.Width $script:Tests.RenderResolution.HeightValue = $render.Height $script:Tests.RenderResolution.TestPassed = $true } Else { $script:Tests.RenderResolution.TestPassed = $false } } Catch { $script:Tests.GameResolution.TestPassed = $false $script:Tests.RenderResolution.TestPassed = $false } } Function Get-ConfigResolutionBlock { Param ( [string]$SettingName, [string[]]$Lines ) For ($i = 0; $i -lt $Lines.Count; $i++) { If ($Lines[$i] -match "^\s*$SettingName\s*=\s*\[\s*$") { $w = [int]$Lines[$i + 1].Trim() $h = [int]$Lines[$i + 2].Trim() Return @{ Width = $w; Height = $h } } } Return $null } Function Find-Mods { If (-not $script:AppInstallPath) { Write-Host 'Helldivers 2 not found. Skipping mod detection.' Return } $modsFound = Test-Path -Path "$script:AppInstallPath\data\*.patch_*" -PathType Leaf $script:Tests.GameMods.TestPassed = -not $modsFound } Function Show-ModRemovalWarning { Write-Host "$([Environment]::NewLine)WARNING: " -ForegroundColor Red -NoNewLine Write-Host 'This script is about to delete modified game files in' -ForegroundColor Yellow Write-Host "$script:AppInstallPath\data\" -ForegroundColor Cyan Write-Host 'If this location looks incorrect, press ' -ForegroundColor Yellow -NoNewLine Write-Host 'Ctrl ' -NoNewLine Write-Host '+ ' -ForegroundColor Yellow -NoNewLine Write-Host 'C ' -NoNewLine Write-Host 'now to exit.' -ForegroundColor Yellow Pause "$([Environment]::NewLine) Press [SPACEBAR] to continue" } Function Remove-AllMods { If (-not $script:AppInstallPath) { Write-Host 'Helldivers 2 not found. Skipping mod removal.' Return } $dataFolder = $script:AppInstallPath + '\data\' $filesFound = $false Foreach ($file in Get-ChildItem -Path $dataFolder -File) { $filePath = $dataFolder + $file.Name If ($file.Name -match "([0-9a-fA-F]{16})\.patch_") { $filesFound = $true $hex = $matches[1] If (Test-Path $filePath) { Remove-Item -Path $filePath -Force } Foreach ($matchingFile in Get-ChildItem -Path $dataFolder -File | Where-Object { $_.Name -match "$hex" }) { $matchingFilePath = $dataFolder + $matchingFile.Name If (Test-Path $matchingFilePath) { Remove-Item -Path $matchingFilePath -Force } } } } If (-not $filesFound) { Write-Host 'No mod files were found to remove.' -ForegroundColor Cyan } Else { Write-Host 'Removed all .patch_ files and sibling files sharing the same IDs. Please verify game integrity before launching.' -ForegroundColor Cyan } } Function Reset-ShaderCaches { # Clear HD2 shader cache $basePath = Get-HD2ConfigPath If ($basePath) { $shaderCachePath = Join-Path $basePath "shader_cache" Remove-Item (Join-Path $shaderCachePath '*') -Recurse -ErrorAction SilentlyContinue Write-Host "HD2 shader cache cleared successfully!" -ForegroundColor Green } Else { Write-Host "HD2 configuration folder not found. Skipping HD2 shader clear." -ForegroundColor Yellow } Write-Host 'Would you like to clear the Windows DX Shader Cache? (Y/N)' -ForegroundColor Yellow $response = Read-Host If ($response -match '^[Yy]$') { Remove-Item (Join-Path (Join-Path $env:LOCALAPPDATA 'D3DSCache') '*') -Recurse -ErrorAction SilentlyContinue Write-Host "Windows DX Shader Cache cleared." -ForegroundColor Green } Else { Write-Host "Skipped clearing Windows DX Shader Cache." -ForegroundColor DarkYellow } # Detect user's GPU vendors $vendors = $script:SystemInfo.GPUInfo | ForEach-Object { $_.Vendor.ToUpper() } | Sort-Object -Unique If (-not $vendors) { Write-Host 'Please run option H from the main menu first to gather GPU information.' -ForegroundColor Yellow Return } If ($vendors -contains 'NVIDIA') { Write-Host "NVIDIA GPU detected. Clearing the NVIDIA shader cache will clear the cache for all games." -ForegroundColor Yellow Write-Host "Do you want to continue? (Y/N)" -ForegroundColor Yellow $response = Read-Host If ($response -match '^[Yy]$') { Remove-Item (Join-Path (Join-Path $env:LOCALAPPDATA 'NVIDIA\DXCache') '*') -Recurse -ErrorAction SilentlyContinue Remove-Item (Join-Path (Join-Path $env:USERPROFILE 'AppData\LocalLow\NVIDIA\DXCache') '*') -Recurse -ErrorAction SilentlyContinue Write-Host "NVIDIA shader cache cleared." -ForegroundColor Green } Else { Write-Host "Skipped clearing NVIDIA shader cache." -ForegroundColor DarkYellow } } If ($vendors -contains 'AMD') { Write-Host "AMD GPU detected. Clearing AMD shader cache will clear the cache for all games." -ForegroundColor Yellow Write-Host "Do you want to continue? (Y/N)" -ForegroundColor Yellow $response = Read-Host If ($response -match '^[Yy]$') { Remove-Item (Join-Path (Join-Path $env:LOCALAPPDATA 'AMD\DxCache') '*') -Recurse -ErrorAction SilentlyContinue Write-Host "AMD shader cache cleared." -ForegroundColor Green } Else { Write-Host "Skipped clearing the AMD shader cache." -ForegroundColor DarkYellow } } If ($vendors -contains 'Intel') { Write-Host "Intel GPU detected. Clearing Intel shader cache will clear the cache for all games." -ForegroundColor Yellow Write-Host "Do you want to continue? (Y/N)" -ForegroundColor Yellow $response = Read-Host If ($response -match '^[Yy]$') { Remove-Item (Join-Path (Join-Path $env:LOCALAPPDATA 'Intel\DxCache') '*') -Recurse -ErrorAction SilentlyContinue Remove-Item (Join-Path (Join-Path $env:LOCALAPPDATA 'Intel\ShaderCache') '*') -Recurse -ErrorAction SilentlyContinue Write-Host "Intel shader cache cleared." -ForegroundColor Green } Else { Write-Host "Skipped clearing the Intel shader cache." -ForegroundColor DarkYellow } } } Function Get-PageFileSize { $pageFile = Get-CimInstance Win32_PageFileUsage ( $pageFile -and ( $pageFile.AllocatedBaseSize -ne 0 ) ) } Function Get-SecureBootStatus { Try { $secureBoot = Confirm-SecureBootUEFI If ( $secureBoot -eq $true) { $script:Tests.SecureBootEnabled.TestPassed = $true } } Catch { If ( $_.Exception.Message -like "*Cmdlet not supported on this platform:*" ) { $script:Tests.SecureBootEnabled.SecureBootNotSupported = $true $script:Tests.SecureBootEnabled.TestPassed = $false } } } Function Restart-Resume { Return ( Test-Path $PSScriptRoot\HellbombRestartResume ) } Function Get-MenuTitle { $IsAdmin = $false If ($script:DetectedOS -eq 'Windows') { $IsAdmin = ([Security.Principal.WindowsPrincipal] ` [Security.Principal.WindowsIdentity]::GetCurrent() ).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) } If ($script:DetectedOS -eq 'Linux') { $IsAdmin = (id -u) -eq 0 } $AdminBanner = If (-Not $IsAdmin) { @" ⚠️⚠️⚠️ WARNING: Script is NOT running with Administrator privileges! ⚠️⚠️⚠️ >>> SOME TESTS WILL FAIL OR PRODUCE INCORRECT RESULTS. <<< "@ } Else { "" } # Linux early‑alpha warning $LinuxWarning = "" If ($script:DetectedOS -eq 'Linux') { $LinuxWarning = "⚠️⚠️⚠️ Linux support is in Aplha — many features are missing & behavior may be unstable. Pull Requests welcome!" } $Title = @( "-------------------------------------------------------------------------------------------------------", "💣 Hellbomb 💣 Script for Troubleshooting Helldivers 2 || Version 4.0", "-------------------------------------------------------------------------------------------------------", $LinuxWarning, $AdminBanner ) -join "`n" Return $Title } Function Show-ArrowMenu { Param( [string]$Title, [string[]]$Options, [hashtable]$Hotkeys ) $selectedIndex = 0 $oldIndex = 0 $key = $null Clear-Host Write-Host "$(Get-MenuTitle)`n$($Title)" Write-Host "Use ↑ ↓ arrows or press hotkey letter. Enter/→ selects, Esc/← cancels." Write-Host "" $menuStartPosition = [System.Management.Automation.Host.Coordinates]::new( $Host.UI.RawUI.CursorPosition.X, $Host.UI.RawUI.CursorPosition.Y ) $script:menuEnd = [System.Management.Automation.Host.Coordinates]::new( $Host.UI.RawUI.CursorPosition.X, $Host.UI.RawUI.CursorPosition.Y + $Options.Length ) For ($i = 0; $i -lt $Options.Length; $i++) { If ($i -eq $selectedIndex) { Write-Host $Options[$i] -ForegroundColor Cyan } Else { Write-Host $Options[$i] } } $Host.UI.RawUI.CursorPosition = [System.Management.Automation.Host.Coordinates]::new(0, $menuStartPosition.Y + $selectedIndex) Do { $oldIndex = $selectedIndex $key = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") Switch ($key.VirtualKeyCode) { 38 { if ($selectedIndex -gt 0) { $selectedIndex-- } } # Up 40 { if ($selectedIndex -lt ($Options.Length - 1)) { $selectedIndex++ } } # Down 27 { return $null } # Esc 13 { return $selectedIndex } # Enter 39 { return $selectedIndex } # Right Arrow → same as Enter 37 { return $Options.Length - 1 } # Left Arrow → Back (last option) Default { $char = [string]$key.Character if ($null -ne $char -and -not [string]::IsNullOrEmpty($char)) { $char = $char.ToUpper() if ($Hotkeys.ContainsKey($char)) { return $Hotkeys[$char] } } } } #Clear previous highlight $Host.UI.RawUI.CursorPosition = [System.Management.Automation.Host.Coordinates]::new(0, $menuStartPosition.Y + $oldIndex) Write-Host $Options[$oldIndex] -NoNewLine #Set new highlight $Host.UI.RawUI.CursorPosition = [System.Management.Automation.Host.Coordinates]::new(0, $menuStartPosition.Y + $selectedIndex) Write-Host $Options[$selectedIndex] -ForegroundColor Cyan -NoNewLine $Host.UI.RawUI.CursorPosition = [System.Management.Automation.Host.Coordinates]::new(0, $menuStartPosition.Y + $selectedIndex) } While ($true) } Function RunAndPause { Param( [scriptblock]$ScriptBlock ) & $ScriptBlock Write-Host "`n--- Paused ---" Write-Host "Copy any results you want to save, then press [SPACEBAR] to return to the menu." Pause } Function Invoke-HD2StatusChecks { Show-Variables Show-MotherboardInfo If ( $script:DetectedOS -eq 'Windows' ){ Show-WindowsGPUInfo } If ( $script:DetectedOS -eq 'Linux' ) { Show-LinuxGPUInfo } Show-OSInfo If ( $script:DetectedOS -eq 'Windows' ){ Show-WindowsPowerPlan } If ( $script:DetectedOS -eq 'Linux' ) { Show-LinuxPowerPlan } Show-ISPInfo If ( $script:DetectedOS -eq 'Windows' ){ Show-GameLaunchOptions Test-PendingReboot Reset-HostabilityKey Test-Firewall Test-CRL Test-RequiredURLsWindows } If ( $script:DetectedOS -eq 'Linux' ) { Test-RequiredURLsLinux } If ( $script:DetectedOS -eq 'Windows' ){ Test-SystemClockAccuracy Find-BlacklistedDrivers Test-BadPrinters Test-BTAGService Test-VisualC++Redists Test-Programs $script:Tests.NoVegaGPUs.TestPassed = Test-VegaGPUDriver } If ($script:DetectedOS -eq 'Windows') { $script:Tests.PageFileEnabled.TestPassed = Get-PageFileSize Get-SystemUptime} If ($script:DetectedOS -eq 'Windows' ) { Get-HardwareInfo Find-CPUInfo Get-SecureBootStatus Test-AVX2 Test-MemoryChannels Get-MemoryPartNumber -Lines $script:HardwareInfoText Get-MemorySpeed } Find-Mods Get-VSyncConfig Get-GameResolution If ( $script:DetectedOS -eq 'Windows' ){ Test-SSDFreeSpace } If ( $script:DetectedOS -eq 'Windows' ){ Test-FreeDiskSpace } If ( $script:DetectedOS -eq 'Windows' ){ Test-USBGameDrive } If ( $script:DetectedOS -eq 'Windows' ){ Test-FasterDriveAvailable } Test-BetaBranch Show-TestResults Write-Host "`n--- Paused ---" Write-Host "Copy any results you want to save, then press [SPACEBAR] to return to the menu." Pause } Function New-Menu { Param( [string]$Title, [Parameter(Mandatory)] [array]$MenuItems ) # Filter Menu Items by $script:DetectedOS ('Windows' or 'Linux') $MenuItems = @( $MenuItems | Where-Object { -not $_.ContainsKey("OS") -or $_.OS -eq "Any" -or $_.OS -eq $script:DetectedOS } ) $options = $MenuItems.Label $actions = @{} $hotkeys = @{} $OS = @{} for ($i = 0; $i -lt $MenuItems.Count; $i++) { if ($MenuItems[$i].Hotkey) { $hotkeys[$MenuItems[$i].Hotkey.ToUpper()] = $i } $actions[$i] = $MenuItems[$i].Action } do { $choice = Show-ArrowMenu -Title $Title -Options $options -Hotkeys $hotkeys if ($null -eq $choice) { return } $action = $actions[$choice] if ($null -eq $action) { return } Clear-Host try { & $action } catch { Write-Host "Error: $($_.Exception.Message)" -ForegroundColor Red Write-Host "Stack Trace: $($_.ScriptStackTrace)" -ForegroundColor Red Write-Host "$([Environment]::NewLine)Press [SPACEBAR] to continue..." Pause } } while ($true) } Function MainMenu { $menu = @( @{ Label="🔍 |H|D2 Status Checks"; Hotkey="H"; Action={ Invoke-HD2StatusChecks } } @{ Label="🧹 |C|lear Data Options >"; Hotkey="C"; Action={ ClearDataMenu } } @{ Label="🛠️ |G|raphics Options >"; Hotkey="G"; Action={ GraphicsMenu } } @{ Label="🛜 |N|etwork Options >"; Hotkey="N"; Action={ NetworkMenu } } @{ Label="🔊 |A|udio Options >"; Hotkey="A"; Action={ AudioMenu } } @{ Label="🔁 |R|eset/Toggle >"; Hotkey="R"; Action={ ResetToggleComponentsMenu } } @{ Label="❌ E|x|it"; Hotkey="X"; Action = $null } ) New-Menu -Title "" -MenuItems $menu } Function ClearDataMenu { $menu = @( @{ Label = "🧹 |C|lear Settings (AppData)" Hotkey = "C" Action = { RunAndPause { Remove-HD2AppData } } OS = 'Any' } @{ Label = "🧹 Clear Only Shader Caches" Hotkey = $null Action = { RunAndPause { Reset-ShaderCaches } } OS = 'Any' } @{ Label = "🧹 Stea|m| Cloud" Hotkey = "M" Action = { RunAndPause { Reset-HD2SteamCloud } } OS = 'Any' } @{ Label = "🧹 Hostability Key |Z|" Hotkey = "Z" Action = { RunAndPause { Reset-HostabilityKey } } OS = 'Any' } @{ Label = "❌ |Q|uick Mod Removal" Hotkey = "Q" Action = { RunAndPause { Show-ModRemovalWarning; Remove-AllMods} } OS = 'Any' } @{ Label = "⬅️ |B|ack" Hotkey = "B" Action = $null OS = 'Any' } ) New-Menu -Title "🧹 Clear Data Options" -MenuItems $menu } Function GraphicsMenu { $menu = @( @{ Label = "🛠️ Select Correct G|P|U" Hotkey = "P" Action = { RunAndPause { Open-AdvancedGraphics } } OS = 'Windows' } @{ Label = "📺 |O|ptimizations Toggle" Hotkey = "O" Action = { RunAndPause { Switch-FullScreenOptimizations } } OS = 'Windows' } @{ Label = "⬅️ |B|ack" Hotkey = "B" Action = $null OS = 'Any' } ) New-Menu -Title "🛠️ Graphics Options" -MenuItems $menu } Function NetworkMenu { $menu = @( @{ Label = "🛜 |W|i-Fi LAN Test" Hotkey = "W" Action = { RunAndPause { Test-Wifi }; } OS = 'Windows' } @{ Label = "🌐 NA|T| Test" Hotkey = "T" Action = { Test-DoubleNAT } OS = 'Windows' } @{ Label = "⬅️ |B|ack" Hotkey = "B" Action = $null } ) New-Menu -Title "🛜 Network Options" -MenuItems $menu } Function AudioMenu { $menu = @( @{ Label = "🔈 |B|luetooth Telephony Service" Hotkey = $null Action = { RunAndPause { Switch-BTAGService } } OS = 'Windows' } @{ Label = "⬅️ Back" Hotkey = "" Action = $null OS = 'Windows' } @{ Label = "⬅️ Back" Hotkey = "B" Action = $null OS = 'Linux' } ) New-Menu -Title "🔊 Audio Options" -MenuItems $menu } Function ResetToggleComponentsMenu { $menu = @( @{ Label = "🔁 |G|ameGuard Re-install" Hotkey = "G" Action = { RunAndPause { Reset-GameGuard } } OS = 'Windows' } @{ Label = "🔁 |S|team Reset" Hotkey = "S" Action = { RunAndPause { Reset-Steam } } OS = 'Windows' } @{ Label = "🗑️ |U|ninstall VC++ Redists" Hotkey = "U" Action = { RunAndPause { Uninstall-VCRedist } } OS = 'Windows' } @{ Label = "➕ |I|nstall VC++ Redists" Hotkey = "I" Action = { RunAndPause { Install-VCRedist } } OS = 'Windows' } @{ Label = "🎮 |D|isable/Enable GameInput Service (Toggle)" Hotkey = "D" Action = { RunAndPause { Switch-gameInput } } OS = 'Windows' } @{ Label = "⬅️ |B|ack" Hotkey = "B" Action = $null OS = 'Any' } ) New-Menu -Title "🔊 Reset/Toggle Components" -MenuItems $menu } Function Show-TestResults { $keyDisplayOrderWindows = @( "GameResolution", "RenderResolution", "SecureBootEnabled", "VSyncDisabled", "IntelMicrocodeCheck", "AVX2", "NoVegaGPUs", "LongSysUptime", "SystemClockAccurate", "PendingReboot", "PageFileEnabled", "BadPrinter", "GetMemorySpeed", "MultiChannelMemory", "MatchingMemory", "FirewallRules", "DomainTest", "GameMods", "BetaBranchActive", "BTAGSDisabled", "SSDFreeSpace", "FreeDiskSpace", "USBGameDrive", "FasterDriveAvailable" ) $keyDisplayOrderLinux = @( "GameResolution", "RenderResolution", "DomainTest", "GameMods", "BetaBranchActive" ) If ( $script:DetectedOS -eq 'Windows' ) { $keyDisplayOrder = $keyDisplayOrderWindows } If ( $script:DetectedOS -eq 'Linux' ) { $keyDisplayOrder = $keyDisplayOrderLinux } ForEach ( $key in $keyDisplayOrder ) { $test = $script:Tests[$key] If ($test.TestPassed -ne $true) { Invoke-Expression $test.TestFailMsg } Else { # Check if TestPassedMsg exists using Get-Member If ( $test.ContainsKey('TestPassedMsg') ) { Invoke-Expression $test.TestPassedMsg } } If ( $test.ContainsKey('AlwaysDisplayMsg') ) { Invoke-Expression $test.AlwaysDisplayMsg } } # After showing, reset URL tests ForEach ($domain in $script:Tests.DomainTest.DomainList) { $domain.PassedTest = $null } } Function Get-MostRecentlyUsedSteamProfilePath { # Get all immediate subfolders $subfolders = Get-ChildItem -Path (Join-Path $SteamPath -ChildPath 'userdata') -Directory # Initialize variables to track the most recently modified subfolder $mostRecentTime = [datetime]::MinValue # Iterate through each subfolder to find the most recently modified files ForEach ($subfolder in $subfolders) { $files = Get-ChildItem -Path $subfolder.FullName -Recurse -File ForEach ($file in $files) { If ($file.LastWriteTime -gt $mostRecentTime) { $mostRecentTime = $file.LastWriteTime $script:mostRecentSteamUserProfilePath = $subfolder.FullName } } } } Function Read-VDF { param( [string]$Content ) $root = @{} $stack = [System.Collections.Generic.Stack[hashtable]]::new() $stack.Push($root) $pendingKey = $null $regex = [regex]::new('"((?:\\.|[^"\\])*)"|[{}]', [Text.RegularExpressions.RegexOptions]::Compiled) foreach ($m in $regex.Matches($Content)) { switch ($m.Value) { '{' { if ($pendingKey) { $child = @{} $parent = $stack.Peek() $parent[$pendingKey] = $child $stack.Push($child) $pendingKey = $null } else { throw "Unexpected '{' at position $($m.Index)" } } '}' { if ($stack.Count -gt 1) { $stack.Pop() | Out-Null } $pendingKey = $null } default { $raw = $m.Groups[1].Value $unescaped = [regex]::Unescape($raw) if ($pendingKey) { $parent = $stack.Peek() $parent[$pendingKey] = $unescaped $pendingKey = $null } else { $pendingKey = $unescaped } } } } return $root } function Get-VDFValue { param( [hashtable]$Root, [string[]]$Path ) $current = $Root foreach ($key in $Path) { if ($current -isnot [System.Collections.IDictionary]) { return $null } if (-not $current.Contains($key)) { return $null } $current = $current[$key] } return $current } function Split-VDFPath { param([string]$Path) $regex = '"((?:\\.|[^"\\])*)"|[^.]+' $parts = @() foreach ($m in [regex]::Matches($Path, $regex)) { if ($m.Groups[1].Success) { $parts += [regex]::Unescape($m.Groups[1].Value) } else { $parts += $m.Value } } return $parts } Write-Host 'Locating Steam...' -ForegroundColor Cyan # Set AppID $script:AppID = "553850" $script:AppIDFound = $false #Steam Path Detection $script:SteamPath = switch ($script:DetectedOS) { "Windows" { try { (Get-ItemProperty -Path "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Valve\Steam").InstallPath } catch { Write-Host "[FAIL]" -NoNewLine -ForegroundColor Red Write-Host "Steam was not detected. Exiting Steam to fix this issue..." -ForegroundColor Cyan Pause "$([Environment]::NewLine) Press [SPACEBAR] to continue" $steamProcess = Get-Process -Name "steam" -ErrorAction SilentlyContinue If ($null -ne $steamProcess) { Stop-Process -Name "steam" -Force } Else { Write-Host "[FAIL]" -NoNewLine -ForegroundColor Red Write-Host "Steam was not running. Ensure Steam is installed and re-run the script." -ForegroundColor Cyan exit } (Get-ItemProperty -Path "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Valve\Steam").InstallPath } } "Linux" { $possiblePaths = @( "$HOME/.steam/steam", "$HOME/.local/share/Steam", "$HOME/.var/app/com.valvesoftware.Steam/.steam/steam" ) $validPath = $possiblePaths | Where-Object { Test-Path $_ } | Select-Object -First 1 if($null -eq $validPath) { Write-Host "[FAIL]" -NoNewLine -ForegroundColor Red Write-Host "Steam was not detected. Exiting...." -ForegroundColor Cyan exit } $validPath } Default { Write-Host "[FAIL]" -NoNewLine -ForegroundColor Red Write-Host "OS not yet supported. Exiting...." -ForegroundColor Cyan exit } } Write-Host 'Locating Steam Library Data...' -ForegroundColor Cyan #Library Parsing switch ($script:DetectedOS) { "Windows" { $LibraryPath = Join-Path $script:SteamPath -ChildPath "steamapps\libraryfolders.vdf" $LibraryData = Get-Content -Path $LibraryPath -Raw -Encoding UTF8 $ParsedLibrary = Read-VDF -Content $LibraryData ForEach($libraryEntry in $ParsedLibrary["libraryfolders"].GetEnumerator()) { $library = $libraryEntry.Value if(-Not $library["apps"].ContainsKey($script:AppID)) { continue } $script:AppIDFound = $true $GameDataPath = Join-Path $library["path"] -ChildPath "steamapps\appmanifest_$script:AppID.acf" $GameDataContent = $null Try { $GameDataContent = Get-Content -Path $GameDataPath -Raw -Encoding UTF8 -ErrorAction Stop } Catch { Write-Host "Error retrieving $GameDataPath" -ForegroundColor Yellow Write-Host "If you moved Helldivers 2 without telling Steam, this can cause problems." -ForegroundColor Cyan Write-Host "See https://help.steampowered.com/en/faqs/view/4578-18A7-C819-8620." -ForegroundColor Cyan Write-Host "Several options will crash the script including mod deletion, resetting GameGuard, Full Screen Optimizations toggle and setting GPU options." -ForegroundColor Yellow Write-Host "Press [SPACEBAR] to continue..." pause $script:AppInstallPath = $false break } $ParsedGameData = Read-VDF $GameDataContent $script:BuildID = $ParsedGameData["AppState"]["buildid"] Write-Host "Parsed BuildID: $script:BuildID" -ForegroundColor Cyan $script:AppInstallPath = [System.IO.Path]::Combine($library["path"], "steamapps\common", $ParsedGameData["AppState"]["installdir"]) $script:AppManifestPath = Join-Path $library["path"] -ChildPath "\steamapps\appmanifest_$script:AppID.acf" } } "Linux" { $script:AppIDFound = $true $script:AppInstallPath = Join-Path $script:SteamPath -ChildPath "steamapps\common/Helldivers 2" $script:AppManifestPath = Join-Path $script:SteamPath -ChildPath "\steamapps\appmanifest_$script:AppID.acf" $GameData = Get-Content -Path $script:AppManifestPath -Encoding UTF8 $ParsedGameData = Read-VDF $GameData $script:BuildID = $ParsedGameData["AppState"]["buildid"] } Default { } } Get-MostRecentlyUsedSteamProfilePath $HelldiversProcess = [PSCustomObject]@{ ProcessName = 'helldivers2' ErrorMsg = ' ⚠️ The Helldivers 2 process is currently running. ⚠️ Please close the game. If the game appears closed, restart the system, and re-run this script. ' } $script:InstalledProgramsList = $null Write-Host 'Checking to see if Helldivers 2 is currently running...' -ForegroundColor Cyan Get-IsProcessRunning $HelldiversProcess If ( $script:DetectedOS -eq 'Windows' ) { $script:InstalledProgramsList = Get-InstalledPrograms } Write-Host "Building menu... $([Environment]::NewLine)$([Environment]::NewLine)" Try { MainMenu } catch { Write-Host "An error occurred that crashed the script." Write-Host "Error: $($_.Exception.Message)" Write-Host $_.ScriptStackTrace Pause } Finally { $Host.UI.RawUI.CursorPosition = $script:menuEnd }