#!/usr/bin/env python3
"""Detached poller: watch the FirstPartyAuthTokenRetriever auth-request region for
ANY change (does FUT-entry ever enqueue an auth request?).

DoTick @0x146f199c0 (read at rip 0x146f199e3) polls *[0x1448a3b20]+0x4e98+0x08 every
frame and always sees 0 -> never requests a token. If entering FUT enqueues a request,
one of these bytes changes. Pure /proc/mem reads (no ptrace) so it survives across turns.
Logs every change with a timestamp to /tmp/auth_watch.log.
"""
import glob, os, struct, time

AUTHBLOCK_PP = 0x1448a3b20
SLOT_OFF = 0x4e98
SPAN = 0x40
ORIGINMGR_PP = 0x1448acf50
LOG = "/tmp/auth_watch.log"

def log(m):
    line = f"[{time.strftime('%H:%M:%S')}] {m}"
    print(line, flush=True)
    with open(LOG, "a") as f: f.write(line + "\n")

def find_pid():
    for d in glob.glob('/proc/[0-9]*'):
        try:
            if open(d + '/comm').read().strip() == 'FIFA17.exe':
                return int(os.path.basename(d))
        except Exception:
            pass
    return None

def main():
    open(LOG, "w").close()
    log("=== auth_watch start ===")
    pid = None; f = None; last = None; last_flag = None
    while True:
        p = find_pid()
        if p != pid:
            pid = p; last = None; last_flag = None
            if f: f.close(); f = None
            if pid:
                f = open(f"/proc/{pid}/mem", "rb")
                log(f"FIFA pid={pid}")
        if not pid:
            time.sleep(0.2); continue
        try:
            f.seek(AUTHBLOCK_PP); ab = struct.unpack('<Q', f.read(8))[0]
            f.seek(ORIGINMGR_PP); om = struct.unpack('<Q', f.read(8))[0]
            snap = None
            if ab:
                f.seek(ab + SLOT_OFF); snap = f.read(SPAN)
            flag = None
            if om:
                f.seek(om + 0x13); flag = f.read(1)[0]
        except Exception:
            time.sleep(0.05); continue
        if snap is not None and snap != last:
            hx = " ".join(f"{b:02x}" for b in snap)
            log(f"AUTH-REGION CHANGE @[{ab+SLOT_OFF:#x}]:")
            log(f"   {hx}")
            # decode the two 8-byte slots the retriever cares about
            s08 = struct.unpack('<Q', snap[0x08:0x10])[0]
            s10 = struct.unpack('<Q', snap[0x10:0x18])[0]
            log(f"   +0x08={s08:#x}  +0x10={s10:#x}  (nonzero = auth request enqueued!)")
            last = snap
        if flag is not None and flag != last_flag:
            log(f"m_isLoggedIn -> {flag}")
            last_flag = flag
        time.sleep(0.01)

if __name__ == "__main__":
    main()
