#!/usr/bin/env python3
"""Read the FIFA 17 TRADING gate byte out of the live client. READ-ONLY.

Extends tools/gate_byte_probe.py with vtable slot +0x270 (IS_TRADING_ENABLED,
displacement 0x1fd2e) plus the two pile-size dwords, which the transfer-market
analysis names as the market screen's CardsDLL-supplied inputs.

Opens /proc/<pid>/mem O_RDONLY and preads. Nothing here can write.
"""
import os, struct

pid = None
for d in os.listdir('/proc'):
    if d.isdigit():
        try:
            if open('/proc/%s/comm' % d).read().strip() == 'FIFA17.exe':
                pid = int(d)
                break
        except Exception:
            pass
assert pid, "FIFA17.exe not running"

base = None
for ln in open('/proc/%d/maps' % pid):
    if 'CardsDLL' in ln:
        base = int(ln.split('-')[0], 16)
assert base, "CardsDLL not mapped (client has not reached Ultimate Team)"
slide = base - 0x180000000

fd = os.open('/proc/%d/mem' % pid, os.O_RDONLY)


def rd(va, n):
    return os.pread(fd, n, va)


# Control: the FNV atom-hash prologue must match the on-disk PE before any other
# address is trusted.
pe = open('/mnt/games/FIFA 17/CardsDLL_Win64_retail.dll', 'rb').read()


def f(va):
    return va - 0x180000000 - 0x1000 + 0x400


ok = pe[f(0x180180d00):f(0x180180d00) + 32] == rd(0x180180d00 + slide, 32)
print("pid=%d slide=%#x FNV control=%s" % (pid, slide, "MATCH" if ok else "MISMATCH"))
assert ok, "slide not proven; refusing to read further"

obj = struct.unpack('<Q', rd(0x1802e6398 + slide, 8))[0]
vt = struct.unpack('<Q', rd(obj, 8))[0]
print("model=%#x vtable(static)=%#x" % (obj, vt - slide))

SLOTS = [
    (0x270, 'IS_TRADING_ENABLED   '),
    (0x2b0, 'IS_FRIENDLY_SEASON   '),
    (0x2c8, 'IS_DRAFT_MODE        '),
    (0x2e0, 'packOpeningAnimation '),
]
print("\n-- gate bytes decoded from their accessor stubs --")
for off, name in SLOTS:
    slot = struct.unpack('<Q', rd(vt + off, 8))[0]
    stub = rd(slot, 8)
    if stub[:3] == b'\x0f\xb6\x81':
        disp = struct.unpack('<I', stub[3:7])[0]
        val = rd(obj + disp, 1)[0]
        print("  slot +%#05x  %s disp=%#x  VALUE=%d" % (off, name, disp, val))
    else:
        print("  slot +%#05x  %s NOT a movzx stub: %s" % (off, name, stub.hex()))

print("\n-- market screen inputs --")
for disp, name in [(0x1fd1c, 'TRADE_PILE_SIZE'), (0x1fd20, 'watchListSize  '),
                   (0x1fd2e, 'tradingEnabled '), (0x1fd2f, 'storeEnabled   ')]:
    print("  model+%#x  %s = %d" % (disp, name, rd(obj + disp, 1)[0]))

os.close(fd)
