CommonLibSSE NG
REL Namespace Reference

Namespaces

 detail
 
 literals
 

Classes

class  IDDatabase
 
class  ID
 
class  RelocationID
 
class  VariantID
 
class  Segment
 
class  Module
 
class  Offset
 
class  VariantOffset
 
class  Relocation
 
class  Version
 

Functions

template<stl::nttp::string S>
constexpr auto make_pattern () noexcept
 
template<class F , class... Args>
std::invoke_result_t< F, Args... > invoke (F &&a_func, Args &&... a_args) noexcept(std::is_nothrow_invocable_v< F, Args... >) requires(std
 
void safe_write (std::uintptr_t a_dst, const void *a_src, std::size_t a_count)
 
template<std::integral T>
void safe_write (std::uintptr_t a_dst, const T &a_data)
 
template<class T >
void safe_write (std::uintptr_t a_dst, std::span< T > a_data)
 
void safe_fill (std::uintptr_t a_dst, std::uint8_t a_value, std::size_t a_count)
 
template<class T >
SKYRIM_ADDRRelocate ([[maybe_unused]] T &&a_seAndVR, [[maybe_unused]] T &&a_ae) noexcept
 
template<class T >
SKYRIM_RELRelocate ([[maybe_unused]] T a_se, [[maybe_unused]] T a_ae, [[maybe_unused]] T a_vr) noexcept
 
template<class Fn , class... Args>
detail::RelocateVirtualHelper< Fn >::return_type RelocateVirtual ([[maybe_unused]] std::ptrdiff_t a_seAndAEVtableOffset, [[maybe_unused]] std::ptrdiff_t a_vrVtableOffset, [[maybe_unused]] std::ptrdiff_t a_seAndAEVtableIndex, [[maybe_unused]] std::ptrdiff_t a_vrVtableIndex, typename detail::RelocateVirtualHelper< Fn >::this_type *a_self, Args &&... a_args)
 
template<class Fn , class... Args>
detail::RelocateVirtualHelper< Fn >::return_type RelocateVirtual (std::ptrdiff_t a_seAndAEVtableIndex, std::ptrdiff_t a_vrVtableIndex, typename detail::RelocateVirtualHelper< Fn >::this_type *a_self, Args &&... a_args)
 
template<class T , class This >
T & RelocateMember (This *a_self, std::ptrdiff_t a_seAndAE, std::ptrdiff_t a_vr)
 
template<class T , class This >
T & RelocateMember (This *a_self, std::ptrdiff_t offset)
 
template<class T , class This >
T & RelocateMemberIf (bool condition, This *a_self, std::ptrdiff_t a, std::ptrdiff_t b)
 
template<class T , class This >
T & RelocateMemberIfNewer (Version v, This *a_self, std::ptrdiff_t older, std::ptrdiff_t newer)
 
constexpr bool operator== (const Version &a_lhs, const Version &a_rhs) noexcept
 
constexpr std::strong_ordering operator<=> (const Version &a_lhs, const Version &a_rhs) noexcept
 
std::optional< VersionGetFileVersion (stl::zwstring a_filename)
 

Variables

constexpr std::uint8_t NOP = 0x90
 
constexpr std::uint8_t NOP2 [] = { 0x66, 0x90 }
 
constexpr std::uint8_t NOP3 [] = { 0x0F, 0x1F, 0x00 }
 
constexpr std::uint8_t NOP4 [] = { 0x0F, 0x1F, 0x40, 0x00 }
 
constexpr std::uint8_t NOP5 [] = { 0x0F, 0x1F, 0x44, 0x00, 0x00 }
 
constexpr std::uint8_t NOP6 [] = { 0x66, 0x0F, 0x1F, 0x44, 0x00, 0x00 }
 
constexpr std::uint8_t NOP7 [] = { 0x0F, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00 }
 
constexpr std::uint8_t NOP8 [] = { 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00 }
 
constexpr std::uint8_t NOP9 [] = { 0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00 }
 
constexpr std::uint8_t JMP8 = 0xEB
 
constexpr std::uint8_t JMP32 = 0xE9
 
constexpr std::uint8_t RET = 0xC3
 
constexpr std::uint8_t INT3 = 0xCC
 

Function Documentation

◆ GetFileVersion()

std::optional<Version> REL::GetFileVersion ( stl::zwstring  a_filename)

◆ invoke()

template<class F , class... Args>
std::invoke_result_t<F, Args...> REL::invoke ( F &&  a_func,
Args &&...  a_args 
)
noexcept

◆ make_pattern()

template<stl::nttp::string S>
constexpr auto REL::make_pattern ( )
constexprnoexcept

◆ operator<=>()

constexpr std::strong_ordering REL::operator<=> ( const Version a_lhs,
const Version a_rhs 
)
constexprnoexcept

◆ operator==()

constexpr bool REL::operator== ( const Version a_lhs,
const Version a_rhs 
)
constexprnoexcept

◆ Relocate() [1/2]

template<class T >
SKYRIM_ADDR T REL::Relocate ( [[maybe_unused] ] T &&  a_seAndVR,
[[maybe_unused] ] T &&  a_ae 
)
noexcept

Return the correct value of two choices between SE/VR, and AE versions of Skyrim.

This is commonly used to select between relative offsets within a function, when hooking a call instruction. In such cases the function can be identified by its Address Library ID, but the offset within the function may vary between Skyrim versions. This selection is made at runtime, allowing the same compiled code to run in multiple versions of Skyrim.

Template Parameters
Tthe type of value to return.
Parameters
a_seAndVRthe value to use for SE and VR.
a_aethe value to use for AE.
Returns
Either a_seAndVR if the current runtime is Skyrim SE or VR, or a_ae if the runtime is AE.

◆ Relocate() [2/2]

template<class T >
SKYRIM_REL T REL::Relocate ( [[maybe_unused] ] T  a_se,
[[maybe_unused] ] T  a_ae,
[[maybe_unused] ] T  a_vr 
)
noexcept

Return the correct value of two choices between SE, AE, and VR versions of Skyrim.

This is commonly used to select between relative offsets within a function, when hooking a call instruction. In such cases the function can be identified by its Address Library ID, but the offset within the function may vary between Skyrim versions. This selection is made at runtime, allowing the same compiled code to run in multiple versions of Skyrim.

Template Parameters
Tthe type of value to return.
Parameters
a_sethe value to use for SE.
a_aethe value to use for AE.
a_vrthe value to use for VR.
Returns
Either a_se if the current runtime is Skyrim SE, or a_ae if the runtime is AE, or a_vr if running Skyrim VR.

◆ RelocateMember() [1/2]

template<class T , class This >
T& REL::RelocateMember ( This *  a_self,
std::ptrdiff_t  a_seAndAE,
std::ptrdiff_t  a_vr 
)
inline

Gets a member variable in a cross-platform way, using runtime-specific memory offsets.

This function handles the variant memory structures used in Skyrim VR as compared to versions of SE. It allows a memory offset relative to the object's base address for SE (and AE) and a separate one for VR. This simplifies the process of creating functions to get member variables that are at different offsets in different runtimes from a single build.

Template Parameters
Tthe type of the member being accessed.
Thisthe type of the target object that has the member.
Parameters
a_selfthe target object that has the member.
a_seAndAEthe memory offset of the member in Skyrim SE and AE.
a_vrthe memory offset of the member in Skyrim VR.
Returns
A reference to the member.

◆ RelocateMember() [2/2]

template<class T , class This >
T& REL::RelocateMember ( This *  a_self,
std::ptrdiff_t  offset 
)
inline

◆ RelocateMemberIf()

template<class T , class This >
T& REL::RelocateMemberIf ( bool  condition,
This *  a_self,
std::ptrdiff_t  a,
std::ptrdiff_t  b 
)
inline

◆ RelocateMemberIfNewer()

template<class T , class This >
T& REL::RelocateMemberIfNewer ( Version  v,
This *  a_self,
std::ptrdiff_t  older,
std::ptrdiff_t  newer 
)
inline

◆ RelocateVirtual() [1/2]

template<class Fn , class... Args>
detail::RelocateVirtualHelper<Fn>::return_type REL::RelocateVirtual ( [[maybe_unused] ] std::ptrdiff_t  a_seAndAEVtableOffset,
[[maybe_unused] ] std::ptrdiff_t  a_vrVtableOffset,
[[maybe_unused] ] std::ptrdiff_t  a_seAndAEVtableIndex,
[[maybe_unused] ] std::ptrdiff_t  a_vrVtableIndex,
typename detail::RelocateVirtualHelper< Fn >::this_type *  a_self,
Args &&...  a_args 
)
inline

Invokes a virtual function in a cross-platform way where the vtable structure is variant across AE/SE and VR runtimes.

Some classes in Skyrim VR add new virtual functions in the middle of the vtable structure, which makes it ABI-incompatible with AE/SE. A naive virtual function call, therefore, cannot work across all runtimes without the plugin being recompiled specifically for VR. This call works with types which have variant vtables to allow a non-virtual function definition to be created in the virtual function's place, and to have that call dynamically lookup the correct function based on the vtable structure expected in the current runtime.

Template Parameters
Fnthe type of the function being called.
Argsthe types of the arguments being passed.
Parameters
a_seAndAEVtableOffsetthe offset from the this pointer to the vtable with the virtual function in SE/AE.
a_vrVtableIndexthe offset from the this pointer to the vtable with the virtual function in VR.
a_seAndAEVtableIndexthe index of the function in the class' vtable in SE and AE.
a_vrVtableIndexthe index of the function in the class' vtable in VR.
a_selfthe this argument for the call.
a_argsthe remaining arguments for the call, if any.
Returns
The result of the function call.

◆ RelocateVirtual() [2/2]

template<class Fn , class... Args>
detail::RelocateVirtualHelper<Fn>::return_type REL::RelocateVirtual ( std::ptrdiff_t  a_seAndAEVtableIndex,
std::ptrdiff_t  a_vrVtableIndex,
typename detail::RelocateVirtualHelper< Fn >::this_type *  a_self,
Args &&...  a_args 
)
inline

Invokes a virtual function in a cross-platform way where the vtable structure is variant across AE/SE and VR runtimes.

Some classes in Skyrim VR add new virtual functions in the middle of the vtable structure, which makes it ABI-incompatible with AE/SE. A naive virtual function call, therefore, cannot work across all runtimes without the plugin being recompiled specifically for VR. This call works with types which have variant vtables to allow a non-virtual function definition to be created in the virtual function's place, and to have that call dynamically lookup the correct function based on the vtable structure expected in the current runtime.

This call assumes the vtable to be used is the one at offset 0, i.e. it invokes a virtual function either on the first parent class or the current class.

Template Parameters
Fnthe type of the function being called.
Argsthe types of the arguments being passed.
Parameters
a_seAndAEVtableIndexthe index of the function in the class' vtable in SE and AE.
a_vrVtableIndexthe index of the function in the class' vtable in VR.
a_selfthe this argument for the call.
a_argsthe remaining arguments for the call, if any.
Returns
The result of the function call.

◆ safe_fill()

void REL::safe_fill ( std::uintptr_t  a_dst,
std::uint8_t  a_value,
std::size_t  a_count 
)

◆ safe_write() [1/3]

template<std::integral T>
void REL::safe_write ( std::uintptr_t  a_dst,
const T &  a_data 
)

◆ safe_write() [2/3]

void REL::safe_write ( std::uintptr_t  a_dst,
const void *  a_src,
std::size_t  a_count 
)

◆ safe_write() [3/3]

template<class T >
void REL::safe_write ( std::uintptr_t  a_dst,
std::span< T >  a_data 
)

Variable Documentation

◆ INT3

constexpr std::uint8_t REL::INT3 = 0xCC
inlineconstexpr

◆ JMP32

constexpr std::uint8_t REL::JMP32 = 0xE9
inlineconstexpr

◆ JMP8

constexpr std::uint8_t REL::JMP8 = 0xEB
inlineconstexpr

◆ NOP

constexpr std::uint8_t REL::NOP = 0x90
inlineconstexpr

◆ NOP2

constexpr std::uint8_t REL::NOP2[] = { 0x66, 0x90 }
inlineconstexpr

◆ NOP3

constexpr std::uint8_t REL::NOP3[] = { 0x0F, 0x1F, 0x00 }
inlineconstexpr

◆ NOP4

constexpr std::uint8_t REL::NOP4[] = { 0x0F, 0x1F, 0x40, 0x00 }
inlineconstexpr

◆ NOP5

constexpr std::uint8_t REL::NOP5[] = { 0x0F, 0x1F, 0x44, 0x00, 0x00 }
inlineconstexpr

◆ NOP6

constexpr std::uint8_t REL::NOP6[] = { 0x66, 0x0F, 0x1F, 0x44, 0x00, 0x00 }
inlineconstexpr

◆ NOP7

constexpr std::uint8_t REL::NOP7[] = { 0x0F, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00 }
inlineconstexpr

◆ NOP8

constexpr std::uint8_t REL::NOP8[] = { 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00 }
inlineconstexpr

◆ NOP9

constexpr std::uint8_t REL::NOP9[] = { 0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00 }
inlineconstexpr

◆ RET

constexpr std::uint8_t REL::RET = 0xC3
inlineconstexpr