diff --git a/tools/windows/README.md b/tools/windows/README.md index 85223dc..15ba82f 100644 --- a/tools/windows/README.md +++ b/tools/windows/README.md @@ -19,23 +19,32 @@ documents the native launch/routing/rollback model. | `stp-selector.exe` | ssl/LSX selector companion. | | `openfut.cfg` | operator-facing routing override (see below). | -## Launch (native - there is NO launcher script by design) +## Launch — the OpenFUT Launcher is the normal entry point -Run `C:\FIFA 17\_fifa17.exe` **as Administrator**. The correct, reproducible way: +Use the **"OpenFUT Launcher"** shortcut (Desktop / Start Menu). It reconciles +`openfut.cfg` from its settings, shows backend health, and starts the game, so +Windows and Linux are driven exactly the same way. -- Double-click the **"FIFA 17 (OpenFUT)"** shortcut (Desktop and Start Menu). - It targets `_fifa17.exe`, working dir `C:\FIFA 17`, with the RunAsAdmin bit set. -- `_fifa17.exe` is also flagged `RUNASADMIN` in - `HKCU\Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers`, - so any launch (Explorer double-click included) elevates via UAC. +Launching the game directly still works and is the smaller-blast-radius option +when validating a hook change, because it does not rewrite `openfut.cfg`: +double-click the **"FIFA 17 (OpenFUT)"** shortcut, which targets `_fifa17.exe` +with working dir `C:\FIFA 17`. + +**Elevation comes from the shortcuts, not from AppCompatFlags.** Both `.lnk` +files carry the RunAsAdmin bit (byte 21, flag `0x20`); there is NO `RUNASADMIN` +entry under +`HKCU\Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers`. +So running `_fifa17.exe` straight from Explorer does **not** elevate, and the +hook then fails to write `C:\openfut_hook.log`. Always go through a shortcut, or +right-click → Run as administrator. The launcher elevates once and the game it +spawns inherits that token, so there is no second UAC prompt. On launch the Windows loader maps `version.dll` from the game directory (hijack), `stp-origin_emu.dll` emulates Origin login for the configured persona, and the hook redirects EA endpoints to the OpenFUT backend. -> Do **not** wrap the launch in a script. The elevation + shortcut is the -> supported mechanism. FIFA under native Windows also ignores synthetic input, -> so in-game steps are performed by the operator one at a time. +> Do **not** wrap the launch in a script. FIFA under native Windows ignores +> synthetic input, so in-game steps are performed by the operator one at a time. ### OpenFUT Launcher (GUI) @@ -44,11 +53,15 @@ The `openfut-launcher` egui app runs natively on Windows (built for one-button front end: it checks the backend, reconciles `openfut.cfg` from its settings, and starts the game. -- Binary: `C:\OpenFUT\openfut-launcher.exe`; config: `%APPDATA%\openfut-launcher\config.json`. -- Launch it from the **"OpenFUT Launcher"** shortcut (Desktop / Start Menu). The - launcher itself is flagged `RUNASADMIN`, so it elevates once at start; the game - it spawns inherits that elevation (no second UAC prompt, no crack loader - "run as admin" failure). +- Binary: `C:\openfut\openfut-launcher.exe`; config: `%APPDATA%\openfut-launcher\config.json`. +- `hook_dll_path` MUST point at a **source** copy of the hook, not at the + deployed `C:\FIFA 17\version.dll`. Pointing it at the destination makes + "Deploy" a self-copy, and Remove-then-Deploy leaves no source to deploy from. + Canonical location: `C:\openfut\openfut_hook.dll`. +- The Deploy button only appears when no `version.dll` is present, so a normal + launch never overwrites a hand-deployed hook. +- `game_profile.runner` is unused on Windows (`validate()` only requires it on + unix); `executable` + `game_dir` are what matter. - On Windows the launcher does NOT spawn LSX/autopatch (they are in-process: `stp-origin_emu.dll` + the `version.dll` hook) and does NOT arm the host (routing is purely `openfut.cfg`, which it writes into `C:\FIFA 17`). @@ -85,39 +98,56 @@ Copy-Item 'C:\FIFA 17\version.dll.stale-849k.bak' 'C:\FIFA 17\version.dll' -Forc Always keep a `*.bak` of the live hook before redeploying (the preflight checks that a rollback backup exists and differs from the live DLL). -## Roster / "FUT Squad Update" — the client MUST reach the roster by HOSTNAME +## Roster / "FUT Squad Update" — handled in-client, no client DNS changes If FUT fails with **"An error occurred downloading the FUT Squad Update"**, the client could not fetch `https:///fifa17/fut/rosterupdate.xml`. FIFA 17's ProtoSSL verifies that certificate by **dNSName only**. Pointing the roster at an IP does **not** work even though our certificate carries -`IP Address:10.10.0.120` in its SANs — this was retested on Windows on -2026-08-23 and rejected. Do not retry an IP roster host, and do not reissue the -certificate for an IP SAN. The roster must be reached as -`winter15.gosredirector.ea.com`, which our certificate does carry as a dNSName. +`IP Address:10.10.0.120` in its SANs — retested on Windows 2026-08-23 and +rejected, confirming the divergence on Windows and not just under Wine. Do not +retry an IP roster host and do not reissue the certificate for an IP SAN. -Public DNS resolves that name to EA's dead `159.153.51.20`, so the client has to -be told to resolve it to us. **Use an NRPT rule, not the hosts file.** A hosts -edit on this machine previously took its entire internet down; NRPT is per-name, -auditable with `Get-DnsClientNrptRule`, and reverts in one command. +**The hook solves this at the socket.** `ea_ports::FIFA17_ROSTER` (8081) is a +recognised signature, so the existing `connect`/`WSAConnect`/`ConnectEx` detour +rewrites the roster dial to the configured server while leaving the URL — and +therefore SNI — as `winter15.gosredirector.ea.com`. The certificate still +validates because the *name* never changed; only the destination did. Nothing is +required on the client: no hosts entry, no NRPT rule, no external resolver. -On the server, run the scoped resolver (needs root for UDP 53): +Confirm from the hook log (`C:\openfut_hook.log`); note it prints octets +reversed: + +``` +connect_hook: call 20.51.153.159:8081 <- 159.153.51.20, the dial +connect_hook: v4 :8081 → 10.10.0.120:8081 <- the rewrite +``` + +Serve the roster on the destination port and start Blaze with +`OPENFUT_ROSTER_HOST=winter15.gosredirector.ea.com:8081`. Use `roster_port=` in +`openfut.cfg` only to move the destination; the parser rejects unknown keys, so +that key is written only when it differs from the default. + +### Contingency: EA's DNS record disappears + +One dependency survives: the client must resolve `winter15.gosredirector.ea.com` +to *something* to reach `connect()` at all. EA's record is live today. If it is +ever withdrawn, resolution fails before the hook can act, and the fallback is +`tools/windows/scoped-dns.py` — a resolver that pins that one name to us and +forwards every other query upstream verbatim, so it cannot strand the client: ```bash sudo python3 tools/windows/scoped-dns.py ``` -It answers **only** `winter15.gosredirector.ea.com` and forwards every other -query upstream verbatim, so it cannot strand a client that is pointed at it. -Then start Blaze with `OPENFUT_ROSTER_HOST=winter15.gosredirector.ea.com:8081`. - -On the client, as Administrator: +Point the client at it with an NRPT rule, never a hosts entry (a hosts edit on +this machine previously took its whole internet down): ```powershell Add-DnsClientNrptRule -Namespace "winter15.gosredirector.ea.com" -NameServers "10.10.0.120" # revert: -Get-DnsClientNrptRule | Where-Object Namespace -eq "winter15.gosredirector.ea.com" | Remove-DnsClientNrptRule -Force +Get-DnsClientNrptRule | Where-Object { $_.Namespace -like "*winter15*" } | Remove-DnsClientNrptRule -Force ``` ## Preflight