// Verify pedal map and speed limiter candidates by checking their xrefs
//@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 VerifyCandidates 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;
    }

    private void checkAddr(String label, long addr, ReferenceManager refMgr, FunctionManager fm, Listing listing) {
        Address a = currentProgram.getAddressFactory().getAddress(Long.toHexString(addr));
        List<Reference> refs = collect(refMgr, a);
        println(String.format("\n--- %s @ 0x%08X: %d xrefs ---", label, addr, refs.size()));
        for (Reference r : refs) {
            Address from = r.getFromAddress();
            Function func = fm.getFunctionContaining(from);
            String fname = (func != null) ? func.getName() : "<no-func>";
            Instruction ins = listing.getInstructionAt(from);
            String insStr = (ins != null) ? ins.toString() : "<no-ins>";
            println(String.format("  from 0x%s in [%s] type=%s  insn=%s",
                    from, fname, r.getReferenceType().getName(), insStr));
        }
    }

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

        // Candidates to verify
        println("================================================================");
        println("VERIFY: PEDAL MAP CANDIDATES");
        println("================================================================");
        checkAddr("pedal_curve_1 (00 08 17 27 6A B7 FF FF rep×8)", 0x80020290L, refMgr, fm, listing);
        checkAddr("pedal_curve_2 (00 02 04 08 14 30 7F FF rep×8)", 0x80020307L, refMgr, fm, listing);

        println("\n================================================================");
        println("VERIFY: SPEED LIMITER CANDIDATES");
        println("================================================================");
        // The FA-table (250 km/h)
        checkAddr("speed_table_FA (E8 03 FA 00 ...)",     0x80022084L, refMgr, fm, listing);
        // The C8-table (200 km/h)
        checkAddr("speed_table_C8 (D9 00 C8 00 ...)",     0x80021F36L, refMgr, fm, listing);
        // The DC-table (220 km/h)
        checkAddr("speed_table_DC (...DC 00 F8 34...)",   0x80022674L, refMgr, fm, listing);
        // Often there's a "limiter speed" near tachometer/odometer scaling area
        checkAddr("near 0x80022090 (E1 00 C8 00 06 00)",  0x80022090L, refMgr, fm, listing);
        checkAddr("near 0x80022630 (00 00 DC 00)",        0x80022630L, refMgr, fm, listing);

        // ===========================================================================
        // P0130 search: DTC code 0x0130 in calibration
        // ===========================================================================
        println("\n================================================================");
        println("P0130 (lambda sensor) — DTC code search");
        println("================================================================");
        // P0130 = 0x0130. May be stored as 0x0130 or 0x3001 (BE→LE swap).
        ghidra.program.model.address.AddressFactory af = currentProgram.getAddressFactory();
        ghidra.program.model.mem.Memory mem = currentProgram.getMemory();
        long calS = 0x80020000L, calE = 0x80040000L;
        int[] codes = {0x0130, 0x3001};
        for (int code : codes) {
            int found = 0;
            for (long off = calS; off < calE - 1; off++) {
                int lo = mem.getByte(af.getAddress(Long.toHexString(off))) & 0xFF;
                int hi = mem.getByte(af.getAddress(Long.toHexString(off+1))) & 0xFF;
                int val = (hi << 8) | lo;
                if (val == code) {
                    if (found < 8) {
                        StringBuilder ctx = new StringBuilder();
                        for (int k=-4; k<8; k++) {
                            try {
                                byte b = mem.getByte(af.getAddress(Long.toHexString(off+k)));
                                ctx.append(String.format("%02X ", b & 0xFF));
                            } catch (Exception e) {}
                        }
                        println(String.format("  code 0x%04X @ 0x%08X  ctx: %s", code, off, ctx.toString()));
                    }
                    found++;
                }
            }
            if (found > 0) println(String.format("    -> %d occurrences for 0x%04X", found, code));
        }

        println("\nDONE");
    }
}
