param( [switch]$UpdateMangoUnlock, [string]$DownloadUrl = "", [string]$ReleaseTag = "", [string]$ReleaseVersion = "" ) $PluginDownloadUrl = "https://github.com/TheGrapeJuice/mangoplugin/releases/download/v3.5.0/MangoUnlock.zip" $UpdateAppDataDirName = "MangoUnlock" $UpdateWorkDirName = "update" $UpdateProgressFile = "update_state.json" $RemoveExistingPluginsAndThemes = $true # ============================================================================ # UI # ============================================================================ cls [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 $Orange = "$([char]0x1b)[38;2;255;165;0m" $Cyan = "$([char]0x1b)[96m" $Yellow = "$([char]0x1b)[93m" $Green = "$([char]0x1b)[92m" $Red = "$([char]0x1b)[91m" $Gray = "$([char]0x1b)[90m" $Reset = "$([char]0x1b)[0m" $RawBanner = @" __/\\\\____________/\\\\_____/\\\\\\\\\_____/\\\\\_____/\\\_____/\\\\\\\\\\\\_______/\\\\\______ _\/\\\\\\________/\\\\\\___/\\\\\\\\\\\\\__\/\\\\\\___\/\\\___/\\\//////////______/\\\///\\\____ _\/\\\//\\\____/\\\//\\\__/\\\/////////\\\_\/\\\/\\\__\/\\\__/\\\_______________/\\\/__\///\\\__ _\/\\\\///\\\/\\\/_\/\\\_\/\\\_______\/\\\_\/\\\//\\\_\/\\\_\/\\\____/\\\\\\\__/\\\______\//\\\_ _\/\\\__\///\\\/___\/\\\_\/\\\\\\\\\\\\\\\_\/\\\\//\\\\/\\\_\/\\\___\/////\\\_\/\\\_______\/\\\_ _\/\\\____\///_____\/\\\_\/\\\/////////\\\_\/\\\_\//\\\/\\\_\/\\\_______\/\\\_\//\\\______/\\\__ _\/\\\_____________\/\\\_\/\\\_______\/\\\_\/\\\__\//\\\\\\_\/\\\_______\/\\\__\///\\\___/\\\___ _\/\\\_____________\/\\\_\/\\\_______\/\\\_\/\\\___\//\\\\\_\//\\\\\\\\\\\\/_____\///\\\\\/_____ _\///______________\///__\///________\///__\///_____\/////___\////////////_________\/////_______ "@ function Center-Text { param( [string]$Text, [string]$Color = $Reset ) $width = $Host.UI.RawUI.WindowSize.Width $lines = $Text -split "`n" foreach ($line in $lines) { $line = $line.TrimEnd() $padding = [Math]::Max(0, ($width - $line.Length) / 2) Write-Host (" " * $padding) "$Color$line$Reset" } } function Show-Banner { Clear-Host Write-Host "" Center-Text $RawBanner $Orange Write-Host "" Center-Text "Steam + Millennium + OpenSteamTool + MangoUnlock Setup" $Cyan Write-Host "" Center-Text "Reinstaller" $Cyan Write-Host "" Center-Text ("-" * 70) $Cyan Write-Host "" } function Log-Message { param( [string]$Message, [string]$Color = $Reset ) $width = $Host.UI.RawUI.WindowSize.Width $prefix = "[$([DateTime]::Now.ToString('HH:mm:ss'))] " $full = $prefix + $Message $padding = [Math]::Max(0, ($width - $full.Length) / 2) Write-Host (" " * $padding) "$Color$full$Reset" } function Start-Section { param([string]$Title) Write-Host "" Center-Text ">>> $Title <<<" $Yellow Center-Text ("=" * ($Title.Length + 8)) $Yellow Write-Host "" } # ============================================================================ # Logic # ============================================================================ $ErrorActionPreference = "Stop" function Get-SteamPath { $key = "HKCU:\Software\Valve\Steam" if (-not (Test-Path $key)) { throw "Could not find Steam in the registry ($key). Is Steam installed?" } $steamPath = (Get-ItemProperty -Path $key -Name SteamPath -ErrorAction Stop).SteamPath if (-not $steamPath -or -not (Test-Path $steamPath)) { throw "Steam registry entry found, but the path '$steamPath' doesn't exist." } return $steamPath.Replace("/", "\") } function Stop-SteamProcess { $procs = Get-Process -Name "steam" -ErrorAction SilentlyContinue if ($procs) { Log-Message "Stopping Steam..." $Yellow $procs | Stop-Process -Force -ErrorAction SilentlyContinue Start-Sleep -Seconds 2 } while (Get-Process -Name "steam" -ErrorAction SilentlyContinue) { Center-Text ">>> Waiting for Steam to exit... <<<" $Red Start-Sleep -Seconds 1.5 } } function Remove-PathIfExists($path) { if (Test-Path $path) { Log-Message "Removing: $path" $Gray Remove-Item -Path $path -Recurse -Force -ErrorAction SilentlyContinue } } function Uninstall-OldMillennium($steamPath) { $millenniumPath = Join-Path $steamPath "millennium" $isNewLayout = Test-Path $millenniumPath if ($isNewLayout) { # Current Millennium layout (matches SteamClientHomebrew/Installer's # uninstall_select.cc "Millennium" + "Custom Steam Components" + # "Dependencies" components) Remove-PathIfExists (Join-Path $millenniumPath "lib") Remove-PathIfExists (Join-Path $millenniumPath "bin") Remove-PathIfExists (Join-Path $steamPath "wsock32.dll") Remove-PathIfExists (Join-Path $millenniumPath "ext\data\assets") Remove-PathIfExists (Join-Path $millenniumPath "ext\data\shims") Remove-PathIfExists (Join-Path $steamPath "ext\data\assets") Remove-PathIfExists (Join-Path $steamPath "ext\data\shims") Remove-PathIfExists (Join-Path $steamPath "ext\data\cache") Remove-PathIfExists (Join-Path $steamPath "ext\data\pyx64") if ($RemoveExistingPluginsAndThemes) { Remove-PathIfExists (Join-Path $millenniumPath "themes") Remove-PathIfExists (Join-Path $steamPath "steamui\skins") Remove-PathIfExists (Join-Path $millenniumPath "plugins") Remove-PathIfExists (Join-Path $steamPath "plugins") } } else { # Legacy layout (Millennium <=2.35.0): everything loose in Steam's root Remove-PathIfExists (Join-Path $steamPath "user32.dll") Remove-PathIfExists (Join-Path $steamPath "version.dll") Remove-PathIfExists (Join-Path $steamPath "wsock32.dll") Remove-PathIfExists (Join-Path $steamPath "millennium.dll") Remove-PathIfExists (Join-Path $steamPath "millennium.hhx64.dll") Remove-PathIfExists (Join-Path $steamPath "python311.dll") Remove-PathIfExists (Join-Path $steamPath "ext\compat32\millennium_x86.dll") Remove-PathIfExists (Join-Path $steamPath "ext\compat32\python311.dll") Remove-PathIfExists (Join-Path $steamPath "ext\compat64\millennium_x64.dll") Remove-PathIfExists (Join-Path $steamPath "ext\compat64\python311.dll") } } function Remove-CloudRedirectDll($steamPath) { Remove-PathIfExists (Join-Path $steamPath "cloud_redirect.dll") } function Get-LatestMillenniumAsset { Log-Message "Checking latest Millennium release..." $Yellow $headers = @{ "User-Agent" = "MangoUnlock-Reinstaller" } $releases = Invoke-RestMethod -Uri "https://api.github.com/repos/SteamClientHomebrew/Millennium/releases?per_page=20" -Headers $headers $release = $releases | Where-Object { -not $_.prerelease } | Select-Object -First 1 if (-not $release) { $release = $releases | Select-Object -First 1 } if (-not $release) { throw "Could not find any Millennium releases on GitHub." } $tag = $release.tag_name $assetName = "millennium-$tag-windows-x86_64.zip" $asset = $release.assets | Where-Object { $_.name -eq $assetName } | Select-Object -First 1 if (-not $asset) { throw "Could not find asset '$assetName' in release $tag." } Log-Message "Latest Millennium version: $tag" $Green return $asset } function Get-LatestOpenSteamToolAsset { Log-Message "Checking latest OpenSteamTool release..." $Yellow $headers = @{ "User-Agent" = "MangoUnlock-Reinstaller" } $release = Invoke-RestMethod -Uri "https://api.github.com/repos/OpenSteam001/OpenSteamTool/releases/latest" -Headers $headers if (-not $release) { throw "Could not find any OpenSteamTool releases on GitHub." } # Two assets ship per release: "-Release.zip" (what you want) and a much # larger "-Debug.zip" (symbols, for the tool's own devs) - match the # suffix specifically so Debug never gets picked by accident. $asset = $release.assets | Where-Object { $_.name -like "*-Release.zip" } | Select-Object -First 1 if (-not $asset) { throw "Could not find a '-Release.zip' asset in OpenSteamTool release $($release.tag_name)." } Log-Message "Latest OpenSteamTool version: $($release.tag_name)" $Green return $asset } function Install-ZipRelease($steamPath, $asset) { $zipPath = Join-Path $env:TEMP $asset.name Log-Message "Downloading $($asset.name) ($([math]::Round($asset.size / 1MB, 1)) MB)..." $Yellow Invoke-WebRequest -Uri $asset.browser_download_url -OutFile $zipPath -UseBasicParsing if ($asset.digest -and $asset.digest.StartsWith("sha256:")) { Log-Message "Verifying SHA-256..." $Yellow $expected = $asset.digest.Substring(7) $actual = (Get-FileHash -Path $zipPath -Algorithm SHA256).Hash.ToLower() if ($actual -ne $expected) { Remove-Item $zipPath -Force -ErrorAction SilentlyContinue throw "Downloaded file's SHA-256 does not match GitHub's reported digest. Download may be corrupt - do not proceed." } Log-Message "Digest OK" $Green } Log-Message "Extracting into $steamPath..." $Yellow Expand-Archive -Path $zipPath -DestinationPath $steamPath -Force Remove-Item $zipPath -Force -ErrorAction SilentlyContinue Log-Message "Done." $Green } function Move-StPlugInLuaFiles($steamPath) { $sourceDir = Join-Path $steamPath "config\stplug-in" $targetDir = Join-Path $steamPath "config\lua" if (-not (Test-Path $targetDir)) { New-Item -ItemType Directory -Path $targetDir -Force | Out-Null } if (-not (Test-Path $sourceDir)) { Log-Message "No legacy stplug-in Lua folder found to migrate." $Gray return } $luaFiles = Get-ChildItem -Path $sourceDir -Filter "*.lua" -File -ErrorAction SilentlyContinue if (-not $luaFiles) { Log-Message "No legacy Lua files found in $sourceDir." $Gray return } foreach ($file in $luaFiles) { $destination = Join-Path $targetDir $file.Name Move-Item -Path $file.FullName -Destination $destination -Force } Log-Message "Migrated legacy Lua files to $targetDir" $Green } function Install-MangoUnlockPlugin($steamPath) { if ($PluginDownloadUrl -like "*REPLACE_ME*") { Log-Message "Skipping plugin install - PluginDownloadUrl hasn't been set yet." $Yellow return } Start-Section "Installing MangoUnlock Plugin" $pluginZip = Join-Path $env:TEMP "MangoUnlock.zip" $extractDir = Join-Path $env:TEMP ("MangoUnlock-" + [guid]::NewGuid().ToString("N")) try { Log-Message "Downloading plugin from source..." $Yellow Invoke-WebRequest -Uri $PluginDownloadUrl -OutFile $pluginZip -UseBasicParsing $pluginsDir = Join-Path $steamPath "millennium\plugins" if (-not (Test-Path $pluginsDir)) { # Millennium's own release zip doesn't ship this folder - it's just # a normal directory Millennium scans on launch, safe to create. New-Item -ItemType Directory -Path $pluginsDir -Force | Out-Null } $targetDir = Join-Path $pluginsDir "MangoUnlock" if (Test-Path $targetDir) { Remove-Item $targetDir -Recurse -Force } New-Item -ItemType Directory -Path $targetDir -Force | Out-Null Expand-Archive -Path $pluginZip -DestinationPath $extractDir -Force $sourceDir = $extractDir if (-not (Test-Path (Join-Path $sourceDir "plugin.json"))) { $candidateDirs = @(Get-ChildItem -Path $extractDir -Directory -Force | Where-Object { Test-Path (Join-Path $_.FullName "plugin.json") }) if ($candidateDirs.Count -eq 1) { $sourceDir = $candidateDirs[0].FullName } } if (-not (Test-Path (Join-Path $sourceDir "plugin.json"))) { throw "plugin.json not found in MangoUnlock.zip." } Get-ChildItem -Path $sourceDir -Force | Move-Item -Destination $targetDir -Force Log-Message "MangoUnlock installed to $targetDir" $Green } finally { Remove-Item $pluginZip -Force -ErrorAction SilentlyContinue Remove-Item $extractDir -Recurse -Force -ErrorAction SilentlyContinue } } function Get-MangoUnlockAppDataDir { return Join-Path $env:APPDATA $UpdateAppDataDirName } function Ensure-Directory($path) { if (-not (Test-Path $path)) { New-Item -ItemType Directory -Path $path -Force | Out-Null } } function Get-MangoUnlockPluginDir($steamPath) { return Join-Path $steamPath "millennium\plugins\MangoUnlock" } function Get-UpdateWorkDir { $root = Get-MangoUnlockAppDataDir $work = Join-Path $root $UpdateWorkDirName Ensure-Directory $work return $work } function Get-UpdateStatePath { return Join-Path (Get-UpdateWorkDir) $UpdateProgressFile } function Write-UpdateProgress($status, $message, $percent, $step) { $path = Get-UpdateStatePath Ensure-Directory (Split-Path $path -Parent) $state = @{ status = $status message = $message percent = [Math]::Max(0, [Math]::Min(100, [int]$percent)) step = $step timestamp = [int64]([DateTimeOffset]::UtcNow.ToUnixTimeSeconds()) } Set-Content -LiteralPath $path -Value ($state | ConvertTo-Json -Depth 4) -Encoding UTF8 } function Clear-UpdateProgress { $path = Get-UpdateStatePath if (Test-Path $path) { Remove-Item -LiteralPath $path -Force -ErrorAction SilentlyContinue } } function Find-ReleaseSourceDir($extractDir) { $sourceDir = $extractDir if (-not (Test-Path (Join-Path $sourceDir "plugin.json"))) { $candidateDirs = @(Get-ChildItem -Path $extractDir -Directory -Force | Where-Object { Test-Path (Join-Path $_.FullName "plugin.json") }) if ($candidateDirs.Count -eq 1) { $sourceDir = $candidateDirs[0].FullName } } if (-not (Test-Path (Join-Path $sourceDir "plugin.json"))) { throw "plugin.json not found in MangoUnlock update archive." } return $sourceDir } function Test-LfsPointerFile($path) { if (-not (Test-Path $path)) { return $false } try { $file = Get-Item -LiteralPath $path -ErrorAction Stop if ($file.Length -gt 300) { return $false } $firstLine = Get-Content -LiteralPath $path -TotalCount 1 -ErrorAction Stop return $firstLine -eq "version https://git-lfs.github.com/spec/v1" } catch { return $false } } function Assert-HydratedReleaseRuntime($sourceDir) { $required = @( "backend\python\python.exe", "backend\python\pythonw.exe", "backend\tools\7zip\7z.exe", "backend\tools\7zip\7z.dll" ) foreach ($relativePath in $required) { $path = Join-Path $sourceDir $relativePath if (-not (Test-Path $path)) { throw "Update archive is missing bundled runtime file: $relativePath" } if (Test-LfsPointerFile $path) { throw "Update archive contains Git LFS pointer files instead of real bundled runtime files. Rebuild MangoUnlock.zip from a checkout with 'git lfs pull'." } } } function Get-ChromeForTestingCacheDir { return Join-Path $env:APPDATA "mangoplugin\chrome_for_testing" } function Move-BundledChromeForTestingToCache($pluginDir) { $bundledDir = Join-Path $pluginDir "backend\chrome_for_testing" $cacheDir = Get-ChromeForTestingCacheDir $bundledChrome = Join-Path $bundledDir "chrome-win64\chrome.exe" $bundledDriver = Join-Path $bundledDir "chromedriver-win64\chromedriver.exe" $cachedChrome = Join-Path $cacheDir "chrome-win64\chrome.exe" $cachedDriver = Join-Path $cacheDir "chromedriver-win64\chromedriver.exe" if (-not (Test-Path -LiteralPath $bundledChrome) -or -not (Test-Path -LiteralPath $bundledDriver)) { return } if ((Test-Path -LiteralPath $cachedChrome) -and (Test-Path -LiteralPath $cachedDriver)) { Log-Message "Chrome for Testing cache already exists; removing bundled copy." $Gray Remove-Item -LiteralPath $bundledDir -Recurse -Force -ErrorAction SilentlyContinue return } $cacheParent = Split-Path -LiteralPath $cacheDir -Parent Ensure-Directory $cacheParent if (Test-Path -LiteralPath $cacheDir) { Remove-Item -LiteralPath $cacheDir -Recurse -Force -ErrorAction SilentlyContinue } Log-Message "Moving Chrome for Testing into the persistent user cache..." $Yellow Move-Item -LiteralPath $bundledDir -Destination $cacheParent -Force Log-Message "Chrome for Testing cache preserved at $cacheDir" $Green } function Copy-FilesWithProgress($sourceDir, $destinationDir, $startPercent, $endPercent, $status, $message) { $files = @(Get-ChildItem -Path $sourceDir -Recurse -File -Force) $total = [Math]::Max(1, $files.Count) $index = 0 $lastPercent = -1 foreach ($file in $files) { $index++ $relativePath = $file.FullName.Substring($sourceDir.Length).TrimStart("\\") $destinationPath = Join-Path $destinationDir $relativePath Ensure-Directory (Split-Path $destinationPath -Parent) $shouldCopy = $true if (Test-Path $destinationPath) { try { $existing = Get-Item -LiteralPath $destinationPath -ErrorAction Stop $sameLength = $existing.Length -eq $file.Length $sameTimestamp = [Math]::Abs(($existing.LastWriteTimeUtc - $file.LastWriteTimeUtc).TotalSeconds) -lt 2 if ($sameLength -and $sameTimestamp) { $shouldCopy = $false } } catch { $shouldCopy = $true } } if ($shouldCopy) { Copy-Item -Path $file.FullName -Destination $destinationPath -Force } $span = [Math]::Max(1, $endPercent - $startPercent) $percent = $startPercent + [Math]::Floor(($index / $total) * $span) if ($percent -ne $lastPercent -or $index -eq $total) { Write-UpdateProgress $status ($message + " ($index/$total)") $percent $status $lastPercent = $percent } } } function Backup-UserFiles($pluginDir, $backupDir) { $preserved = @( "backend\appidlogs.txt", "backend\loadedappids.txt", "backend\multiplayer.json", "backend\multiplayer_fixes.json", "backend\multiplayer_fix_state.json" ) foreach ($relativePath in $preserved) { $sourcePath = Join-Path $pluginDir $relativePath if (Test-Path $sourcePath) { $destinationPath = Join-Path $backupDir $relativePath Ensure-Directory (Split-Path $destinationPath -Parent) Copy-Item -Path $sourcePath -Destination $destinationPath -Force } } $requestDir = Join-Path $pluginDir "backend" if (Test-Path $requestDir) { Get-ChildItem -Path $requestDir -Filter "mp_fix_request_*.json" -File -ErrorAction SilentlyContinue | ForEach-Object { $destinationPath = Join-Path $backupDir ("backend\" + $_.Name) Ensure-Directory (Split-Path $destinationPath -Parent) Copy-Item -Path $_.FullName -Destination $destinationPath -Force } } } function Restore-UserFiles($pluginDir, $backupDir) { if (-not (Test-Path $backupDir)) { return } Get-ChildItem -Path $backupDir -Recurse -File -ErrorAction SilentlyContinue | ForEach-Object { $relativePath = $_.FullName.Substring($backupDir.Length).TrimStart("\\") $destinationPath = Join-Path $pluginDir $relativePath Ensure-Directory (Split-Path $destinationPath -Parent) Copy-Item -Path $_.FullName -Destination $destinationPath -Force } } function Install-MangoUnlockUpdate($steamPath, $downloadUrl, $releaseTag, $releaseVersion) { if (-not $downloadUrl -or $downloadUrl -like "*REPLACE_ME*") { throw "Missing update download URL." } $workDir = Get-UpdateWorkDir $zipPath = Join-Path $workDir "MangoUnlock-update.zip" $extractDir = Join-Path $workDir "extract" $backupDir = Join-Path $workDir "backup" $pluginDir = Get-MangoUnlockPluginDir $steamPath Clear-UpdateProgress if (Test-Path $extractDir) { Remove-Item $extractDir -Recurse -Force -ErrorAction SilentlyContinue } if (Test-Path $backupDir) { Remove-Item $backupDir -Recurse -Force -ErrorAction SilentlyContinue } Ensure-Directory $extractDir Ensure-Directory $backupDir Write-UpdateProgress "downloading" "Downloading MangoUnlock update..." 5 "downloading" Log-Message "Downloading MangoUnlock update..." $Yellow Invoke-WebRequest -Uri $downloadUrl -OutFile $zipPath -UseBasicParsing Write-UpdateProgress "extracting" "Preparing the release archive..." 20 "extracting" Log-Message "Update downloaded to $zipPath" $Green Log-Message "Preparing release files..." $Yellow Expand-Archive -Path $zipPath -DestinationPath $extractDir -Force $sourceDir = Find-ReleaseSourceDir $extractDir Assert-HydratedReleaseRuntime $sourceDir if (-not (Test-Path $pluginDir)) { Ensure-Directory $pluginDir } Write-UpdateProgress "stopping" "Closing Steam before applying the update..." 30 "stopping" Stop-SteamProcess Move-BundledChromeForTestingToCache -pluginDir $pluginDir Write-UpdateProgress "backing_up" "Backing up your saved plugin data..." 35 "backing_up" Backup-UserFiles -pluginDir $pluginDir -backupDir $backupDir Write-UpdateProgress "applying" "Replacing plugin files..." 45 "applying" Log-Message "Applying update into $pluginDir..." $Yellow Copy-FilesWithProgress -sourceDir $sourceDir -destinationDir $pluginDir -startPercent 45 -endPercent 84 -status "applying" -message "Replacing plugin files" Write-UpdateProgress "restoring" "Restoring your saved plugin data..." 88 "restoring" Restore-UserFiles -pluginDir $pluginDir -backupDir $backupDir Remove-Item $zipPath -Force -ErrorAction SilentlyContinue Remove-Item $extractDir -Recurse -Force -ErrorAction SilentlyContinue Remove-Item $backupDir -Recurse -Force -ErrorAction SilentlyContinue $updatedLabel = $ReleaseVersion if ([string]::IsNullOrWhiteSpace($updatedLabel)) { $updatedLabel = $ReleaseTag } Write-UpdateProgress "launching" "Update complete. Steam is restarting now." 100 "launching" Log-Message "MangoUnlock updated to $updatedLabel." $Green Log-Message "Restarting Steam..." $Cyan $restartScript = Join-Path $pluginDir "backend\restart_steam.cmd" if (Test-Path $restartScript) { Start-Process -FilePath "$env:ComSpec" -ArgumentList "/C", "`"$restartScript`"" -WindowStyle Hidden } else { Stop-SteamProcess Start-Process -FilePath (Join-Path $steamPath "steam.exe") } Write-UpdateProgress "done" "Update complete. Steam is starting now." 100 "done" } if ($UpdateMangoUnlock) { Show-Banner try { $steamPath = Get-SteamPath Start-Section "Updating MangoUnlock" Install-MangoUnlockUpdate -steamPath $steamPath -downloadUrl $DownloadUrl -releaseTag $ReleaseTag -releaseVersion $ReleaseVersion Start-Sleep -Seconds 2 exit 0 } catch { Log-Message "FAILED: $($_.Exception.Message)" $Red Center-Text "Press any key to exit..." $Red $null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") exit 1 } } # ============================================================================ # Main # ============================================================================ Show-Banner try { $steamPath = Get-SteamPath Log-Message "Steam found at: $steamPath" $Green Start-Section "Stopping Steam" Stop-SteamProcess Log-Message "Steam is not running." $Green Start-Section "Removing Old Millennium + cloud_redirect.dll" Uninstall-OldMillennium -steamPath $steamPath Remove-CloudRedirectDll -steamPath $steamPath Start-Section "Installing OpenSteamTool" $openSteamToolAsset = Get-LatestOpenSteamToolAsset Install-ZipRelease -steamPath $steamPath -asset $openSteamToolAsset Move-StPlugInLuaFiles -steamPath $steamPath Start-Section "Installing Millennium" $millenniumAsset = Get-LatestMillenniumAsset Install-ZipRelease -steamPath $steamPath -asset $millenniumAsset Install-MangoUnlockPlugin -steamPath $steamPath Start-Section "Launching Steam" Log-Message "Starting Steam..." $Cyan Start-Process -FilePath (Join-Path $steamPath "steam.exe") Log-Message "All done!" $Green Start-Sleep -Seconds 2 exit 0 } catch { Log-Message "FAILED: $($_.Exception.Message)" $Red Center-Text "Press any key to exit..." $Red $null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") exit 1 }