9bb4f85802
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
67 lines
3.7 KiB
PowerShell
67 lines
3.7 KiB
PowerShell
<#
|
|
One-off repair: point each install in the REAL config.json at its folder on disk, using the <Name>-<id4> format.
|
|
|
|
Background: the earlier layout migrations were run from inside Claude's packaged app, whose %APPDATA% writes land in a
|
|
private copy of the profile. The folders under %LOCALAPPDATA%\CVRManager\installs were really moved, but the real
|
|
%APPDATA%\CVRManager\config.json was never updated, so the app showed "Not installed". This script fixes the real file.
|
|
It must be started OUTSIDE Claude's sandbox (a normal terminal, or Win32_Process.Create), otherwise it edits the private copy.
|
|
|
|
Each -Map entry is "<folder currently on disk>=<install id, at least 4 chars>".
|
|
The folder is renamed to <SafeName>-<first 4 of id> and the install's Folder in config.json is set to the new name.
|
|
|
|
Dry run by default.
|
|
pwsh -File 2026-10-05-repair-real-config-folders.ps1 -Map 'Public Scripting-6e3b=9ce8','Public Scripting copy-2f65=1864'
|
|
pwsh -File ... -Apply
|
|
#>
|
|
[CmdletBinding()]
|
|
param(
|
|
[Parameter(Mandatory)][string[]]$Map,
|
|
[string]$ConfigDir = (Join-Path $env:APPDATA 'CVRManager'),
|
|
[switch]$Apply
|
|
)
|
|
$ErrorActionPreference = 'Stop'
|
|
|
|
function Get-SafeFolderName([string]$name) {
|
|
$bad = [IO.Path]::GetInvalidFileNameChars()
|
|
$clean = -join ($name.ToCharArray() | ForEach-Object { if ($bad -contains $_) { ' ' } else { $_ } })
|
|
while ($clean.Contains(' ')) { $clean = $clean.Replace(' ', ' ') }
|
|
$clean = $clean.Trim().TrimEnd('.')
|
|
if ($clean.Length -gt 60) { $clean = $clean.Substring(0, 60).TrimEnd() }
|
|
if ($clean.Length -eq 0) { 'Install' } else { $clean }
|
|
}
|
|
|
|
$configPath = Join-Path $ConfigDir 'config.json'
|
|
$cfg = Get-Content $configPath -Raw | ConvertFrom-Json
|
|
$lib = if ($cfg.LibraryRoot) { $cfg.LibraryRoot } else { Join-Path $env:LOCALAPPDATA 'CVRManager' }
|
|
$installs = Join-Path $lib 'installs'
|
|
"Config: $configPath"
|
|
"Library: $lib"
|
|
|
|
if ($Apply -and (Get-Process CVRManager -ErrorAction SilentlyContinue)) { throw 'Close CVRManager first.' }
|
|
if ($Apply -and (Get-Process ChilloutVR -ErrorAction SilentlyContinue | Where-Object { $_.Path -and $_.Path.StartsWith($installs, [StringComparison]::OrdinalIgnoreCase) })) { throw 'Close the game first.' }
|
|
|
|
$plans = foreach ($entry in $Map) {
|
|
$dirName, $idPrefix = $entry.Split('=', 2)
|
|
$inst = @($cfg.Installs | Where-Object { $_.Id.StartsWith($idPrefix, [StringComparison]::OrdinalIgnoreCase) })
|
|
if ($inst.Count -ne 1) { throw "Id '$idPrefix' matches $($inst.Count) installs." }
|
|
$inst = $inst[0]
|
|
if (-not (Test-Path (Join-Path $installs $dirName))) { throw "No folder $dirName in $installs" }
|
|
[pscustomobject]@{ Install = $inst; From = $dirName; To = "$(Get-SafeFolderName $inst.Name)-$($inst.Id.Substring(0, 4))" }
|
|
}
|
|
foreach ($p in $plans) { " $($p.Install.Name) (id $($p.Install.Id)): disk '$($p.From)' -> '$($p.To)'; config Folder '$($p.Install.Folder)' -> '$($p.To)'" }
|
|
$targets = $plans | ForEach-Object { $_.To }
|
|
if (($targets | Select-Object -Unique).Count -ne $targets.Count) { throw 'Two installs would get the same folder.' }
|
|
foreach ($p in $plans) { if ($p.From -ine $p.To -and (Test-Path (Join-Path $installs $p.To))) { throw "Target folder '$($p.To)' already exists." } }
|
|
if (-not $Apply) { ''; 'Dry run only. Re-run with -Apply.'; return }
|
|
|
|
$backup = "$configPath.bak-$(Get-Date -Format 'yyyyMMdd-HHmmss')"
|
|
Copy-Item $configPath $backup
|
|
"Config backed up to $backup"
|
|
foreach ($p in $plans) {
|
|
if ($p.From -cne $p.To) { [IO.Directory]::Move((Join-Path $installs $p.From), (Join-Path $installs $p.To)) }
|
|
$p.Install.Folder = $p.To
|
|
[IO.File]::WriteAllText($configPath, ($cfg | ConvertTo-Json -Depth 30), (New-Object Text.UTF8Encoding($false)))
|
|
" done: $($p.Install.Name) -> $($p.To)"
|
|
}
|
|
'Config updated. Start CVRManager again.'
|