#pragma once #define SPDLOG_COMPILED_LIB #include #include #include #include #undef cdecl // Workaround for Clang 14 CMake configure error. #include #include #include // 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 void asm_replace(std::uintptr_t a_from, std::size_t a_size) { asm_replace(a_from, a_size, reinterpret_cast(T::func)); } template void write_thunk_jump(std::uintptr_t a_src) { auto& trampoline = SKSE::GetTrampoline(); T::func = trampoline.write_branch(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 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(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(mem); trampoline.write_branch<5>(a_src, T::thunk); } }