fifa17: cardtype 9 is unnameable -- measured, and the gap closes as a negative
Serving owned balls (subtype 30), league logos (31) and fcc_misccards
(231/232/233/236) was the last projection gap. The open guess was that their
caption would come from `localizedName` on the wire, "probably", and they were
withheld out of caution.
Measured against the running client instead (new
tools/cardtype_dispatch_probe.py, read-only, reproducible, every step with a
positive control). They cannot be named at all:
1. The merge jump table at rva 0x141eb4 is indexed cardtype-1 with 10 entries.
Cardtypes 1..5 and 10 each get a DB-merge arm; cardtypes 6,7,8,9 ALL land on
one shared tail at 0x180141e8a that runs no query and writes no name.
2. `cmp [reg+0x4c], 9` (cardtype): ZERO sites in .text. For contrast, cardtype
1 has 13 and cardtype 7 has 6.
3. `cmp [reg+0x50], 30` and `..., 31` (cardsubtypeid -- the field that actually
selects a club-item caption): ZERO sites each, while kit 9, stadium 10 and
badge 11 all appear, which is the control. The only cardtype-9 subtypes
present anywhere are the four misccards ids, and all four are one boolean
predicate near 0x1801a72da that returns FALSE for them: an exclusion, not a
resolver. That predicate is NOT identified and is not claimed to be.
4. The cardtype-7 resolver is gated `cmp [rax+0x4c], 7` at 0x1800f6f04, so a
cardtype-9 item never reaches it. Its jne path formats AWARD_LABEL_%i --
the trophy path, not a fallback that would name a ball.
Nothing reads a localizedName for these subtypes, so sending one cannot become a
caption. Withholding them is a measured limit of the client, not caution, and no
server change can lift it.
CORRECTION: FUN_180119bd0 was recorded as "zero refs in CardsDLL -> almost
certainly an export, its caller is in FIFA17.exe". It is not an export. Its
address occurs exactly once in the whole process, at 0x18021c738 in CardsDLL's
own .rdata, and nothing in FIFA17.exe references it. It is virtual: vtable base
0x18021c2a0, slot +0x498, index 147 -- independently reproducing the recorded
"manager vtable slot +0x498" by a different method. Finding the boundary needs
the constructor-LEA trick; walking back over .text-pointing qwords runs 826 slots
through several adjacent vtables.
Bonus: the shared tail cardtypes 6-9 fall into IS the discard level ladder
(movzx [rdi+0xb4]; cmp 0x4b; cmp 0x41; store [rdi+0x54]), confirming
discard::discard_level instruction for instruction against the live client.
Adapter 244 tests, fmt clean.
This commit is contained in:
@@ -304,8 +304,44 @@ elimination:**
|
||||
| kit | **9** | 7 | `FUN_180119bd0` → `FUT_UC_KITS` + `TeamName_Abbr15_<teamid>` | `teamid` |
|
||||
| stadium | **10** | 7 | `FUN_180119bd0` → `Stadium` + `StadiumName_<assetId>` | `assetId` |
|
||||
| badge | **11** | 7 | `FUN_180119bd0` → `Badge` + `TeamName_Abbr15_<teamid>` | `teamid` |
|
||||
| ball | **30** (0x1e) | 9 | none; `FUT_UC_BALL` caption only | `localizedName` |
|
||||
| league logo | **31** (0x1f) | 9 | `FUN_180098f20` keyed on leagueid | `localizedName`, probably |
|
||||
| ball | **30** (0x1e) | 9 | NONE — see the 2026-08-21 measurement below | unnameable |
|
||||
| league logo | **31** (0x1f) | 9 | NONE — see the 2026-08-21 measurement below | unnameable |
|
||||
|
||||
**MEASURED 2026-08-21 against the running client (`tools/cardtype_dispatch_probe.py`,
|
||||
pid 6580): no cardtype-9 family can be named, and no server change can alter that.**
|
||||
Four independent reads, each with a passing positive control:
|
||||
|
||||
1. The merge switch's jump table at rva `0x141eb4` is indexed by `cardtype - 1`
|
||||
and has exactly 10 entries. Cardtypes 1–5 and 10 each get their own DB-merge
|
||||
arm; **cardtypes 6, 7, 8 and 9 all land on the shared tail `0x180141e8a`**,
|
||||
which issues no query and writes no name — it only derives the discard level
|
||||
from the rating.
|
||||
2. Census of every `cmp [reg+0x4c], imm` (cardtype): 0 → 1 site, 1 → 13, 6 → 1,
|
||||
7 → 6, **9 → ZERO**.
|
||||
3. Census of every `cmp [reg+0x50], imm` (cardsubtypeid), which is what actually
|
||||
selects a club-item caption: kit 9, stadium 10 and badge 11 all present
|
||||
(control), **ball 30 → ZERO sites, league logo 31 → ZERO sites**. The only
|
||||
cardtype-9 subtypes that appear at all are `fcc_misccards` 231/232/233/236,
|
||||
and all four sites are one boolean predicate near `0x1801a72da` that returns
|
||||
FALSE for them — an exclusion, not a resolver. (That predicate's identity is
|
||||
NOT established; it reads `+0x49`, `+0x145` and a vtable slot `+0x270`.)
|
||||
4. The cardtype-7 resolver is reached only under `cmp DWORD PTR [rax+0x4c], 0x7`
|
||||
at `0x1800f6f04`, so a cardtype-9 item can never arrive there. Its `jne` path
|
||||
formats `AWARD_LABEL_%i` (`0x1801fd5a0`) — the TROPHY path, not a fallback
|
||||
that would name a ball.
|
||||
|
||||
So the earlier "`localizedName`, probably" for these two rows was optimistic:
|
||||
there is no code that would read it for a caption. Withholding ball and league
|
||||
logo from the projection is a measured limit of the client, not caution.
|
||||
|
||||
CORRECTION, same measurement: `FUN_180119bd0` was recorded elsewhere as having
|
||||
"zero refs in CardsDLL → almost certainly an export, its caller is in
|
||||
FIFA17.exe". It is **not** an export. Its address occurs exactly ONCE in the
|
||||
whole process, at `0x18021c738` in CardsDLL's own `.rdata`, and nothing in
|
||||
FIFA17.exe references it. It is a virtual function: vtable base `0x18021c2a0`,
|
||||
slot **+0x498**, index 147 (ctor LEAs at `0x18010ce10` / `0x18011111b`) — which
|
||||
independently reproduces the "manager vtable slot +0x498" recorded below, by a
|
||||
different method. It has 7 distinct `call [reg+0x498]` sites.
|
||||
|
||||
The premise that all five live in cardtype 9 is wrong, and the root fact is not an
|
||||
inference from a call site. `FUN_1800d8330`, read in full at 714 chars by two
|
||||
|
||||
Reference in New Issue
Block a user