Guest User

Untitled

a guest
Jul 21st, 2025
29
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. # MTG Arena Account Switcher by /u/rentar
  2. # See https://www.reddit.com/r/MagicArena/comments/1m5vnz5/i_built_a_little_tool_to_switch_between_arena/
  3. param(
  4.     [Parameter(Mandatory=$false)]
  5.     [String]$Account
  6. )
  7.  
  8. $ErrorActionPreference="Stop"
  9.  
  10. $SteamExe="${Env:Programfiles(x86)}\Steam\steam.exe"
  11.  
  12. $PathMtga="HKCU:\Software\Wizards Of The Coast\MTGA"
  13. $PathMtgaAccounts="HKCU:\Software\Wizards Of The Coast\MTGA-Accounts"
  14.  
  15. # I previously thought the _h123456789 part of the keys was somehow dynamic, but it seems those are just Unity PlayerPrefs, which means they are static.
  16. # This function was used before but seem unnecessary now.
  17. #function Get-RefreshTokenName {
  18. #   (reg query "HKCU\Software\Wizards Of The Coast\MTGA" | Where-Object { $_ -match "WAS-RefreshToken"}).Trim().Split(" ")[0]
  19. #}
  20.  
  21. $PropertyWASRefreshToken = "WAS-RefreshTokenE_h2698947118"
  22. $PropertyWASRememberMe = "WAS-RememberMe_h1678977314"
  23. $PropertyLastLoginString = "LastLoginString_h2517025721"
  24.  
  25. function ConvertFrom-Base64 {
  26.     param([byte[]] $bytes)
  27.     ConvertFrom-Base64String([System.Text.Encoding]::ASCII.GetString($bytes))
  28. }
  29.  
  30. function ConvertFrom-Base64String {
  31.     param([string] $string)
  32.     # Base64 strings seem to sometimes be 0-terminated and sometimes have 0x1D at the end, just remove those.
  33.     $string = $string.TrimEnd("`u{0}`u{1d}".ToCharArray())
  34.     while ($string.Length % 4 -ne 0) {
  35.         $string += "="
  36.     }
  37.     $decoded = [System.Convert]::FromBase64String($string)
  38.     [System.Text.Encoding]::UTF8.GetString($decoded)
  39. }
  40.  
  41. function Get-RefreshToken {
  42.     Get-ItemPropertyValue -Path $PathMtga -Name $PropertyWASRefreshToken
  43. }
  44.  
  45. function Get-BackupValue {
  46.     param(
  47.         [string] $account,
  48.         [string] $property)
  49.     try {
  50.         Get-ItemPropertyValue -Path "$PathMtgaAccounts/$account" -Name $property
  51.     } catch {
  52.         # Get-ItemPropertyValue throws an unchangable exception when the property doesn't exist.
  53.         # I'm not alone in thinking this is silly https://github.com/PowerShell/PowerShell/issues/5906
  54.     }
  55. }
  56.  
  57. function ConvertFrom-JWT {
  58.     param([byte[]] $refreshToken)
  59.     $rtDecoded = ConvertFrom-Base64($refreshToken)
  60.     $rtParts = $rtDecoded.Split('.')
  61.     # Refresh Token seems to be a JWT. The first part is the header, the second the payload, the third the signature.
  62.     # Since we don't verify anthing, we only care about the payload.
  63.     $payload = ConvertFrom-Base64String($rtParts[1])
  64.     $payload | ConvertFrom-Json
  65. }
  66.  
  67. function Backup-CurrentRefreshToken {
  68.     $refreshToken = Get-RefreshToken
  69.     $payload = ConvertFrom-JWT($refreshToken)
  70.     $account = $payload.sub
  71.     $refreshTokenBackup = Get-BackupValue $account $PropertyWASRefreshToken
  72.     if (-not $refreshTokenBackup) {
  73.         Write-Output "Account ${account}: not seen before, backing up."
  74.         $null = New-Item -Path $PathMtgaAccounts -Name $account -ErrorAction Ignore
  75.         Copy-ItemProperty -Path $PathMtga -Destination "$PathMtgaAccounts/$account" -Name $PropertyLastLoginString
  76.         Copy-ItemProperty -Path $PathMtga -Destination "$PathMtgaAccounts/$account" -Name $PropertyWASRefreshToken
  77.     } else {
  78.         $payloadBackup = ConvertFrom-JWT($refreshTokenBackup)
  79.         if ($payloadBackup.exp -lt $payload.exp) {
  80.             Write-Output "Account ${account}: New refresh token found, backing up ($($payloadBackup.exp) < $($payload.exp))."
  81.             Copy-ItemProperty -Path $PathMtga -Destination "$PathMtgaAccounts/$account" -Name $PropertyWASRefreshToken
  82.         }
  83.     }
  84. }
  85.  
  86. function Get-AccountDetails {
  87.     param([string]$account)
  88.    
  89.     $refreshToken = Get-BackupValue $account $PropertyWASRefreshToken
  90.     $lastLoginString = [System.Text.Encoding]::UTF8.GetString((Get-backupValue $account $PropertyLastLoginString)).Trim("`u{0}".ToCharArray())
  91.     $payload = ConvertFrom-JWT $refreshToken
  92.  
  93.     [pscustomobject]@{ ID=$account; Email=$lastLoginString; RefreshedAt=(Get-Date -UnixTimeSeconds $payload.iat); Expiration=(Get-Date -UnixTimeSeconds $payload.exp)}
  94. }
  95.  
  96. function Show-KnownAccounts {
  97.     Get-ChildItem -Path $PathMtgaAccounts -Name | ForEach-Object { Get-AccountDetails $_ } | Format-Table
  98. }
  99.  
  100. function Wait-ForKey {
  101.     Write-Output 'Press any key to continue...'
  102.     $null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown');
  103. }
  104.  
  105. # Create Key to store the various accounts
  106. New-Item -Path PathMtgaAccounts -ErrorAction Ignore
  107.  
  108. Backup-CurrentRefreshToken
  109.  
  110. if ($Account) {
  111.     $accountRefreshToken = Get-BackupValue $Account $PropertyWASRefreshToken
  112.     if (-not $accountRefreshToken) {
  113.         Write-Output "Account ${Account} not found."
  114.         Show-KnownAccounts
  115.         Wait-ForKey
  116.         exit 0
  117.     }
  118.  
  119.     Copy-ItemProperty -Path "$PathMtgaAccounts/$Account" -Destination $PathMtga -Name $PropertyWASRefreshToken
  120.     Set-ItemProperty -Path $PathMtga -Name $PropertyWASRememberMe -Value ([System.Text.Encoding]::ASCII).GetBytes("True`u{0}")
  121.     # Launch Arena if it's not already running.
  122.     $arenaProc = Get-Process -Name "MTGA" -ErrorAction SilentlyContinue
  123.     if (-Not $arenaProc) {
  124.         & $SteamExe steam://rungameid/2141910
  125.     }
  126. } else {
  127.     Write-Output "No account parameter specified. Specify account ID to launch Arena with that account."
  128.     Show-KnownAccounts
  129.     Wait-ForKey
  130. }
  131.  
Add Comment
Please, Sign In to add comment