"""q_cd_route_4 -- close the bound: how base+suffix are actually composed.

HYPOTHESIS
  Column 1 of each 48-byte row of the 125-row action table at 0x1802caa20 is an index
  into the 48-row URL-base table at 0x18021df80, and the URL is base-template then the
  class's own suffix/query builder.  Exactly four rows carry index 3 = "ut/%s/club".
  If that is right, the club route family is closed by the table.

  KNOWN SOFT SPOT, the reason for this query: no row carries index 43 = "ut/v2/%s/store",
  yet ut/v2/%s/store is a live-proven route.  So either the base index can be overridden
  at runtime, or the base table is indexed from somewhere else as well.  Until that is
  settled the "exactly four" bound is a strong default, not an absolute.

CONTROL
  The one RIP-relative reference to the action table found by scanning .text of the
  on-disk PE is at 0x180123f46.  Decompiling its container should show the row layout
  being read (name, base index, upper name, flag, factory) -- if the field it reads at
  +0x08 is NOT used as a table index, the base-index reading is wrong and every
  "exactly four" statement in this dimension has to be withdrawn.

  Second target: 0x18012ea74 is the only reference to the literal "club" at
  0x180221e40, which sits inside the FutStickerBookSearch literal block.  Printing it
  says whether the club-search class composes its own base (which would make the base
  table irrelevant for it) or uses the literal for something else entirely.

  Everything is printed with len(src) and in full; no absence claim rests on a
  truncated body.
"""
import traceback

try:
    def show(label, ea):
        f = fm.getFunctionContaining(addr(ea))
        print("=" * 78)
        print("### %s  @ %s  -> %s" % (label, hex(ea), f.getName() if f else "NO FUNC"))
        if f is None:
            return
        print("    entry %s  body %s" % (f.getEntryPoint(), f.getBody()))
        src = dec(f)
        print("    len(src) = %d" % len(src))
        print(src)
        try:
            cs = sorted(set(int(x.getEntryPoint().getOffset())
                            for x in f.getCallingFunctions(mon)))
            print("    callers: %s" % ", ".join(hex(c) for c in cs))
        except Exception as e:
            print("    callers: <%s>" % e)

    show("action-table reader (only ref to 0x1802caa20)", 0x180123f46)
    show("only ref to \"club\" literal 0x180221e40", 0x18012ea74)
    show("URL append helper used by every suffix builder", 0x180008020)
    show("StaffStats suffix thunk (raw addr 0x18012b080)", 0x18012b080)
except Exception:
    traceback.print_exc()
