// Find what selects between FUN_80076e2c and FUN_800770cc (likely hardware variant byte)
//@category Analysis

import java.util.ArrayList;
import java.util.List;
import ghidra.app.script.GhidraScript;
import ghidra.app.decompiler.DecompInterface;
import ghidra.app.decompiler.DecompileResults;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressFactory;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.FunctionManager;
import ghidra.program.model.listing.Instruction;
import ghidra.program.model.listing.Listing;
import ghidra.program.model.symbol.Reference;
import ghidra.program.model.symbol.ReferenceIterator;
import ghidra.program.model.symbol.ReferenceManager;

public class FindWriterSelector extends GhidraScript {

    private List<Reference> collect(ReferenceManager refMgr, Address a) {
        List<Reference> list = new ArrayList<>();
        ReferenceIterator it = refMgr.getReferencesTo(a);
        while (it.hasNext()) list.add(it.next());
        return list;
    }

    @Override
    public void run() throws Exception {
        AddressFactory af = currentProgram.getAddressFactory();
        FunctionManager fm = currentProgram.getFunctionManager();
        Listing listing = currentProgram.getListing();
        ReferenceManager refMgr = currentProgram.getReferenceManager();

        DecompInterface decomp = new DecompInterface();
        decomp.openProgram(currentProgram);

        long[] funcs = { 0x80076E2CL, 0x800770CCL };
        String[] labels = { "writer_A_80076e2c", "writer_B_800770cc" };

        for (int idx = 0; idx < funcs.length; idx++) {
            Address fa = af.getAddress(Long.toHexString(funcs[idx]));
            println("\n================================================================");
            println("CALLERS of " + labels[idx] + " (FUN_" + Long.toHexString(funcs[idx]) + ")");
            println("================================================================");

            List<Reference> callers = collect(refMgr, fa);
            for (Reference r : callers) {
                Address from = r.getFromAddress();
                Function caller = fm.getFunctionContaining(from);
                String cname = (caller != null) ? caller.getName() : "<no-func>";
                String cent = (caller != null) ? "0x"+caller.getEntryPoint() : "n/a";
                Instruction ins = listing.getInstructionAt(from);
                println(String.format("  from 0x%s in [%s @ %s]: %s",
                        from, cname, cent, ins != null ? ins.toString() : "?"));
                // Show 20 bytes of context before call
                long fromOff = from.getOffset();
                println("    --- context before call ---");
                for (long off = fromOff - 0x30; off < fromOff + 0x4; off++) {
                    Address a = af.getAddress(Long.toHexString(off));
                    Instruction i = listing.getInstructionAt(a);
                    if (i != null) {
                        String mark = (off == fromOff) ? "  <<<<< CALL" : "";
                        println(String.format("      0x%s: %s%s", a, i.toString(), mark));
                        off += i.getLength() - 1;
                    }
                }
            }

            // Also: decompile caller function if any
            if (callers.size() > 0) {
                Address fromAddr = callers.get(0).getFromAddress();
                Function caller = fm.getFunctionContaining(fromAddr);
                if (caller != null) {
                    println("\n--- DECOMPILED CALLER " + caller.getName() + " ---");
                    DecompileResults res = decomp.decompileFunction(caller, 60, monitor);
                    if (res.decompileCompleted()) {
                        String code = res.getDecompiledFunction().getC();
                        // Print first ~80 lines
                        String[] lines = code.split("\n");
                        for (int i = 0; i < Math.min(80, lines.length); i++) {
                            println(lines[i]);
                        }
                    }
                }
            }
        }

        decomp.dispose();
        println("\nDONE");
    }
}
