9bb4f85802
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
100 lines
5.5 KiB
PowerShell
100 lines
5.5 KiB
PowerShell
<#
|
|
One-off migration: import the beta access codes listed in "<library>\branch codes.txt" (one code per line) into CVRManager.
|
|
|
|
Each code is resolved with Steam (anonymous CheckAppBetaPassword, the same call the app's "Enter beta code" box makes), so a
|
|
code that unlocks several branches is stored once per branch. For every branch it unlocks:
|
|
- config.json -> Unlocked gets { Name, Code } (an existing entry for that branch, e.g. a name-only one, gets the code filled in)
|
|
- installs on that branch get BranchPassword set, so Update works without opening the dropdown
|
|
Codes are never printed; they are shown as a length only.
|
|
The codes are checked as your signed-in Steam account, so sign in first: CVRManager > Settings > "Sign in (beta codes)".
|
|
The saved login token is read with the same Windows user that signed in, so run this as that user.
|
|
|
|
Needs a built app for the resolver: -BinDir (default bin\v26 next to the project) must contain CVRManager.dll and SteamKit2.dll.
|
|
Run it OUTSIDE Claude's sandbox (a normal terminal), otherwise it edits a private copy of %APPDATA%.
|
|
|
|
Dry run by default (resolves codes, writes nothing).
|
|
pwsh -File 2026-10-05-import-branch-codes.ps1
|
|
pwsh -File 2026-10-05-import-branch-codes.ps1 -Apply
|
|
Close CVRManager first with -Apply. config.json is backed up next to itself.
|
|
#>
|
|
[CmdletBinding()]
|
|
param(
|
|
[string]$ConfigDir = (Join-Path $env:APPDATA 'CVRManager'),
|
|
[string]$CodesFile,
|
|
[string]$BinDir = (Join-Path $PSScriptRoot '..\..\bin\v26'),
|
|
[switch]$Apply
|
|
)
|
|
$ErrorActionPreference = 'Stop'
|
|
|
|
$configPath = Join-Path $ConfigDir 'config.json'
|
|
if (-not (Test-Path $configPath)) { throw "No config at $configPath" }
|
|
$cfg = Get-Content $configPath -Raw | ConvertFrom-Json
|
|
$lib = if ($cfg.LibraryRoot) { $cfg.LibraryRoot } else { Join-Path $env:LOCALAPPDATA 'CVRManager' }
|
|
if (-not $CodesFile) { $CodesFile = Join-Path $lib 'branch codes.txt' }
|
|
if (-not (Test-Path $CodesFile)) { throw "No codes file at $CodesFile" }
|
|
if ($Apply -and (Get-Process CVRManager -ErrorAction SilentlyContinue)) { throw 'Close CVRManager first; it would overwrite the change.' }
|
|
|
|
$BinDir = (Resolve-Path $BinDir).Path
|
|
[void][Reflection.Assembly]::LoadFrom((Join-Path $BinDir 'SteamKit2.dll'))
|
|
[void][Reflection.Assembly]::LoadFrom((Join-Path $BinDir 'CVRManager.dll'))
|
|
$resolver = [CVRManager.Services.BranchCodeService]
|
|
$account = $cfg.SteamUser
|
|
$token = [CVRManager.Services.TokenVault]::Unprotect($cfg.SteamToken)
|
|
if (-not $token) { throw 'Not signed in to Steam in the app. Open CVRManager > Settings > "Sign in (beta codes)", then run this again.' }
|
|
"Checking codes as Steam account '$account'."
|
|
|
|
$codes = @(Get-Content $CodesFile | ForEach-Object { $_.Trim() } | Where-Object { $_ } | Select-Object -Unique)
|
|
"Codes in file: $($codes.Count)"
|
|
|
|
$known = @($cfg.Unlocked | Where-Object { $_.Code } | ForEach-Object { $_.Code } | Select-Object -Unique |
|
|
Where-Object { $c = $_; -not @($cfg.Unlocked | Where-Object { $_.Code -ceq $c -and -not $_.Key }).Count })
|
|
$resolved = @()
|
|
$n = 0
|
|
foreach ($code in $codes) {
|
|
$n++
|
|
if ($known -ccontains $code) { " code $n (len $($code.Length)): already stored with its key, skipped"; continue }
|
|
for ($try = 1; $try -le 6; $try++) {
|
|
try {
|
|
$keys = $resolver::ResolveAsync($code, $account, $token, [Threading.CancellationToken]::None).GetAwaiter().GetResult()
|
|
$names = @($keys.Keys)
|
|
" code $n (len $($code.Length)): unlocks $($names.Count) branch(es): $($names -join ', ')"
|
|
foreach ($name in $names) { $resolved += [pscustomobject]@{ Name = $name; Code = $code; Key = $resolver::ProtectKey($keys[$name]) } }
|
|
break
|
|
}
|
|
catch {
|
|
$msg = $_.Exception.InnerException.Message ?? $_.Exception.Message
|
|
if ($msg -match 'RateLimitExceeded' -and $try -lt 6) { " code ${n}: Steam rate limit, waiting 60 s (try $try of 5)"; Start-Sleep 60; continue }
|
|
" code $n (len $($code.Length)): NOT resolved - $msg"
|
|
break
|
|
}
|
|
}
|
|
}
|
|
if ($resolved.Count -eq 0) { 'Nothing resolved; nothing to do.'; return }
|
|
|
|
$unlocked = [System.Collections.Generic.List[object]]::new()
|
|
foreach ($u in @($cfg.Unlocked)) { $unlocked.Add($u) }
|
|
$added = 0; $filled = 0; $same = 0; $installsSet = 0
|
|
foreach ($r in $resolved) {
|
|
$existing = $unlocked | Where-Object { $_.Name -eq $r.Name } | Select-Object -First 1
|
|
if (-not $existing) { $unlocked.Add([pscustomobject]@{ Name = $r.Name; Code = $r.Code; Key = $r.Key }); $added++ }
|
|
elseif ($existing.Code -ceq $r.Code) { $same++; $existing | Add-Member -NotePropertyName Key -NotePropertyValue $r.Key -Force }
|
|
else { if ([string]::IsNullOrEmpty($existing.Code)) { $filled++ } else { " note: replacing the stored code for '$($r.Name)'" }; $existing.Code = $r.Code; $existing | Add-Member -NotePropertyName Key -NotePropertyValue $r.Key -Force }
|
|
foreach ($i in @($cfg.Installs | Where-Object { $_.Branch -eq $r.Name -and $_.BranchPassword -cne $r.Code })) { $i.BranchPassword = $r.Code; $installsSet++ }
|
|
}
|
|
"Unlocked entries: $added added, $filled filled in, $same already correct. Installs given a branch password: $installsSet."
|
|
if (-not $Apply) { ''; 'Dry run only. Re-run with -Apply to save.'; return }
|
|
|
|
$backup = "$configPath.bak-$(Get-Date -Format 'yyyyMMdd-HHmmss')"
|
|
Copy-Item $configPath $backup
|
|
"Config backed up to $backup"
|
|
$cfg.Unlocked = @($unlocked)
|
|
[IO.File]::WriteAllText($configPath, ($cfg | ConvertTo-Json -Depth 30), (New-Object Text.UTF8Encoding($false)))
|
|
'Config updated. Start CVRManager again.'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|