<# One-off migration: the app no longer has a built-in "Menu patch" hook. Your menu patch becomes an ordinary command hook. - writes "\hooks\menupatch\apply.ps1": copies the MenuPatch folder (and PatchMenus.bat) from this folder into the game folder, then runs MenuPatch\patch.ps1 there, exiting with its exit code - config.json: the hook with id "menupatch" gets Command = that apply.ps1 and loses its "Kind" Your existing master folder (\hooks\menupatch\MenuPatch and PatchMenus.bat) is not touched. Run it OUTSIDE Claude's sandbox (a normal terminal), otherwise it edits a private copy of %APPDATA%. Dry run by default; -Apply writes. Close CVRManager first with -Apply. config.json is backed up next to itself. #> [CmdletBinding()] param( [string]$ConfigDir = (Join-Path $env:APPDATA 'CVRManager'), [switch]$Apply ) $ErrorActionPreference = 'Stop' $configPath = Join-Path $ConfigDir 'config.json' if (-not (Test-Path $configPath)) { throw "No config at $configPath" } if ($Apply -and (Get-Process CVRManager -ErrorAction SilentlyContinue)) { throw 'Close CVRManager first; it would overwrite the change.' } $cfg = Get-Content $configPath -Raw | ConvertFrom-Json $lib = if ($cfg.LibraryRoot) { $cfg.LibraryRoot } else { Join-Path $env:LOCALAPPDATA 'CVRManager' } $master = Join-Path $lib 'hooks\menupatch' $apply1 = Join-Path $master 'apply.ps1' $hook = @($cfg.Hooks) | Where-Object { $_.Id -eq 'menupatch' } | Select-Object -First 1 if (-not $hook) { 'No hook with id "menupatch" in the config; nothing to migrate.'; return } "Hook '$($hook.Name)': Kind=$($hook.Kind), Command='$($hook.Command)'" "Master folder: $master (MenuPatch present: $(Test-Path (Join-Path $master 'MenuPatch\patch.ps1')))" "apply.ps1 will be written to: $apply1" $script = @' $ErrorActionPreference = 'Stop' $game = $env:CVR_GAME_DIR if (-not $game) { throw 'CVR_GAME_DIR is not set; run this from CVRManager.' } $src = Join-Path $PSScriptRoot 'MenuPatch' if (-not (Test-Path (Join-Path $src 'patch.ps1'))) { throw "No MenuPatch\patch.ps1 in $PSScriptRoot" } robocopy $src (Join-Path $game 'MenuPatch') /E /XD backup 'reference-*' /NFL /NDL /NJH /NJS /NP | Out-Null if ($LASTEXITCODE -ge 8) { throw "robocopy failed ($LASTEXITCODE)" } $bat = Join-Path $PSScriptRoot 'PatchMenus.bat' if (Test-Path $bat) { Copy-Item $bat (Join-Path $game 'PatchMenus.bat') -Force } & (Join-Path $game 'MenuPatch\patch.ps1') if ($LASTEXITCODE -eq 2) { exit 0 } exit $LASTEXITCODE '@ if (-not $Apply) { ''; 'Dry run only. Re-run with -Apply to save.'; return } New-Item -ItemType Directory $master -Force | Out-Null [IO.File]::WriteAllText($apply1, $script, (New-Object Text.UTF8Encoding($false))) $backup = "$configPath.bak-$(Get-Date -Format 'yyyyMMdd-HHmmss')" Copy-Item $configPath $backup $hook.Command = $apply1 $hook.Arguments = '' $hook.PSObject.Properties.Remove('Kind') [IO.File]::WriteAllText($configPath, ($cfg | ConvertTo-Json -Depth 30), (New-Object Text.UTF8Encoding($false))) "Config backed up to $backup" 'Done. Start CVRManager again.'