Files
OpenFUT/fifa17-recon/tools/atomdump.py
T
funman300 4e89cce37d fifa17-recon: store fix (v2/store gate + flags) + full FUT endpoint map
Store "not available" root cause reversed from CardsDLL:
- ut/v2/game/fifa17/store is an ELIGIBILITY gate (FutStorePackQuantities
  deser 0x1801758c0), not a quantity list. It reads one key "result"
  (atom 0x288); the store screen refuses to open unless SUCCESS. Was
  unhandled -> catch-all {} -> "not available". Now returns {"result":"SUCCESS"}.
- Store-screen entitlement checks (0x18001749d/0x1800175a2) read IS_*/
  *_PURCHASE_ENABLED Blaze flags, separate from storeEnabled. Added the full
  confirmed set (14 flags) to FUT_RS4_CONFIG.
- Catalog: assetId (0x23) is the real pack identity; extPrice inner keys are
  amount/currency (not mtx). (Also gated client-side by GetSystemMetrics>1024x768.)

Full FUT API reversed (clean-room, CardsDLL only) into docs/ENDPOINT_MAP.md:
~100 FutXServerResponse types across 7 feature groups (market, SBC, draft,
seasons/match, club, store, user/hub), each with deserializer VA, atom-mapped
field schema + types, freeze-risk flags, and minimal known-good JSON.
Tooling kept: tools/atomdump.py (dumps the 907-atom key table at 0x1802d2760)
-> docs/fut_atoms.tsv. Research prompt: docs/OPENCODE_ENDPOINT_PROMPT.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PN5bmpDVQR1aXgefyWAt7o
2026-08-02 17:13:11 -07:00

54 lines
1.5 KiB
Python

#!/usr/bin/env python3
# Dump the FUT atom name table (atom index -> key string) from CardsDLL.
# Table at VA 0x1802d2760 is an array of char* pointers into .rdata.
import struct, sys
DLL = "/tmp/fut/cardsdll.dll"
data = open(DLL, "rb").read()
# (VA_start, size, file_off) from objdump -h
SECTIONS = [
(0x180001000, 0x1e3f62, 0x400), # .text
(0x1801e5000, 0xa4094, 0x1e4400), # .rdata
(0x18028a000, 0x54000, 0x288600), # .data
]
def va_to_off(va):
for start, size, off in SECTIONS:
if start <= va < start + size:
return off + (va - start)
return None
def read_cstr(va, maxlen=128):
off = va_to_off(va)
if off is None:
return None
end = data.find(b"\x00", off, off + maxlen)
if end < 0:
return None
try:
return data[off:end].decode("ascii")
except UnicodeDecodeError:
return None
TABLE_VA = 0x1802d2760
off = va_to_off(TABLE_VA)
atoms = {}
for i in range(0, 1200):
ptr = struct.unpack_from("<Q", data, off + i * 8)[0]
if ptr == 0:
s = None
else:
s = read_cstr(ptr)
if s is None:
# allow a few gaps then stop if we run off the end
if i > 40 and all(struct.unpack_from("<Q", data, off + (i + k) * 8)[0] == 0 for k in range(4)):
break
continue
if s.isprintable() and 1 <= len(s) <= 40:
atoms[i] = s
for i in sorted(atoms):
print(f"{i}\t0x{i:x}\t{atoms[i]}")
print(f"# total {len(atoms)} atoms", file=sys.stderr)