571c5f9261
Task A, static phase. Ghidra 12.1.2 headless via the repo's own pyghidra harness
over CardsDLL_Win64_retail.dll (13,382 functions). Queries and raw decompiler
output committed under docs/evidence/market-sold-re-2026-08-17/.
RECOVERED FROM THE BINARY
1. No sold token, now EXHAUSTIVELY: both vocabularies dumped to their sentinels
rather than sampled. tradeState is exactly 4 rows; itemState is exactly 12
(invalid/free/WAITING_FOR_GAME/inGame/forSale/offered/activeBadge/
activeHomeKit/activeAwayKit/activeBall/activeStadium/active=255). A sold row
MUST therefore be a combination of existing atoms.
2. What closed does, complete, from the auctionInfo deserializer 0x18013e410:
IS_GLOW = (tradeState==closed) ? bidState != none
: bidState in {outbid, buyNow}
INBOX = bidState in {highest, buyNow}
3. The full record -> Flash map from the publisher 0x1801bf030, superseding the
partial list. The prize: record +0xbf is published as COINS_AWARDED, fed by the
coinsProcessed atom 0x2f4. The corpus had recorded that atom's type and noted
its consumer was never found; it is now traced. DURATION also renders the
localised FUT_AUCTION_EXPIRED when expires underflows.
4. highest vs buyNow on a closed row is UNDECIDABLE from CardsDLL, by proof: both
yield IS_GLOW=1/INBOX=1, bit-identical. But bidState is ALSO published verbatim
as YOURBID alongside STATE and COINS_AWARDED, so the movie does receive the raw
values - the discrimination exists and lives entirely in unread ActionScript.
This retires the question as a static target, and it contradicts the
third-party lore that a seller's sold row is closed+buyNow (the corpus's own
lifecycle table says closed+highest and assigns buyNow to the buyer).
5. The clear-sold verb EXISTS. Builder 0x1801647c0 emits "/sold" when the tradeId
field is zero and "/%lld" otherwise, on route base ut/delete/%s/trade, response
class RS4 FutISRemoveTradeServerResponse. Confirmed by the client's own
request-name table entry RemoveAllSoldFromTradePile. A BULK clear-sold verb only
makes sense if sold rows PERSIST in the seller's pile until cleared, which is
incompatible with our Fix A invariant - so the sold path will require revisiting
it under live validation.
6. The seller's SOLD counter is real, proven end to end with no inference: the hub
tradePile sub-deserializer 0x18013ead0 writes atom sold 0x2c9 to +0x1d8, and the
tile publisher 0x1800b1dc0 renders +0x1d8 as Flash TEXT3 under the localised
caption FUT_TF_SOLD. Siblings: selling -> +0x1d2 -> FUT_TF_SELLING,
count -> +0x1d4 -> FUT_UC_ITEMS, plus FUT_TF_WINNING/FUT_TF_OUTBID on the
Transfer Targets tile. We and the Python oracle both hardcode sold:0, so that
bucket can never fill.
7. Reusable method: an atom id is the INDEX into the alphabetical atom-name pointer
table at base 0x1802d2760. Validated 12/12 against the known auctionInfo atoms
and cross-checked against fifa17-recon/docs/fut_atoms.tsv. Documented gotcha:
resolve a name by the pointer slot INSIDE the table, never by the first matching
string in the binary, or you get confident nonsense.
8. An auction-outcome vocabulary exists (auctionSoldBid 0x39, auctionSoldBuyNow
0x3a, auctionWon*/auctionLost*) but NO deserializer consumes it - every
candidate function was checked for the value-SKIP/atom-loop signature and none
qualifies. Server-side or telemetry only; it does not carry sold state here.
TASK B IS UNDECIDABLE FROM THE CLIENT, and this is a proof of absence: no 0.95 or
0.05 constant of either width, no tax/fee/net/proceeds caption, and no fee
arithmetic anywhere. The client never computes or displays a net, so no experiment
against our own server can measure the rounding - whatever we credit is what it
displays, and there is no oracle. Only an original EA-era seller-balance capture
could settle it. The rule stays an explicit CHOICE (floor the fee, so
fee + proceeds == gross exactly) and is now pinned at the requested boundaries
100/101/119/120/149/150/151/199/200 plus 15,000 and i64::MAX.
Settlement NOT promoted. No production process, port or database was touched.
179 lines
5.1 KiB
Plaintext
179 lines
5.1 KiB
Plaintext
==============================================================================
|
|
== (a) hub tradePile sub-deserializer 0x18013ead0
|
|
== atom ids of interest: count=0xbc notification=0x1da selling=0x2b8 sold=0x2c9
|
|
==============================================================================
|
|
|
|
undefined4 FUN_18013ead0(longlong param_1,longlong param_2,longlong param_3)
|
|
|
|
{
|
|
undefined2 uVar1;
|
|
int iVar2;
|
|
int iVar3;
|
|
undefined8 uVar4;
|
|
|
|
iVar3 = 0x38c;
|
|
iVar2 = FUN_1801c7f10(param_3);
|
|
do {
|
|
if (iVar2 == 10) {
|
|
*(undefined1 *)(param_1 + 0x1da) = 1;
|
|
return 10;
|
|
}
|
|
if (*(int *)(param_3 + 0xd0) == 0xb) {
|
|
iVar3 = FUN_180180d00(*(undefined8 *)(param_3 + 0xf8));
|
|
iVar2 = FUN_1801c7f10(param_3);
|
|
if (iVar2 != 6) goto LAB_18013eb2e;
|
|
}
|
|
else {
|
|
LAB_18013eb2e:
|
|
if (iVar3 == 0xbc) {
|
|
uVar4 = FUN_1801c79d0(param_3);
|
|
uVar1 = FUN_1800d7b10(uVar4);
|
|
*(undefined2 *)(param_1 + 0x1d4) = uVar1;
|
|
}
|
|
else if (iVar3 == 0x1da) {
|
|
uVar4 = FUN_1801c79d0(param_3);
|
|
uVar1 = FUN_1800d7b10(uVar4);
|
|
*(undefined2 *)(param_1 + 0x1d6) = uVar1;
|
|
}
|
|
else if (iVar3 == 0x2b8) {
|
|
uVar4 = FUN_1801c79d0(param_3);
|
|
uVar1 = FUN_1800d7b10(uVar4);
|
|
*(undefined2 *)(param_1 + 0x1d2) = uVar1;
|
|
uVar4 = FUN_1801c79d0(param_3);
|
|
uVar1 = FUN_1800d7b10(uVar4);
|
|
*(undefined2 *)(param_2 + 0x36) = uVar1;
|
|
}
|
|
else if (iVar3 == 0x2c9) {
|
|
uVar4 = FUN_1801c79d0(param_3);
|
|
uVar1 = FUN_1800d7b10(uVar4);
|
|
*(undefined2 *)(param_1 + 0x1d8) = uVar1;
|
|
}
|
|
else {
|
|
FUN_180135ff0(iVar3,param_3);
|
|
}
|
|
}
|
|
iVar2 = FUN_1801c7f10(param_3);
|
|
} while( true );
|
|
}
|
|
|
|
|
|
|
|
==============================================================================
|
|
== (b) /sold request builder 0x1801647c0
|
|
==============================================================================
|
|
|
|
/* WARNING: Function: __security_check_cookie replaced with injection: security_check_cookie */
|
|
|
|
void FUN_1801647c0(longlong param_1,undefined8 param_2,undefined4 param_3)
|
|
|
|
{
|
|
undefined1 auStack_58 [32];
|
|
undefined1 local_38;
|
|
undefined8 local_37;
|
|
undefined8 local_2f;
|
|
undefined8 local_27;
|
|
undefined4 local_1f;
|
|
undefined2 local_1b;
|
|
undefined1 local_19;
|
|
ulonglong local_18;
|
|
|
|
local_18 = DAT_1802db6e0 ^ (ulonglong)auStack_58;
|
|
local_38 = 0;
|
|
local_37 = 0;
|
|
local_2f = 0;
|
|
local_27 = 0;
|
|
local_1f = 0;
|
|
local_1b = 0;
|
|
local_19 = 0;
|
|
if (*(longlong *)(param_1 + 0x10) == 0) {
|
|
FUN_180007f80(&local_38,0x20,"/sold");
|
|
}
|
|
else {
|
|
FUN_180007f80(&local_38,0x20,"/%lld");
|
|
}
|
|
FUN_180008100(param_2,&local_38,param_3);
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
==============================================================================
|
|
== (c) FutGetAuctionCount deserializer 0x180163770
|
|
==============================================================================
|
|
|
|
/* WARNING: Function: __security_check_cookie replaced with injection: security_check_cookie */
|
|
|
|
undefined8 FUN_180163770(undefined8 param_1,undefined8 *param_2)
|
|
|
|
{
|
|
undefined2 uVar1;
|
|
int iVar2;
|
|
undefined4 uVar3;
|
|
undefined8 uVar4;
|
|
longlong *plVar5;
|
|
longlong lVar6;
|
|
undefined1 auStack_168 [32];
|
|
int local_148 [2];
|
|
undefined8 local_140;
|
|
undefined1 local_138 [288];
|
|
ulonglong local_18;
|
|
|
|
local_140 = 0xfffffffffffffffe;
|
|
local_18 = DAT_1802db6e0 ^ (ulonglong)auStack_168;
|
|
FUN_1801c63e0(local_138,0,0);
|
|
local_148[0] = 0x38c;
|
|
uVar4 = FUN_180008130(*param_2);
|
|
FUN_1801c8270(local_138,*param_2,uVar4,0);
|
|
FUN_1801c7f10(local_138);
|
|
FUN_1801c7f10(local_138);
|
|
plVar5 = (longlong *)FUN_18011a830();
|
|
lVar6 = (**(code **)(*plVar5 + 0x130))(plVar5);
|
|
*(undefined4 *)(lVar6 + 0x30) = 0xffffffff;
|
|
*(undefined8 *)(lVar6 + 0x34) = 0;
|
|
iVar2 = FUN_1801c7f10(local_138);
|
|
while (iVar2 != 10) {
|
|
iVar2 = FUN_180141ee0(local_148,local_138);
|
|
if (iVar2 != 6) {
|
|
if (local_148[0] == 0xbc) {
|
|
uVar4 = FUN_1801c79d0(local_138);
|
|
uVar1 = FUN_1800d7b10(uVar4);
|
|
*(undefined2 *)(lVar6 + 0x34) = uVar1;
|
|
}
|
|
else if (local_148[0] == 0x1bf) {
|
|
uVar4 = FUN_1801c79d0(local_138);
|
|
uVar3 = FUN_1800d7af0(uVar4);
|
|
*(undefined4 *)(lVar6 + 0x30) = uVar3;
|
|
}
|
|
else if (local_148[0] == 0x1e5) {
|
|
uVar4 = FUN_1801c79d0(local_138);
|
|
uVar1 = FUN_1800d7b10(uVar4);
|
|
*(undefined2 *)(lVar6 + 0x3a) = uVar1;
|
|
}
|
|
else if (local_148[0] == 0x2b8) {
|
|
uVar4 = FUN_1801c79d0(local_138);
|
|
uVar1 = FUN_1800d7b10(uVar4);
|
|
*(undefined2 *)(lVar6 + 0x36) = uVar1;
|
|
}
|
|
else if (local_148[0] == 0x2c9) {
|
|
uVar4 = FUN_1801c79d0(local_138);
|
|
uVar1 = FUN_1800d7b10(uVar4);
|
|
*(undefined2 *)(lVar6 + 0x38) = uVar1;
|
|
}
|
|
else {
|
|
FUN_180135ff0(local_148[0],local_138);
|
|
}
|
|
}
|
|
iVar2 = FUN_1801c7f10(local_138);
|
|
}
|
|
*(undefined1 *)(lVar6 + 0x28) = 1;
|
|
FUN_1801c6560(local_138);
|
|
return 1;
|
|
}
|
|
|
|
|
|
|
|
==============================================================================
|
|
== callers of the hub tradePile sub-deserializer (which response embeds it)
|
|
==============================================================================
|
|
0x180139610 FUN_180139610
|