# OpenFUT FIFA 17 - native Windows client

The FIFA 17 client host (`10.10.0.105`, Windows 11 Pro) runs FIFA 17 **natively**
(no Wine/Proton/umu). This directory holds the read-only preflight verifier and
documents the native launch/routing/rollback model.

## Install layout (`C:\FIFA 17`)

| File | Role |
|---|---|
| `FIFA17.exe` | retail game exe (sha256 `29C31CEF…`). **Never modify/patch.** ImageBase `0x140000000`. |
| `_fifa17.exe` | native crack loader (Chemicalflood). This is what you launch. `asInvoker` manifest -> must be elevated externally. |
| `version.dll` | **OpenFUT hook** (in-process via the version.dll load-order hijack). ImageBase `0x180000000`. |
| `version.dll.stale-849k.bak` | **rollback** copy of the previous hook. |
| `CardsDLL_Win64_retail.dll` | FUT card/SBC/kit logic. ImageBase `0x180000000`. |
| `powdll_Win64_retail.dll` | Pack-Opening-World (EASFC store). ImageBase `0x180000000`. |
| `sysdll_Win64_retail.dll` | EA networking / ProtoSSL (cert, ea.com). |
| `stp-origin_emu.dll` + `stp-origin_emu.ini` | Origin/LSX login emulator (in-process; opens LSX `:4216` locally at runtime). Persona configured in the `.ini`. |
| `stp-selector.exe` | ssl/LSX selector companion. |
| `openfut.cfg` | operator-facing routing override (see below). |

## Launch — the OpenFUT Launcher is the normal entry point

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.

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. FIFA under native Windows ignores
> synthetic input, so in-game steps are performed by the operator one at a time.

### OpenFUT Launcher (GUI)

The `openfut-launcher` egui app runs natively on Windows (built for
`x86_64-pc-windows-gnu`; the Linux Proton path is `cfg`-gated out). It is the
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`.
- `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`).
- Rebuild from Linux: `cargo build -p openfut-launcher --release --target x86_64-pc-windows-gnu`.

## Routing (`openfut.cfg`)

```
host=10.10.0.120
https_port=8443
blaze_redirector_port=42127
blaze_main_port=42130
```

The hook carries `10.10.0.120` as its baked-in default; `openfut.cfg` is the
override. `10.10.0.120` hosts **both** production and staging.

> **Production safety:** `blaze_main_port=42130` is the **production** Blaze.
> Before any match/economy exercise, repoint `blaze_main_port` (and the matching
> UTAS/HTTPS route) to the staging port so no traffic reaches the prod container.
> The preflight raises a WARN whenever `42130` is configured.

## Rollback

The hook is a single file swap; no installer state.

```powershell
# disable OpenFUT hook (restore previous DLL)
Copy-Item 'C:\FIFA 17\version.dll' 'C:\FIFA 17\version.dll.disabled.bak' -Force
Copy-Item 'C:\FIFA 17\version.dll.stale-849k.bak' 'C:\FIFA 17\version.dll' -Force
# re-arm: copy the desired hook build over version.dll again
```

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" — 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://<roster>/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 — 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.

**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.

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
```

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 -like "*winter15*" } | Remove-DnsClientNrptRule -Force
```

## Preflight

`openfut-client-preflight.ps1` is **read-only**: it never launches the game,
never elevates, never writes game files, never mutates economy state. It verifies
the retail exe hash, companion DLLs, hook + rollback, routing + backend
reachability, login persona, launcher elevation, and the RE toolchain
(x64dbg, cargo). Exit 0 = OK, 1 = blocking failure.

```powershell
powershell -NoProfile -ExecutionPolicy Bypass -File .\openfut-client-preflight.ps1
```

## Runtime RE (x64dbg)

See the Vault note **`02 Reverse Engineering/FIFA 17/Windows Client Runtime & x64dbg.md`**
for the attach workflow and the RVA<->VA (ASLR) math. In short: these modules'
preferred ImageBase is `0x180000000` (`0x140000000` for `FIFA17.exe`); in x64dbg
a module name evaluates to its runtime (ASLR) base, so a Ghidra address maps to a
breakpoint as `bp CardsDLL_Win64_retail.dll+<RVA>` where
`RVA = ghidra_addr - 0x180000000`.
