// Hooks.cpp -- Inline hooking (5-byte JMP detour with trampoline)
#include "Hooks.h"
#include "Logger.h"

static BYTE* FollowJumps(BYTE* p)
{
    int limit = 8;
    while (p[0] == 0xE9 && limit-- > 0)
    {
        INT32 offset = *(INT32*)(p + 1);
        BYTE* next = p + 5 + offset;
        LogFmtIf("  [FollowJumps] E9 @ 0x%08X -> 0x%08X", (DWORD)p, (DWORD)next);
        p = next;
    }
    return p;
}

void InstallHook(Hook* hook, void* target, void* replacement, int copySize)
{
    BYTE* real = FollowJumps((BYTE*)target);
    LogFmtIf("  [InstallHook] target=0x%08X resolved=0x%08X first_byte=0x%02X copySize=%d",
        (DWORD)target, (DWORD)real, real[0], copySize);

    hook->original = real;
    hook->copySize = copySize;
    memcpy(hook->savedBytes, real, copySize);

    // Allocate trampoline: copySize bytes + 5 byte jump back
    hook->trampoline = VirtualAlloc(NULL, copySize + 8, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    if (hook->trampoline)
    {
        BYTE* t = (BYTE*)hook->trampoline;
        memcpy(t, hook->savedBytes, copySize);
        t[copySize] = 0xE9;
        *(DWORD*)(t + copySize + 1) = ((DWORD)real + copySize) - ((DWORD)t + copySize + 5);
    }

    DWORD oldProt;
    VirtualProtect(real, copySize, PAGE_EXECUTE_READWRITE, &oldProt);
    BYTE* p = (BYTE*)real;
    p[0] = 0xE9;
    *(DWORD*)(p + 1) = (DWORD)replacement - (DWORD)real - 5;
    // NOP out any leftover bytes between our JMP and the next instruction
    for (int i = 5; i < copySize; i++) p[i] = 0x90;
    VirtualProtect(real, copySize, oldProt, &oldProt);
    FlushInstructionCache(GetCurrentProcess(), real, copySize);
    if (hook->trampoline)
        FlushInstructionCache(GetCurrentProcess(), hook->trampoline, copySize + 8);
}