a7e3e43ae9
Completed the refusing-modes workflow (ground truth + 4 per-mode investigations + adversarial verify each + synthesis). All four mode families -- Seasons, Draft, SBC/Objectives, Tournaments -- are NOT_SERVER_REACHABLE, HIGH confidence, all four adversarial refutations failed. Live re-confirmed on pid 24653 (slide proven via FNV control): every named mode-gating byte reads ENABLED=1 (IS_FRIENDLY_SEASON_ENABLED +0x1fd3a, IS_TOURNAMENT_QUIT_ENABLED +0x1fd3b, IS_DRAFT_MODE_ENABLED +0x1fd3d, plus the unnamed offline-draft-enable +0x1fd3e) yet the tiles stay greyed. The new lead this pass added -- do the six /hub mode sub-objects gate availability? -- is refuted: friendlySeason/offlineSeason/onlineSeason/draftSummary/tournament/ tournamentProgress carry only stats and display strings, no enabled/available/ unlocked atom. They are cosmetic, exactly like hub.tradePile. The one server-writable input that exists (friendlySeasonsEnabled -> +0x1fd3a via applier FUN_18011dc50) has its sole reader in the packed FIFA17.exe front-end via a vtable getter with no CardsDLL caller, and it is already 1. The refusal is decided in the Denuvo-packed Frostbite front-end, which has no server surface. docs/plan-2026-08-06-refusing-modes.md: full evidence chains, gate-byte table, the six sub-deser field maps, per-mode verdicts. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lrx9to3pihN6Sm9sXgc8np
370 lines
22 KiB
Markdown
370 lines
22 KiB
Markdown
# The refusing modes: Seasons, Draft, SBC/Objectives, Tournaments — where the greying is decided
|
|
|
|
Written 2026-08-06. One reconnaissance pass over the live gate-byte block and the
|
|
six `/hub` mode sub-deserializers, then four parallel per-mode investigations
|
|
(Seasons, Draft, SBC+Objectives, Tournaments), each followed by an independent
|
|
adversarial verification round. FIFA 17 was running throughout as **pid 24653**,
|
|
sitting at the FUT hub, and was read strictly read-only. No server was restarted,
|
|
no server code was changed, no memory was poked, and FIFA was never launched or
|
|
killed.
|
|
|
|
Slide for every live read: `live = static - 0x180000000 + 0x6ffffc140000`, i.e.
|
|
slide `0x6ffe7c140000`, re-derived from `/proc/24653/maps` and proved by
|
|
`tools/gate_byte_probe.py` reporting **CONTROL FNV MATCH** against the FNV hasher
|
|
prologue at `0x180180d00`. CardsDLL is mapped from `/mnt/games/FIFA 17/
|
|
CardsDLL_Win64_retail.dll`; the on-disk copy read with `objdump` is
|
|
`/tmp/fut/cardsdll.dll`, image base `0x180000000`. Every address below is
|
|
live-verified.
|
|
|
|
This document answers one question the brief posed: is the refusal of these four
|
|
mode families decided by a **server-reachable input we are failing to send** (a hub
|
|
mode sub-object, a massinfo member, a settings/config field, or a dedicated
|
|
endpoint), **or** is it decided in the **Denuvo-packed FIFA17.exe / Frostbite
|
|
front-end** with no server surface at all?
|
|
|
|
---
|
|
|
|
## 1. Headline — final verdicts (after adversarial verify)
|
|
|
|
Every mode was independently re-derived by a second agent that attempted to refute
|
|
the first. **All four refutations failed. All four verdicts stand.**
|
|
|
|
| Mode | Atoms | Final verdict | Confidence | Verify |
|
|
|---|---|---|---|---|
|
|
| **FUT Seasons** (offline + online + friendly) | `friendlySeason 0x131`, `offlineSeason 0x1ec`, `onlineSeason 0x1f6` | **NOT_SERVER_REACHABLE** | HIGH | agrees (SAME) |
|
|
| **FUT Draft** (offline + online) | `draftSummary 0xe4` | **NOT_SERVER_REACHABLE** | HIGH | agrees (SAME), strengthened |
|
|
| **SBC + Objectives** | `objectivesForCurrentUser 0x1e3`, `allObjectivesForCurrentGameSpaceId 0x15` | **NOT_SERVER_REACHABLE** | HIGH | agrees (SAME), prior chain corrected |
|
|
| **FUT Tournaments** | `tournament 0x328`, `tournamentProgress 0x32c` | **NOT_SERVER_REACHABLE** | HIGH | agrees (SAME) |
|
|
|
|
**There is no server fix for any of the four.** Every server-reachable input that
|
|
touches these modes is either cosmetic (a hub stat list feeding a caption/count),
|
|
an *output* value the client emits and never branches on, or a settings byte that
|
|
is **already live=1** while the tile stays greyed. The decision lives in the packed
|
|
front-end. This is the same shape as the transfer-market finding of the same day —
|
|
except there the switch (`userInfo.feature.trade`) was ours to flip; here **no such
|
|
switch exists on the wire.**
|
|
|
|
---
|
|
|
|
## 2. Ground truth
|
|
|
|
### The named gate-byte block (`FutDataManagerImpl`, live pid 24653)
|
|
|
|
Names were resolved by finding the config serializer at `0x18006ccd0`, which pairs
|
|
each `IS_*_ENABLED` string key (`.rdata 0x1801fc118..`) with a getter vtable slot,
|
|
then decoding each slot's accessor stub (`0f b6 81 <disp32> c3`) to its model
|
|
displacement. All values read live, slide-proven.
|
|
|
|
| Name | Displacement / slot | Live value |
|
|
|---|---|---|
|
|
| (unnamed) | `+0x1fd24` | 0 |
|
|
| (unnamed) | `+0x1fd2c` | 1 |
|
|
| (unnamed) | `+0x1fd2d` | 1 |
|
|
| **IS_TRADING_ENABLED** | `+0x1fd2e` (slot+0x270) | 1 |
|
|
| (unnamed) | `+0x1fd30` | 1 |
|
|
| (unnamed) | `+0x1fd37` | 1 |
|
|
| **IS_FRIENDLY_SEASON_ENABLED** | `+0x1fd3a` (slot+0x2b0) | 1 |
|
|
| **IS_TOURNAMENT_QUIT_ENABLED** | `+0x1fd3b` (slot+0x2b8) | 1 |
|
|
| **IS_PROCESSING_STATE_ENABLED** | `+0x1fd3c` (slot+0x2c0) | 1 |
|
|
| **IS_DRAFT_MODE_ENABLED** | `+0x1fd3d` (slot+0x2c8) | 1 |
|
|
| (unnamed) | `+0x1fd3e` (offline-draft-enable) | 1 |
|
|
| **IS_STORY_MODE_REWARD_ENABLED** | `+0x1fd3f` (slot+0x2d8) | 1 |
|
|
| **IS_RETURNING_USER_REWARDS_SCREEN_ENABLED** | `+0x1fd40` (slot+0x2f0) | 0 |
|
|
| (unnamed) | `+0x1fd41` | 0 |
|
|
| (unnamed) | `+0x1fd42` (allowGracePeriod, SBC) | 0 |
|
|
| (unnamed) | `+0x1fd43` | 0 |
|
|
| **objectives-enable** (corrected — see §5.3) | `+0x1fd44` | 1 |
|
|
| **packOpeningAnimation** | `+0x1fd45` | 1 |
|
|
| (unnamed) | `+0x1fd46` | 1 |
|
|
| (unnamed) | `+0x1fd47` | 0 |
|
|
| (unnamed) | `+0x1fd48` | 1 |
|
|
| **IS_STORE_ENABLED** | computed getter slot+0x280 @`0x18011c600` (not a byte field) | (computed) |
|
|
|
|
Every named gate byte that governs a **refusing** mode reads **ENABLED=1** live.
|
|
The only `0`-valued `*_ENABLED` byte, `IS_RETURNING_USER_REWARDS_SCREEN_ENABLED`,
|
|
does not gate any of the four mode families. This re-confirms the brief's prior
|
|
ground truth: the gate-byte layer does **not** explain the refusals.
|
|
|
|
### The six `/hub` mode sub-deserializers (`/hub` parser = `FUN_180139610`)
|
|
|
|
The hub parser reads 18 atoms via a running-sum sub/dec ladder; six dispatch to the
|
|
refusing modes. Each nested sub-deser was read in full. **None carries an
|
|
enable/available/unlocked boolean.**
|
|
|
|
| Atom | Name | Sub-deser VA | Fields (all cosmetic/data) |
|
|
|---|---|---|---|
|
|
| `0x131` | friendlySeason | `0x1801392a0` | creationTime, dataVersion, opponentPersonaId, opponentUserPoints, round, seasonId, userPoints, defId (8 ints) |
|
|
| `0x1ec` | offlineSeason | `0x18013c3a0` | divisionId, gamesPlayed, points, progressDataVersion, totalGames (strings) |
|
|
| `0x1f6` | onlineSeason | `0x18013c3a0` (shared) | divisionId, gamesPlayed, points, progressDataVersion, totalGames (strings) |
|
|
| `0xe4` | draftSummary | `0x180138d60` | draftState (str-enum), gamesWon (int) |
|
|
| `0x328` | tournament | `0x18013dc00` | id, assetName, imageFormat, silhouetteName, timeUntilEnd, tournamentType, AMATEUR, live_offline, offerState (display) |
|
|
| `0x32c` | tournamentProgress | `0x18013df20` | data, tutorialClientData (free-form std::map) |
|
|
|
|
The recurring trap: several of these desers write a per-field byte
|
|
(`offline/onlineSeason` `[r14+0xa]=1`; `tournament` `[rdi+0x162]=1`) that an early
|
|
naive pass could mistake for a JSON enable flag. Every such write is a
|
|
**parser-local "field present" marker**, written identically for every field —
|
|
**not** a JSON-sourced availability input. This is the same class of mistake that
|
|
made `hub.tradePile` look like a gate before it was shown to be a mere count.
|
|
|
|
---
|
|
|
|
## 3. FUT Seasons — NOT_SERVER_REACHABLE (HIGH)
|
|
|
|
**Atoms:** `friendlySeason 0x131`, `offlineSeason 0x1ec`, `onlineSeason 0x1f6`.
|
|
**Gate byte:** `IS_FRIENDLY_SEASON_ENABLED +0x1fd3a`, live=1.
|
|
|
|
**Evidence chain.** The decisive site is the gate byte `+0x1fd3a`. A whole-`.text`
|
|
grep finds **exactly two** references:
|
|
|
|
- **Writer** `0x18011dd2d`: `mov byte[rdi+0x1fd3a],al` inside settings applier
|
|
`FUN_18011dc50`, preceded by `cmp dword[rbx+0x58],1 / sete al` — the byte is
|
|
`(settings.field+0x58 == 1)`, sourced from config key `friendlySeasonsEnabled`.
|
|
This is the **only** writer.
|
|
- **Reader** `0x18011c500`: `movzx eax,byte[rcx+0x1fd3a]; ret` — a standalone
|
|
vtable getter stub (slot+0x2b0). Its absolute address appears in the file exactly
|
|
once, at the vtable, and grep finds **no** call/jmp to `0x18011c500` anywhere in
|
|
CardsDLL `.text`. Its only consumer is the packed FIFA17.exe front-end via vtable
|
|
dispatch.
|
|
|
|
The one server-writable input (`friendlySeasonsEnabled → +0x1fd3a`) is **already 1
|
|
live**, and the tile is still greyed — so the front-end does not gate on this byte
|
|
alone; it reads additional non-server state.
|
|
|
|
- **Hub sub-objects** carry no enable flag. `offline/onlineSeason` share deser
|
|
`0x18013c3a0`, which FNV-hashes string keys and for each stores a division/games/
|
|
points/version stat; `friendlySeason 0x1801392a0` is 8 numeric stats. The
|
|
`[r14+0xa]=1` write is the "field present" marker. These feed a caption/count.
|
|
- **Settings/massinfo:** `friendlySeasonsEnabled` is the sole season key the applier
|
|
consumes → `+0x1fd3a`, already covered. No massinfo member carries a season enable.
|
|
There is **no** `onlineSeasonEnabled`/`offlineSeasonEnabled` config key or gate
|
|
byte anywhere in the DLL — verify enumerated all 24 gate-region getter stubs and
|
|
the only season getter is `+0x1fd3a`.
|
|
- **Dedicated endpoint:** `/season` and `/season/user` routes exist in
|
|
`utas_server.py` (guarded by `FUT_MODES`) but the client has **never** requested
|
|
them — 0 season hits across `captures/`, 486 real ProtoHttp requests over ~30
|
|
boots, none for `/season`. And the tile greys at hub load, *before* any `/season`
|
|
request could fire.
|
|
- **Front-end:** the only season-enable identifiers in the whole DLL are the config
|
|
*input* `friendlySeasonsEnabled` and the *output* getter name
|
|
`IS_FRIENDLY_SEASON_ENABLED`. The viewmodel names
|
|
(`futonlineseasonsviewmodel`, `futofflineseasonsviewmodel`,
|
|
`futfriendlyseasons*viewmodel`) live in the Denuvo-packed FIFA17.exe.
|
|
|
|
**Authority boundary.** `friendlySeasonsEnabled` is a **server-writable input**,
|
|
but it is already at ENABLED with its only reader **off-DLL (client)**. Offline/
|
|
online seasons have **no server surface at all** — no config key, no gate byte, no
|
|
getter. The grey/refuse decision is **client-side**.
|
|
|
|
---
|
|
|
|
## 4. FUT Draft — NOT_SERVER_REACHABLE (HIGH, strengthened by verify)
|
|
|
|
**Atoms:** `draftSummary 0xe4`. **Gate byte:** `IS_DRAFT_MODE_ENABLED +0x1fd3d`,
|
|
live=1.
|
|
|
|
**Evidence chain.** Cross-ref of displacement `0x1fd3d` returns exactly two real
|
|
sites (a `lea` to `0x1801fd3d8` and an instruction at address `0x18011fd3d` are
|
|
coincidental, not xrefs):
|
|
|
|
- **Accessor stub** `0x18011c4b0`: `movzx eax,[rcx+0x1fd3d]; ret` (getter vtable
|
|
`.rdata 0x18021c568`).
|
|
- **Writer** `0x18011dd5a`: `mov [rdi+0x1fd3d],al` in applier `FUN_18011dc50`,
|
|
`al = (settings[rbx+0x5c]==1)` = parsed `enableDraftMode`.
|
|
|
|
There is **no cmp/test/branch** on this byte anywhere. Its only CardsDLL consumer
|
|
is the config serializer `0x18006ccd0`, which walks the `IS_*_ENABLED` key table and
|
|
`call [rax+0x2c8]` to **emit** the value outward. So `IS_DRAFT_MODE_ENABLED` is an
|
|
**output the client serializes, not an input any logic branches on.**
|
|
|
|
**The verifier strengthened this** by finding a consumer the first pass missed: a
|
|
flux "DESTINATION" navigation emitter around `0x1800b2700`. At `0x1800b2711` it
|
|
loads getter slot `+0x2c8` (draft-enable, `+0x1fd3d`) into `sil` and slot `+0x2d0`
|
|
(offline-draft-enable, `+0x1fd3e`) into `[rsp+0x21]`. All six `GOTO_DRAFT_DISABLED`
|
|
emit sites (`0x1800b2cb2`, `0x1800b2dc9`, `0x1800b333b/347`, `0x1800b349f/4a7`) are
|
|
guarded by `test sil,sil` / `cmp [rsp+0x21],0` and route to `GOTO_DRAFT_DISABLED`
|
|
**only when those bytes are 0**, else to `GOTO_DRAFT_OFFLINE/ONLINE`. Both bytes are
|
|
**live=1**, so this emitter — the closest thing to a nav decision inside CardsDLL —
|
|
already produces the ENABLED destinations, yet the tile is still greyed.
|
|
|
|
- **Hub sub-object** `draftSummary 0xe4`, member deser `0x180138d60`: exactly two
|
|
atoms — `draftState 0xe3` (STRING → enum decoder `0x180138cc0`, a resume-state
|
|
enum: INVALID + 2..8) and `gamesWon 0x13a` (INT). Wrapper `0x18013980c` loops
|
|
`ONLINE 0x1f1` / `SINGLE_PLAYER 0x29d`, each → `0x180138d60`. No enable atom;
|
|
`draftState` is the continue-state read after entry, not a tile gate.
|
|
- **Settings/massinfo:** atoms `enableDraftMode 0xf9` / `enableOfflineDraftMode
|
|
0xfa` / `enableSinglePlayerDraftMode 0xff` land on sibling emit-only bytes
|
|
`+0x1fd3d`/`+0x1fd3e`/`+0x1fd3c` via the same applier — none branched on.
|
|
- **Dedicated endpoints:** `GET /squad/mode/draft/state` (deser `0x180147070`) and
|
|
`POST /purchase/mode/N/draft` (deser `0x18014c260`) are already routed in utas —
|
|
but these are the **post-click** entry/session flow (render the draft screen, buy
|
|
entry *after* the tile is pressed), not a tile-availability query.
|
|
- **Front-end:** token strings (`USER_HAVE_DRAFT_TOKENS 0x1802055f8`,
|
|
`GOTO_DRAFT_DISABLED 0x180209aa8`, etc.) are bare key-name `lea` emitters with no
|
|
greying branch. Decision is in the packed FIFA17.exe.
|
|
|
|
**Authority boundary.** The two server-writable inputs (`enableDraftMode`,
|
|
`enableOfflineDraftMode`) are **already at their enabled value**, and **every**
|
|
CardsDLL consumer of them (config serializer *and* the navigation emitter) already
|
|
treats draft as enabled. The persistent greying is decided **client-side** on
|
|
non-server state.
|
|
|
|
---
|
|
|
|
## 5. SBC + Objectives — NOT_SERVER_REACHABLE (HIGH, prior chain corrected)
|
|
|
|
**Atoms:** `objectivesForCurrentUser 0x1e3`, `allObjectivesForCurrentGameSpaceId
|
|
0x15`. **No `IS_OBJECTIVES`/`IS_SBC` gate-byte name exists** — the task premise that
|
|
these are governed by no named `FutDataManagerImpl` gate byte is confirmed.
|
|
|
|
### 5.1 Hub sub-object = cosmetic list
|
|
|
|
In `FUN_180139610` both objectives atoms share one arm: `objectivesForCurrentUser
|
|
0x1e3` (`0x180139794`) and `allObjectivesForCurrentGameSpaceId 0x15`
|
|
(`0x1801397ad`) both jump to `0x1801398fe`, guarded by the parser-local marker
|
|
`cmp BYTE [rsp+0x21],0x1`, calling sub-deser `0x18013a7f0`. That deser parses a
|
|
nested `objectives 0x1e2` **array** of records (element parser `0x18006c9b0`) with
|
|
**no** enabled/available/unlocked atom — it feeds the "MANAGER TASKS N/M" tile
|
|
count/caption, the same cosmetic class as `hub.tradePile`.
|
|
|
|
### 5.2 No dedicated endpoint at the hub
|
|
|
|
The live log across 26+ hub sessions shows the client requests only `/hub` and
|
|
`/settings`; it **never** calls `/sbs/*` (grep count 0) or any `/objectives`
|
|
endpoint. `utas_server.py` has no `/sbs` route. No `FutGetObjectivesServerResponse`
|
|
class exists — objectives are **ManagerQuests**, client-driven. The `sbs/*` structs
|
|
that exist serve challenge **content after entry**, never polled at the hub.
|
|
|
|
### 5.3 The correction (verify fixed the first pass's chain)
|
|
|
|
The first pass mis-traced objectives to settings field `[0x1c]` → model `+0x1fd28`
|
|
(default 60). **The verifier re-derived the settings jump table (dispatch
|
|
`0x18013ca1e`, byte-idx `0x18013ced4`, jtbl `0x18013ce90`) and found the truth:**
|
|
|
|
- `enableObjectives 0xfd` **and** `enableObjectivesAsManagerTasks 0xfe` route to
|
|
handler `0x18013cabd` = clear-only-on-zero into settings field `[0x70]`; applier
|
|
`0x18011ddc7` (`cmp [rbx+0x70],1; sete al; mov [rdi+0x1fd44],al`) maps it to model
|
|
gate byte **`+0x1fd44`** — which is **inside** the named gate block (not outside,
|
|
as the first pass claimed), reads **1 (ENABLED) live**, and has exactly one reader
|
|
DLL-wide: a getter stub `0x18011c570` returning the byte to the front-end with no
|
|
internal gating use.
|
|
- The first pass's `+0x1fd28` (default 60) is actually
|
|
`squadBuildingSetsGracePeriodMinutes 0x2d0`, a numeric grace-period param —
|
|
behavioral, not availability.
|
|
- **SBC side:** `enableSquadBuildingSetsFeature 0x100` falls in the dispatch **gap**
|
|
(`0x100-0x18=0xe8 > 0xe7 → DEFAULT/no handler`), as do `squadBuildingSetsClientData
|
|
0x2cf` and `squadChallenge 0x2d1`. Only numeric SBC params have handlers
|
|
(`allowGracePeriod 0x18 → +0x1fd42`, `allowUntradeable 0x19 → +0x206f8`,
|
|
`gracePeriodMinutes 0x2d0 → [0x1c]/+0x1fd28`). **No SBC availability model byte
|
|
exists.**
|
|
|
|
So the single server-controllable objectives-enable input (`+0x1fd44`) is already at
|
|
1 yet the tile refuses, and SBC has **no** server enable surface whatsoever.
|
|
|
|
**Authority boundary.** Objectives-enable is a **server-writable byte already ON**,
|
|
read only by the **client**. SBC availability has **no server surface** — its enable
|
|
key is in the settings dispatch gap and lands on no byte. Decision is **client-side**
|
|
(`futmanagerquestsviewmodel`; providers `FUT_MQ_QUESTS_DATA_DP` /
|
|
`FUT_SQUAD_QUESTS_DP`) in the packed FIFA17.exe.
|
|
|
|
---
|
|
|
|
## 6. FUT Tournaments — NOT_SERVER_REACHABLE (HIGH)
|
|
|
|
**Atoms:** `tournament 0x328`, `tournamentProgress 0x32c`. **Gate byte:**
|
|
`IS_TOURNAMENT_QUIT_ENABLED +0x1fd3b`, live=1 — but this governs **quitting** a
|
|
tournament, not tile availability, and no `tournamentEnabled` atom exists in
|
|
`docs/fut_atoms.tsv`.
|
|
|
|
**Evidence chain.**
|
|
|
|
- **Hub sub-objects, both cosmetic.** `tournament 0x328` deser `0x18013dc00` writes
|
|
only display fields: id `[rdi+0x150]`, round `[rdi+0x160]`, timeUntilEnd
|
|
`[rdi+0x158]`, silhouette-int `[rdi+0x15c]`, string blobs `[rdi]`/`[rdi+0xa8]`
|
|
(assetName/silhouette/type), an `imageFormat=="dds"` render bool `[rdi+0x163]`
|
|
(strcmp vs `.rdata 0x180219400`), and a `tournamentType` enum `[rdi+0x154]`
|
|
decoded to `live_offline 0x195`/`live_online 0x196`/`offline 0x1e8`/`online 0x1f0`
|
|
— a categorization, not availability. The `[rdi+0x162]=1` write is a
|
|
record-completeness marker (all core fields present), not a JSON enable.
|
|
`tournamentProgress 0x32c` deser `0x18013df20` builds a std::map (ctor
|
|
`0x1801e5210`) of string keys `data 0xc9` / `tutorialClientData ~0x353` — free-form
|
|
clientData, no enable atom. (The earlier `0x28a = returningUserRewardsScreenEnabled`
|
|
label was a running-sum mis-decode; the true sum is `0xc9+0x28a=0x353
|
|
tutorialClientData`.)
|
|
- **Massinfo/settings.** `tournamentCoins 809 → +0x30` and `teamOfTournamentWinner
|
|
776 (bool) → +0x34` appear only in the **FutDestroyMatch** reward deser
|
|
`0x180121b60` — a match payout reached only *after* you are inside a tournament
|
|
match; a reward count/trophy flag, not a tile gate. The settings applier switch
|
|
`0x18013c6d0` has 42 arms; the only tournament arm is `tournamentQuitEnabled 0x32D
|
|
→ +0x1fd3b` (quit, live=1).
|
|
- **Gate byte** `+0x1fd3b`: getter stub `0x18011c660` is the vtable **emit**
|
|
accessor the config serializer `0x18006ccd0` pairs with the JSON key to write it
|
|
out — the client emits it, does not read it as a server input. Writer
|
|
`0x18011dd3d`, `al = sete(cmp settings[rbx+off],1)`, defaults to 1. Already 1,
|
|
wrong feature.
|
|
- **Dedicated endpoint, never called.** `tournament_list` (deser `0x180169ef0`) and
|
|
`tournament_user` (deser `0x180147cb0`) exist in `utas_server.py` but grep over
|
|
`captures/` and the live `/tmp/utas_server.log` (3224 lines) finds **zero**
|
|
ProtoHttp requests for any `/tournament` path across all boots — same as `/season`.
|
|
The responses are never consumed.
|
|
- **Front-end.** No CardsDLL response deserializer writes any "tournament
|
|
available/unlocked" field. `eligibilities 0xf1` / `unlocks 0x35c` / `available 0x3e`
|
|
are SBC/store vocab per `docs/ENDPOINT_MAP.md`, not wired to tournaments. Decision
|
|
is in the packed FIFA17.exe.
|
|
|
|
**Authority boundary.** The only server-touchable tournament byte
|
|
(`IS_TOURNAMENT_QUIT_ENABLED`) is an **emitted output** governing a different
|
|
feature, already 1. Everything else is cosmetic hub data or post-entry reward data.
|
|
Tile availability is decided **client-side**.
|
|
|
|
---
|
|
|
|
## 7. What changed vs the prior conclusion
|
|
|
|
The prior workflow examined **only the `FutDataManagerImpl` gate bytes** and
|
|
concluded "no server fix" for these modes. This workflow re-opened the question by
|
|
chasing the **hub-atom lead** — the six mode sub-deserializers we do not currently
|
|
populate — plus massinfo members, settings arms, and dedicated endpoints.
|
|
|
|
**The hub-atom lead does not change the conclusion for any mode.** Per mode:
|
|
|
|
- **Seasons:** the hub `friendlySeason`/`offline`/`onlineSeason` sub-objects are
|
|
numeric stat blobs (division/games/points), cosmetic like `hub.tradePile`. The
|
|
`[r14+0xa]=1` byte is a "field present" marker, not a JSON enable. No change —
|
|
still NOT_SERVER_REACHABLE.
|
|
- **Draft:** `draftSummary` carries only `draftState`+`gamesWon`; verify additionally
|
|
found the in-DLL navigation emitter already routes to the *enabled* destination on
|
|
current live state. No change — verdict **strengthened**.
|
|
- **SBC/Objectives:** the objectives hub arm is a cosmetic list feeding "MANAGER
|
|
TASKS N/M". Verify *corrected the prior chain* — the real objectives-enable byte is
|
|
`+0x1fd44` (inside the gate block, live=1), and SBC's enable key falls in a
|
|
dispatch gap with no byte at all. No change to the verdict; the correction only
|
|
hardens it.
|
|
- **Tournaments:** both hub sub-objects are display/clientData only. No change.
|
|
|
|
**Net:** examining the hub atoms was the right next step, and it closed the lead
|
|
rather than opening a fix. Every server-reachable surface for these four modes is
|
|
now accounted for and none is an availability input. The prior "no server fix"
|
|
conclusion holds, now on much broader evidence.
|
|
|
|
---
|
|
|
|
## 8. Client-vs-server authority boundaries (explicit)
|
|
|
|
| Surface | Who writes it | Who reads it | Is it a mode-availability gate? |
|
|
|---|---|---|---|
|
|
| Gate bytes `+0x1fd3a/3b/3d/44` etc. | **server** (settings applier `FUN_18011dc50`) | **client** (getter stubs, off-DLL vtable dispatch) + config serializer `0x18006ccd0` (emit) | No — all live=1, never branched on inside CardsDLL |
|
|
| Hub mode sub-objects (`0x131/1ec/1f6/e4/328/32c`) | **server** (`/hub` body) | CardsDLL parsers → cosmetic captions/counts | No — no enable atom in any of the six desers |
|
|
| `[r14+0xa]=1`, `[rdi+0x162]=1`, `[rsp+0x21]==1` markers | CardsDLL parser (local) | same parser | No — "field present" bookkeeping, never JSON-sourced |
|
|
| Settings config keys (`friendlySeasonsEnabled`, `enableDraftMode`, `enableObjectives`, `tournamentQuitEnabled`) | **server** (`/settings`) | applier → gate bytes → **client** | No — inputs already at enabled; readers are off-DLL |
|
|
| SBC enable (`enableSquadBuildingSetsFeature 0x100`) | — | — | **No surface** — falls in the settings dispatch gap, lands on no byte |
|
|
| Offline/online season enable | — | — | **No surface** — no config key, no gate byte, no getter |
|
|
| `/season`, `/tournament`, `/sbs/*` endpoints | server (utas, routed) | never requested at hub | No — client never polls them; tile greys before any request |
|
|
| DestroyMatch reward fields (`tournamentCoins`, `teamOfTournamentWinner`) | server (post-match) | reward payout | No — reached only inside a match |
|
|
| The greying/refusal decision itself | — | **client** (Denuvo-packed FIFA17.exe / Frostbite viewmodels) | **This is the gate — and it has no server surface** |
|
|
|
|
The single load-bearing fact across all four modes: **every server-writable enable
|
|
input that exists is already at ENABLED live, its only reader is the client, and the
|
|
tile refuses anyway.** No response body we can send flips a state the front-end has
|
|
already decided.
|