<# CoD4 RTX client installer: CoD4x 1.8 (cod4x_242) + cod4x_rtx module + NVIDIA RTX Remix 1.4.2. Run it (Windows PowerShell): irm https://racknerd-8a2bb48.tail0916b6.ts.net/rtx | iex or save it and run: powershell -ExecutionPolicy Bypass -File install-rtx.ps1 [-Connect host:port] [-NoLaunch] [-Uninstall] Options through irm|iex use environment variables instead: COD4RTX_GAMEDIR, COD4RTX_DIR, COD4RTX_CONNECT, COD4RTX_NOLAUNCH=1, COD4RTX_FULLSCREEN=1, COD4RTX_UNINSTALL=1. What it does: 1. Finds your Call of Duty 4 install (Steam or retail). It is only READ, never modified. 2. Builds a separate RTX game folder next to it (default %LOCALAPPDATA%\CoD4-RTX\game): big game data is hard-linked (no extra disk space), everything else is copied, then the CoD4x game files, the Remix runtime and the RTX configs go in. Your normal CoD4 / CoD4x keeps working exactly as before (Remix's d3d9.dll only lives in the RTX folder). 3. Adds our client to %LOCALAPPDATA%\CallofDuty4MW\bin\cod4x_242 (beside, not over, the official CoD4x client) and the official CoD4x launcher files only if you do not have them. 4. Creates a "CoD4 RTX" desktop shortcut and starts the game with it. Every download is checked against a SHA-256 pinned in this script; any mismatch stops the install. Third-party files come from their official GitHub releases (NVIDIA, CoD4x); only our own client files come from https://racknerd-8a2bb48.tail0916b6.ts.net. #> param( [string] $GameDir = $env:COD4RTX_GAMEDIR, [string] $InstallDir = $env:COD4RTX_DIR, [string] $Connect = $env:COD4RTX_CONNECT, [switch] $NoLaunch, [switch] $Fullscreen, [switch] $Uninstall ) function Install-CoD4Rtx { param([string] $GameDir, [string] $InstallDir, [string] $Connect, [bool] $NoLaunch, [bool] $Fullscreen, [bool] $Uninstall) # Everything stays inside this function: under `irm | iex` the script runs in the player's own # session, so no `exit` (it would close their window) and no leaked preferences. $ErrorActionPreference = 'Stop' $ProgressPreference = 'SilentlyContinue' # PS 5.1's progress bar makes downloads crawl [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12 Add-Type -AssemblyName System.IO.Compression.FileSystem # ---- filled in by tools/build-rtx-dist.ps1 ---- $Version = '2026.10.19-0516' $BaseUrl = 'https://racknerd-8a2bb48.tail0916b6.ts.net' $DefaultConnect = '172.245.184.209:28960' $Bundle = @{ Name = 'cod4-rtx-bundle-75548d72feff.zip'; Sha256 = '75548d72feff4843d21eb3354a2754a6a9ee485dded85fdbf83869336371dd8e' } if ($env:COD4RTX_BASEURL) { $BaseUrl = $env:COD4RTX_BASEURL } # testing: a URL or a local folder # ---- pinned third-party components (same pins as the dev pipeline, client.ps1) ---- # Remix 1.4.2 only: 1.5.x grew the Remix API struct and the bridge writes past cod4x_rtx's copy. $Remix = @{ Name = 'remix-1.4.2-release.zip'; Sha256 = '4716eb0779020c630813aec30e59c7417270d46bd2fb474c5dfda9a29d32cfe5' Url = 'https://github.com/NVIDIAGameWorks/rtx-remix/releases/download/remix-1.4.2/remix-1.4.2-release.zip' } $cx = 'https://github.com/callofduty4x/CoD4x_Client_pub/releases/download/21.8' $CoD4x = @( @{ Name = 'core'; Sha256 = 'c7ecd2c49d7feb4afc6f2c69f4abf1a87c2a289d53199aa46eff54fecdf28c74'; Url = "$cx/core" } # zip: iw3mp.exe @{ Name = 'mss'; Sha256 = '066ca450a6c9e3e64d1851a19eaa476376412668229202e52692a864917f3097'; Url = "$cx/mss" } # zip: miles32.dll @{ Name = 'jcod4x_00.iwd'; Sha256 = '8188b0bedb08aebbd51aad023ed6bb8a0e9fb996d682ae5c787407bfb66e3733'; Url = "$cx/jcod4x_00.iwd"; To = 'main' } @{ Name = 'cod4x_patchv2.ff'; Sha256 = '02a834ca1c1278ec9bd2113f402850454f5c39aa787603dc5132cbb114fda01a'; Url = "$cx/cod4x_patchv2.ff"; To = 'zone' } @{ Name = 'cod4x_patch.ff'; Sha256 = 'e55cd5dfe89833b80ca4b37c0ca6f18c0056fcac9d97eb08003307c55645e71d'; Url = "$cx/cod4x_patch.ff"; To = 'zone' } @{ Name = 'cod4x_ambfix.ff'; Sha256 = 'a94db0826dbfeca936209bf3b43c2eab6ff6f3480a86ecb9a4e76c766b70eacd'; Url = "$cx/cod4x_ambfix.ff"; To = 'zone' } @{ Name = 'launcher.dll'; Sha256 = '61b43581397d7be1b6002cc37fae5d4ed12e88e037550c7053db312a5a342c08'; To = 'bin' Url = 'https://github.com/callofduty4x/CoD4x-launcher/releases/download/1.1.3/launcher.dll' } @{ Name = 'mss32.dll'; Sha256 = '58c1fd0ccfd27b88675375232d3ae3a45a55f3d18b2c210d0d3c20f7854c08c5' Url = 'https://github.com/callofduty4x/CoD4x-mss/releases/download/1.0.2/mss32.dll' } # CoD4x Miles proxy ) # iw3mp.exe from `core` with the Large Address Aware flag set (what the CoD4x launcher expects). $ExeSha256 = 'f50f8a520581754e73cb7cae67b02adcbad5f2e71aaa412a013bb3ef57a391a5' $Protocol = '242' # launcher loads bind4x_242d4x_242.dll for +set protocolversion 242 $ShortcutName = 'CoD4 RTX' function Say($m) { Write-Host "[cod4-rtx] $m" } function Warn($m) { Write-Host "[cod4-rtx] $m" -ForegroundColor Yellow } function Sha256Of($p) { (Get-FileHash $p -Algorithm SHA256).Hash.ToLower() } $AppData = Join-Path $env:LOCALAPPDATA 'CallofDuty4MW' if (-not $InstallDir) { $InstallDir = Join-Path $env:LOCALAPPDATA 'CoD4-RTX' } $Game = Join-Path $InstallDir 'game' $Cache = Join-Path $InstallDir 'downloads' $ForkBin = Join-Path $AppData "bin\cod4x_$Protocol" $Lnk = Join-Path ([Environment]::GetFolderPath('Desktop')) "$ShortcutName.lnk" $running = @(Get-CimInstance Win32_Process -Filter "Name='iw3mp.exe'" -ErrorAction SilentlyContinue | Where-Object { $_.ExecutablePath -and $_.ExecutablePath.StartsWith($Game, 'OrdinalIgnoreCase') }) if ($running.Count) { throw "the RTX game is running (pid $($running[0].ProcessId)); close it and run the installer again" } if ($Uninstall) { foreach ($p in @($Game, $Cache, $ForkBin, $Lnk)) { if (Test-Path $p) { Remove-Item $p -Recurse -Force; Say "removed $p" } } if ((Test-Path $InstallDir) -and -not (Get-ChildItem $InstallDir -Force)) { Remove-Item $InstallDir -Force } Say "uninstalled. Your CoD4 install, the official CoD4x client and your player profiles were not touched." return } Say "CoD4 RTX client $Version" # ---- 1. find CoD4 ---- function Test-GameDir($d) { $d -and (Test-Path (Join-Path $d 'iw3mp.exe')) -and (Test-Path (Join-Path $d 'main\iw_00.iwd')) } if (-not $GameDir) { $cands = @() $steam = (Get-ItemProperty 'HKCU:\Software\Valve\Steam' -ErrorAction SilentlyContinue).SteamPath if ($steam) { $libs = @($steam.Replace('/', '\')) $vdf = Join-Path $steam 'steamapps\libraryfolders.vdf' if (Test-Path $vdf) { $libs += @(Select-String -Path $vdf -Pattern '"path"\s+"([^"]+)"' | ForEach-Object { $_.Matches[0].Groups[1].Value.Replace('\\', '\') }) } $cands += @($libs | ForEach-Object { Join-Path $_ 'steamapps\common\Call of Duty 4' }) } foreach ($k in 'HKLM:\SOFTWARE\WOW6432Node\Activision\Call of Duty 4', 'HKLM:\SOFTWARE\Activision\Call of Duty 4') { $p = (Get-ItemProperty $k -ErrorAction SilentlyContinue).InstallPath if ($p) { $cands += $p } } $cands += "${env:ProgramFiles(x86)}\Steam\steamapps\common\Call of Duty 4" $GameDir = $cands | Where-Object { Test-GameDir $_ } | Select-Object -First 1 } if (-not (Test-GameDir $GameDir)) { throw "Call of Duty 4 not found. Set its folder and run again: `$env:COD4RTX_GAMEDIR = 'D:\Games\Call of Duty 4'" } $GameDir = (Resolve-Path $GameDir).Path.TrimEnd('\') Say "CoD4 found: $GameDir (read only)" if ($Game.StartsWith($GameDir + '\', 'OrdinalIgnoreCase')) { throw "the install folder must not be inside the CoD4 folder" } # ---- 2. preflight warnings (nothing is changed) ---- $gpu = @(Get-CimInstance Win32_VideoController -ErrorAction SilentlyContinue | ForEach-Object Name) if (-not ($gpu -match 'NVIDIA')) { Warn "no NVIDIA GPU found ($($gpu -join ', ')). RTX Remix needs an NVIDIA RTX card." } $sac = (Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\CI\Policy' -ErrorAction SilentlyContinue).VerifiedAndReputablePolicyState if ($sac -eq 1) { Warn "Smart App Control is ON. It blocks unsigned DLLs, and our client DLLs are not signed yet, so the RTX game may fail to load them. That is your call; this installer does not change it." } # ---- 3. downloads (cached, SHA-256 checked) ---- New-Item -ItemType Directory -Force $Cache | Out-Null function Get-Pinned($item) { $dst = Join-Path $Cache $item.Name if ((Test-Path $dst) -and (Sha256Of $dst) -eq $item.Sha256) { return $dst } $src = $item.Url Say "downloading $($item.Name) ..." $part = "$dst.part" if ($src -match '^https?://') { (New-Object Net.WebClient).DownloadFile($src, $part) } else { Copy-Item $src $part -Force # local folder (testing) } $got = Sha256Of $part if ($got -ne $item.Sha256) { Remove-Item $part -Force; throw "$($item.Name): SHA-256 mismatch (want $($item.Sha256), got $got). Nothing was installed from it." } Move-Item $part $dst -Force return $dst } function Join-Base($name) { if ($BaseUrl -match '^https?://') { "$($BaseUrl.TrimEnd('/'))/$name" } else { Join-Path $BaseUrl $name } } $Bundle.Url = Join-Base $Bundle.Name $files = @{} foreach ($i in @($Remix, $Bundle) + $CoD4x) { $files[$i.Name] = Get-Pinned $i } Say "all downloads verified" function Expand-Fresh($zip, $dir) { if (Test-Path $dir) { Remove-Item $dir -Recurse -Force } [IO.Compression.ZipFile]::ExtractToDirectory($zip, $dir) } $unpack = Join-Path $Cache 'unpack' Expand-Fresh $files['core'] "$unpack\core" Expand-Fresh $files['mss'] "$unpack\mss" Expand-Fresh $files[$Bundle.Name] "$unpack\bundle" $remixDir = Join-Path $Cache 'remix-1.4.2' if (-not (Test-Path (Join-Path $remixDir '.trex\NvRemixBridge.exe'))) { Expand-Fresh $files[$Remix.Name] $remixDir } # ---- 4. the RTX game folder ---- # main/zone/miles are pure game data: hard links (fall back to a copy on another drive). # Everything else from the root is copied. The player's own Mods/usermaps/players stay out # (Promod and the RTX iwd come from the server on connect, fast-download), and so does any # Remix/iw3xo they already have there (stale runtime files must not mix with 1.4.2). New-Item -ItemType Directory -Force $Game | Out-Null $linkDirs = @('main', 'zone', 'miles'); $skipDirs = @('mods', 'usermaps', 'players', '.trex', 'rtx-remix', 'iw3xo') $linked = 0; $copied = 0; $linkFailed = $false Get-ChildItem $GameDir -Recurse -File -Force | ForEach-Object { $rel = $_.FullName.Substring($GameDir.Length + 1) $top = ($rel -split '\\')[0].ToLower() if ($skipDirs -contains $top) { return } $dst = Join-Path $Game $rel if (Test-Path $dst) { return } New-Item -ItemType Directory -Force (Split-Path $dst) | Out-Null if ($linkDirs -contains $top -and -not $linkFailed) { try { New-Item -ItemType HardLink -Path $dst -Target $_.FullName -ErrorAction Stop | Out-Null; $linked++; return } catch { $linkFailed = $true; Warn "cannot hard-link from $GameDir (different drive?); copying the game data instead (several GB)" } } Copy-Item $_.FullName $dst; $copied++ } Say "game folder $Game : $linked hard-linked, $copied copied" # CoD4x game-dir files, as the CoD4x launcher lays them out. $exe = Join-Path $Game 'iw3mp.exe' $b = [IO.File]::ReadAllBytes("$unpack\core\iw3mp.exe") [BitConverter]::GetBytes([uint16]0x0123).CopyTo($b, 286) # PE characteristics: + Large Address Aware [IO.File]::WriteAllBytes($exe, $b) if ((Sha256Of $exe) -ne $ExeSha256) { throw "patched iw3mp.exe has an unexpected hash" } Copy-Item "$unpack\mss\miles32.dll" (Join-Path $Game 'miles32.dll') -Force Copy-Item $files['mss32.dll'] (Join-Path $Game 'mss32.dll') -Force # Remix runtime (d3d9.dll + .trex), then our files on top: iw3xo RTX assets + our Remix configs. Copy-Item (Join-Path $remixDir '*') $Game -Recurse -Force Copy-Item (Join-Path "$unpack\bundle" 'game\*') $Game -Recurse -Force Set-Content (Join-Path $Game '.trex\remix-runtime.txt') 'remix-1.4.2' -Encoding ascii Say "Remix 1.4.2 + RTX configs installed" # ---- 5. %LOCALAPPDATA%\CallofDuty4MW ---- # Our client goes in its own bin\cod4x_242. Shared CoD4x files are added only when missing, # so an existing official CoD4x install is never downgraded. foreach ($i in $CoD4x | Where-Object { $_.To }) { $dir = Join-Path $AppData $i.To $dst = Join-Path $dir $i.Name if (Test-Path $dst) { if ((Sha256Of $dst) -ne $i.Sha256) { Say "keeping your existing $($i.To)\$($i.Name) (differs from CoD4x 21.8; usually a newer CoD4x)" } continue } New-Item -ItemType Directory -Force $dir | Out-Null Copy-Item $files[$i.Name] $dst Say "added $($i.To)\$($i.Name)" } New-Item -ItemType Directory -Force $ForkBin | Out-Null Get-ChildItem (Join-Path "$unpack\bundle" "appdata\bin\cod4x_$Protocol") -File | ForEach-Object { $dst = Join-Path $ForkBin $_.Name if (-not ((Test-Path $dst) -and (Sha256Of $dst) -eq (Sha256Of $_.FullName))) { Copy-Item $_.FullName $dst -Force } } Say "client installed: $ForkBin" # Own CoD4 player profile for RTX (Remix forces render settings into config_mp.cfg; keep them out # of the normal profile). Copied from the active one; on a fresh install the game creates one. $profiles = Join-Path $AppData 'players\profiles' $rtxProfile = $null $active = $null if (Test-Path "$profiles\active.txt") { $active = (Get-Content "$profiles\active.txt" -Raw).Trim() } if ($active -and $active -match '^[\w .-]+$' -and (Test-Path "$profiles\$active")) { $rtxProfile = if ($active.EndsWith('_rtx')) { $active } else { "${active}_rtx" } if (-not (Test-Path "$profiles\$rtxProfile")) { Copy-Item "$profiles\$active" "$profiles\$rtxProfile" -Recurse Say "created player profile $rtxProfile (copy of $active)" } } # ---- 6. shortcut + launch ---- if (-not $Connect) { $Connect = $DefaultConnect } $gameArgs = @('-rtx', '+set', 'protocolversion', $Protocol, '+set', 'r_smp_worker', '0', '+set', 'r_xassetnum', '"fx=1200 image=7168 material=8192 xmodel=5125 xanim=8192 loaded_sound=2700 localize=14000 physpreset=128"') if ($rtxProfile) { $gameArgs += @('+set', 'com_playerProfile', $rtxProfile) } $gameArgs += @('+set', 'r_fullscreen', $(if ($Fullscreen) { '1' } else { '0' })) $connectArgs = @(); if ($Connect) { $connectArgs = @('+connect', $Connect) } $sh = New-Object -ComObject WScript.Shell $s = $sh.CreateShortcut($Lnk) $s.TargetPath = $exe; $s.WorkingDirectory = $Game; $s.Arguments = (($gameArgs + $connectArgs) -join ' ') $s.Description = 'Call of Duty 4 (CoD4x 1.8) with NVIDIA RTX Remix' $s.Save() Say "desktop shortcut: $Lnk" Write-Host '' Say "RTX map packs (better materials/lights per map) are a separate, optional download from ModDB:" Say " https://www.moddb.com/mods/cod4-mp-rtx-remix-iw3xo (extract into $Game\rtx-remix\mods)" Say "In game: Alt+X opens the Remix menu." if ($NoLaunch) { Say "done (not launching: -NoLaunch)"; return } Say "starting the RTX client$(if ($Connect) { " -> $Connect" }) ..." Start-Process -FilePath $exe -ArgumentList (($gameArgs + $connectArgs) -join ' ') -WorkingDirectory $Game } Install-CoD4Rtx -GameDir $GameDir -InstallDir $InstallDir -Connect $Connect ` -NoLaunch ($NoLaunch -or $env:COD4RTX_NOLAUNCH -eq '1') -Fullscreen ($Fullscreen -or $env:COD4RTX_FULLSCREEN -eq '1') ` -Uninstall ($Uninstall -or $env:COD4RTX_UNINSTALL -eq '1')