<# .SYNOPSIS Connects your Steam copy of Super Mecha Champions to the Super Mecha Crows server. -Revert undoes everything. .DESCRIPTION This is the installer behind Install-SMC.cmd. It is a plain text file: read on to see exactly what it does. It makes three changes. Each one is backed up first, and -Revert (Uninstall-SMC.cmd) puts all three back. 1. The game's update file package\patch_npk\p_script.npk backup: p_script.npk.orig Two entries are added to it: - ConnectHelper_original: the game's own connection script, copied out of YOUR game (package\script.npk) - ConnectHelper: our small script (revival-ConnectHelper.py, readable in this kit). It loads the original and puts in the Crows server's public key. It keeps one small file from the Crows server in package\Documents\crows (-Revert removes that folder). Everything that was already in the file stays as it was. package\patch_npk\patch_npk_info.config (the game's list of update files and their sizes) is rewritten to match. 2. The certificate list package\Documents\cacert.pem backup: cacert.pem.orig The Crows server's certificate (ca.crt) is added, so the game accepts the server's web address. 3. The hosts file C:\Windows\System32\drivers\etc\hosts backup: hosts.smc-backup A marked block sends the NetEase web addresses built into the game (hostnames.txt) to the Crows server. The rest of the file is left alone. This step needs administrator rights, so Windows asks once. Close the game first. Afterwards start the game from Steam as usual. .EXAMPLE .\Install-SMC.ps1 # server from server.txt, game folder found through Steam .\Install-SMC.ps1 -Server horsecock.art -GameDir "D:\Steam\steamapps\common\Super Mecha Champions" .\Install-SMC.ps1 -Revert # undo everything .\Install-SMC.ps1 -LauncherCopy -GameDir F:\SMC-Revival # a Steam-free launcher copy (tools\make_dist.ps1) -LauncherCopy is for a launcher copy of the game (SMC-Launcher.cmd next to bin\ and package\), which already has its own script patch: only steps 2 and 3 run, the game folder is never looked up through Steam, and -Revert only removes the hosts block (the copy's own patch and certificate stay; it needs them for any server). #> param( [string] $Server, [string] $GameDir, [switch] $Revert, [switch] $LauncherCopy, [switch] $Elevated, # used internally: the administrator relaunch that writes the hosts file [string] $HostsFile = (Join-Path $env:SystemRoot 'System32\drivers\etc\hosts') # tests point this elsewhere ) $ErrorActionPreference = 'Stop' $Kit = $PSScriptRoot # ===================================================================================================================== # Settings # ===================================================================================================================== # Where to ask for help. Printed whenever something goes wrong. $HelpAddress = 'smc-help@horsecock.art' $VerifyHint = "In Steam: right-click Super Mecha Champions > Properties > Installed Files > 'Verify integrity of game " + "files'. When that finishes, run Install-SMC.cmd again." # Markers around our block in the hosts file and in cacert.pem, so we can find (and remove) exactly our lines. $BlockBegin = '# SMC-revival-begin (Super Mecha Champions: send the NetEase addresses to the Super Mecha Crows server)' $BlockEnd = '# SMC-revival-end' $CaMarker = '# SMC-revival-ca' # File sizes of the last Steam version of the game. The kit is made for that version only. $FinalResPatchSize = 87402532 # package\patch_npk\p_res_1.npk $StockScriptPatchSize = 3049508 # package\patch_npk\p_script.npk, as Steam installs it # Inside the game's archives, every file is found by a number calculated from its name (a "hash"). $ConnectHelperHash = 0x0a8ef233c6e45863 # logic\gutils\ConnectHelper.nxs $ConnectHelperOriginalHash = 0x44f33887ea680070 # logic\gutils\ConnectHelper_original.nxs $ConnectHelperSize = 20192 # the game's ConnectHelper in package\script.npk, in bytes # The game scrambles its archives with this key (RC4). It is the game's own key, the same in every copy of the game. $GameArchiveKey = [System.Text.Encoding]::ASCII.GetBytes('61ea476e-8201-11e5-864b-fcaa147137b7') # ===================================================================================================================== # Reading and writing the game's archive files (.npk) # # An .npk file has three parts: # header 24 bytes: "NXPK", the number of files, three settings, and where the table of contents starts # the files one after another # table of contents 32 bytes per file: name hash, position, size, size, two checksums, two flags. # It is scrambled with the game's key and sorted by hash (the game looks things up in it the way # you look up a name in a phone book, so it must stay sorted). # ===================================================================================================================== function Convert-Rc4([byte[]] $Data) { # RC4 scrambles and unscrambles in the same way: running it twice gives back the original bytes. $s = [byte[]](0..255) $j = 0 for ($i = 0; $i -lt 256; $i++) { $j = ($j + $s[$i] + $GameArchiveKey[$i % $GameArchiveKey.Length]) -band 255 $t = $s[$i]; $s[$i] = $s[$j]; $s[$j] = $t } $out = New-Object byte[] $Data.Length $i = 0; $j = 0 for ($n = 0; $n -lt $Data.Length; $n++) { $i = ($i + 1) -band 255 $j = ($j + $s[$i]) -band 255 $t = $s[$i]; $s[$i] = $s[$j]; $s[$j] = $t $out[$n] = $Data[$n] -bxor $s[($s[$i] + $s[$j]) -band 255] } return , $out } function Get-Crc32([byte[]] $Data) { # The standard CRC-32 checksum (the same one zip files use). $table = New-Object 'uint32[]' 256 for ($n = 0; $n -lt 256; $n++) { $c = [uint32]$n for ($k = 0; $k -lt 8; $k++) { if ($c -band 1) { $c = [uint32](0xEDB88320L -bxor ($c -shr 1)) } else { $c = $c -shr 1 } } $table[$n] = $c } $crc = [uint32]0xFFFFFFFFL foreach ($b in $Data) { $crc = [uint32]($table[($crc -bxor $b) -band 0xFF] -bxor ($crc -shr 8)) } return [uint32]($crc -bxor 0xFFFFFFFFL) } function Read-Bytes([string] $Path, [long] $Offset, [int] $Count) { $file = [System.IO.File]::OpenRead($Path) try { $buffer = New-Object byte[] $Count [void] $file.Seek($Offset, 'Begin') $done = 0 while ($done -lt $Count) { $got = $file.Read($buffer, $done, $Count - $done) if ($got -le 0) { throw "$Path ended early" } $done += $got } return , $buffer } finally { $file.Close() } } function Read-Archive([string] $Path) { # Returns the header and the table of contents of an .npk file. $header = Read-Bytes $Path 0 24 if ([System.Text.Encoding]::ASCII.GetString($header, 0, 4) -ne 'NXPK') { throw "$Path is not a game archive" } $count = [System.BitConverter]::ToUInt32($header, 4) $tocStart = [System.BitConverter]::ToUInt32($header, 20) $toc = Convert-Rc4 (Read-Bytes $Path $tocStart ($count * 32)) $entries = New-Object System.Collections.Generic.List[object] for ($n = 0; $n -lt $count; $n++) { $p = $n * 32 $entries.Add([pscustomobject]@{ Hash = [System.BitConverter]::ToUInt64($toc, $p) Position = [System.BitConverter]::ToUInt32($toc, $p + 8) Size = [System.BitConverter]::ToUInt32($toc, $p + 12) OriginalSize = [System.BitConverter]::ToUInt32($toc, $p + 16) Checksum1 = [System.BitConverter]::ToUInt32($toc, $p + 20) Checksum2 = [System.BitConverter]::ToUInt32($toc, $p + 24) Flag1 = [System.BitConverter]::ToUInt16($toc, $p + 28) Flag2 = [System.BitConverter]::ToUInt16($toc, $p + 30) }) } return [pscustomobject]@{ Header = $header; TocStart = $tocStart; Entries = $entries } } function Find-Entry($Archive, [uint64] $Hash) { return $Archive.Entries | Where-Object { $_.Hash -eq $Hash } | Select-Object -First 1 } function Write-ArchiveWithNewFiles([string] $Source, [string] $Target, $NewFiles) { # Writes $Target = everything of $Source unchanged + the new files + a new, sorted table of contents. # $NewFiles: a list of @{ Hash = ...; Data = }. They are stored the same way as the game's own patch files # (flags 3 and 0, checksum = CRC-32). $archive = Read-Archive $Source $entries = New-Object System.Collections.Generic.List[object] $entries.AddRange($archive.Entries) $position = [uint32]$archive.TocStart # the new files go where the old table of contents started foreach ($f in $NewFiles) { $entries.Add([pscustomobject]@{ Hash = [uint64]$f.Hash; Position = $position; Size = [uint32]$f.Data.Length OriginalSize = [uint32]$f.Data.Length; Checksum1 = [uint32]1; Checksum2 = (Get-Crc32 $f.Data) Flag1 = [uint16]3; Flag2 = [uint16]0 }) $position += [uint32]$f.Data.Length } $sorted = @($entries | Sort-Object Hash) $toc = New-Object System.IO.MemoryStream $w = New-Object System.IO.BinaryWriter($toc) foreach ($e in $sorted) { $w.Write([uint64]$e.Hash); $w.Write([uint32]$e.Position); $w.Write([uint32]$e.Size) $w.Write([uint32]$e.OriginalSize); $w.Write([uint32]$e.Checksum1); $w.Write([uint32]$e.Checksum2) $w.Write([uint16]$e.Flag1); $w.Write([uint16]$e.Flag2) } $w.Flush() $header = [byte[]]$archive.Header.Clone() [System.BitConverter]::GetBytes([uint32]$sorted.Count).CopyTo($header, 4) # number of files [System.BitConverter]::GetBytes([uint32]$position).CopyTo($header, 20) # where the table of contents starts $out = [System.IO.File]::Create($Target) try { $out.Write($header, 0, 24) $body = Read-Bytes $Source 24 ($archive.TocStart - 24) # the original files, unchanged $out.Write($body, 0, $body.Length) foreach ($f in $NewFiles) { $out.Write($f.Data, 0, $f.Data.Length) } $scrambled = Convert-Rc4 $toc.ToArray() $out.Write($scrambled, 0, $scrambled.Length) } finally { $out.Close() } } function ConvertTo-Zlib([byte[]] $Data) { # zlib format = 2 header bytes + deflate-compressed data + an Adler-32 checksum. $ms = New-Object System.IO.MemoryStream $ms.WriteByte(0x78); $ms.WriteByte(0x9C) $deflate = New-Object System.IO.Compression.DeflateStream($ms, [System.IO.Compression.CompressionMode]::Compress, $true) $deflate.Write($Data, 0, $Data.Length); $deflate.Close() $a = 1; $b = 0 foreach ($x in $Data) { $a = ($a + $x) % 65521; $b = ($b + $a) % 65521 } $checksum = [long]$b * 65536 + $a foreach ($shift in 24, 16, 8, 0) { $ms.WriteByte([byte](($checksum -shr $shift) -band 255)) } return , $ms.ToArray() } # ===================================================================================================================== # 1. The script patch # ===================================================================================================================== function Install-ScriptPatch([string] $Game) { $patchDir = Join-Path $Game 'package\patch_npk' $patchFile = Join-Path $patchDir 'p_script.npk' $backup = "$patchFile.orig" $resPatch = Join-Path $patchDir 'p_res_1.npk' # Is this the last Steam version of the game? if (-not (Test-Path $resPatch) -or (Get-Item $resPatch).Length -ne $FinalResPatchSize) { throw "This game file is missing or is not the expected size:`n $resPatch`n" + "This kit needs the last version of the game that Steam gave out. $VerifyHint" } # Keep the original update file. On a second run the backup already exists and stays as it is. if (-not (Test-Path $backup)) { if (-not (Test-Path $patchFile)) { throw "This game file is missing:`n $patchFile`n$VerifyHint" } Copy-Item $patchFile $backup " backup made: $backup" } if ((Get-Item $backup).Length -ne $StockScriptPatchSize) { throw "The backup of the game's update file is not the file Steam installs:`n $backup`n" + "Delete that backup file. $VerifyHint" } $stock = Read-Archive $backup if ((Find-Entry $stock $ConnectHelperHash) -or (Find-Entry $stock $ConnectHelperOriginalHash)) { throw "The backup of the game's update file already contains the Crows entries, so it is not the original:`n" + " $backup`nDelete that backup file. $VerifyHint" } # The game's own ConnectHelper, from your game's main script archive. It is stored scrambled with the game's # key; unscrambled it is the same form the patch file uses. $scripts = Join-Path $Game 'package\script.npk' ' reading the game''s own connection script (ConnectHelper) from package\script.npk, a few seconds...' $entry = Find-Entry (Read-Archive $scripts) $ConnectHelperHash if (-not $entry -or $entry.Size -ne $ConnectHelperSize) { throw "The game's connection script in this file is not the expected one, so this is not the last Steam " + "version of the game:`n $scripts`n$VerifyHint" } $original = Convert-Rc4 (Read-Bytes $scripts $entry.Position $entry.Size) if ([System.Text.Encoding]::ASCII.GetString($original, 0, 3) -ne 'NXS') { throw "Could not read the game's connection script from:`n $scripts`n$VerifyHint" } # Our script, compiled in the game's format (its readable form: revival-ConnectHelper.py). $ours = [System.IO.File]::ReadAllBytes((Join-Path $Kit 'revival-ConnectHelper.nxs')) # Write the new patch file next to the old one first, then swap it in. $new = "$patchFile.new" Write-ArchiveWithNewFiles $backup $new @( @{ Hash = $ConnectHelperOriginalHash; Data = $original }, @{ Hash = $ConnectHelperHash; Data = $ours }) Move-Item $new $patchFile -Force " update file written: $patchFile (your original file + 2 entries)" # The kit number, for support: the server only takes Steam logins from its current kit (revival-ConnectHelper.py). $kitText = Get-Content (Join-Path $Kit 'revival-ConnectHelper.py') -Raw -ErrorAction SilentlyContinue if ($kitText -match 'CROWS_KIT\s*=\s*(\d+)') { " kit number $($Matches[1]) installed" } # The game only loads patch files listed, with their exact size, in patch_npk_info.config. $config = Join-Path $patchDir 'patch_npk_info.config' if (Test-Path $config) { if (-not (Test-Path "$config.orig") -and -not (Test-Path "$config.was_absent")) { Copy-Item $config "$config.orig" } } elseif (-not (Test-Path "$config.was_absent")) { New-Item -ItemType File "$config.was_absent" | Out-Null } $listed = Get-ChildItem $patchDir -Filter '*.npk' | Sort-Object Name | ForEach-Object { '"{0}": {{"size": {1}}}' -f $_.Name, $_.Length } $json = '{' + ($listed -join ', ') + '}' [System.IO.File]::WriteAllBytes($config, (ConvertTo-Zlib ([System.Text.Encoding]::UTF8.GetBytes($json)))) " list of update files rewritten: $config" } # ===================================================================================================================== # 2. The certificate # ===================================================================================================================== function Install-Certificate([string] $Game) { $pem = Join-Path $Game 'package\Documents\cacert.pem' if (-not (Test-Path $pem)) { throw "This game file does not exist yet:`n $pem`nThe game creates it on its first start. Start the game " + "once from Steam, close it, then run Install-SMC.cmd again." } $ca = (Get-Content (Join-Path $Kit 'ca.crt') -Raw).Trim() $text = Get-Content $pem -Raw if ($text.Contains($CaMarker) -or $text.Replace("`r", '').Contains($ca.Replace("`r", ''))) { ' certificate already added'; return } if (-not (Test-Path "$pem.orig")) { Copy-Item $pem "$pem.orig" } [System.IO.File]::AppendAllText($pem, "`n$CaMarker`n$ca`n", [System.Text.Encoding]::ASCII) " certificate added to $pem" } # ===================================================================================================================== # 3. The hosts file # ===================================================================================================================== function Set-HostsBlock([string] $Ip) { # $Ip = '' removes our block $existing = @() if (Test-Path $HostsFile) { $existing = [System.IO.File]::ReadAllLines($HostsFile) } if ($existing.Count -gt 0) { try { [System.IO.File]::WriteAllLines($HostsFile + '.smc-backup', $existing) } catch { } } $kept = New-Object System.Collections.Generic.List[string] $inBlock = $false foreach ($line in $existing) { if ($line.StartsWith('# SMC-revival-begin')) { $inBlock = $true; continue } if ($line.StartsWith($BlockEnd)) { $inBlock = $false; continue } if (-not $inBlock) { $kept.Add($line) } } while ($kept.Count -gt 0 -and [string]::IsNullOrWhiteSpace($kept[$kept.Count - 1])) { $kept.RemoveAt($kept.Count - 1) } $lines = $kept.ToArray() if ($Ip) { $names = Get-Content (Join-Path $Kit 'hostnames.txt') | Where-Object { $_ -and -not $_.StartsWith('#') } $lines += @('') + @($BlockBegin) + ($names | ForEach-Object { '{0} {1}' -f $Ip, $_.Trim() }) + @($BlockEnd) } $content = ($lines -join "`r`n") + "`r`n" for ($i = 0; $i -lt 10; $i++) { # other software often holds the file open for a moment try { [System.IO.File]::WriteAllText($HostsFile, $content, [System.Text.Encoding]::ASCII); break } catch { if ($i -eq 9) { throw "The hosts file could not be written ($($_.Exception.Message)). Some antivirus programs guard this " + "file: allow the change there, or pause the antivirus for a minute, then try again. " + "A copy of the file as it was: $HostsFile.smc-backup" } Start-Sleep -Milliseconds 400 } } try { ipconfig /flushdns | Out-Null } catch { } } # ===================================================================================================================== # Other helpers # ===================================================================================================================== function Test-Admin { $id = [Security.Principal.WindowsIdentity]::GetCurrent() (New-Object Security.Principal.WindowsPrincipal $id).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) } function Find-GameDir { # Looks in every Steam library folder for the game. $steam = (Get-ItemProperty 'HKCU:\Software\Valve\Steam' -ErrorAction SilentlyContinue).SteamPath $libs = @() if ($steam) { $libs += $steam $vdf = Join-Path $steam 'steamapps\libraryfolders.vdf' if (Test-Path $vdf) { foreach ($m in [regex]::Matches((Get-Content $vdf -Raw), '"path"\s+"([^"]+)"')) { $libs += $m.Groups[1].Value -replace '\\\\', '\' } } } foreach ($lib in $libs | Select-Object -Unique) { $d = Join-Path $lib 'steamapps\common\Super Mecha Champions' if (Test-Path (Join-Path $d 'package\script.npk')) { return $d } } return $null } function Resolve-ServerIp([string] $Name) { $ip = $null if ([System.Net.IPAddress]::TryParse($Name, [ref] $ip)) { return $Name } try { $a = [System.Net.Dns]::GetHostAddresses($Name) | Where-Object { $_.AddressFamily -eq 'InterNetwork' } | Select-Object -First 1 } catch { $a = $null } if (-not $a) { throw "The server address '$Name' (from server.txt) could not be looked up. Is this PC online? If it is, " + "the address in server.txt may have a typo." } return $a.IPAddressToString } function Test-GameFolder([string] $Game) { # The game writes its own caches next to its archives (package\res: sounds, physics and shader caches). # 2026-09-24: a friend's game could not create package\res / res_patch / script_patch ("Failed to create # directories"): gray intro video, sounds and caches missing. Create them here and check the folder is writable. foreach ($d in @('res', 'res_patch', 'script_patch')) { New-Item -ItemType Directory -Force -Path (Join-Path $Game "package\$d") | Out-Null } $probe = Join-Path $Game 'package\res\smc-write-test.tmp' try { [System.IO.File]::WriteAllText($probe, 'ok'); Remove-Item $probe -Force; ' the game can write into its own folder' } catch { Write-Warning ("The game cannot write into its own folder:`n $Game\package`n ($($_.Exception.Message))`n" + " Without that, sounds and videos will be missing. The usual cause is Windows Security's Controlled folder`n" + " access, or another antivirus. Run Allow-SMC-Defender.cmd after this, or add the game folder to your`n" + " antivirus program's exceptions.") } $cc = Join-Path $Game 'bin\cclive_bin\MLiveCCPlayer.dll' if (-not (Test-Path $cc)) { Write-Warning ("The game's video player is missing:`n $cc`n Windows Security may have removed it. Run " + "Allow-SMC-Defender.cmd after this. If the file is still missing afterwards, verify the game files in Steam.") } } function Restore-GameFiles([string] $Game) { # Puts every backup back. The backups themselves stay, so running this twice is harmless. $patchDir = Join-Path $Game 'package\patch_npk' $patchFile = Join-Path $patchDir 'p_script.npk' if (Test-Path "$patchFile.orig") { Copy-Item "$patchFile.orig" $patchFile -Force; ' p_script.npk restored' } $config = Join-Path $patchDir 'patch_npk_info.config' if (Test-Path "$config.was_absent") { Remove-Item $config -ErrorAction SilentlyContinue; Remove-Item "$config.was_absent"; ' patch_npk_info.config removed (it was not there before)' } elseif (Test-Path "$config.orig") { Copy-Item "$config.orig" $config -Force; ' patch_npk_info.config restored' } $pem = Join-Path $Game 'package\Documents\cacert.pem' if (Test-Path "$pem.orig") { Copy-Item "$pem.orig" $pem -Force; ' cacert.pem restored' } $crows = Join-Path $Game 'package\Documents\crows' # the kit script's own update copy (kit.py + kit.json) if (Test-Path $crows) { Remove-Item $crows -Recurse -Force -ErrorAction SilentlyContinue; ' package\Documents\crows removed (the kit update copy)' } } # ===================================================================================================================== # Main # ===================================================================================================================== # The administrator relaunch only writes the hosts file. if ($Elevated) { try { Set-HostsBlock $Server; exit 0 } catch { $_.Exception.Message | Out-File (Join-Path $env:TEMP 'smc-install-error.txt'); exit 1 } } # Everything below runs inside one try block, so that any problem ends in a plain message instead of a wall of red. $again = if ($Revert) { 'Uninstall-SMC.cmd' } else { 'Install-SMC.cmd' } if ($LauncherCopy) { $again = if ($Revert) { 'Uninstall-Launcher.cmd' } else { 'Install-Launcher.cmd' } } try { '' if ($LauncherCopy) { if ($Revert) { 'Super Mecha Crows: removing the launcher copy''s hosts block' } else { 'Super Mecha Crows: connecting the launcher copy to the Crows server' } } elseif ($Revert) { 'Super Mecha Crows: putting Super Mecha Champions back to how Steam installed it' } else { 'Super Mecha Crows: connecting Super Mecha Champions to the Crows server' } '' if (Get-Process -Name 'client' -ErrorAction SilentlyContinue) { throw 'Super Mecha Champions is still running. Close the game, then run this again.' } if ($LauncherCopy) { # Never through Steam: the launcher copy is a separate folder. Default = the folder the kit was unzipped into. if (-not $GameDir -and (Test-Path (Join-Path (Split-Path $Kit -Parent) 'SMC-Launcher.ps1'))) { $GameDir = Split-Path $Kit -Parent } if (-not $GameDir) { 'Paste the folder of the launcher copy (the one with SMC-Launcher.cmd in it, e.g. F:\SMC-Revival) and press Enter.' $GameDir = "$(Read-Host 'Launcher copy folder')".Trim().Trim('"') if (-not $GameDir) { throw 'No game folder was given.' } } if (-not (Test-Path (Join-Path $GameDir 'SMC-Launcher.ps1')) -or -not (Test-Path (Join-Path $GameDir 'package\script.npk'))) { throw "That is not a launcher copy (SMC-Launcher.ps1 and package\script.npk expected):`n $GameDir" } } if (-not $GameDir) { $GameDir = Find-GameDir } if (-not $GameDir -or -not (Test-Path (Join-Path $GameDir 'package\script.npk'))) { 'The game was not found through Steam. In Steam: right-click Super Mecha Champions > Manage > Browse local' 'files. Copy the folder path from the address bar of the window that opens, paste it here and press Enter.' $answer = Read-Host 'Game folder' $GameDir = "$answer".Trim().Trim('"') if (-not $GameDir) { throw 'No game folder was given.' } if (-not (Test-Path (Join-Path $GameDir 'package\script.npk'))) { throw "That is not the game folder (there is no package\script.npk in it):`n $GameDir" } } "Game folder: $GameDir" $ip = '' if ($Revert -and $LauncherCopy) { 'Removing the block from the hosts file (the game folder stays as it is)...' } elseif ($Revert) { 'Putting the original game files back...' Restore-GameFiles $GameDir 'Removing the block from the hosts file...' } else { if (-not $Server) { $Server = (Get-Content (Join-Path $Kit 'server.txt') -ErrorAction SilentlyContinue | Select-Object -First 1) } if (-not $Server) { throw 'No server address: server.txt is missing from the kit folder. Unzip the whole kit, then try again.' } $ip = Resolve-ServerIp $Server.Trim() "Server: $Server ($ip)" if ($LauncherCopy) { '[1/4] The game''s update file: skipped (the launcher copy has its own)' } else { '[1/4] The game''s update file' Install-ScriptPatch $GameDir } '[2/4] The certificate list' Install-Certificate $GameDir '[3/4] Game folder check' Test-GameFolder $GameDir '[4/4] The hosts file' } $arg = @('-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', ('"{0}"' -f $PSCommandPath), '-Elevated', '-Server', $(if ($ip) { $ip } else { '""' })) $system = $HostsFile -eq (Join-Path $env:SystemRoot 'System32\drivers\etc\hosts') if ((Test-Admin) -or -not $system) { Set-HostsBlock $ip } else { ' Windows will now ask for permission to change the hosts file: click Yes...' try { $p = Start-Process -FilePath 'powershell.exe' -ArgumentList $arg -Verb RunAs -Wait -PassThru } catch { throw "Windows asked for permission to change the hosts file and did not get it. Run $again again and click Yes when Windows asks." } if ($p.ExitCode -ne 0) { throw "The hosts file was not changed:`n $((Get-Content (Join-Path $env:TEMP 'smc-install-error.txt') -Raw).Trim())" } } '' if ($Revert -and $LauncherCopy) { ' hosts block removed.'; ''; 'Done.' } elseif ($Revert) { ' hosts block removed.'; ''; 'Done. The game is back to how it was.' } elseif ($LauncherCopy) { " hosts block written: the NetEase addresses on this PC now lead to $ip." '' "Done. Start SMC-Launcher.cmd, type $ip as the Server, pick a username and password, click Play." 'Do not click Setup hosts in the launcher: it is already done.' } else { " hosts block written: the NetEase addresses on this PC now lead to $ip." '' 'Done. Start Super Mecha Champions from Steam and pick the server SMC-Steam-US.' } } catch { '' Write-Host 'SOMETHING WENT WRONG' -ForegroundColor Red Write-Host '' Write-Host (' ' + (($_.Exception.Message).Trim() -replace "`n", "`n ")) -ForegroundColor Red '' "Whatever was already done is safe to leave as it is. Fix the problem above, then double-click $again again;" $(if ($LauncherCopy) { 'running it more than once is safe. To remove the hosts block again, double-click Uninstall-Launcher.cmd.' } else { 'running it more than once is safe. To put the game back to how it was, double-click Uninstall-SMC.cmd.' }) "Need help? Send a photo or screenshot of this window to $HelpAddress." exit 1 }