CommonLibSSE NG
Loading...
Searching...
No Matches
Relocation.h
Go to the documentation of this file.
1#pragma once
2
3#include "rapidcsv.h"
4
5#define REL_MAKE_MEMBER_FUNCTION_POD_TYPE_HELPER_IMPL(a_nopropQual, a_propQual, ...) \
6 template < \
7 class R, \
8 class Cls, \
9 class... Args> \
10 struct member_function_pod_type<R (Cls::*)(Args...) __VA_ARGS__ a_nopropQual a_propQual> \
11 { \
12 using type = R(__VA_ARGS__ Cls*, Args...) a_propQual; \
13 }; \
14 \
15 template < \
16 class R, \
17 class Cls, \
18 class... Args> \
19 struct member_function_pod_type<R (Cls::*)(Args..., ...) __VA_ARGS__ a_nopropQual a_propQual> \
20 { \
21 using type = R(__VA_ARGS__ Cls*, Args..., ...) a_propQual; \
22 };
23
24#define REL_MAKE_MEMBER_FUNCTION_POD_TYPE_HELPER(a_qualifer, ...) \
25 REL_MAKE_MEMBER_FUNCTION_POD_TYPE_HELPER_IMPL(a_qualifer, , ##__VA_ARGS__) \
26 REL_MAKE_MEMBER_FUNCTION_POD_TYPE_HELPER_IMPL(a_qualifer, noexcept, ##__VA_ARGS__)
27
28#define REL_MAKE_MEMBER_FUNCTION_POD_TYPE(...) \
29 REL_MAKE_MEMBER_FUNCTION_POD_TYPE_HELPER(, __VA_ARGS__) \
30 REL_MAKE_MEMBER_FUNCTION_POD_TYPE_HELPER(&, ##__VA_ARGS__) \
31 REL_MAKE_MEMBER_FUNCTION_POD_TYPE_HELPER(&&, ##__VA_ARGS__)
32
33#define REL_MAKE_MEMBER_FUNCTION_NON_POD_TYPE_HELPER_IMPL(a_nopropQual, a_propQual, ...) \
34 template < \
35 class R, \
36 class Cls, \
37 class... Args> \
38 struct member_function_non_pod_type<R (Cls::*)(Args...) __VA_ARGS__ a_nopropQual a_propQual> \
39 { \
40 using type = R&(__VA_ARGS__ Cls*, void*, Args...)a_propQual; \
41 }; \
42 \
43 template < \
44 class R, \
45 class Cls, \
46 class... Args> \
47 struct member_function_non_pod_type<R (Cls::*)(Args..., ...) __VA_ARGS__ a_nopropQual a_propQual> \
48 { \
49 using type = R&(__VA_ARGS__ Cls*, void*, Args..., ...)a_propQual; \
50 };
51
52#define REL_MAKE_MEMBER_FUNCTION_NON_POD_TYPE_HELPER(a_qualifer, ...) \
53 REL_MAKE_MEMBER_FUNCTION_NON_POD_TYPE_HELPER_IMPL(a_qualifer, , ##__VA_ARGS__) \
54 REL_MAKE_MEMBER_FUNCTION_NON_POD_TYPE_HELPER_IMPL(a_qualifer, noexcept, ##__VA_ARGS__)
55
56#define REL_MAKE_MEMBER_FUNCTION_NON_POD_TYPE(...) \
57 REL_MAKE_MEMBER_FUNCTION_NON_POD_TYPE_HELPER(, __VA_ARGS__) \
58 REL_MAKE_MEMBER_FUNCTION_NON_POD_TYPE_HELPER(&, ##__VA_ARGS__) \
59 REL_MAKE_MEMBER_FUNCTION_NON_POD_TYPE_HELPER(&&, ##__VA_ARGS__)
60
61#if defined(ENABLE_SKYRIM_VR) && (defined(ENABLE_SKYRIM_AE) || defined(ENABLE_SKYRIM_SE))
65# define SKYRIM_CROSS_VR
66#endif
67
68#if !defined(ENABLE_SKYRIM_AE) || (!defined(ENABLE_SKYRIM_SE) && !defined(ENABLE_SKYRIM_VR))
76# define SKYRIM_ADDR constexpr
77#else
85# define SKYRIM_ADDR inline
86#endif
87
88#if (!defined(ENABLE_SKYRIM_AE) && !defined(ENABLE_SKYRIM_VR)) || (!defined(ENABLE_SKYRIM_SE) && !defined(ENABLE_SKYRIM_VR)) || (!defined(ENABLE_SKYRIM_AE) && !defined(ENABLE_SKYRIM_SE))
96# define SKYRIM_REL constexpr
97
105# define SKYRIM_REL_CONSTEXPR constexpr
106#else
114# define SKYRIM_REL inline
115
123# define SKYRIM_REL_CONSTEXPR
124#endif
125
126#ifndef SKYRIM_CROSS_VR
134# define SKYRIM_REL_VR constexpr
135
143# define SKYRIM_REL_VR_CONSTEXPR constexpr
144
152# define SKYRIM_REL_VR_VIRTUAL virtual
153#else
161# define SKYRIM_REL_VR inline
162
170# define SKYRIM_REL_VR_CONSTEXPR
171
179# define SKYRIM_REL_VR_VIRTUAL
180#endif
181
182namespace REL {
183 namespace detail {
185 public:
186 memory_map() noexcept = default;
187
188 memory_map(const memory_map &) = delete;
189
190 memory_map(memory_map &&a_rhs) noexcept:
191 _mapping(a_rhs._mapping),
192 _view(a_rhs._view) {
193 a_rhs._mapping = nullptr;
194 a_rhs._view = nullptr;
195 }
196
198
199 memory_map &operator=(const memory_map &) = delete;
200
201 memory_map &operator=(memory_map &&a_rhs) noexcept {
202 if (this != std::addressof(a_rhs)) {
203 _mapping = a_rhs._mapping;
204 a_rhs._mapping = nullptr;
205
206 _view = a_rhs._view;
207 a_rhs._view = nullptr;
208 }
209 return *this;
210 }
211
212 [[nodiscard]] void *data() noexcept { return _view; }
213
214 bool open(stl::zwstring a_name, std::size_t a_size);
215
216 bool create(stl::zwstring a_name, std::size_t a_size);
217
218 void close();
219
220 private:
221 void *_mapping{nullptr};
222 void *_view{nullptr};
223 };
224
225 template<class>
227
231 REL_MAKE_MEMBER_FUNCTION_POD_TYPE(const volatile);
232
233 template<class F>
235
236 template<class>
238
243
244 template<class F>
246
247 // https://docs.microsoft.com/en-us/cpp/build/x64-calling-convention
248
249 template<class T>
251 std::disjunction<
252 std::bool_constant<sizeof(T) == 1>,
253 std::bool_constant<sizeof(T) == 2>,
254 std::bool_constant<sizeof(T) == 4>,
255 std::bool_constant<sizeof(T) == 8>> {
256 };
257
258 template<class T>
260 std::conjunction<
261 std::is_trivially_constructible<T>,
262 std::is_trivially_destructible<T>,
263 std::is_trivially_copy_assignable<T>,
264 std::negation<
265 std::is_polymorphic<T>>> {
266 };
267
268 template<class T>
270 std::is_standard_layout<T> {
271 };
272
273 template<class T, class = void>
274 struct is_x64_pod :
275 std::true_type {
276 };
277
278 template<class T>
280 T,
281 std::enable_if_t<
282 std::is_union_v<T>>> :
283 std::false_type {
284 };
285
286 template<class T>
288 T,
289 std::enable_if_t<
290 std::is_class_v<T>>> :
291 std::conjunction<
292 meets_length_req<T>,
293 meets_function_req<T>,
294 meets_member_req<T>> {
295 };
296
297 template<class T>
298 inline constexpr bool is_x64_pod_v = is_x64_pod<T>::value;
299
300 template<
301 class F,
302 class First,
303 class... Rest>
304 decltype(auto) invoke_member_function_non_pod(F &&a_func, First &&a_first, Rest &&... a_rest) //
305 noexcept(std::is_nothrow_invocable_v<F, First, Rest...>) {
306 using result_t = std::invoke_result_t<F, First, Rest...>;
307 std::aligned_storage_t<sizeof(result_t), alignof(result_t)> result;
308
310 auto func = stl::unrestricted_cast<func_t *>(std::forward<F>(a_func));
311
312 return func(std::forward<First>(a_first), std::addressof(result), std::forward<Rest>(a_rest)...);
313 }
314 }
315
316 inline constexpr std::uint8_t NOP = 0x90;
317 inline constexpr std::uint8_t NOP2[] = { 0x66, 0x90 };
318 inline constexpr std::uint8_t NOP3[] = { 0x0F, 0x1F, 0x00 };
319 inline constexpr std::uint8_t NOP4[] = { 0x0F, 0x1F, 0x40, 0x00 };
320 inline constexpr std::uint8_t NOP5[] = { 0x0F, 0x1F, 0x44, 0x00, 0x00 };
321 inline constexpr std::uint8_t NOP6[] = { 0x66, 0x0F, 0x1F, 0x44, 0x00, 0x00 };
322 inline constexpr std::uint8_t NOP7[] = { 0x0F, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00 };
323 inline constexpr std::uint8_t NOP8[] = { 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00 };
324 inline constexpr std::uint8_t NOP9[] = { 0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00 };
325
326 inline constexpr std::uint8_t JMP8 = 0xEB;
327 inline constexpr std::uint8_t JMP32 = 0xE9;
328 inline constexpr std::uint8_t RET = 0xC3;
329 inline constexpr std::uint8_t INT3 = 0xCC;
330
331 template<class F, class... Args>
332 std::invoke_result_t<F, Args...> invoke(F &&a_func, Args &&... a_args) //
333 noexcept(std::is_nothrow_invocable_v<F, Args...>) //
334 requires(std::invocable<F, Args...>)
335 {
336 if constexpr (std::is_member_function_pointer_v<std::decay_t<F>>) {
337 if constexpr (detail::is_x64_pod_v<std::invoke_result_t<F, Args...>>) { // member functions == free functions in x64
339 auto func = stl::unrestricted_cast<func_t *>(std::forward<F>(a_func));
340 return func(std::forward<Args>(a_args)...);
341 } else { // shift args to insert result
342 return detail::invoke_member_function_non_pod(std::forward<F>(a_func), std::forward<Args>(a_args)...);
343 }
344 } else {
345 return std::forward<F>(a_func)(std::forward<Args>(a_args)...);
346 }
347 }
348
349 inline void safe_write(std::uintptr_t a_dst, const void *a_src, std::size_t a_count) {
350 std::uint32_t old{0};
351 auto success =
353 reinterpret_cast<void *>(a_dst),
354 a_count,
356 std::addressof(old));
357 if (success != 0) {
358 std::memcpy(reinterpret_cast<void *>(a_dst), a_src, a_count);
359 success =
361 reinterpret_cast<void *>(a_dst),
362 a_count,
363 old,
364 std::addressof(old));
365 }
366
367 assert(success != 0);
368 }
369
370 template<std::integral T>
371 void safe_write(std::uintptr_t a_dst, const T &a_data) {
372 safe_write(a_dst, std::addressof(a_data), sizeof(T));
373 }
374
375 template<class T>
376 void safe_write(std::uintptr_t a_dst, std::span<T> a_data) {
377 safe_write(a_dst, a_data.data(), a_data.size_bytes());
378 }
379
380 inline void safe_fill(std::uintptr_t a_dst, std::uint8_t a_value, std::size_t a_count) {
381 std::uint32_t old{0};
382 auto success =
384 reinterpret_cast<void *>(a_dst),
385 a_count,
387 std::addressof(old));
388 if (success != 0) {
389 std::fill_n(reinterpret_cast<std::uint8_t *>(a_dst), a_count, a_value);
390 success =
392 reinterpret_cast<void *>(a_dst),
393 a_count,
394 old,
395 std::addressof(old));
396 }
397
398 assert(success != 0);
399 }
400
401 class Version {
402 public:
403 using value_type = std::uint16_t;
406
407 constexpr Version() noexcept = default;
408
409 explicit constexpr Version(std::array<value_type, 4> a_version) noexcept:
410 _impl(a_version) {}
411
412 constexpr Version(value_type a_v1, value_type a_v2 = 0, value_type a_v3 = 0, value_type a_v4 = 0) noexcept:
413 _impl{a_v1, a_v2, a_v3, a_v4} {}
414
415 explicit constexpr Version(std::string_view a_version) {
416 std::array<value_type, 4> powers{1, 1, 1, 1};
417 std::size_t position = 0;
418 for (std::size_t i = 0; i < a_version.size(); ++i) {
419 if (a_version[i] == '.') {
420 if (++position == powers.size()) {
421 throw std::invalid_argument("Too many parts in version number.");
422 }
423 } else {
424 powers[position] *= 10;
425 }
426 }
427 position = 0;
428 for (std::size_t i = 0; i < a_version.size(); ++i) {
429 if (a_version[i] == '.') {
430 ++position;
431 } else if (a_version[i] < '0' || a_version[i] > '9') {
432 throw std::invalid_argument("Invalid character in version number.");
433 } else {
434 powers[position] /= 10;
435 _impl[position] += static_cast<value_type>((a_version[i] - '0') * powers[position]);
436 }
437 }
438 }
439
440 [[nodiscard]] constexpr reference operator[](std::size_t a_idx) noexcept { return _impl[a_idx]; }
441
442 [[nodiscard]] constexpr const_reference operator[](std::size_t a_idx) const noexcept { return _impl[a_idx]; }
443
444 [[nodiscard]] constexpr decltype(auto) begin() const noexcept { return _impl.begin(); }
445
446 [[nodiscard]] constexpr decltype(auto) cbegin() const noexcept { return _impl.cbegin(); }
447
448 [[nodiscard]] constexpr decltype(auto) end() const noexcept { return _impl.end(); }
449
450 [[nodiscard]] constexpr decltype(auto) cend() const noexcept { return _impl.cend(); }
451
452 [[nodiscard]] std::strong_ordering constexpr compare(const Version &a_rhs) const noexcept {
453 for (std::size_t i = 0; i < _impl.size(); ++i) {
454 if ((*this)[i] != a_rhs[i]) {
455 return (*this)[i] < a_rhs[i] ? std::strong_ordering::less : std::strong_ordering::greater;
456 }
457 }
458 return std::strong_ordering::equal;
459 }
460
461 [[nodiscard]] constexpr std::uint32_t pack() const noexcept {
462 return static_cast<std::uint32_t>(
463 (_impl[0] & 0x0FF) << 24u |
464 (_impl[1] & 0x0FF) << 16u |
465 (_impl[2] & 0xFFF) << 4u |
466 (_impl[3] & 0x00F) << 0u);
467 }
468
469 [[nodiscard]] constexpr value_type major() const noexcept {
470 return _impl[0];
471 }
472
473 [[nodiscard]] constexpr value_type minor() const noexcept {
474 return _impl[1];
475 }
476
477 [[nodiscard]] constexpr value_type patch() const noexcept {
478 return _impl[2];
479 }
480
481 [[nodiscard]] constexpr value_type build() const noexcept {
482 return _impl[3];
483 }
484
485 [[nodiscard]] std::string string(std::string_view a_separator = "-"sv) const {
486 std::string result;
487 for (auto &&ver: _impl) {
488 result += std::to_string(ver);
489 result.append(a_separator.data(), a_separator.size());
490 }
491 result.erase(result.size() - a_separator.size(), a_separator.size());
492 return result;
493 }
494
495 [[nodiscard]] std::wstring wstring(std::wstring_view a_separator = L"-"sv) const {
496 std::wstring result;
497 for (auto &&ver: _impl) {
498 result += std::to_wstring(ver);
499 result.append(a_separator.data(), a_separator.size());
500 }
501 result.erase(result.size() - a_separator.size(), a_separator.size());
502 return result;
503 }
504
505 [[nodiscard]] static constexpr Version unpack(std::uint32_t a_packedVersion) noexcept {
506 return REL::Version{
507 static_cast<value_type>((a_packedVersion >> 24) & 0x0FF),
508 static_cast<value_type>((a_packedVersion >> 16) & 0x0FF),
509 static_cast<value_type>((a_packedVersion >> 4) & 0xFFF),
510 static_cast<value_type>(a_packedVersion & 0x0F)
511 };
512 }
513
514 private:
515 std::array<value_type, 4> _impl{0, 0, 0, 0};
516 };
517
518 [[nodiscard]] constexpr bool operator==(const Version &a_lhs, const Version &a_rhs) noexcept {
519 return a_lhs.compare(a_rhs) == std::strong_ordering::equal;
520 }
521
522 [[nodiscard]] constexpr std::strong_ordering
523 operator<=>(const Version &a_lhs, const Version &a_rhs) noexcept { return a_lhs.compare(a_rhs); }
524
525 namespace literals {
526 namespace detail {
527 template<std::size_t Index, char C>
528 constexpr uint8_t read_version(std::array<typename REL::Version::value_type, 4> &result) {
529 static_assert(C >= '0' && C <= '9', "Invalid character in semantic version literal.");
530 static_assert(Index < 4, "Too many components in semantic version literal.");
531 result[Index] += (C - '0');
532 return 10;
533 }
534
535 template<std::size_t Index, char C, char... Rest>
536 requires (sizeof...(Rest) > 0)
537 constexpr uint8_t read_version(std::array<typename REL::Version::value_type, 4> &result) {
538 static_assert(C == '.' || (C >= '0' && C <= '9'), "Invalid character in semantic version literal.");
539 static_assert(Index < 4, "Too many components in semantic version literal.");
540 if constexpr (C == '.') {
541 read_version<Index + 1, Rest...>(result);
542 return 1;
543 } else {
544 auto position = read_version<Index, Rest...>(result);
545 result[Index] += (C - '0') * position;
546 return position * 10;
547 }
548 }
549 }
550
551 template<char... C>
552 [[nodiscard]] constexpr REL::Version operator ""_v() noexcept {
553 std::array<typename REL::Version::value_type, 4> result{0, 0, 0, 0};
554 detail::read_version<0, C...>(result);
555 return REL::Version(result);
556 }
557
558 [[nodiscard]] constexpr REL::Version operator ""_v(const char *str, std::size_t len) {
559 return Version(std::string_view(str, len));
560 }
561 }
562
563 [[nodiscard]] inline std::optional<Version> get_file_version(stl::zwstring a_filename) {
564 std::uint32_t dummy;
565 std::vector<char> buf(GetFileVersionInfoSize(a_filename.data(), std::addressof(dummy)));
566 if (buf.empty()) {
567 return std::nullopt;
568 }
569
570 if (!GetFileVersionInfo(a_filename.data(), 0, static_cast<std::uint32_t>(buf.size()), buf.data())) {
571 return std::nullopt;
572 }
573
574 void *verBuf{nullptr};
575 std::uint32_t verLen{0};
576 if (!VerQueryValue(buf.data(), L"\\StringFileInfo\\040904B0\\ProductVersion", std::addressof(verBuf),
577 std::addressof(verLen))) {
578 return std::nullopt;
579 }
580
581 Version version;
582 std::wistringstream ss(
583 std::wstring(static_cast<const wchar_t *>(verBuf), verLen));
584 std::wstring token;
585 for (std::size_t i = 0; i < 4 && std::getline(ss, token, L'.'); ++i) {
586 version[i] = static_cast<std::uint16_t>(std::stoi(token));
587 }
588
589 return version;
590 }
591
592 class Segment {
593 public:
594 enum Name : std::size_t {
603 total
604 };
605
606 Segment() noexcept = default;
607
608 Segment(std::uintptr_t a_proxyBase, std::uintptr_t a_address, std::uintptr_t a_size) noexcept:
609 _proxyBase(a_proxyBase),
610 _address(a_address),
611 _size(a_size) {}
612
613 [[nodiscard]] std::uintptr_t address() const noexcept { return _address; }
614
615 [[nodiscard]] std::size_t offset() const noexcept { return address() - _proxyBase; }
616
617 [[nodiscard]] std::size_t size() const noexcept { return _size; }
618
619 [[nodiscard]] void *pointer() const noexcept { return reinterpret_cast<void *>(address()); }
620
621 template<class T>
622 [[nodiscard]] T *pointer() const noexcept {
623 return static_cast<T *>(pointer());
624 }
625
626 private:
627 friend class Module;
628
629 std::uintptr_t _proxyBase{0};
630 std::uintptr_t _address{0};
631 std::size_t _size{0};
632 };
633
634 class Module {
635 public:
639 enum class Runtime : uint8_t {
640 Unknown = 0,
641
645 AE = 1 << 0,
646
650 SE = 1 << 1,
651
655 VR = 1 << 2
656 };
657
658 [[nodiscard]] static Module &get() {
659 if (_initialized.load(std::memory_order_relaxed)) {
660 return _instance;
661 }
662 [[maybe_unused]] std::unique_lock lock(_initLock);
663 _instance.init();
664 _initialized = true;
665 return _instance;
666 }
667
668#ifdef ENABLE_COMMONLIBSSE_TESTING
680 static bool inject(std::wstring_view a_filePath) {
681 _instance.clear();
682 _initialized = true;
683 return _instance.init(a_filePath);
684 }
685
703 static bool inject(Runtime a_runtime = Runtime::Unknown) {
704 if (a_runtime == Runtime::Unknown) {
705 return inject(Runtime::SE) || inject(Runtime::VR);
706 }
707
708 constexpr std::size_t bufferSize = 4096; // Max NTFS path length.
709 const wchar_t *subKey =
710 a_runtime == Runtime::VR ?
711 LR"(SOFTWARE\Bethesda Softworks\Skyrim VR)" :
712 LR"(SOFTWARE\Bethesda Softworks\Skyrim Special Edition)";
713 unsigned long length = bufferSize * sizeof(wchar_t);
714 std::uint8_t value[bufferSize];
715 if (WinAPI::RegGetValueW(WinAPI::HKEY_LOCAL_MACHINE, subKey, L"Installed Path", 0x20002, nullptr, value, &length) !=
716 0) {
717 return false;
718 }
719 std::filesystem::path installPath(reinterpret_cast<wchar_t *>(value));
720 installPath /= a_runtime == Runtime::VR ? L"SkyrimVR.exe" : L"SkyrimSE.exe";
721 return inject(installPath.c_str());
722 }
723
724 static bool mock(REL::Version a_version, Runtime a_runtime = Runtime::Unknown,
725 std::wstring_view a_filename = L"SkyrimSE.exe"sv, std::uintptr_t a_base = 0,
726 std::array<std::uintptr_t, Segment::total> a_segmentSizes =
727 {0x1603000, 0, 0x8ee000, 0x1887000, 0x15c000, 0x3000, 0x2000, 0x1000}) {
728 _instance.clear();
729 _initialized = true;
730
731 if (a_filename.empty() || !a_segmentSizes[0]) {
732 return false;
733 }
734
735 _instance._filename = _instance._filePath = a_filename.data();
736 _instance._version = a_version;
737 if (a_runtime == Runtime::Unknown) {
738 switch (a_version[1]) {
739 case 4:
740 _instance._runtime = Runtime::VR;
741 break;
742 case 6:
743 _instance._runtime = Runtime::AE;
744 break;
745 default:
746 _instance._runtime = Runtime::SE;
747 }
748 } else {
749 _instance._runtime = a_runtime;
750 }
751 _instance._base = a_base;
752
753 auto currentAddress = a_base + 0x1000;
754 for (std::size_t i = 0; i < a_segmentSizes.size(); ++i) {
755 auto &segment = _instance._segments[i];
756 segment._size = a_segmentSizes[i];
757 if (segment._size) {
758 segment._proxyBase = a_base;
759 segment._address = (currentAddress += segment._size);
760 }
761 }
762
763 return true;
764 }
765
766 static void reset() {
767 _initialized = false;
768 _instance.clear();
769 }
770#endif
771
772 [[nodiscard]] std::uintptr_t base() const noexcept { return _base; }
773
774 [[nodiscard]] stl::zwstring filename() const noexcept { return _filename; }
775
776 [[nodiscard]] stl::zwstring filePath() const noexcept { return _filePath; }
777
778 [[nodiscard]] Version version() const noexcept { return _version; }
779
780 [[nodiscard]] Segment segment(Segment::Name a_segment) const noexcept { return _segments[a_segment]; }
781
782 [[nodiscard]] void *pointer() const noexcept { return reinterpret_cast<void *>(base()); }
783
784 template<class T>
785 [[nodiscard]] T *pointer() const noexcept {
786 return static_cast<T *>(pointer());
787 }
788
792 [[nodiscard]] static SKYRIM_REL Runtime GetRuntime() noexcept {
793#if (!defined(ENABLE_SKYRIM_AE) && !defined(ENABLE_SKYRIM_VR))
794 return Runtime::SE;
795#elif (!defined(ENABLE_SKYRIM_SE) && !defined(ENABLE_SKYRIM_VR))
796 return Runtime::AE;
797#elif (!defined(ENABLE_SKYRIM_AE) && !defined(ENABLE_SKYRIM_SE))
798 return Runtime::VR;
799#else
800 return get()._runtime;
801#endif
802 }
803
807 [[nodiscard]] static SKYRIM_REL bool IsAE() noexcept {
808 return GetRuntime() == Runtime::AE;
809 }
810
814 [[nodiscard]] static SKYRIM_REL bool IsSE() noexcept {
815 return GetRuntime() == Runtime::SE;
816 }
817
821 [[nodiscard]] static SKYRIM_REL_VR bool IsVR() noexcept {
822#ifndef ENABLE_SKYRIM_VR
823 return false;
824#elif !defined(ENABLE_SKYRIM_AE) && !defined(ENABLE_SKYRIM_SE)
825 return true;
826#else
827 return GetRuntime() == Runtime::VR;
828#endif
829 }
830
831 private:
832 Module() = default;
833
834 Module(const Module &) = delete;
835
836 Module(Module &&) = delete;
837
838 ~Module() noexcept = default;
839
840 Module &operator=(const Module &) = delete;
841
842 Module &operator=(Module &&) = delete;
843
844 bool init() {
845 const auto getFilename = [&]() {
847 ENVIRONMENT.data(),
848 _filename.data(),
849 static_cast<std::uint32_t>(_filename.size()));
850 };
851
852 void *moduleHandle = nullptr;
853 _filename.resize(getFilename());
854 if (const auto result = getFilename();
855 result != _filename.size() - 1 ||
856 result == 0) {
857 for (auto runtime: RUNTIMES) {
858 _filename = runtime;
859 moduleHandle = GetModuleHandle(_filename.c_str());
860 if (moduleHandle) {
861 break;
862 }
863 }
864 }
865 _filePath = _filename;
866 if (!moduleHandle) {
868 fmt::format(
869 "Failed to obtain module handle for: \"{0}\".\n"
870 "You have likely renamed the executable to something unexpected. "
871 "Renaming the executable back to \"{0}\" may resolve the issue."sv,
872 stl::utf16_to_utf8(_filename).value_or("<unicode conversion error>"s)));
873 }
874 return load(moduleHandle, true);
875 }
876
877 bool init(std::wstring_view a_filePath) {
878 std::filesystem::path exePath(a_filePath);
879 _filename = exePath.filename().wstring();
880 _filePath = exePath.wstring();
881 _injectedModule = LoadLibrary(_filePath.c_str());
882 if (_injectedModule) {
883 return load(_injectedModule, false);
884 }
885 return false;
886 }
887
888 [[nodiscard]] bool load(void *a_handle, bool a_failOnError) {
889 _base = reinterpret_cast<std::uintptr_t>(a_handle);
890 if (!load_version(a_failOnError)) {
891 return false;
892 }
893 load_segments();
894 return true;
895 }
896
897 void load_segments();
898
899 bool load_version(bool a_failOnError) {
900 const auto version = get_file_version(_filePath);
901 if (version) {
902 _version = *version;
903 switch (_version[1]) {
904 case 4:
905 _runtime = Runtime::VR;
906 break;
907 case 6:
908 _runtime = Runtime::AE;
909 break;
910 default:
911 _runtime = Runtime::SE;
912 }
913 return true;
914 }
916 fmt::format(
917 "Failed to obtain file version info for: {}\n"
918 "Please contact the author of this script extender plugin for further assistance."sv,
919 stl::utf16_to_utf8(_filename).value_or("<unicode conversion error>"s)), a_failOnError);
920 }
921
922 void clear();
923
924 static constexpr std::array SEGMENTS{
925 std::make_pair(".text"sv, IMAGE_SCN_MEM_EXECUTE),
926 std::make_pair(".idata"sv, static_cast<std::uint32_t>(0)),
927 std::make_pair(".rdata"sv, static_cast<std::uint32_t>(0)),
928 std::make_pair(".data"sv, static_cast<std::uint32_t>(0)),
929 std::make_pair(".pdata"sv, static_cast<std::uint32_t>(0)),
930 std::make_pair(".tls"sv, static_cast<std::uint32_t>(0)),
931 std::make_pair(".text"sv, IMAGE_SCN_MEM_WRITE),
932 std::make_pair(".gfids"sv, static_cast<std::uint32_t>(0))
933 };
934
935 static constexpr auto ENVIRONMENT = L"SKSE_RUNTIME"sv;
936 static constexpr std::array<std::wstring_view, 2> RUNTIMES{{L"SkyrimVR.exe",
937 L"SkyrimSE.exe"}};
938
939 static Module _instance;
940 static inline std::atomic_bool _initialized{false};
941 static inline std::mutex _initLock;
942 WinAPI::HMODULE _injectedModule{nullptr};
943 std::wstring _filename;
944 std::wstring _filePath;
945 std::array<Segment, Segment::total> _segments;
946 Version _version;
947 std::uintptr_t _base{0};
948 Runtime _runtime{Runtime::AE};
949 };
950
952 private:
953 struct mapping_t {
954 std::uint64_t id;
955 std::uint64_t offset;
956 };
957
958 public:
959 enum class Format {
960 SSEv1,
961 SSEv2,
962 VR
963 };
964
965 class Offset2ID {
966 public:
967 using value_type = mapping_t;
968 using container_type = std::vector<value_type>;
969 using size_type = typename container_type::size_type;
970 using const_iterator = typename container_type::const_iterator;
971 using const_reverse_iterator = typename container_type::const_reverse_iterator;
972
973 template<class ExecutionPolicy>
974 explicit Offset2ID(ExecutionPolicy &&a_policy) //
975 requires(std::is_execution_policy_v<std::decay_t<ExecutionPolicy>>)
976 {
977 const std::span<const mapping_t> id2offset = IDDatabase::get()._id2offset;
978 _offset2id.reserve(id2offset.size());
979 _offset2id.insert(_offset2id.begin(), id2offset.begin(), id2offset.end());
980 std::sort(
981 a_policy,
982 _offset2id.begin(),
983 _offset2id.end(),
984 [](auto &&a_lhs, auto &&a_rhs) {
985 return a_lhs.offset < a_rhs.offset;
986 });
987 }
988
990 Offset2ID(std::execution::sequenced_policy{}) {}
991
992 [[nodiscard]] std::uint64_t operator()(std::size_t a_offset) const {
993 const mapping_t elem{0, a_offset};
994 const auto it = std::lower_bound(
995 _offset2id.begin(),
996 _offset2id.end(),
997 elem,
998 [](auto &&a_lhs, auto &&a_rhs) {
999 return a_lhs.offset < a_rhs.offset;
1000 });
1001 if (it == _offset2id.end()) {
1003 fmt::format(
1004 "Failed to find the offset within the database: 0x{:08X}"sv,
1005 a_offset));
1006 }
1007
1008 return it->id;
1009 }
1010
1011 [[nodiscard]] const_iterator begin() const noexcept { return _offset2id.begin(); }
1012
1013 [[nodiscard]] const_iterator cbegin() const noexcept { return _offset2id.cbegin(); }
1014
1015 [[nodiscard]] const_iterator end() const noexcept { return _offset2id.end(); }
1016
1017 [[nodiscard]] const_iterator cend() const noexcept { return _offset2id.cend(); }
1018
1019 [[nodiscard]] const_reverse_iterator rbegin() const noexcept { return _offset2id.rbegin(); }
1020
1021 [[nodiscard]] const_reverse_iterator crbegin() const noexcept { return _offset2id.crbegin(); }
1022
1023 [[nodiscard]] const_reverse_iterator rend() const noexcept { return _offset2id.rend(); }
1024
1025 [[nodiscard]] const_reverse_iterator crend() const noexcept { return _offset2id.crend(); }
1026
1027 [[nodiscard]] size_type size() const noexcept { return _offset2id.size(); }
1028
1029 private:
1030 container_type _offset2id;
1031 };
1032
1033 [[nodiscard]] static IDDatabase &get() {
1034 if (_initialized.load(std::memory_order_relaxed)) {
1035 return _instance;
1036 }
1037 [[maybe_unused]] std::unique_lock lock(_initLock);
1038 _instance.load();
1039 _initialized.store(true, std::memory_order_relaxed);
1040 return _instance;
1041 }
1042
1043#ifdef ENABLE_COMMONLIBSSE_TESTING
1044 [[nodiscard]] static bool inject(std::wstring_view a_filePath, Format a_format) {
1045 return inject(a_filePath, a_format, Module::get().version());
1046 }
1047
1048 [[nodiscard]] static bool inject(std::wstring_view a_filePath, Format a_format, Version a_version) {
1049 _initialized = true;
1050 _instance.clear();
1051 switch (a_format) {
1052 case Format::SSEv1:
1053 return _instance.load_file(a_filePath.data(), a_version, 1, false);
1054 case Format::SSEv2:
1055 return _instance.load_file(a_filePath.data(), a_version, 2, false);
1056 case Format::VR:
1057 return _instance.load_csv(a_filePath.data(), a_version, false);
1058 default:
1059 return false;
1060 }
1061 }
1062
1063 static void reset() {
1064 _instance.clear();
1065 _initialized = false;
1066 }
1067#endif
1068
1069 [[nodiscard]] inline std::size_t id2offset(std::uint64_t a_id) const {
1070 mapping_t elem{a_id, 0};
1071 const auto it = std::lower_bound(
1072 _id2offset.begin(),
1073 _id2offset.end(),
1074 elem,
1075 [](auto &&a_lhs, auto &&a_rhs) {
1076 return a_lhs.id < a_rhs.id;
1077 });
1078
1079 bool failed = false;
1080 if (it == _id2offset.end()) {
1081 failed = true;
1083 if (it->id != a_id) {
1084 failed = true;
1085 }
1086 }
1087 if (failed) {
1089 fmt::format(
1090 "Failed to find the id within the address library: {}\n"
1091 "This means this script extender plugin is incompatible with the address "
1092 "library for this version of the game, and thus does not support it."sv,
1093 a_id));
1094 }
1095
1096 return static_cast<std::size_t>(it->offset);
1097 }
1098
1099 private:
1100 friend class Module;
1101
1102 friend Offset2ID;
1103
1104 class istream_t {
1105 public:
1106 using stream_type = std::ifstream;
1107 using pointer = stream_type *;
1108 using const_pointer = const stream_type *;
1109 using reference = stream_type &;
1110 using const_reference = const stream_type &;
1111
1112 inline istream_t(stl::zwstring a_filename, std::ios_base::openmode a_mode) :
1113 _stream(a_filename.data(), a_mode) {
1114 if (!_stream.is_open()) {
1115 stl::report_and_fail("failed to open address library file");
1116 }
1117
1118 _stream.exceptions(std::ios::badbit | std::ios::failbit | std::ios::eofbit);
1119 }
1120
1121 inline void ignore(std::streamsize a_count) { _stream.ignore(a_count); }
1122
1123 template<class T>
1124 inline void readin(T &a_val) {
1125 _stream.read(reinterpret_cast<char *>(std::addressof(a_val)), sizeof(T));
1126 }
1127
1128 template<
1129 class T,
1130 std::enable_if_t<
1131 std::is_arithmetic_v<T>,
1132 int> = 0>
1133 inline T readout() {
1134 T val{};
1135 readin(val);
1136 return val;
1137 }
1138
1139 private:
1140 stream_type _stream;
1141 };
1142
1143 class header_t {
1144 public:
1145 void read(istream_t &a_in, std::uint8_t a_formatVersion) {
1146 std::int32_t format{};
1147 a_in.readin(format);
1148 if (format != a_formatVersion) {
1150 fmt::format(
1151 "Unsupported address library format: {}\n"
1152 "This means this script extender plugin is incompatible with the address "
1153 "library available for this version of the game, and thus does not "
1154 "support it."sv,
1155 format));
1156 }
1157
1158 std::int32_t version[4]{};
1159 std::int32_t nameLen{};
1160 a_in.readin(version);
1161 a_in.readin(nameLen);
1162 a_in.ignore(nameLen);
1163
1164 a_in.readin(_pointerSize);
1165 a_in.readin(_addressCount);
1166
1167 for (std::size_t i = 0; i < std::extent_v<decltype(version)>; ++i) {
1168 _version[i] = static_cast<std::uint16_t>(version[i]);
1169 }
1170 }
1171
1172 [[nodiscard]] std::size_t address_count() const noexcept { return static_cast<std::size_t>(_addressCount); }
1173
1174 [[nodiscard]] std::uint64_t
1175 pointer_size() const noexcept { return static_cast<std::uint64_t>(_pointerSize); }
1176
1177 [[nodiscard]] Version version() const noexcept { return _version; }
1178
1179 private:
1180 Version _version;
1181 std::int32_t _pointerSize{0};
1182 std::int32_t _addressCount{0};
1183 };
1184
1185 IDDatabase() = default;
1186
1187 IDDatabase(const IDDatabase &) = delete;
1188
1189 IDDatabase(IDDatabase &&) = delete;
1190
1191 ~IDDatabase() = default;
1192
1193 IDDatabase &operator=(const IDDatabase &) = delete;
1194
1195 IDDatabase &operator=(IDDatabase &&) = delete;
1196
1197 void load() {
1198 const auto version = Module::get().version();
1200 const auto filename =
1202 fmt::format(
1203 "Data/SKSE/Plugins/version-{}.csv"sv,
1204 version.string()))
1205 .value_or(L"<unknown filename>"s);
1206 load_csv(filename, version, true);
1207 } else {
1208 const auto filename =
1210 Module::IsAE() ?
1211 fmt::format("Data/SKSE/Plugins/versionlib-{}.bin"sv,
1212 version.string()) :
1213 fmt::format("Data/SKSE/Plugins/version-{}.bin"sv,
1214 version.string()))
1215 .value_or(L"<unknown filename>"s);
1216 load_file(filename, version, Module::IsAE() ? 2 : 1, true);
1217 }
1218 }
1219
1220 bool load_file(stl::zwstring a_filename, Version a_version, std::uint8_t a_formatVersion, bool a_failOnError) {
1221 try {
1222 istream_t in(a_filename.data(), std::ios::in | std::ios::binary);
1223 header_t header;
1224 header.read(in, a_formatVersion);
1225 if (header.version() != a_version) {
1226 return stl::report_and_error("version mismatch"sv, a_failOnError);
1227 }
1228
1229 auto mapname = L"CommonLibSSEOffsets-v2-"s;
1230 mapname += a_version.wstring();
1231 const auto byteSize = static_cast<std::size_t>(header.address_count()) * sizeof(mapping_t);
1232 if (_mmap.open(mapname, byteSize)) {
1233 _id2offset = { static_cast<mapping_t*>(_mmap.data()), header.address_count() };
1234 } else if (_mmap.create(mapname, byteSize)) {
1235 _id2offset = { static_cast<mapping_t*>(_mmap.data()), header.address_count() };
1236 unpack_file(in, header, a_failOnError);
1237 std::sort(
1238 _id2offset.begin(),
1239 _id2offset.end(),
1240 [](auto&& a_lhs, auto&& a_rhs) {
1241 return a_lhs.id < a_rhs.id;
1242 });
1243 } else {
1244 return stl::report_and_error("failed to create shared mapping"sv, a_failOnError);
1245 }
1246 } catch (const std::system_error &) {
1247 return stl::report_and_error(
1248 fmt::format(
1249 "Failed to locate an appropriate address library with the path: {}\n"
1250 "This means you are missing the address library for this specific version of "
1251 "the game. Please continue to the mod page for address library to download "
1252 "an appropriate version. If one is not available, then it is likely that "
1253 "address library has not yet added support for this version of the game."sv,
1254 stl::utf16_to_utf8(a_filename).value_or("<unknown filename>"s)), a_failOnError);
1255 return false;
1256 }
1257 return true;
1258 }
1259
1260 bool load_csv(stl::zwstring a_filename, Version a_version, bool a_failOnError) {
1261 auto nstring = SKSE::stl::utf16_to_utf8(a_filename).value_or(""s);
1262 if (!std::filesystem::exists(nstring)) {
1263 return stl::report_and_error(
1264 fmt::format("Required VR Address Library file {} does not exist"sv, nstring),
1265 a_failOnError);
1266 }
1267 rapidcsv::Document in(nstring);
1268 std::size_t id, address_count;
1269 std::string version, offset;
1270 auto mapname = L"CommonLibSSEOffsets-v2-"s;
1271 mapname += a_version.wstring();
1272 address_count = in.GetCell<std::size_t>(0, 0);
1273 version = in.GetCell<std::string>(1, 0);
1274 const auto byteSize = static_cast<std::size_t>(address_count * sizeof(mapping_t));
1275 if (!_mmap.open(mapname, byteSize) &&
1276 !_mmap.create(mapname, byteSize)) {
1277 return stl::report_and_error("failed to create shared mapping"sv, a_failOnError);
1278 }
1279 _id2offset = {static_cast<mapping_t *>(_mmap.data()), static_cast<std::size_t>(address_count)};
1280 if (in.GetRowCount() > address_count + 1) {
1281 return stl::report_and_error(
1282 fmt::format("VR Address Library {} tried to exceed {} allocated entries."sv,
1283 version, address_count), a_failOnError);
1284 } else if (in.GetRowCount() < address_count + 1) {
1285 return stl::report_and_error(
1286 fmt::format(
1287 "VR Address Library {} loaded only {} entries but expected {}. Please redownload."sv,
1288 version, in.GetRowCount() - 1, address_count), a_failOnError);
1289 }
1290 std::size_t index = 1;
1291 for (; index < in.GetRowCount(); ++index) {
1292 id = in.GetCell<std::size_t>(0, index);
1293 offset = in.GetCell<std::string>(1, index);
1294 _id2offset[index - 1] = {static_cast<std::uint64_t>(id),
1295 static_cast<std::uint64_t>(std::stoul(offset, nullptr, 16))};
1296 }
1297 std::sort(
1298 _id2offset.begin(),
1299 _id2offset.end(),
1300 [](auto &&a_lhs, auto &&a_rhs) {
1301 return a_lhs.id < a_rhs.id;
1302 });
1303 return true;
1304 }
1305
1306 bool unpack_file(istream_t &a_in, header_t a_header, bool a_failOnError) {
1307 std::uint8_t type = 0;
1308 std::uint64_t id = 0;
1309 std::uint64_t offset = 0;
1310 std::uint64_t prevID = 0;
1311 std::uint64_t prevOffset = 0;
1312 for (auto &mapping: _id2offset) {
1313 a_in.readin(type);
1314 const auto lo = static_cast<std::uint8_t>(type & 0xF);
1315 const auto hi = static_cast<std::uint8_t>(type >> 4);
1316
1317 switch (lo) {
1318 case 0:
1319 a_in.readin(id);
1320 break;
1321 case 1:
1322 id = prevID + 1;
1323 break;
1324 case 2:
1325 id = prevID + a_in.readout<std::uint8_t>();
1326 break;
1327 case 3:
1328 id = prevID - a_in.readout<std::uint8_t>();
1329 break;
1330 case 4:
1331 id = prevID + a_in.readout<std::uint16_t>();
1332 break;
1333 case 5:
1334 id = prevID - a_in.readout<std::uint16_t>();
1335 break;
1336 case 6:
1337 id = a_in.readout<std::uint16_t>();
1338 break;
1339 case 7:
1340 id = a_in.readout<std::uint32_t>();
1341 break;
1342 default:
1343 return stl::report_and_error("unhandled type"sv, a_failOnError);
1344 }
1345
1346 const std::uint64_t tmp = (hi & 8) != 0 ? (prevOffset / a_header.pointer_size()) : prevOffset;
1347
1348 switch (hi & 7) {
1349 case 0:
1350 a_in.readin(offset);
1351 break;
1352 case 1:
1353 offset = tmp + 1;
1354 break;
1355 case 2:
1356 offset = tmp + a_in.readout<std::uint8_t>();
1357 break;
1358 case 3:
1359 offset = tmp - a_in.readout<std::uint8_t>();
1360 break;
1361 case 4:
1362 offset = tmp + a_in.readout<std::uint16_t>();
1363 break;
1364 case 5:
1365 offset = tmp - a_in.readout<std::uint16_t>();
1366 break;
1367 case 6:
1368 offset = a_in.readout<std::uint16_t>();
1369 break;
1370 case 7:
1371 offset = a_in.readout<std::uint32_t>();
1372 break;
1373 default:
1374 return stl::report_and_error("unhandled type"sv, a_failOnError);
1375 }
1376
1377 if ((hi & 8) != 0) {
1378 offset *= a_header.pointer_size();
1379 }
1380
1381 mapping = {id, offset};
1382
1383 prevOffset = offset;
1384 prevID = id;
1385 }
1386 return true;
1387 }
1388
1389 void clear() {
1390 _mmap.close();
1391 _id2offset = {};
1392 }
1393
1394 static IDDatabase _instance;
1395 static inline std::atomic_bool _initialized{false};
1396 static inline std::mutex _initLock;
1397 detail::memory_map _mmap;
1398 std::span<mapping_t> _id2offset;
1399 };
1400
1401 class Offset {
1402 public:
1403 constexpr Offset() noexcept = default;
1404
1405 explicit constexpr Offset(std::size_t a_offset) noexcept:
1406 _offset(a_offset) {}
1407
1408 constexpr Offset &operator=(std::size_t a_offset) noexcept {
1409 _offset = a_offset;
1410 return *this;
1411 }
1412
1413 [[nodiscard]] std::uintptr_t address() const { return base() + offset(); }
1414
1415 [[nodiscard]] constexpr std::size_t offset() const noexcept { return _offset; }
1416
1417 private:
1418 [[nodiscard]] static std::uintptr_t base() { return Module::get().base(); }
1419
1420 std::size_t _offset{0};
1421 };
1422
1424 public:
1425 constexpr VariantOffset() noexcept = default;
1426
1427 explicit constexpr VariantOffset([[maybe_unused]] std::size_t a_seOffset,
1428 [[maybe_unused]] std::size_t a_aeOffset,
1429 [[maybe_unused]] std::size_t a_vrOffset) noexcept {
1430#ifdef ENABLE_SKYRIM_SE
1431 _seOffset = a_seOffset;
1432#endif
1433#ifdef ENABLE_SKYRIM_AE
1434 _aeOffset = a_aeOffset;
1435#endif
1436#ifdef ENABLE_SKYRIM_VR
1437 _vrOffset = a_vrOffset;
1438#endif
1439 }
1440
1441 [[nodiscard]] std::uintptr_t address() const {
1442 auto thisOffset = offset();
1443 return thisOffset ? base() + thisOffset : 0;
1444 }
1445
1446 [[nodiscard]] SKYRIM_REL std::size_t offset() const noexcept {
1447 switch (Module::GetRuntime()) {
1448#ifdef ENABLE_SKYRIM_AE
1450 return _aeOffset;
1451#endif
1452#ifdef ENABLE_SKYRIM_SE
1454 return _seOffset;
1455#endif
1456#ifdef ENABLE_SKYRIM_VR
1458 return _vrOffset;
1459#endif
1460 default:
1461 return 0;
1462 }
1463 }
1464
1465 [[nodiscard]] SKYRIM_REL explicit operator Offset() const noexcept { return Offset(offset()); }
1466
1467 private:
1468 [[nodiscard]] static std::uintptr_t base() { return Module::get().base(); }
1469
1470#ifdef ENABLE_SKYRIM_SE
1471 std::size_t _seOffset{0};
1472#endif
1473#ifdef ENABLE_SKYRIM_AE
1474 std::size_t _aeOffset{0};
1475#endif
1476#ifdef ENABLE_SKYRIM_VR
1477 std::size_t _vrOffset{0};
1478#endif
1479 };
1480
1481 class ID {
1482 public:
1483 constexpr ID() noexcept = default;
1484
1485 explicit constexpr ID(std::uint64_t a_id) noexcept:
1486 _id(a_id) {}
1487
1488 constexpr ID &operator=(std::uint64_t a_id) noexcept {
1489 _id = a_id;
1490 return *this;
1491 }
1492
1493 [[nodiscard]] std::uintptr_t address() const { return base() + offset(); }
1494
1495 [[nodiscard]] constexpr std::uint64_t id() const noexcept { return _id; }
1496
1497 [[nodiscard]] std::size_t offset() const { return IDDatabase::get().id2offset(_id); }
1498
1499 private:
1500 [[nodiscard]] static std::uintptr_t base() { return Module::get().base(); }
1501
1502 std::uint64_t _id{0};
1503 };
1504
1506 public:
1507 constexpr RelocationID() noexcept = default;
1508
1509 explicit constexpr RelocationID([[maybe_unused]] std::uint64_t a_seID,
1510 [[maybe_unused]] std::uint64_t a_aeID) noexcept {
1511#ifdef ENABLE_SKYRIM_SE
1512 _seID = a_seID;
1513#endif
1514#ifdef ENABLE_SKYRIM_AE
1515 _aeID = a_aeID;
1516#endif
1517#ifdef ENABLE_SKYRIM_VR
1518 _vrID = a_seID;
1519#endif
1520 }
1521
1522 explicit constexpr RelocationID([[maybe_unused]] std::uint64_t a_seID, [[maybe_unused]] std::uint64_t a_aeID,
1523 [[maybe_unused]] std::uint64_t a_vrID) noexcept {
1524#ifdef ENABLE_SKYRIM_SE
1525 _seID = a_seID;
1526#endif
1527#ifdef ENABLE_SKYRIM_AE
1528 _aeID = a_aeID;
1529#endif
1530#ifdef ENABLE_SKYRIM_VR
1531 _vrID = a_vrID;
1532#endif
1533 }
1534
1535 [[nodiscard]] std::uintptr_t address() const {
1536 auto thisOffset = offset();
1537 return thisOffset ? base() + offset() : 0;
1538 }
1539
1540 [[nodiscard]] std::size_t offset() const {
1541 auto thisID = id();
1542 return thisID ? IDDatabase::get().id2offset(thisID) : 0;
1543 }
1544
1545 [[nodiscard]] SKYRIM_REL std::uint64_t id() const noexcept {
1546 switch (Module::GetRuntime()) {
1547#ifdef ENABLE_SKYRIM_AE
1549 return _aeID;
1550#endif
1551#ifdef ENABLE_SKYRIM_SE
1553 return _seID;
1554#endif
1555#ifdef ENABLE_SKYRIM_VR
1557 return _vrID;
1558#endif
1559 default:
1560 return 0;
1561 }
1562 }
1563
1564 [[nodiscard]] SKYRIM_REL explicit operator ID() const noexcept {
1565 return ID(id());
1566 }
1567
1568 private:
1569 [[nodiscard]] static std::uintptr_t base() { return Module::get().base(); }
1570
1571#ifdef ENABLE_SKYRIM_SE
1572 std::uint64_t _seID{0};
1573#endif
1574#ifdef ENABLE_SKYRIM_AE
1575 std::uint64_t _aeID{0};
1576#endif
1577#ifdef ENABLE_SKYRIM_VR
1578 std::uint64_t _vrID{0};
1579#endif
1580 };
1581
1583 public:
1584 constexpr VariantID() noexcept = default;
1585
1586 explicit constexpr VariantID([[maybe_unused]] std::uint64_t a_seID, [[maybe_unused]] std::uint64_t a_aeID,
1587 [[maybe_unused]] std::uint64_t a_vrOffset) noexcept {
1588#ifdef ENABLE_SKYRIM_SE
1589 _seID = a_seID;
1590#endif
1591#ifdef ENABLE_SKYRIM_AE
1592 _aeID = a_aeID;
1593#endif
1594#ifdef ENABLE_SKYRIM_VR
1595 _vrOffset = a_vrOffset;
1596#endif
1597 }
1598
1599 [[nodiscard]] std::uintptr_t address() const {
1600 auto thisOffset = offset();
1601 return thisOffset ? base() + offset() : 0;
1602 }
1603
1604 [[nodiscard]] std::size_t offset() const {
1605 switch (Module::GetRuntime()) {
1606#ifdef ENABLE_SKYRIM_AE
1608 return _aeID ? IDDatabase::get().id2offset(_aeID) : 0;
1609#endif
1610#ifdef ENABLE_SKYRIM_SE
1612 return _seID ? IDDatabase::get().id2offset(_seID) : 0;
1613#endif
1614#ifdef ENABLE_SKYRIM_VR
1616 return _vrOffset;
1617#endif
1618 default:
1619 return 0;
1620 }
1621 }
1622
1623 private:
1624 [[nodiscard]] static std::uintptr_t base() { return Module::get().base(); }
1625
1626#ifdef ENABLE_SKYRIM_SE
1627 std::uint64_t _seID{0};
1628#endif
1629#ifdef ENABLE_SKYRIM_AE
1630 std::uint64_t _aeID{0};
1631#endif
1632#ifdef ENABLE_SKYRIM_VR
1633 std::uint64_t _vrOffset{0};
1634#endif
1635 };
1636
1637 template<class T>
1639 public:
1641 std::conditional_t<
1642 std::is_member_pointer_v<T> || std::is_function_v<std::remove_pointer_t<T>>,
1643 std::decay_t<T>,
1644 T>;
1645
1646 constexpr Relocation() noexcept = default;
1647
1648 explicit constexpr Relocation(std::uintptr_t a_address) noexcept:
1649 _impl{a_address} {}
1650
1651 explicit Relocation(Offset a_offset) :
1652 _impl{a_offset.address()} {}
1653
1654 explicit Relocation(VariantOffset a_offset) :
1655 _impl{a_offset.address()} {}
1656
1657 explicit Relocation(ID a_id) :
1658 _impl{a_id.address()} {}
1659
1660 explicit Relocation(ID a_id, std::ptrdiff_t a_offset) :
1661 _impl{a_id.address() + a_offset} {}
1662
1663 explicit Relocation(ID a_id, Offset a_offset) :
1664 _impl{a_id.address() + a_offset.offset()} {}
1665
1666 explicit Relocation(ID a_id, VariantOffset a_offset) :
1667 _impl{a_id.address() + a_offset.offset()} {}
1668
1669 explicit Relocation(RelocationID a_id) :
1670 _impl{a_id.address()} {}
1671
1672 explicit Relocation(RelocationID a_id, std::ptrdiff_t a_offset) :
1673 _impl{a_id.address() + a_offset} {}
1674
1675 explicit Relocation(RelocationID a_id, Offset a_offset) :
1676 _impl{a_id.address() + a_offset.offset()} {}
1677
1678 explicit Relocation(RelocationID a_id, VariantOffset a_offset) :
1679 _impl{a_id.address() + a_offset.offset()} {}
1680
1681 explicit Relocation(VariantID a_id) :
1682 _impl{a_id.address()} {}
1683
1684 explicit Relocation(VariantID a_id, std::ptrdiff_t a_offset) :
1685 _impl{a_id.address() + a_offset} {}
1686
1687 explicit Relocation(VariantID a_id, Offset a_offset) :
1688 _impl{a_id.address() + a_offset.offset()} {}
1689
1690 explicit Relocation(VariantID a_id, VariantOffset a_offset) :
1691 _impl{a_id.address() + a_offset.offset()} {}
1692
1693 constexpr Relocation &operator=(std::uintptr_t a_address) noexcept {
1694 _impl = a_address;
1695 return *this;
1696 }
1697
1699 _impl = a_offset.address();
1700 return *this;
1701 }
1702
1704 _impl = a_offset.address();
1705 return *this;
1706 }
1707
1709 _impl = a_id.address();
1710 return *this;
1711 }
1712
1714 _impl = a_id.address();
1715 return *this;
1716 }
1717
1719 _impl = a_id.address();
1720 return *this;
1721 }
1722
1723 template<class U = value_type>
1724 [[nodiscard]] decltype(auto) operator*() const noexcept //
1725 requires(std::is_pointer_v<U>)
1726 {
1727 return *get();
1728 }
1729
1730 template<class U = value_type>
1731 [[nodiscard]] auto operator->() const noexcept //
1732 requires(std::is_pointer_v<U>)
1733 {
1734 return get();
1735 }
1736
1737 template<class... Args>
1738 std::invoke_result_t<const value_type &, Args...> operator()(Args &&... a_args) const //
1739 noexcept(std::is_nothrow_invocable_v<const value_type &, Args...>) //
1740 requires(std::invocable<const value_type &, Args...>)
1741 {
1742 return REL::invoke(get(), std::forward<Args>(a_args)...);
1743 }
1744
1745 [[nodiscard]]
1746
1747 constexpr std::uintptr_t address() const noexcept { return _impl; }
1748
1749 [[nodiscard]] std::size_t offset() const { return _impl - base(); }
1750
1751 [[nodiscard]] value_type get() const //
1752 noexcept(std::is_nothrow_copy_constructible_v<value_type>) {
1753 assert(_impl != 0);
1754 return stl::unrestricted_cast<value_type>(_impl);
1755 }
1756
1757 template<class U = value_type>
1758 std::uintptr_t write_vfunc(std::size_t a_idx, std::uintptr_t a_newFunc) //
1759 requires(std::same_as<U, std::uintptr_t>)
1760 {
1761 const auto addr = address() + (sizeof(void *) * a_idx);
1762 const auto result = *reinterpret_cast<std::uintptr_t *>(addr);
1763 safe_write(addr, a_newFunc);
1764 return result;
1765 }
1766
1767 template<class F>
1768 std::uintptr_t write_vfunc(std::size_t a_idx, F a_newFunc) //
1769 requires(std::same_as<value_type, std::uintptr_t>)
1770 {
1771 return write_vfunc(a_idx, stl::unrestricted_cast<std::uintptr_t>(a_newFunc));
1772 }
1773
1774 private:
1775 // clang-format off
1776 [[nodiscard]] static std::uintptr_t base() { return Module::get().base(); }
1777 // clang-format on
1778
1779 std::uintptr_t _impl{0};
1780 };
1781
1782 namespace detail {
1783 namespace characters {
1784 [[nodiscard]] constexpr bool hexadecimal(char a_ch) noexcept {
1785 return ('0' <= a_ch && a_ch <= '9') ||
1786 ('A' <= a_ch && a_ch <= 'F') ||
1787 ('a' <= a_ch && a_ch <= 'f');
1788 }
1789
1790 [[nodiscard]] constexpr bool space(char a_ch) noexcept {
1791 return a_ch == ' ';
1792 }
1793
1794 [[nodiscard]] constexpr bool wildcard(char a_ch) noexcept {
1795 return a_ch == '?';
1796 }
1797 }
1798
1799 namespace rules {
1800 namespace detail {
1801 [[nodiscard]] constexpr std::byte hexacharacters_to_hexadecimal(char a_hi, char a_lo) noexcept {
1802 constexpr auto lut = []() noexcept {
1803 std::array<std::uint8_t, (std::numeric_limits<unsigned char>::max)() + 1> a = {};
1804
1805 const auto iterate = [&](std::uint8_t a_iFirst, unsigned char a_cFirst,
1806 unsigned char a_cLast) noexcept {
1807 for (; a_cFirst <= a_cLast; ++a_cFirst, ++a_iFirst) {
1808 a[a_cFirst] = a_iFirst;
1809 }
1810 };
1811
1812 iterate(0, '0', '9');
1813 iterate(0xA, 'A', 'F');
1814 iterate(0xa, 'a', 'f');
1815
1816 return a;
1817 }();
1818
1819 return static_cast<std::byte>(
1820 lut[static_cast<unsigned char>(a_hi)] * 0x10u +
1821 lut[static_cast<unsigned char>(a_lo)]);
1822 }
1823 }
1824
1825 template<char HI, char LO>
1827 public:
1828 [[nodiscard]] static constexpr bool match(std::byte a_byte) noexcept {
1829 constexpr auto expected = detail::hexacharacters_to_hexadecimal(HI, LO);
1830 return a_byte == expected;
1831 }
1832 };
1833
1834 static_assert(Hexadecimal<'5', '7'>::match(std::byte{0x57}));
1835 static_assert(Hexadecimal<'6', '5'>::match(std::byte{0x65}));
1836 static_assert(Hexadecimal<'B', 'D'>::match(std::byte{0xBD}));
1837 static_assert(Hexadecimal<'1', 'C'>::match(std::byte{0x1C}));
1838 static_assert(Hexadecimal<'F', '2'>::match(std::byte{0xF2}));
1839 static_assert(Hexadecimal<'9', 'f'>::match(std::byte{0x9f}));
1840
1841 static_assert(!Hexadecimal<'D', '4'>::match(std::byte{0xF8}));
1842 static_assert(!Hexadecimal<'6', '7'>::match(std::byte{0xAA}));
1843 static_assert(!Hexadecimal<'7', '8'>::match(std::byte{0xE3}));
1844 static_assert(!Hexadecimal<'6', 'E'>::match(std::byte{0x61}));
1845
1846 class Wildcard {
1847 public:
1848 [[nodiscard]] static constexpr bool match(std::byte) noexcept {
1849 return true;
1850 }
1851 };
1852
1853 static_assert(Wildcard::match(std::byte{0xB9}));
1854 static_assert(Wildcard::match(std::byte{0x96}));
1855 static_assert(Wildcard::match(std::byte{0x35}));
1856 static_assert(Wildcard::match(std::byte{0xE4}));
1857
1858 template<char, char>
1859 void rule_for() noexcept;
1860
1861 template<char C1, char C2>
1862 Hexadecimal<C1, C2>
1863 rule_for() noexcept requires (characters::hexadecimal(C1) && characters::hexadecimal(C2));
1864
1865 template<char C1, char C2>
1866 Wildcard rule_for() noexcept requires (characters::wildcard(C1) && characters::wildcard(C2));
1867 }
1868
1869 template<class... Rules>
1871 public:
1872 static_assert(sizeof...(Rules) >= 1, "must provide at least 1 rule for the pattern matcher");
1873
1874 [[nodiscard]] constexpr bool match(std::span<const std::byte, sizeof...(Rules)> a_bytes) const noexcept {
1875 std::size_t i = 0;
1876 return (Rules::match(a_bytes[i++]) && ...);
1877 }
1878
1879 [[nodiscard]] bool match(std::uintptr_t a_address) const noexcept {
1880 return this->match(*reinterpret_cast<const std::byte(*)[sizeof...(Rules)]>(a_address));
1881 }
1882
1883 void match_or_fail(std::uintptr_t a_address,
1884 SKSE::stl::source_location a_loc = SKSE::stl::source_location::current()) const noexcept {
1885 if (!this->match(a_address)) {
1886 const auto version = Module::get().version();
1888 fmt::format(
1889 "A pattern has failed to match.\n"
1890 "This means the plugin is incompatible with the current version of the game ({}.{}.{}). "
1891 "Head to the mod page of this plugin to see if an update is available."sv,
1892 version[0],
1893 version[1],
1894 version[2]),
1895 a_loc);
1896 }
1897 }
1898 };
1899
1900 void consteval_error(const char *a_error);
1901
1902 template<stl::nttp::string S, class... Rules>
1903 [[nodiscard]] constexpr auto do_make_pattern() noexcept {
1904 if constexpr (S.length() == 0) {
1905 return PatternMatcher<Rules...>();
1906 } else if constexpr (S.length() == 1) {
1907 constexpr char c = S[0];
1908 if constexpr (characters::hexadecimal(c) || characters::wildcard(c)) {
1910 "the given pattern has an unpaired rule (rules are required to be written in pairs of 2)");
1911 } else {
1912 consteval_error("the given pattern has trailing characters at the end (which is not allowed)");
1913 }
1914 } else {
1915 using rule_t = decltype(rules::rule_for<S[0], S[1]>());
1916 if constexpr (std::same_as<rule_t, void>) {
1917 consteval_error("the given pattern failed to match any known rules");
1918 } else {
1919 if constexpr (S.length() <= 3) {
1920 return do_make_pattern<S.template substr<2>(), Rules..., rule_t>();
1921 } else if constexpr (characters::space(S.value_at(2))) {
1922 return do_make_pattern<S.template substr<3>(), Rules..., rule_t>();
1923 } else {
1924 consteval_error("a space character is required to split byte patterns");
1925 }
1926 }
1927 }
1928 }
1929
1930 template<class... Bytes>
1931 [[nodiscard]] consteval auto make_byte_array(Bytes... a_bytes) noexcept
1932 -> std::array<std::byte, sizeof...(Bytes)> {
1933 static_assert((std::integral<Bytes> && ...), "all bytes must be an integral type");
1934 return {static_cast<std::byte>(a_bytes)...};
1935 }
1936 }
1937
1938 template<stl::nttp::string S>
1939 [[nodiscard]] constexpr auto make_pattern() noexcept {
1940 return detail::do_make_pattern<S>();
1941 }
1942
1943 static_assert(make_pattern<"40 10 F2 ??">().match(
1944 detail::make_byte_array(0x40, 0x10, 0xF2, 0x41)));
1945 static_assert(make_pattern<"B8 D0 ?? ?? D4 6E">().match(
1946 detail::make_byte_array(0xB8, 0xD0, 0x35, 0x2A, 0xD4, 0x6E)));
1947
1963 template<class T>
1964 [[nodiscard]] SKYRIM_ADDR T Relocate([[maybe_unused]] T &&a_seAndVR, [[maybe_unused]] T &&a_ae) noexcept {
1965#ifndef ENABLE_SKYRIM_AE
1966 return a_seAndVR;
1967#elif !defined(ENABLE_SKYRIM_SE) && !defined(ENABLE_SKYRIM_VR)
1968 return a_ae;
1969#else
1970 return Module::IsAE() ? a_ae : a_seAndVR;
1971#endif
1972 }
1973
1991 template<class T>
1992 [[nodiscard]] SKYRIM_REL T Relocate([[maybe_unused]] T a_se, [[maybe_unused]] T a_ae,
1993 [[maybe_unused]] T a_vr) noexcept {
1994#if !defined(ENABLE_SKYRIM_AE) && !defined(ENABLE_SKYRIM_VR)
1995 return a_se;
1996#elif !defined(ENABLE_SKYRIM_SE) && !defined(ENABLE_SKYRIM_VR)
1997 return a_ae;
1998#elif !defined(ENABLE_SKYRIM_AE) && !defined(ENABLE_SKYRIM_SE)
1999 return a_vr;
2000#else
2001 switch (Module::get().GetRuntime()) {
2003 return a_ae;
2005 return a_vr;
2006 default:
2007 return a_se;
2008 }
2009#endif
2010 }
2011
2012 namespace detail {
2013 template<class T>
2015 };
2016
2017 template<class Ret, class This>
2018 struct RelocateVirtualHelper<Ret(This *)> {
2019 using this_type = This;
2020 using return_type = Ret;
2021 using function_type = Ret(This *);
2022 };
2023
2024 template<class Ret, class This, class... Args>
2025 struct RelocateVirtualHelper<Ret(This *, Args...)> {
2026 using this_type = This;
2027 using return_type = Ret;
2028 using function_type = Ret(This *, Args...);
2029 };
2030
2031 template<class Ret, class This>
2032 struct RelocateVirtualHelper<Ret (This::*)()> {
2033 using this_type = This;
2034 using return_type = Ret;
2035 using function_type = Ret(This *);
2036 };
2037
2038 template<class Ret, class This, class... Args>
2039 struct RelocateVirtualHelper<Ret (This::*)(Args...)> {
2040 using this_type = This;
2041 using return_type = Ret;
2042 using function_type = Ret(This *, Args...);
2043 };
2044
2045 template<class Ret, class This>
2046 struct RelocateVirtualHelper<Ret (This::*)() const> {
2047 using this_type = const This;
2048 using return_type = Ret;
2049 using function_type = Ret(const This *);
2050 };
2051
2052 template<class Ret, class This, class... Args>
2053 struct RelocateVirtualHelper<Ret (This::*)(Args...) const> {
2054 using this_type = const This;
2055 using return_type = Ret;
2056 using function_type = Ret(const This *, Args...);
2057 };
2058 }
2059
2080 template<class Fn, class... Args>
2082 [[maybe_unused]] std::ptrdiff_t a_seAndAEVtableOffset, [[maybe_unused]] std::ptrdiff_t a_vrVtableOffset,
2083 [[maybe_unused]] std::ptrdiff_t a_seAndAEVtableIndex, [[maybe_unused]] std::ptrdiff_t a_vrVtableIndex,
2084 typename detail::RelocateVirtualHelper<Fn>::this_type *a_self, Args &&... a_args) {
2085 return (*reinterpret_cast<typename detail::RelocateVirtualHelper<Fn>::function_type **>(
2086 *reinterpret_cast<const uintptr_t *>(reinterpret_cast<uintptr_t>(a_self) +
2087 #ifndef ENABLE_SKYRIM_VR
2088 a_seAndAEVtableOffset) +
2089 a_seAndAEVtableIndex
2090 #elif !defined(ENABLE_SKYRIM_AE) && !defined(ENABLE_SKYRIM_SE)
2091 a_vrVtableOffset) +
2092 a_vrVtableIndex
2093 #else
2094 (Module::IsVR() ? a_vrVtableOffset : a_seAndAEVtableOffset)) +
2095 (Module::IsVR() ? a_vrVtableIndex : a_seAndAEVtableIndex)
2096 #endif
2097 * sizeof(uintptr_t)))(a_self, std::forward<Args>(a_args)...);
2098 }
2099
2123 template<class Fn, class... Args>
2125 std::ptrdiff_t a_seAndAEVtableIndex, std::ptrdiff_t a_vrVtableIndex,
2126 typename detail::RelocateVirtualHelper<Fn>::this_type *a_self, Args &&... a_args) {
2127 return RelocateVirtual<Fn, Args...>(0, 0, a_seAndAEVtableIndex, a_vrVtableIndex, a_self,
2128 std::forward<Args>(a_args)...);
2129 }
2130
2148 template<class T, class This>
2149 [[nodiscard]] inline T &RelocateMember(This *a_self, std::ptrdiff_t a_seAndAE, std::ptrdiff_t a_vr) {
2150 return *reinterpret_cast<T *>(reinterpret_cast<uintptr_t>(a_self) + Relocate(a_seAndAE, a_seAndAE, a_vr));
2151 }
2152
2153 template<class T, class This>
2154 [[nodiscard]] inline T &RelocateMember(This *a_self, std::ptrdiff_t offset) {
2155 return *reinterpret_cast<T *>(reinterpret_cast<uintptr_t>(a_self) + offset);
2156 }
2157
2158 template<class T, class This>
2159 [[nodiscard]] inline T &RelocateMemberIf(bool condition, This *a_self, std::ptrdiff_t a, std::ptrdiff_t b) {
2160 return *reinterpret_cast<T *>(reinterpret_cast<uintptr_t>(a_self) + (condition ? a : b));
2161 }
2162
2163 template<class T, class This>
2164 [[nodiscard]] inline T &RelocateMemberIfNewer(Version v, This *a_self, std::ptrdiff_t older, std::ptrdiff_t newer) {
2165 return *reinterpret_cast<T *>(reinterpret_cast<uintptr_t>(a_self) +
2166 (REL::Module::get().version().compare(v) == std::strong_ordering::less ? older : newer));
2167 }
2168}
2169
2170namespace std {
2171 [[nodiscard]] inline std::string to_string(REL::Version a_version) {
2172 return a_version.string("."sv);
2173 }
2174
2175#ifdef __cpp_lib_format
2176
2177 template<class CharT>
2178 struct formatter<REL::Version, CharT> : formatter<std::string, CharT> {
2179 template<class FormatContext>
2180 auto format(const REL::Version &a_version, FormatContext &a_ctx) {
2181 return formatter<std::string, CharT>::format(to_string(a_version), a_ctx);
2182 }
2183 };
2184
2185#endif
2186}
2187
2188namespace fmt {
2189 template<class CharT>
2190 struct formatter<REL::Version, CharT> : formatter<std::string, CharT> {
2191 template<class FormatContext>
2192 auto format(const REL::Version &a_version, FormatContext &a_ctx) {
2193 return formatter<std::string, CharT>::format(std::to_string(a_version), a_ctx);
2194 }
2195 };
2196}
2197
2198#undef REL_MAKE_MEMBER_FUNCTION_NON_POD_TYPE
2199#undef REL_MAKE_MEMBER_FUNCTION_NON_POD_TYPE_HELPER
2200#undef REL_MAKE_MEMBER_FUNCTION_NON_POD_TYPE_HELPER_IMPL
2201
2202#undef REL_MAKE_MEMBER_FUNCTION_POD_TYPE
2203#undef REL_MAKE_MEMBER_FUNCTION_POD_TYPE_HELPER
2204#undef REL_MAKE_MEMBER_FUNCTION_POD_TYPE_HELPER_IMPL
#define SKYRIM_REL_VR
Definition Relocation.h:134
#define SKYRIM_REL_VR_CONSTEXPR
Definition Relocation.h:143
#define SKYRIM_REL_CONSTEXPR
Definition Relocation.h:105
#define SKYRIM_ADDR
Definition Relocation.h:76
#define SKYRIM_REL
Definition Relocation.h:96
#define GetEnvironmentVariable
Definition WinAPI.h:580
#define IMAGE_SCN_MEM_WRITE
Definition WinAPI.h:572
#define VerQueryValue
Definition WinAPI.h:583
#define LoadLibrary
Definition WinAPI.h:586
#define GetFileVersionInfo
Definition WinAPI.h:584
#define IMAGE_SCN_MEM_EXECUTE
Definition WinAPI.h:571
#define GetModuleHandle
Definition WinAPI.h:585
#define GetFileVersionInfoSize
Definition WinAPI.h:581
#define PAGE_EXECUTE_READWRITE
Definition WinAPI.h:578
Definition Relocation.h:965
Offset2ID()
Definition Relocation.h:989
typename container_type::const_iterator const_iterator
Definition Relocation.h:970
typename container_type::const_reverse_iterator const_reverse_iterator
Definition Relocation.h:971
const_reverse_iterator crbegin() const noexcept
Definition Relocation.h:1021
const_iterator begin() const noexcept
Definition Relocation.h:1011
std::vector< value_type > container_type
Definition Relocation.h:968
size_type size() const noexcept
Definition Relocation.h:1027
Offset2ID(ExecutionPolicy &&a_policy)
Definition Relocation.h:974
const_reverse_iterator rbegin() const noexcept
Definition Relocation.h:1019
const_reverse_iterator crend() const noexcept
Definition Relocation.h:1025
const_iterator cbegin() const noexcept
Definition Relocation.h:1013
typename container_type::size_type size_type
Definition Relocation.h:969
const_iterator cend() const noexcept
Definition Relocation.h:1017
std::uint64_t operator()(std::size_t a_offset) const
Definition Relocation.h:992
mapping_t value_type
Definition Relocation.h:967
const_reverse_iterator rend() const noexcept
Definition Relocation.h:1023
const_iterator end() const noexcept
Definition Relocation.h:1015
Definition Relocation.h:951
static IDDatabase & get()
Definition Relocation.h:1033
std::size_t id2offset(std::uint64_t a_id) const
Definition Relocation.h:1069
Format
Definition Relocation.h:959
Definition Relocation.h:1481
std::size_t offset() const
Definition Relocation.h:1497
constexpr ID & operator=(std::uint64_t a_id) noexcept
Definition Relocation.h:1488
constexpr std::uint64_t id() const noexcept
Definition Relocation.h:1495
constexpr ID() noexcept=default
std::uintptr_t address() const
Definition Relocation.h:1493
Definition Relocation.h:634
static SKYRIM_REL_VR bool IsVR() noexcept
Definition Relocation.h:821
static SKYRIM_REL bool IsSE() noexcept
Definition Relocation.h:814
Runtime
Definition Relocation.h:639
Version version() const noexcept
Definition Relocation.h:778
T * pointer() const noexcept
Definition Relocation.h:785
std::uintptr_t base() const noexcept
Definition Relocation.h:772
stl::zwstring filename() const noexcept
Definition Relocation.h:774
static Module & get()
Definition Relocation.h:658
void * pointer() const noexcept
Definition Relocation.h:782
stl::zwstring filePath() const noexcept
Definition Relocation.h:776
Segment segment(Segment::Name a_segment) const noexcept
Definition Relocation.h:780
static SKYRIM_REL Runtime GetRuntime() noexcept
Definition Relocation.h:792
static SKYRIM_REL bool IsAE() noexcept
Definition Relocation.h:807
Definition Relocation.h:1401
constexpr Offset() noexcept=default
std::uintptr_t address() const
Definition Relocation.h:1413
constexpr Offset & operator=(std::size_t a_offset) noexcept
Definition Relocation.h:1408
constexpr std::size_t offset() const noexcept
Definition Relocation.h:1415
Definition Relocation.h:1638
std::uintptr_t write_vfunc(std::size_t a_idx, std::uintptr_t a_newFunc)
Definition Relocation.h:1758
constexpr std::uintptr_t address() const noexcept
Definition Relocation.h:1747
Relocation & operator=(VariantOffset a_offset)
Definition Relocation.h:1703
std::conditional_t< std::is_member_pointer_v< T >||std::is_function_v< std::remove_pointer_t< T > >, std::decay_t< T >, T > value_type
Definition Relocation.h:1644
Relocation & operator=(ID a_id)
Definition Relocation.h:1708
Relocation(ID a_id, Offset a_offset)
Definition Relocation.h:1663
Relocation & operator=(VariantID a_id)
Definition Relocation.h:1718
Relocation(VariantID a_id, VariantOffset a_offset)
Definition Relocation.h:1690
value_type get() const noexcept(std::is_nothrow_copy_constructible_v< value_type >)
Definition Relocation.h:1751
Relocation(Offset a_offset)
Definition Relocation.h:1651
auto operator->() const noexcept
Definition Relocation.h:1731
Relocation(RelocationID a_id, Offset a_offset)
Definition Relocation.h:1675
Relocation(ID a_id, std::ptrdiff_t a_offset)
Definition Relocation.h:1660
Relocation(VariantID a_id, Offset a_offset)
Definition Relocation.h:1687
constexpr Relocation & operator=(std::uintptr_t a_address) noexcept
Definition Relocation.h:1693
Relocation(VariantID a_id)
Definition Relocation.h:1681
Relocation(RelocationID a_id, std::ptrdiff_t a_offset)
Definition Relocation.h:1672
Relocation(VariantID a_id, std::ptrdiff_t a_offset)
Definition Relocation.h:1684
constexpr Relocation() noexcept=default
Relocation(ID a_id, VariantOffset a_offset)
Definition Relocation.h:1666
Relocation(VariantOffset a_offset)
Definition Relocation.h:1654
Relocation(ID a_id)
Definition Relocation.h:1657
Relocation(RelocationID a_id)
Definition Relocation.h:1669
Relocation(RelocationID a_id, VariantOffset a_offset)
Definition Relocation.h:1678
std::invoke_result_t< const value_type &, Args... > operator()(Args &&... a_args) const noexcept(std::is_nothrow_invocable_v< const value_type &, Args... >)
Definition Relocation.h:1738
std::size_t offset() const
Definition Relocation.h:1749
Relocation & operator=(RelocationID a_id)
Definition Relocation.h:1713
Relocation & operator=(Offset a_offset)
Definition Relocation.h:1698
std::uintptr_t write_vfunc(std::size_t a_idx, F a_newFunc)
Definition Relocation.h:1768
Definition Relocation.h:1505
constexpr RelocationID(std::uint64_t a_seID, std::uint64_t a_aeID, std::uint64_t a_vrID) noexcept
Definition Relocation.h:1522
std::size_t offset() const
Definition Relocation.h:1540
SKYRIM_REL std::uint64_t id() const noexcept
Definition Relocation.h:1545
std::uintptr_t address() const
Definition Relocation.h:1535
constexpr RelocationID() noexcept=default
Definition Relocation.h:592
std::size_t size() const noexcept
Definition Relocation.h:617
Name
Definition Relocation.h:594
@ data
Definition Relocation.h:598
@ tls
Definition Relocation.h:600
@ textw
Definition Relocation.h:601
@ gfids
Definition Relocation.h:602
@ total
Definition Relocation.h:603
@ idata
Definition Relocation.h:596
@ textx
Definition Relocation.h:595
@ pdata
Definition Relocation.h:599
@ rdata
Definition Relocation.h:597
void * pointer() const noexcept
Definition Relocation.h:619
std::uintptr_t address() const noexcept
Definition Relocation.h:613
Segment() noexcept=default
std::size_t offset() const noexcept
Definition Relocation.h:615
T * pointer() const noexcept
Definition Relocation.h:622
Definition Relocation.h:1582
std::uintptr_t address() const
Definition Relocation.h:1599
constexpr VariantID() noexcept=default
std::size_t offset() const
Definition Relocation.h:1604
Definition Relocation.h:1423
std::uintptr_t address() const
Definition Relocation.h:1441
SKYRIM_REL std::size_t offset() const noexcept
Definition Relocation.h:1446
constexpr VariantOffset() noexcept=default
Definition Relocation.h:401
const value_type & const_reference
Definition Relocation.h:405
constexpr value_type build() const noexcept
Definition Relocation.h:481
constexpr decltype(auto) end() const noexcept
Definition Relocation.h:448
constexpr Version(value_type a_v1, value_type a_v2=0, value_type a_v3=0, value_type a_v4=0) noexcept
Definition Relocation.h:412
constexpr value_type patch() const noexcept
Definition Relocation.h:477
constexpr reference operator[](std::size_t a_idx) noexcept
Definition Relocation.h:440
std::strong_ordering constexpr compare(const Version &a_rhs) const noexcept
Definition Relocation.h:452
constexpr decltype(auto) cbegin() const noexcept
Definition Relocation.h:446
std::string string(std::string_view a_separator="-"sv) const
Definition Relocation.h:485
std::wstring wstring(std::wstring_view a_separator=L"-"sv) const
Definition Relocation.h:495
constexpr value_type minor() const noexcept
Definition Relocation.h:473
constexpr std::uint32_t pack() const noexcept
Definition Relocation.h:461
constexpr Version(std::string_view a_version)
Definition Relocation.h:415
constexpr const_reference operator[](std::size_t a_idx) const noexcept
Definition Relocation.h:442
static constexpr Version unpack(std::uint32_t a_packedVersion) noexcept
Definition Relocation.h:505
std::uint16_t value_type
Definition Relocation.h:403
constexpr decltype(auto) cend() const noexcept
Definition Relocation.h:450
value_type & reference
Definition Relocation.h:404
constexpr Version() noexcept=default
constexpr decltype(auto) begin() const noexcept
Definition Relocation.h:444
constexpr value_type major() const noexcept
Definition Relocation.h:469
Definition Relocation.h:1870
constexpr bool match(std::span< const std::byte, sizeof...(Rules)> a_bytes) const noexcept
Definition Relocation.h:1874
bool match(std::uintptr_t a_address) const noexcept
Definition Relocation.h:1879
void match_or_fail(std::uintptr_t a_address, SKSE::stl::source_location a_loc=SKSE::stl::source_location::current()) const noexcept
Definition Relocation.h:1883
Definition Relocation.h:184
~memory_map()
Definition Relocation.h:197
memory_map & operator=(memory_map &&a_rhs) noexcept
Definition Relocation.h:201
memory_map & operator=(const memory_map &)=delete
memory_map() noexcept=default
void * data() noexcept
Definition Relocation.h:212
bool create(stl::zwstring a_name, std::size_t a_size)
bool open(stl::zwstring a_name, std::size_t a_size)
Definition Relocation.h:1826
static constexpr bool match(std::byte a_byte) noexcept
Definition Relocation.h:1828
Definition Relocation.h:1846
static constexpr bool match(std::byte) noexcept
Definition Relocation.h:1848
constexpr bool space(char a_ch) noexcept
Definition Relocation.h:1790
constexpr bool hexadecimal(char a_ch) noexcept
Definition Relocation.h:1784
constexpr bool wildcard(char a_ch) noexcept
Definition Relocation.h:1794
constexpr std::byte hexacharacters_to_hexadecimal(char a_hi, char a_lo) noexcept
Definition Relocation.h:1801
void rule_for() noexcept
typename member_function_non_pod_type< F >::type member_function_non_pod_type_t
Definition Relocation.h:245
decltype(auto) invoke_member_function_non_pod(F &&a_func, First &&a_first, Rest &&... a_rest) noexcept(std::is_nothrow_invocable_v< F, First, Rest... >)
Definition Relocation.h:304
consteval auto make_byte_array(Bytes... a_bytes) noexcept -> std::array< std::byte, sizeof...(Bytes)>
Definition Relocation.h:1931
typename member_function_pod_type< F >::type member_function_pod_type_t
Definition Relocation.h:234
constexpr bool is_x64_pod_v
Definition Relocation.h:298
void consteval_error(const char *a_error)
constexpr auto do_make_pattern() noexcept
Definition Relocation.h:1903
REL_MAKE_MEMBER_FUNCTION_POD_TYPE()
REL_MAKE_MEMBER_FUNCTION_NON_POD_TYPE()
constexpr uint8_t read_version(std::array< typename REL::Version::value_type, 4 > &result)
Definition Relocation.h:528
Definition Relocation.h:182
std::invoke_result_t< F, Args... > invoke(F &&a_func, Args &&... a_args) noexcept(std::is_nothrow_invocable_v< F, Args... >)
Definition Relocation.h:332
constexpr std::uint8_t NOP6[]
Definition Relocation.h:321
constexpr std::uint8_t JMP8
Definition Relocation.h:326
std::optional< Version > get_file_version(stl::zwstring a_filename)
Definition Relocation.h:563
constexpr std::uint8_t NOP
Definition Relocation.h:316
detail::RelocateVirtualHelper< Fn >::return_type RelocateVirtual(std::ptrdiff_t a_seAndAEVtableOffset, std::ptrdiff_t a_vrVtableOffset, std::ptrdiff_t a_seAndAEVtableIndex, std::ptrdiff_t a_vrVtableIndex, typename detail::RelocateVirtualHelper< Fn >::this_type *a_self, Args &&... a_args)
Definition Relocation.h:2081
constexpr std::uint8_t NOP4[]
Definition Relocation.h:319
constexpr std::strong_ordering operator<=>(const Version &a_lhs, const Version &a_rhs) noexcept
Definition Relocation.h:523
constexpr std::uint8_t INT3
Definition Relocation.h:329
constexpr std::uint8_t NOP8[]
Definition Relocation.h:323
T & RelocateMember(This *a_self, std::ptrdiff_t a_seAndAE, std::ptrdiff_t a_vr)
Definition Relocation.h:2149
constexpr bool operator==(const Version &a_lhs, const Version &a_rhs) noexcept
Definition Relocation.h:518
constexpr std::uint8_t NOP2[]
Definition Relocation.h:317
T & RelocateMemberIf(bool condition, This *a_self, std::ptrdiff_t a, std::ptrdiff_t b)
Definition Relocation.h:2159
void safe_fill(std::uintptr_t a_dst, std::uint8_t a_value, std::size_t a_count)
Definition Relocation.h:380
void safe_write(std::uintptr_t a_dst, const void *a_src, std::size_t a_count)
Definition Relocation.h:349
constexpr std::uint8_t NOP5[]
Definition Relocation.h:320
constexpr std::uint8_t NOP3[]
Definition Relocation.h:318
constexpr auto make_pattern() noexcept
Definition Relocation.h:1939
constexpr std::uint8_t NOP9[]
Definition Relocation.h:324
SKYRIM_ADDR T Relocate(T &&a_seAndVR, T &&a_ae) noexcept
Definition Relocation.h:1964
T & RelocateMemberIfNewer(Version v, This *a_self, std::ptrdiff_t older, std::ptrdiff_t newer)
Definition Relocation.h:2164
constexpr std::uint8_t RET
Definition Relocation.h:328
constexpr std::uint8_t JMP32
Definition Relocation.h:327
constexpr std::uint8_t NOP7[]
Definition Relocation.h:322
bool VirtualProtect(void *a_address, std::size_t a_size, std::uint32_t a_newProtect, std::uint32_t *a_oldProtect) noexcept
auto HKEY_LOCAL_MACHINE
Definition WinAPI.h:84
HINSTANCE HMODULE
Definition WinAPI.h:79
long RegGetValueW(HKEY hkey, const char *subKey, const char *value, unsigned long flags, unsigned long *type, void *data, unsigned long *length)
string(const CharT(&)[N]) -> string< CharT, N - 1 >
bool report_and_error(std::string_view a_msg, bool a_fail=true, SKSE::stl::source_location a_loc=SKSE::stl::source_location::current())
Definition PCH.h:658
std::source_location source_location
Definition PCH.h:600
auto utf16_to_utf8(std::wstring_view a_in) noexcept -> std::optional< std::string >
Definition PCH.h:571
auto utf8_to_utf16(std::string_view a_in) noexcept -> std::optional< std::wstring >
Definition PCH.h:545
basic_zstring< wchar_t > zwstring
Definition PCH.h:79
void report_and_fail(std::string_view a_msg, SKSE::stl::source_location a_loc=SKSE::stl::source_location::current())
Definition PCH.h:721
Definition ActorValueList.h:50
Definition ActorValueList.h:28
std::string to_string(RE::ActorValue a_actorValue)
Definition ActorValueList.h:29
bool getline(RE::NiBinaryStream &a_input, std::basic_string< CharT, Traits, Allocator > &a_str)
Definition NiBinaryStream.h:96
Ret(This *, Args...) function_type
Definition Relocation.h:2028
Ret(This *) function_type
Definition Relocation.h:2021
Ret return_type
Definition Relocation.h:2020
This this_type
Definition Relocation.h:2019
const This this_type
Definition Relocation.h:2047
Ret(const This *) function_type
Definition Relocation.h:2049
Ret return_type
Definition Relocation.h:2034
Ret(This *) function_type
Definition Relocation.h:2035
This this_type
Definition Relocation.h:2033
Ret(const This *, Args...) function_type
Definition Relocation.h:2056
Ret(This *, Args...) function_type
Definition Relocation.h:2042
Definition Relocation.h:2014
Definition Relocation.h:275
Definition Relocation.h:265
Definition Relocation.h:255
Definition Relocation.h:270
Definition Relocation.h:226
auto format(const REL::Version &a_version, FormatContext &a_ctx)
Definition Relocation.h:2192