kit selector: withdraw the "client dead end" verdict, measure what is actually resident

The 2026-08-21 entry concluded the pre-match kit selector "is a client dead end,
not a missing wire field" because nothing stores 4 into item +0x60. Withdrawn.
It rested on two mistakes:

  1. +0x60 == 4 DOES occur live - a record with +0x4c == 2 and +0x60 == 4 reached
     the art-clone driver FUN_1801c3480. The immediate-store scan cannot see it,
     so "nothing can satisfy the gate" was never licensed by that evidence.
  2. It annotated `cmp [rdi+0x4c], 7` with "<- we produce this" without measuring
     it. Its own live half showed only {1: players, 0: staff}: zero cardtype-7
     records. That is the finding, and it was read as the opposite.

Measured now against the live client parked on the kit selector, read-only via
/proc/PID/mem over 3047 MiB, searching the exact u32 values the server sent:
players and staff are resident with sane fields; kit, badge and stadium are all
absent by resourceId AND by instance id. The host served ?type=kit total=2
emitted=2 at 17:50:09 this session and neither kit produced a record.

So the blocker sits upstream of the +0x60 gate: no cardtype-7 record is ever
created, so the club scan FUN_1800d73d0 has nothing to match, KIT_DESC never
fires and KITS_AVAILABLE reads 0. Cause is not yet settled - either our wire
shape (the cardtype-7 arm wants name/localizedName/description, which we do not
send) or cardtype-7 items being transient. Neither is recorded as fact.

Also: kit_gate_probe.py's live half is unreliable. On pid 8793 it reported
"CardsDb is empty" while a byte scan found 1966 resident players, so its
structural chain is stale and its record counts understate reality. Adds
club_record_residency_probe.py, which is read-only and cannot disturb the game.
This commit is contained in:
funman300
2026-08-23 18:51:34 +00:00
parent b55dd16a46
commit 40e53ed02c
2 changed files with 165 additions and 7 deletions
+54 -7
View File
@@ -518,13 +518,60 @@ CORRECTED 2026-08-06 (live diff + deserializer frame arithmetic, record_off = 0x
```
**`+0x60`, extended 2026-08-21.** "Assigned by the owning list, not parsed" is
right, and the stronger statement is now measured: the pre-match kit selector
gates on `+0x60 == 4` at `0x1801c34f2`, and **nothing anywhere stores 4 into that
field** — not in CardsDLL (29 immediate stores, constants `{-2,0,1,908,0x3f800000}`),
not in FIFA17.exe (zero across 79 MB), and no resident record has ever held it
(live: `{1: players, 0: staff}`). Every OTHER input to that gate is already
served. So the empty kit-selection screen is a client dead end, not a missing
wire field. Tool: `fifa17-recon/tools/kit_gate_probe.py`.
right. The pre-match kit selector gates on `+0x60 == 4` at `0x1801c34f2`, and no
instruction in CardsDLL stores that constant immediately (29 stores, constants
`{-2,0,1,908,0x3f800000}`), nor does FIFA17.exe across 79 MB.
Tool: `fifa17-recon/tools/kit_gate_probe.py`.
**CORRECTED 2026-08-23 (live, pid 8793, read-only `/proc/PID/mem`).** The
2026-08-21 entry went on to call the kit selector "a client dead end, not a
missing wire field", on the grounds that "every OTHER input to that gate is
already served". That conclusion is WITHDRAWN. It rested on two mistakes.
1. **`+0x60 == 4` does occur.** A live record reached the art-clone driver
`FUN_1801c3480` holding `+0x4c == 2`, `+0x60 == 4`. So the value arrives by
some path the immediate-store scan cannot see (register copy or computed),
and "nothing can ever satisfy the gate" is false. What the static scan
actually licenses is the narrower claim above.
2. **cardtype 7 was never verified to be produced at all.** The probe annotates
`cmp [rdi+0x4c], 7` with "<- we produce this". Nothing measured that. Its own
live half showed `{1: players, 0: staff}` -- i.e. zero cardtype-7 records --
and that was read as "the only thing missing is +0x60".
**What is actually measured now.** With the client parked on the kit selector,
scanning all 3047 MiB of readable process memory for the exact u32 values the
server sent:
```
resident (record-shaped, sane fields):
player resourceId 83906881 -> cardtype 1, itemState 1, teamid 243, +0x60 1
staff resourceId 9000081 -> cardtype 2
staff resourceId 3000083 -> cardtype 4, subtype 8
staff resourceId 1000509 -> cardtype 2, subtype 4, teamid 241
NOT resident, by resourceId AND by instance id, zero hits each:
kit 6300006 / 100004874 (cardsubtypeid 9)
kit 6400003 / 100004873 (cardsubtypeid 9)
badge 6000005 / 100004875 (cardsubtypeid 11)
stadium 6200000 / 100004876 (cardsubtypeid 10)
```
The client fetched `?type=kit` at 17:50:09 this session and the host logged
`total=2 emitted=2`. Both kits were delivered and NEITHER produced a record.
Every cardtype-7 family is absent while cardtype 1/2/4 are resident.
So the blocker is upstream of the `+0x60` gate: no cardtype-7 record is ever
created, therefore the club scan `FUN_1800d73d0` (`+0x4c==7 && +0x50==9 &&
`+0x5c in {101,102}`) has nothing to match, `KIT_DESC` never fires, and
`KITS_AVAILABLE` reads 0. Whether that is a bad wire shape (the cardtype-7 parse
arm wants `name`/`localizedName`/`description`, which OpenFUT does not send) or
cardtype-7 items being transient by design is NOT yet settled -- do not record
either as fact.
**Method note.** `kit_gate_probe.py`'s live half is unreliable as written: on
pid 8793 it printed "CardsDb is empty (no FUT session loaded)" while a byte scan
found 1966 resident players. Its structural chain is stale, so its record counts
(including the original "27 resident records") understate reality. Prefer the
value scan until the chain is re-derived.
**`definitionId is NOT AN ATOM`, confirmed a fourth way 2026-08-21.** Every real
atom name appears exactly once in CardsDLL's `.rdata` — `resourceId`,