// Decompile candidate cruise functions and look for accesses at offset 0x1E (cruise byte)
//@category Analysis

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.InstructionIterator;
import ghidra.program.model.listing.Listing;

public class AnalyzeCruiseFuncs extends GhidraScript {

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

        // Candidate functions accessing the cruise-config struct via pointer table
        long[] funcs = {
            0x800921c8L,   // FUN_800921c8
            0x80092236L,   // FUN_80092236
            0x80091edeL,   // FUN_80091ede
            0x80099484L,   // FUN_80099484
            0x800a03f2L,   // FUN_800a03f2
        };

        // Cruise-relevant offsets from struct base 0x80020230:
        // 0x2024E - 0x20230 = 0x1E (cruise flag)
        // 0x2025C - 0x20230 = 0x2C
        // 0x2025F - 0x20230 = 0x2F
        // 0x20264 - 0x20230 = 0x34
        // 0x20266 - 0x20230 = 0x36

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

        for (long f : funcs) {
            Address fAddr = af.getAddress(Long.toHexString(f));
            Function func = fm.getFunctionAt(fAddr);
            if (func == null) {
                println("Function not found at 0x" + Long.toHexString(f));
                continue;
            }
            println("\n================================================================");
            println(String.format("FUNCTION 0x%08X size=0x%X", f, func.getBody().getNumAddresses()));
            println("================================================================");

            // Scan instructions for offset accesses
            println("--- Instruction offsets accessed ---");
            InstructionIterator it = listing.getInstructions(func.getBody(), true);
            int count = 0;
            while (it.hasNext()) {
                Instruction ins = it.next();
                String s = ins.toString();
                // Look for pattern like ld.w d15,[a15]#0xXX or st.{w,h,b} [a15]0xXX
                // Print only instructions that have a # offset
                if (s.contains("#0x") || (s.contains("[a") && (s.contains("]0x") || s.contains("]#0x")))) {
                    println(String.format("  0x%s: %s", ins.getAddress(), s));
                    count++;
                    if (count > 80) { println("  ... (truncated)"); break; }
                }
            }

            // Decompile
            println("--- DECOMPILED ---");
            DecompileResults res = decomp.decompileFunction(func, 60, monitor);
            if (res.decompileCompleted()) {
                String code = res.getDecompiledFunction().getC();
                // Print first ~100 lines
                String[] lines = code.split("\n");
                int maxLines = Math.min(100, lines.length);
                for (int i = 0; i < maxLines; i++) {
                    println(lines[i]);
                }
                if (lines.length > maxLines) println("... (truncated, " + lines.length + " total)");
            } else {
                println("Decompile failed: " + res.getErrorMessage());
            }
        }

        decomp.dispose();
        println("\n================================================================");
        println("DONE");
    }
}
