docs(fifa17): correct ENDPOINT_MAP club routes, and close plan section 7
Rows 12, 13 and 16 carried guessed or placeholder URLs (`ut/%s/item?type=…`, `ut/%s/…`). The real binding is a table, not an inference: the 125-row action table at 0x1802caa20 indexes the 48-entry URL-base table at 0x18021df80 through column 1, and base index 3 = ut/%s/club is carried by exactly four rows. So the client can emit exactly four families on that base: ClubSearch, ClubStats, StaffStats, ConsumablesSearch — which also corrects those three rows to /club?<query>, /club/stats/staff and /club/consumables/<cat>, and updates their status now that the Rust host serves them. Added the complete club query grammar (ordered, with its suppression rules and sub-vocabularies, including that the request spells it onSale where the response says forSale), the seven /club/stats forms, the fact that /club/stats/team does NOT exist, and the two base-table holes that are composed outside CardsDLL so nobody re-derives them as findings. Section 7 of the plan is now marked APPLIED and kept as the audit trail.
This commit is contained in:
@@ -944,11 +944,55 @@ freezes any of these — GAPs are "feature missing", not "crash".
|
||||
| 9 | FutDiscardCardByRes | `0x1801279c0` | DELETE `ut/delete/%s/item` (by res) | `totalCredits`(0x326) → int | GAP | HIGH |
|
||||
| 10 | FutMoveCard | `0x180128600` | PUT `ut/%s/item` (move) | `itemData`(0x16b) → **array** [FREEZE-RISK]; `chemistry`(0x81) → bool | GAP | HIGH |
|
||||
| 11 | FutMoveCardByRes | `0x180128e30` | PUT `ut/%s/item` (move by res) | `itemData`(0x16b) → **array** [FREEZE-RISK]; `chemistry`(0x81) → bool (+ 2 str/1 int minor) | GAP | HIGH / extra-fields MED |
|
||||
| 12 | FutConsumablesSearch | `0x180130d10` | GET `ut/%s/item?type=…` (GetFilteredConsumableSearchResults) | `itemData`(0x16b) → **array[consumable-item]** via `0x18013fe00` [FREEZE-RISK]; `displayGroupUseDefaultImage`(0xdb) → int + count scalars | GAP | deser HIGH / scalars MED |
|
||||
| 13 | FutStaffBonus | `0x18012b730` | GET `ut/%s/…` (CardsGetStaffBonuses) | `bonus`(0x5c) → **nested** (branch sets bool @rbp+0x51) [FREEZE-RISK]; `assetId`(0x23) → int | GAP | MED |
|
||||
| 12 | FutConsumablesSearch | `0x180130d10` | GET `ut/%s/club/consumables/<cat>` (ConsumablesSearch) **[CORRECTED 2026-08-21]** | `itemData`(0x16b) → **array[consumable-stack]** via `0x18013fe00` [FREEZE-RISK]; `displayGroupUseDefaultImage`(0xdb) → int + count scalars | SERVED (Rust host) | deser HIGH / scalars MED |
|
||||
| 13 | FutStaffBonus | `0x18012b730` | GET `ut/%s/club/stats/staff` (StaffStats, thunk `0x18012b080`) **[CORRECTED 2026-08-21]** | `bonus`(0x5c) → **nested** (branch sets bool @rbp+0x51) [FREEZE-RISK]; `assetId`(0x23) → int | SERVED (`{}`, the oracle body) | MED |
|
||||
| 14 | FutGetAvailableLoanPlayers | `0x18014e030` → sub `0x18013a1c0` | GET `ut/%s/item` (FUT_AVAILABLE_LOAN_PLAYERS_DP) | `loans`(0x19b) → **array** [FREEZE-RISK]; `itemData`(0x16b) → **array[card-item]** [FREEZE-RISK]; `default`(0xcd) → int | GAP | deser HIGH / fields MED |
|
||||
| 15 | FutSignLoanPlayer | `0x1801642c0` | PUT `ut/%s/item` (sign loan) | **none** (immediate `ret`) | ack — `{}` fine | HIGH |
|
||||
| 16 | FutStickerBookSearch | `0x18012eff0` | GET `ut/%s/…` (stickerbook search) | `itemData`(0x16b) → **array[card-item]** via `0x18013fe00` [FREEZE-RISK] | GAP | HIGH |
|
||||
| 16 | FutStickerBookSearch | `0x18012eff0` | GET `ut/%s/club?<query>` (ClubSearch, `FUN_18012ddf0`) **[CORRECTED 2026-08-21]** | `itemData`(0x16b) → **array[card-item]** via `0x18013fe00` [FREEZE-RISK] | SERVED (Rust host) | HIGH |
|
||||
|
||||
|
||||
### The four `ut/%s/club` routes are a TABLE, not an inference (2026-08-21)
|
||||
|
||||
The URLs for rows 12, 13 and 16 above were previously guessed as `ut/%s/item?…`
|
||||
or left as `ut/%s/…`. The binding is exact: the 125-row action table at
|
||||
`0x1802caa20` indexes the 48-entry URL-base table at `0x18021df80` through column
|
||||
1, and **base index 3 = `ut/%s/club` is carried by exactly four rows** — so the
|
||||
client can emit exactly four request families on that base and no others.
|
||||
|
||||
```
|
||||
| ClubSearch | FUN_18012ddf0 | GET ut/%s/club?<query> | FutStickerBookSearchServerResponse |
|
||||
| ClubStats | FUN_18012f4f0 | GET ut/%s/club/stats/<f>[/<id>] | FutStickerBookStats2ServerResponse |
|
||||
| StaffStats | thunk 0x18012b080 | GET ut/%s/club/stats/staff | FutStaffBonusServerResponse |
|
||||
| ConsumablesSearch | FUN_1801308c0 | GET ut/%s/club/consumables/<cat> | FutConsumablesSearchServerResponse |
|
||||
```
|
||||
|
||||
**Club query grammar**, complete and ordered: `?year=2017` (always, hardcoded),
|
||||
then `type`, `start` (omitted at 0), `count` (omitted at 100), `filter`, then
|
||||
EITHER the filter block (`position, formation, state, level, rare, nation,
|
||||
country, league, playStyle, team, sort`) OR a comma-joined `defId=` list, never
|
||||
both. Live control from the log:
|
||||
`GET /ut/game/fifa17/club?year=2017&type=equippables&count=11&level=any&sort=desc`
|
||||
matches the predicted order and every suppression rule.
|
||||
|
||||
Sub-vocabularies: `filter` = available/base/exact/any; `level` =
|
||||
bronze/silver/gold/any; `sort` = asc/desc; `rare` = the literal string `SP`, not
|
||||
a boolean; `state` = the itemState names plus `any` — and note the REQUEST spells
|
||||
it `onSale` where the RESPONSE value is `forSale`.
|
||||
|
||||
`?type=` has 30 values (`FUN_18012ec50`: 29 cases plus a default of `any`).
|
||||
|
||||
**`/club/stats` has exactly seven forms**: `club`, `year`, `country/<id>`,
|
||||
`league/<id>`, `newcards`, `consumables`, and the separately-dispatched `staff`.
|
||||
**There is no `/club/stats/team/<id>`** — verified twice (the switch has six cases
|
||||
with no such arm, and an exhaustive PE string scan finds no literal containing
|
||||
`stats/team`). Any handling of a `team` stats mode is dead code.
|
||||
|
||||
**Two holes in the base table**, recorded so nobody re-derives them as findings:
|
||||
base index 43 = `ut/v2/%s/store` is carried by no action row and has zero
|
||||
references in `.text`, yet `ut/v2/store` is live-proven; base index 9 =
|
||||
`ut/%s/activeMessage` is a second hole of the same kind. So at least one route is
|
||||
composed OUTSIDE CardsDLL, most likely in the packed exe — every "the table bounds
|
||||
it" statement here is bounded to CardsDLL only.
|
||||
|
||||
Notes:
|
||||
- **`0x1801642c0`** is a shared no-op deserializer (function body = `ret`). Three responses
|
||||
|
||||
@@ -1054,6 +1054,10 @@ change a working wire. Bundle it with the next change that needs a launch.
|
||||
|
||||
## 7. Proposed corrections to existing documents
|
||||
|
||||
> **APPLIED 2026-08-21.** Every correction below has been made in the named file
|
||||
> and marked there with a dated note. This section is kept as the rationale and
|
||||
> the audit trail, not as an outstanding to-do.
|
||||
|
||||
### `docs/CARD_SYSTEM.md`
|
||||
|
||||
**Replace the "STILL UNKNOWN, AND NOT GUESSED" section entirely.** It is answered.
|
||||
|
||||
Reference in New Issue
Block a user