/*
    Purpose:
    Author: <NAME>
    License: All rights reserved (2015 - 2019); licensed under the MIT license.
*/
#include <Scriptability.hpp>
#include "ArchHook.hpp"
#include "ArchCommon.hpp"
#include "ArchStub.hpp"
#include "MachineCode/ArchStubCodegen.hpp"
#include "ArchTrampoline.hpp"

typedef struct HookState
{
    Trampoline_h trampoline;
    Function_h function;
} * HookState_p;

serror_t API::ScriptabilityHook(NativeFunctionPointer_p address, StubTrigger & trigger, Hook_h & handle)
{
    serror_t er;
    NativeFunctionPointer_p stubGenExecStub;
    NativeFunctionPointer_p trampStub;
    Trampoline_h trampHandle;
    Function_h funcHandle;
    HookState_p state;

    state = ScriptabilityHeapAlloc<HookState_p>(sizeof(HookState));
    if (!state)
        return kScriptErrorOutOfMemory;

    er = ScriptabilityStubGen(trigger, true, nullptr, stubGenExecStub, HandleUnion(static_cast<Hook_h>(state)), funcHandle);
    if (SCRIPT_ERROR(er))
    {
        ScriptabilityHeapFree<HookState_p>(state);
        return er;
    }

    er = ScriptabilityTrampolineEx(address, stubGenExecStub, ScriptabilityX64JmpType::SCRIPTABILITY_PATCH_PUSH_RAX_JMP, trampStub, trampHandle);
    if (SCRIPT_ERROR(er))
    {
        ScriptabilityHeapFree<HookState_p>(state);
        return er;
    }

    reinterpret_cast<StubgenStub_p>(funcHandle)->returnAddress = trampStub;

    state->function   = funcHandle;
    state->trampoline = trampHandle;

    handle = state;
    return kScriptStatusOkay;
}

serror_t API::ScriptabilityHookFree(Hook_h handle)
{
    HookState_p state;

    if (!handle)
        return kScriptErrorIllegalParameter;

    state = reinterpret_cast<HookState_p>(handle);

    ScriptabilityFreeStubgen(state->function);
    ScriptabilityFreeTramp(state->trampoline);
    ScriptabilityHeapFree<HookState_p>(state);

    return kScriptStatusOkay;
}