78 lines
2.3 KiB
C++
78 lines
2.3 KiB
C++
#pragma once
|
|
|
|
#define SPDLOG_COMPILED_LIB
|
|
|
|
#include <RE/Skyrim.h>
|
|
#include <SKSE/SKSE.h>
|
|
#include <REL/Relocation.h>
|
|
|
|
#include <ShlObj_core.h>
|
|
#undef cdecl // Workaround for Clang 14 CMake configure error.
|
|
|
|
#include <spdlog/sinks/basic_file_sink.h>
|
|
#include <spdlog/sinks/msvc_sink.h>
|
|
#include <xbyak/xbyak.h>
|
|
|
|
// Compatible declarations with other sample projects.
|
|
#define DLLEXPORT __declspec(dllexport)
|
|
|
|
using namespace std::literals;
|
|
using namespace REL::literals;
|
|
|
|
namespace logger = SKSE::log;
|
|
|
|
namespace SKSE::stl
|
|
{
|
|
void asm_replace(std::uintptr_t a_from, std::size_t a_size, std::uintptr_t a_to);
|
|
|
|
template <class T>
|
|
void asm_replace(std::uintptr_t a_from, std::size_t a_size)
|
|
{
|
|
asm_replace(a_from, a_size, reinterpret_cast<std::uintptr_t>(T::func));
|
|
}
|
|
|
|
template <class T, std::size_t N = 5>
|
|
void write_thunk_jump(std::uintptr_t a_src)
|
|
{
|
|
auto& trampoline = SKSE::GetTrampoline();
|
|
T::func = trampoline.write_branch<N>(a_src, T::thunk);
|
|
}
|
|
|
|
// Detours a whole function: the first BYTES bytes are copied into the trampoline and followed by
|
|
// a jump back into the original, so T::func is a callable stand-in for the untouched function.
|
|
// BYTES must cover complete, position-independent instructions and be at least 5.
|
|
// Costs 14 bytes of trampoline for the branch island plus BYTES + 14 for the stub.
|
|
// Thanks Nukem and po3, via Widescreen Scale Removed by SkyHorizon (GPL-3.0).
|
|
template <class T, std::size_t BYTES>
|
|
void hook_function_prologue(std::uintptr_t a_src)
|
|
{
|
|
static_assert(BYTES >= 5, "not enough room for a jump");
|
|
|
|
// Xbyak::CodeGenerator has a member function std() - the STD instruction - which hides the
|
|
// namespace inside the class body, hence the leading :: on every std:: name below
|
|
struct Stub : Xbyak::CodeGenerator
|
|
{
|
|
Stub(std::uintptr_t a_target, std::size_t a_size)
|
|
{
|
|
for (::std::size_t i = 0; i < a_size; ++i) {
|
|
db(*reinterpret_cast<const ::std::uint8_t*>(a_target + i));
|
|
}
|
|
|
|
jmp(ptr[rip]);
|
|
dq(a_target + a_size);
|
|
}
|
|
};
|
|
|
|
Stub stub(a_src, BYTES);
|
|
stub.ready();
|
|
|
|
auto& trampoline = SKSE::GetTrampoline();
|
|
auto* mem = trampoline.allocate(stub.getSize());
|
|
std::memcpy(mem, stub.getCode(), stub.getSize());
|
|
|
|
// Publish the stand-in before redirecting the function, the thunk needs it on its first call
|
|
T::func = reinterpret_cast<std::uintptr_t>(mem);
|
|
trampoline.write_branch<5>(a_src, T::thunk);
|
|
}
|
|
}
|