// CrashHandler.cpp -- Vectored exception handler for crash logging
//
// Catches fatal exceptions (AV, stack overflow, illegal instruction, etc.)
// and writes a detailed crash report to the log including register dump,
// faulting module, and a raw stack trace.
#include "CrashHandler.h"
#include "Logger.h"

LONG WINAPI CrashHandler(EXCEPTION_POINTERS* ExceptionInfo)
{
    DWORD code = ExceptionInfo->ExceptionRecord->ExceptionCode;

    if (code == EXCEPTION_BREAKPOINT || code == EXCEPTION_SINGLE_STEP)
        return EXCEPTION_CONTINUE_SEARCH;

    bool isCrash = false;
    const char* exceptionName = "UNKNOWN EXCEPTION";

    switch (code)
    {
    case EXCEPTION_ACCESS_VIOLATION:
        exceptionName = "ACCESS VIOLATION (C0000005)";
        isCrash = true;
        if (ExceptionInfo->ExceptionRecord->ExceptionInformation[0] == 0 &&
            ExceptionInfo->ExceptionRecord->ExceptionInformation[1] == 0xFFFFFFFF)
            return EXCEPTION_CONTINUE_SEARCH;
        break;
    case EXCEPTION_STACK_OVERFLOW:
        exceptionName = "STACK OVERFLOW (C00000FD)";
        isCrash = true;
        break;
    case EXCEPTION_ILLEGAL_INSTRUCTION:
        exceptionName = "ILLEGAL INSTRUCTION (C000001D)";
        isCrash = true;
        break;
    case EXCEPTION_PRIV_INSTRUCTION:
        exceptionName = "PRIVILEGED INSTRUCTION (C0000096)";
        isCrash = true;
        break;
    case EXCEPTION_INT_DIVIDE_BY_ZERO:
        exceptionName = "INTEGER DIVIDE BY ZERO (C0000094)";
        isCrash = true;
        break;
    case EXCEPTION_FLT_DIVIDE_BY_ZERO:
        exceptionName = "FLOAT DIVIDE BY ZERO (C000008E)";
        isCrash = true;
        break;
    case 0xE06D7363:
        exceptionName = "C++ EXCEPTION (E06D7363)";
        isCrash = true;
        break;
    }

    if (!isCrash) return EXCEPTION_CONTINUE_SEARCH;

    OpenLogFile();

    if (g_logFile)
    {
        PEXCEPTION_RECORD record = ExceptionInfo->ExceptionRecord;
        PCONTEXT ctx = ExceptionInfo->ContextRecord;

        Log("=================================================");
        LogFmt("!!! %s DETECTED !!!", exceptionName);
        LogFmt("Exception Address: 0x%08X", (DWORD)record->ExceptionAddress);

        if (code == EXCEPTION_ACCESS_VIOLATION && record->NumberParameters >= 2)
        {
            DWORD type = (DWORD)record->ExceptionInformation[0];
            DWORD addr = (DWORD)record->ExceptionInformation[1];
            const char* typeStr = (type == 0) ? "READ from" :
                (type == 1) ? "WRITE to" :
                (type == 8) ? "DEP violation at" : "EXECUTE at";
            LogFmt("Crash Reason     : Attempted to %s address 0x%08X", typeStr, addr);
        }

        HMODULE hModule = NULL;
        if (GetModuleHandleExA(
            GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
            GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
            (LPCSTR)record->ExceptionAddress, &hModule))
        {
            char modName[MAX_PATH] = { 0 };
            GetModuleFileNameA(hModule, modName, MAX_PATH);
            DWORD offset = (DWORD)record->ExceptionAddress - (DWORD)hModule;
            LogFmt("Crashed Module   : %s", modName);
            LogFmt("Module Base      : 0x%08X", (DWORD)hModule);
            LogFmt("Module Offset    : +0x%08X", offset);
        }
        else Log("Crashed Module   : Unknown");

        Log("-------------------------------------------------");
        Log("Register Dump:");
        LogFmt("  EAX=0x%08X  EBX=0x%08X  ECX=0x%08X  EDX=0x%08X",
            ctx->Eax, ctx->Ebx, ctx->Ecx, ctx->Edx);
        LogFmt("  ESI=0x%08X  EDI=0x%08X  ESP=0x%08X  EBP=0x%08X",
            ctx->Esi, ctx->Edi, ctx->Esp, ctx->Ebp);
        LogFmt("  EIP=0x%08X  EFL=0x%08X", ctx->Eip, ctx->EFlags);

        Log("-------------------------------------------------");
        Log("Stack Trace (top 16 frames):");
        DWORD* stack = (DWORD*)ctx->Esp;
        for (int i = 0; i < 16; i++)
        {
            DWORD frameAddr = 0;
            __try { frameAddr = stack[i]; }
            __except (EXCEPTION_EXECUTE_HANDLER) { break; }

            HMODULE hMod = NULL;
            char modName[MAX_PATH] = { 0 };
            if (GetModuleHandleExA(
                GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
                GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
                (LPCSTR)(DWORD_PTR)frameAddr, &hMod))
            {
                GetModuleFileNameA(hMod, modName, MAX_PATH);
                char* fname = strrchr(modName, '\\');
                DWORD offset = frameAddr - (DWORD)hMod;
                LogFmt("[%02d] 0x%08X  %s+0x%08X",
                    i, frameAddr, fname ? fname + 1 : modName, offset);
            }
            else LogFmt("[%02d] 0x%08X  (unknown)", i, frameAddr);
        }

        Log("=================================================");
        fflush(g_logFile);
    }

    return EXCEPTION_CONTINUE_SEARCH;
}
