Files
OpenFUT/fifa17-recon/docs/OPENCODE_ENDPOINT_PROMPT.md
T
funman300 4e89cce37d fifa17-recon: store fix (v2/store gate + flags) + full FUT endpoint map
Store "not available" root cause reversed from CardsDLL:
- ut/v2/game/fifa17/store is an ELIGIBILITY gate (FutStorePackQuantities
  deser 0x1801758c0), not a quantity list. It reads one key "result"
  (atom 0x288); the store screen refuses to open unless SUCCESS. Was
  unhandled -> catch-all {} -> "not available". Now returns {"result":"SUCCESS"}.
- Store-screen entitlement checks (0x18001749d/0x1800175a2) read IS_*/
  *_PURCHASE_ENABLED Blaze flags, separate from storeEnabled. Added the full
  confirmed set (14 flags) to FUT_RS4_CONFIG.
- Catalog: assetId (0x23) is the real pack identity; extPrice inner keys are
  amount/currency (not mtx). (Also gated client-side by GetSystemMetrics>1024x768.)

Full FUT API reversed (clean-room, CardsDLL only) into docs/ENDPOINT_MAP.md:
~100 FutXServerResponse types across 7 feature groups (market, SBC, draft,
seasons/match, club, store, user/hub), each with deserializer VA, atom-mapped
field schema + types, freeze-risk flags, and minimal known-good JSON.
Tooling kept: tools/atomdump.py (dumps the 907-atom key table at 0x1802d2760)
-> docs/fut_atoms.tsv. Research prompt: docs/OPENCODE_ENDPOINT_PROMPT.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PN5bmpDVQR1aXgefyWAt7o
2026-08-02 17:13:11 -07:00

171 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# opencode task — map the remaining FIFA 17 FUT endpoints for the offline rebuild
## Context
OpenFUT runs FIFA 17 Ultimate Team fully offline (clean-room; EA servers are dead).
A working Python backend already exists at `~/Documents/OpenFUT/fifa17-recon/tools/` and
FIFA talks to it (`/etc/hosts``easw.easports.com``127.0.0.1:8099`). Many endpoints
are already reversed and served. Your job is **RESEARCH ONLY**: produce a complete map of
the FUT endpoints that are LEFT to build, using the tools/logs/disassembly that already
exist. Do NOT rewrite the backend — output `~/Documents/OpenFUT/fifa17-recon/docs/ENDPOINT_MAP.md`.
## CLEAN-ROOM RULE (HARD)
Use ONLY files we own + our own client's traffic. NEVER use leaked EA source of any kind.
## Use the ALREADY-BUILT tools — do not rebuild these
1. **`tools/utas_server.py`** — the live backend. Its `ROUTES` table is the AUTHORITATIVE
list of endpoints already handled: `auth`, `delete/auth`, `phishing/{trusteddevice,
validate,question}`, `user/credits`, `user/list`, `user/accountinfo`, `user`, `squad`
(+`/list`), `hub`, `userMassInfo`, `season`, `club`, `item(/resource)/defid`,
`store/purchasegroup`, `store/transaction`, `purchased`. Read it to see what's DONE and
the exact response shapes used. Its `_handle()` logs `!! UNMAPPED PATH -> catch-all 200 {}`
for any endpoint FIFA hits that ISN'T handled yet — those are your gaps.
2. **`/tmp/utas_server.log`** — GROUND TRUTH of every request FIFA makes (method, path,
headers, body) and our response. Start here:
```
grep 'UNMAPPED PATH' -B1 /tmp/utas_server.log # endpoints we stub {}
grep -oE '(GET|POST|PUT) /ut/(v2/)?game/fifa17/[^ ?]*' /tmp/utas_server.log | sort -u
```
This is the real, ordered list of what the client requests per FUT screen.
(Caveat: the log is temp — cleared on reboot — and only reflects FUT screens actually
visited. For richer traffic, run the harness and navigate more FUT menus first.)
3. **`tools/memtool.py`** — live FIFA17 `/proc/mem` reader/patcher (base `0x140000000`),
for inspecting parsed structs live if a format is ambiguous.
4. **`tools/fut_store.py` / `tools/fut_seed.py`** — the data models (club items, squad,
packs) the backend already uses; extend these conceptually, don't reinvent.
5. **`tools/openfut-fut.sh`** — starts the whole harness (lsx/blaze/roster/utas) if you
need it running to capture more traffic. `tools/root_arm.sh` arms host state (needs sudo).
6. **`docs/CARD_SYSTEM.md`** — the reversed card/parser system + method (READ FIRST). It
already documents the SAX-parser internals so you don't re-derive them.
## Disassembly (regenerate once; same method `docs/CARD_SYSTEM.md` uses)
```
mkdir -p /tmp/fut && cp "/mnt/games/FIFA 17/CardsDLL_Win64_retail.dll" /tmp/fut/cardsdll.dll
objdump -d -M intel /tmp/fut/cardsdll.dll > /tmp/fut/cardsdll.asm
strings -t x /tmp/fut/cardsdll.dll > /tmp/fut/cardsdll.strings
```
Shared-parser CHEATSHEET (reuse, don't re-derive): endpoint paths are strings `"ut/%s/..."`
(`%s` = `"game/fifa17"`); response structs are `"RS4:Fut<X>ServerResponse"`; each has a SAX
deserializer that hashes each key via FNV-1a fn `0x180180d00` (seed `0x811c9dc5`) to an ATOM
int, looks the name up in the table at `0x1802d2760` (`table[atom]=char*`), and dispatches
via a jump-table; unknown keys hit the value-SKIP handler `0x180135ff0` (container-aware,
safe). Field TYPE must match its handler (scalar getters `0x1801c79d0`/`0x1801c7620`/
`0x1801c7aa0` vs nested object/array handlers) — a scalar handler fed an object/array
**DESYNCS the parser and hard-freezes the game**, so type fidelity is mandatory.
Endpoint resolve table: `RS4::ServerSettings::resolve 0x180124270`.
## Method (per endpoint)
For each gap endpoint (from the log's UNMAPPED list + the `"ut/%s/..."` strings not yet in
`ROUTES`): grep the strings for its path → find its `Fut<X>ServerResponse` struct → find its
deserializer → list the atoms it dispatches, map atom→key via `0x1802d2760`, note each
field's JSON type + required-vs-skip → write the minimal known-good response JSON.
## WHERE TO START
1. Read `docs/CARD_SYSTEM.md` and skim `tools/utas_server.py` `ROUTES`.
2. Run the two log greps above → the definitive list of endpoints FIFA calls but we only
stub `{}`. Rank them by FUT feature.
3. List ALL `"ut/%s/..."` path strings and subtract the ones already in `ROUTES` → the
endpoints not yet even discovered in traffic.
4. Reverse the response format for each gap, prioritizing the core loop first:
**transfermarket** (search/bid/buy/list/watchlist), **tradepile**, **SBC**
(challenges/submit), **objectives**, **draft**, **seasons/single-player**, **match**
(squad-battles/kickoff result), then club stats / concept squads / loans.
## Deliverable
`docs/ENDPOINT_MAP.md`, one section per feature: for each endpoint — method, path, whether
already handled or a gap, request body shape, response shape (exact keys+types, required vs
optional), deserializer VA, and a minimal known-good example JSON. This is the spec for
finishing the FUT backend (target: port into the Rust `openfut-core` behind a FIFA-17 bridge).
---
## APPENDIX — known atoms + verified response templates (reuse these; don't re-derive)
### Parser internals (already reversed)
- key → atom: FNV-1a fn `0x180180d00` (seed `0x811c9dc5`) → `table[atom]=char*` at `0x1802d2760`
- dispatch: range jump-tables; unknown key → value-SKIP `0x180135ff0` (container-aware, safe)
- scalar getters (leaf, no descend): int/num `0x1801c79d0`, bool `0x1801c7620`, string `0x1801c7aa0`
- endpoint resolve: `RS4::ServerSettings::resolve 0x180124270`; path `"ut/%s/.."`, `%s="game/fifa17"`
- shared ITEM/card deserializer: `0x18013fe00` (used by club, squad slots, pack itemList, purchased)
### Common shared keys (atom in hex | JSON type)
```
itemData 0x16b (obj/array-elem) | id 0x15c (int) | resourceId 0x287 (int) | assetId 0x23 (int)
index 0x163 (int) | kitNumber 0x17a (int) | rating 0x274 (int) | preferredPosition 0x24a (str)
cardsubtypeid 0x6c (int) | attributeList 0x31 (array) | currencies 0xc5 (array)
currencies element: name 0x1d0 (str) | funds 0x134 (int) | finalFunds 0x124 (int)
currency-name literals are CASE-SENSITIVE strings: "coins", "points", "DRAFT_TOKEN"
configs 0xa2 (array) [settings response, key = "configs"]
```
### FutUserCreditsServerResponse — GET user/credits (deser 0x180122c50) [VERIFIED WORKS]
```json
{"currencies":[{"name":"coins","funds":15000,"finalFunds":15000},
{"name":"points","funds":0,"finalFunds":0}],
"unopenedPacks":{"preOrderPacks":0,"recoveredPacks":0}}
```
Coins read from `currencies[name=="coins"].funds`. A bare `{"credits":n}` is SKIPPED → 0.
### FutStoreGetPackTypesServerResponse — GET store/purchasegroup/all (deser 0x1801234e0)
Root key MUST be `"purchase"` `0x260` (array); optional `"timestamp"` `0x31b`. Per-pack
(parser `0x18013af30`): `id 0x15c`(int16, the pack identity) | `packType 0x20f`(str) |
`description 0xd1`(str) | `currencies 0xc5`(array {name,funds,finalFunds} = coins price) |
`extPrice 0x119`(obj {`finalPrice 0x125`, `originalPrice 0x205`}, each a currency→amount map
incl `"mtx"`=FIFA-Points) | `packContentInfo 0x20c`(obj: `bronzeQuantity 0x63`,
`silverQuantity 0x2c6`, `goldQuantity 0x149`, `rareQuantity 0x273`, `itemQuantity 0x170`) |
`quantity 0x26b`(int, 0=unlimited) | `isPremium 0x176` | `saleType 0x298` | `state 0x2eb` |
`visible 0x37d`(sets flag, DON'T send the value—desyncs).
**⚠ The parser is NOT the store gate (CONFIRMED by disassembly).** Per-pack parser epilogue
`0x18013badc` pushes every parsed pack unconditionally — no valid/drop predicate exists in
the parse path. The `"not available"` error (`FUT_CatalogNotAvailable`, msg-id `0x7550`)
comes from TWO downstream gates, neither JSON-schema-related:
1. **Resolution gate `0x18001756e`**: calls `GetSystemMetrics` — if the display is
**≤ 1024×768**, the store is declared unavailable regardless of any JSON. Run FIFA at
**> 1024×768** (1280×720 passes). *Cheapest cause to eliminate — check this first.*
2. **Store-data-model load status** (`0x180013cf0`): `model+0x30` must become `1` and the
screen's cached status `[rsi+0x250]` must not stay `-1`; else post `0x7550` at
`0x180013d6c`. Fed by two entitlement checks — `0x18001749d` (`vtable+0x138`) and
`0x1800175a2` (`vtable+0x280`) — that read the **Blaze client-config purchase flags**:
`IS_STORE_ENABLED`, `IS_COIN_PURCHASABLE`, `IS_FIFAPOINT_AVAILABLE`,
`COINS_PURCHASE_ENABLED`, `POINTS_PURCHASE_ENABLED`, `MONEY_PURCHASE_ENABLED`. These are
SEPARATE from `storeEnabled`/`cardPackStoreEnabled` and must ALSO be set in the Blaze config.
Recognized per-pack enum tokens (send these exact strings to avoid enum-reject):
`saleType` → `"promo"`/`"deal"`; a limit-type field → `NONE`/`QUANTITY`/`TIME`/`TIME_QUANTITY`;
pack `state` → `"active"`. Currency token is lowercase `"coins"`/`"mtx"` (NOT uppercase).
### FutCreatePackServerResponse — PUT store/transaction (pack reveal, deser 0x180162880)
Wrapper key `"createPackResponse"` `0xbe`:
```json
{"createPackResponse":{"itemList":[/*cards*/],"numberItems":7,
"purchasedPackId":102,"duplicateItemIdList":[]}}
```
(atoms: itemList `0x16e`, numberItems `0x1dd`, purchasedPackId `0x264`, duplicateItemIdList `0xec`.)
BUY signal = transaction body has `"packId"` `0x20b` AND `state 0x2eb != "TRANSACTIONCANCEL"`.
state enum strings: `TRANSACTIONCREATED`(carries packId=the buy), `PURCHASECOMPLETE`,
`TRANSACTIONCOMPLETE`, `TRANSACTIONCANCEL`.
`FutGetPurchasedItemsServerResponse` — GET purchased: `{"itemData":[/*cards*/]}` (itemData `0x16b`).
### GetUserMassInfo — GET userMassInfo (deser 0x180174630) [FREEZE-SENSITIVE]
Top-level: `userInfo 0x370`, `squad 0x2cd`, `settings`, `userData`. MUST serve `{}` unless
every field type-matches, else the parser hard-freezes. `userInfo` deser `0x18013ec10`;
SAFE minimal that carries coins:
```json
{"userInfo":{"currencies":[{"name":"coins","funds":15000}],"sessionCoinsBankBalance":15000}}
```
(`sessionCoinsBankBalance 0x2bb`.) DANGER: `squadList` must be an OBJECT `{"squad":[...]}`
NOT an array (array → desync → freeze). Container fields `feature`/`reliability`/
`unopenedPacks`/`bidTokens`/`actives` must match exact shape or be omitted.
### LoadActiveSquad — GET squad/0 (deser 0x18013d1f0)
Keys: `id 0x15c` | `personaId 0x21b` | `squadName 0x2d3` | `formation 0x12b`(str) |
`squadType 0x2d6` | `chemistry 0x81` | `starRating 0x2e2` | `captain 0x69` | `manager 0x1a8`(array)
| `actives 0xb`(array) | `custom 0xc6`(STRING of 33 ints) | `players 0x238`(array of
{index,itemData,kitNumber}) | `kicktakers 0x178`(array). Squad PUT stores slots as
`itemData={id:<clubItemId>}` references (re-embed full items on GET, see `reconstruct_squad`).
> Atoms are index-into-`0x1802d2760`; a few above came from mixed-confidence passes — when a
> value doesn't take, verify the atom by locating the key string in `cardsdll.strings` and
> re-hashing. Field TYPE fidelity is mandatory (scalar-vs-container mismatch = freeze).
> High-confidence/verified: `user/credits`, `createPackResponse`, the store gate analysis,
> and the `userMassInfo` safe-shape.