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