$PluginDownloadUrl = "https://github.com/TheGrapeJuice/mangoplugin/releases/download/v3.0.0/MangoUnlock.zip" $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 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 } } # ============================================================================ # 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 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 }