#!/usr/bin/env python3
"""Dump CardsDLL's 45-row route table from the ON-DISK PE. READ-ONLY, static.

The transfer-market analysis locates the table at .rdata 0x18021df80 as
{char*, char*} rows. This resolves VA->file offset properly through the PE section
table rather than assuming a single .text mapping, then prints every row so we can
see whether any route other than `tradePile` could own a trade-pile ITEM list.
"""
import struct, sys

DLL = "/mnt/games/FIFA 17/CardsDLL_Win64_retail.dll"
TABLE_VA = 0x18021DF80
MAX_ROWS = 64

pe = open(DLL, "rb").read()
e_lfanew = struct.unpack_from("<I", pe, 0x3C)[0]
assert pe[e_lfanew:e_lfanew + 4] == b"PE\0\0", "not a PE"
coff = e_lfanew + 4
nsec, opt_size = struct.unpack_from("<HH", pe, coff + 2), None
num_sections = struct.unpack_from("<H", pe, coff + 2)[0]
opt_size = struct.unpack_from("<H", pe, coff + 16)[0]
opt = coff + 20
magic = struct.unpack_from("<H", pe, opt)[0]
assert magic == 0x20B, "expected PE32+"
image_base = struct.unpack_from("<Q", pe, opt + 24)[0]
sec_off = opt + opt_size

sections = []
for i in range(num_sections):
    b = sec_off + i * 40
    name = pe[b:b + 8].rstrip(b"\0").decode("ascii", "replace")
    vsize, vaddr, rawsize, rawptr = struct.unpack_from("<IIII", pe, b + 8)
    sections.append((name, vaddr, vsize, rawptr, rawsize))

print("image_base=%#x sections=%d" % (image_base, num_sections))
for s in sections:
    print("  %-8s rva=%#010x vsize=%#x rawptr=%#010x rawsize=%#x" % s)


def va2off(va):
    rva = va - image_base
    for name, vaddr, vsize, rawptr, rawsize in sections:
        if vaddr <= rva < vaddr + max(vsize, rawsize):
            off = rva - vaddr + rawptr
            if off < len(pe):
                return off
    return None


def cstr(va, limit=96):
    off = va2off(va)
    if off is None:
        return None
    end = pe.find(b"\0", off, off + limit)
    if end < 0:
        return None
    try:
        return pe[off:end].decode("ascii")
    except UnicodeDecodeError:
        return None


base = va2off(TABLE_VA)
print("\nroute table VA %#x -> file offset %s" % (TABLE_VA, hex(base) if base else None))
assert base, "table VA did not resolve"

print("\n%-4s %-34s %s" % ("#", "field A", "field B"))
rows = 0
for i in range(MAX_ROWS):
    a_va, b_va = struct.unpack_from("<QQ", pe, base + i * 16)
    a, b = cstr(a_va), cstr(b_va)
    if a is None and b is None:
        print("-- table ends after %d rows --" % rows)
        break
    print("%-4d %-34s %s" % (i, repr(a), repr(b)))
    rows += 1
