Two client breakages in a row from detouring FUN_180033770 / sub_180033430 /
FUN_1800d73d0: the first cut froze FIFA at the "are both teams ready" prompt,
and the rate-limited rewrite CRASHED it at the same point. Rate limiting fixed
the I/O problem and the crash still happened, so the fault is the detours
themselves, not the logging.
Most likely cause: sub_180033430 is an address Ghidra never functionised, and at
least one of these is reached in a way a 14-byte inline patch cannot survive -
an interior branch target, or a callee taking stack arguments that the 4-register
wrapper silently drops when it tail-calls the original.
What the aborted runs did establish, and it is worth keeping:
- KIT_SCAN fires for cardtype 7 with subtype 9 selector 2 AND selector 3, so
BOTH the home and away club scans do run.
- The FUT club enumerate (teamId 130000) did NOT occur in the crashed run
before the kit screen, and KIT_DESC never fired at all.
- KITS_AVAILABLE remains 0.
- The "ret" value logged by kit_scan is the same constant for every call and
is not a usable item pointer, so that reading was wrong.
Next attempt must NOT patch this code path. Read the state from outside the
process instead - /proc/PID/mem plus objdump against the live client, which
cannot crash the game because it never writes to it.
The previous version of these three detours hung FIFA at the "are both teams
ready" prompt. FUN_180033770 is POLLED - about 150 calls alternating between the
two real match team ids - and the wrapper did a synchronous write_log on every
one. That is the whole cause; the detours themselves were sound.
Rebuilt so the hot path costs nothing:
- kit_enum returns immediately unless the team is the FUT club (130000), so
the polled case does no formatting and no I/O at all. When it is the FUT
club it reports the out-list length, i.e. how many kits were actually
offered - the number that decides whether the carousel has anything.
- kit_desc dedupes on the packed kit id, so a carousel that re-describes the
same kit logs it once. It decodes teamid/year/slot for comparison against
the active triple.
- kit_scan only reports cardtype 7 and dedupes on (subtype, selector).
Dedupe is a fixed 16-slot lock-free SeenSet - no allocation, no locks, safe to
consult from a polled game thread. A full set stops reporting rather than
evicting, because the point is a bounded log.
Rule this file broke once and must not break again: no trace may log per-call on
a polled function.
Motivation changed too. The kit selector is not cosmetic: it is what blocks
entering a match, which also explains why every match in the capture corpus is a
DNF with an unpopulated params object - entered and backed out of. A screenshot
shows the carousel with two tiles, one named HOME and one labelled "undefined",
both with untextured white shirts, which is exactly sub_180033430 writing NAME
on a match and nothing at all on a miss.
A live run refuted the model the existing traces were built on. KIT_SET never
fires, KIT_GET reports KITS_AVAILABLE=0, and FUN_1801c3480 is entered 810 times
without ever seeing a kit (772 players +0x60=1, 24 zeroed +0x60=4, 12 players
+0x60=6, 2 type-2 +0x60=4). So the DP command 0x7576 ->
FutSquadServiceImpl::setAvailableKits path is simply not the one in use.
The selector is fed by a provider CardsDLL registers into the FIFA engine -
singleton FUN_1800338f0, vtable 0x1801f1d68, slot +0x08 enumerate and +0x10
describe - and no trace covered it. Three passive detours added:
KIT_ENUM FUN_180033770 logs the teamId asked for; it answers only for the
FUT club 130000 and otherwise forwards to the engine
default, so a real team id here means our club items
were never in scope.
KIT_DESC sub_180033430 decodes the packed id into (teamid, year, slot). A
descriptor whose triple does not equal the active
home/away triple is left untouched, which is what
makes the engine substitute its own catalogue kit.
KIT_SCAN FUN_1800d73d0 the club scan behind getActiveKit: reports HIT with
the item fields, or MISS meaning the active triple
stays zero.
Prologues were dumped from the analysed Ghidra project (cardsdll.dll, base
0x180000000). Every copy length is instruction-aligned and none of the three
prologues is rip-relative, so the plain installer is correct for all of them -
unlike FUN_1801c3480, which needs the relocating installer for its
MOV RAX,[rip+...]. Note FUN_1800d73d0's prologue compares EDX against 2, so rdx
is the home/away selector (2/3), not the cardtype an earlier note assumed.
All three log then tail-call: behaviour is unchanged.
Cherry-pick of 4b1d5aa from wip/kit-selector-re, which forked before the TLS
work and cannot be rebased: that branch predates fifa17_tls.rs/patch_mem.rs and
carries a large unrelated lineage (probe/lsx/recv_hook). Only the kit_trace
commit's own contents are taken.
Traces the client-side FUT pre-match kit path in CardsDLL: the GetMatchKits_DP
gate (KITS_AVAILABLE), setAvailableKits (home/away list count), the kit-item
clone driver (item type/subid/teamid at FUN_1801c3480), and the local teamkits
DB clone. Read-only passive detours reusing season_trace's installers, which
this widens to pub(crate).
The one open unknown it answers: the selector requires item+0x60 == 4, a pile
value the server has never been observed to produce (/club emits 1,
/purchased 6).
Verified in the cross-built artifact that the roster TLS gate patch is intact
alongside the new trace (fifa17_tls markers present, KIT_* markers present).