Files
OpenFUT/fifa17-recon/tools
funman300 1e2b073e04 fifa17-recon: route the draft entry purchase, and resolve the envelope ambiguity
LIVE 2026-08-04: the draft-state array fix WORKED. The screen rendered instead of
hanging and the client advanced to the entry-fee screen, then crashed on the next
call, which we had never implemented:

  GET  /squad/mode/draft/state?mode=ONLINE  -> our array body    screen RENDERED
  GET  /user/credits                        -> 7200
  GET  /store/purchasegroup/all             -> the entry-fee screen
  POST /purchase/mode/0/draft   {"currency":"COINS","usePreOrder":0}
                                            -> {}  UNMAPPED, then the crash

Advancing the failure to the next unimplemented call is what a correct fix looks like.

ENVELOPE AMBIGUITY RESOLVED, and ENDPOINT_MAP's note about it is wrong. Two structures
reference RS4:FutPurchaseDraftModeServerResponse:

  0x18014c260  vtable 0x180224ef8, factory 0x18014c090.  3188 chars, OBJECT root
               (prologue tests != 10 = END_OBJECT), 1 skip handler, exactly the seven
               scalar ints. THIS IS THE RESPONSE PARSER.
  0x180150310  vtable 0x1802262f0, factory 0x180150260.  1836 chars, ARRAY root
               (loops until 0xd), ZERO skip handlers -- and NOT a response root at
               all. It parses ENTRANCE CRITERIA: each element's name is strcmp'd
               against the literals "COINS", "POINTS", "DRAFT_TOKEN" and stored at
               +0x28/+0x2c/+0x30. It shares the class-name string because it is the
               fee sub-object, not an "alternate/summary envelope" as documented.

THE CRASH ITSELF DISCRIMINATED, which is worth keeping as a technique. An object-root
parser handed {} parses benignly and leaves defaults; an array-root parser handed {}
desyncs and HANGS, which is exactly what draft/state did before the fix. We observed a
CRASH, not a hang, so the object-root parser is what ran and the failure is downstream
of an empty-but-valid parse. Consistent with 0x18014c260, inconsistent with the other.

Coins are NOT deducted. The client posts the price in the URL and it sent 0, because we
omit entranceCriteria from draft/state so there is no fee to charge. Charging a guessed
amount would be inventing an economy rule.

ALSO FIXED, before it reached the game: the route table hands handlers the compiled
PATTERN, not a match object (the dispatcher calls fn(rx, self)), so calling .group() on
the first argument raised AttributeError and killed the connection outright. That is
strictly worse than the {} it was replacing. Caught by verifying the response actually
changed after the restart rather than assuming the route worked.

Default ON: the behaviour it replaces is a confirmed crash, so no working state is at
risk. FUT_DRAFT_PURCHASE=0 reverts.

392 + 61 checks green, zero tracebacks on a clean boot.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VUT92pz6RWKih9dSr8ZpxW
2026-08-04 14:17:45 -07:00
..