fifa17-recon: offline FUT squad-shell working + full card-system RE
Milestone: FIFA 17 Ultimate Team boots end-to-end on our offline backend
past every EA gate into the hub and a live Squads editor (correct 4-4-2,
5-star squad, no freezes).
Key findings this session:
- userMassInfo MUST stay {} (any content desyncs the massinfo parser
0x180174630 -> tokenizer busy-loop freeze). Deliver the squad via
GET /squad/0 (fetched on Squads-tab entry) instead.
- Player cards render generic because the card view-model (0x1800d7920)
reads identity/rating/face from a resolved record at item+0x10, filled
by a lookup (0x18011cca0) in the FUT item-definition std::map at
CardsDb+0x160c0 -- which is EMPTY offline -> default blank record.
- Version advertising (itemDbVersion/checkServerDbVersion) is proven inert
(JSON fields routed to the skip handler). Owned items don't auto-trigger
a definition fetch. In-place map overwrite is dead (map stays empty).
- Definition-serving endpoints (item/resource, defid, item?idList) built +
ready; the fetch trigger lives in the packed FIFA17.exe.
New: docs/CARD_SYSTEM.md (findings + ordered next-steps plan for real
player cards: patch-POC, dbdata extractor, drive FIFA17.exe fetch, or
live-memory store injection). Plus tools: fut_seed.py (squad ladder +
definition serving), fifadrive.sh, vgamepad.py, and the login-RE toolset.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PN5bmpDVQR1aXgefyWAt7o
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Decode a captured Blaze Fire2 frame: 16-byte header + Heat2 TDF payload.
|
||||
Clean-room: parses the wire bytes of our own client's traffic."""
|
||||
import sys, struct
|
||||
|
||||
def decode_tag(b):
|
||||
# Heat2 tag: 3 bytes -> 4 chars, each 6-bit; 0 -> ' ' (trimmed). char = v ? v+0x20 : ' '
|
||||
a,b1,c = b[0],b[1],b[2]
|
||||
v=[ (a>>2)&0x3f, ((a&0x3)<<4)|((b1>>4)&0xf), ((b1&0xf)<<2)|((c>>6)&0x3), c&0x3f ]
|
||||
return ''.join(chr(x+0x20) if x else ' ' for x in v).rstrip()
|
||||
|
||||
TYPES={0x00:'int',0x01:'string',0x02:'blob',0x03:'struct',0x04:'list',
|
||||
0x05:'map',0x06:'union',0x07:'intlist',0x08:'objtype',0x09:'objid',0x0a:'float'}
|
||||
|
||||
def read_varint(buf,i):
|
||||
# Heat2 varint: 7 bits/byte, high bit = continue; first byte only 6 data bits (bit6=continue)
|
||||
b=buf[i]; i+=1
|
||||
val=b&0x3f
|
||||
if b&0x80:
|
||||
shift=6
|
||||
while True:
|
||||
b=buf[i]; i+=1
|
||||
val|=(b&0x7f)<<shift; shift+=7
|
||||
if not (b&0x80): break
|
||||
return val,i
|
||||
|
||||
def walk(buf, depth=0, i=0, end=None):
|
||||
if end is None: end=len(buf)
|
||||
pad=' '*depth
|
||||
while i < end:
|
||||
if i+4>end:
|
||||
print(f"{pad}[trailing {buf[i:end].hex()}]"); break
|
||||
tag=decode_tag(buf[i:i+3]); typ=buf[i+3]; i+=4
|
||||
tn=TYPES.get(typ,f'0x{typ:02x}')
|
||||
if typ==0x00: # int varint
|
||||
v,i=read_varint(buf,i); print(f"{pad}{tag} (int) = {v}")
|
||||
elif typ==0x01: # string: varint len + bytes (incl null)
|
||||
ln,i=read_varint(buf,i); s=buf[i:i+ln]; i+=ln
|
||||
print(f"{pad}{tag} (str) = {s.rstrip(bytes([0])).decode(errors='replace')!r}")
|
||||
elif typ==0x02: # blob
|
||||
ln,i=read_varint(buf,i); print(f"{pad}{tag} (blob[{ln}]) = {buf[i:i+ln].hex()}"); i+=ln
|
||||
elif typ==0x03: # struct: nested until 0x00 terminator
|
||||
print(f"{pad}{tag} (struct) {{")
|
||||
i=walk(buf,depth+1,i,end) # walk handles 0x00 term
|
||||
print(f"{pad}}}")
|
||||
else:
|
||||
# unknown/complex: dump remainder briefly and stop this level
|
||||
print(f"{pad}{tag} ({tn}) <complex; raw from here> {buf[i:min(i+24,end)].hex()}")
|
||||
# best-effort: skip nothing, bail to avoid misparse
|
||||
return end
|
||||
if i<end and buf[i]==0x00: # struct terminator
|
||||
i+=1; return i
|
||||
return i
|
||||
|
||||
def main():
|
||||
data=open(sys.argv[1],'rb').read()
|
||||
ln=struct.unpack('>I',data[0:4])[0]
|
||||
comp=struct.unpack('>H',data[6:8])[0]
|
||||
cmd=struct.unpack('>H',data[8:10])[0]
|
||||
err=struct.unpack('>H',data[10:12])[0]
|
||||
mtyp=data[12]
|
||||
print(f"== {sys.argv[1]} ==")
|
||||
print(f"Fire2 header: len={ln} component=0x{comp:04x} command=0x{cmd:04x} error=0x{err:04x} msgtype=0x{mtyp:02x}")
|
||||
print(f"payload ({len(data)-16} bytes):")
|
||||
walk(data[16:])
|
||||
|
||||
main()
|
||||
Reference in New Issue
Block a user