#!/usr/bin/env python3
"""Scan ghidra/decomp.c (Ghidra decompiled C) for the PlainDamgeDealerComponent
populate fingerprint: the same pointer var written at +0x10 (float, damageAmount),
+0x14 (damageType) and +0x15 (isMelee). Also reports HitEventInfo (+0x30 damageAmount)
and any function assigning a float constant to a +0x10 offset near those.
"""
import re, sys

BLOCK = re.compile(r"^// ==== (.+?) @ \+0x([0-9a-f]+) ====$")


def blocks(path):
    name = None
    addr = None
    buf = []
    for line in open(path, errors="replace"):
        m = BLOCK.match(line.rstrip("\n"))
        if m:
            if name is not None:
                yield name, addr, "".join(buf)
            name, addr, buf = m.group(1), m.group(2), []
        else:
            buf.append(line)
    if name is not None:
        yield name, addr, "".join(buf)


# var written at a given byte offset: e.g. *(float *)(lVar5 + 0x10) = ...
def vars_writing_offset(code, off):
    pat = re.compile(r"\*\([^)]*\)\(([A-Za-z_][A-Za-z0-9_]*) \+ 0x%x\) =" % off)
    return set(pat.findall(code))


def main():
    path = sys.argv[1] if len(sys.argv) > 1 else "/home/downloadpizza/sand_tools/ghidra/decomp.c"
    hits = []
    for name, addr, code in blocks(path):
        v10 = vars_writing_offset(code, 0x10)
        v14 = vars_writing_offset(code, 0x14)
        v15 = vars_writing_offset(code, 0x15)
        common = v10 & v14 & v15
        if common:
            # confirm the +0x10 write is a float
            for v in common:
                if re.search(r"\*\(float \*\)\(%s \+ 0x10\) =" % re.escape(v), code):
                    hits.append((name, addr, v))
                    break
    print("=== STRICT: +0x10 float, +0x14, +0x15 same ptr ===")
    for name, addr, v in hits:
        print("  +0x%s  var %s  %s" % (addr, v, name))
    print("strict total:", len(hits))

    # LOOSE: float@+0x10 and byte@+0x15 on same ptr (isMelee + damageAmount)
    loose = []
    for name, addr, code in blocks(path):
        common = vars_writing_offset(code, 0x10) & vars_writing_offset(code, 0x15)
        for v in common:
            if re.search(r"\*\(float \*\)\(%s \+ 0x10\) =" % re.escape(v), code):
                loose.append((name, addr, v)); break
    print("\n=== LOOSE: +0x10 float & +0x15 same ptr ===")
    for name, addr, v in loose:
        print("  +0x%s  var %s  %s" % (addr, v, name))
    print("loose total:", len(loose))


if __name__ == "__main__":
    main()
