CommonLibSSE (powerof3)
Loading...
Searching...
No Matches
Relocation.h
Go to the documentation of this file.
1#pragma once
2
3#define REL_MAKE_MEMBER_FUNCTION_POD_TYPE_HELPER_IMPL(a_nopropQual, a_propQual, ...) \
4 template < \
5 class R, \
6 class Cls, \
7 class... Args> \
8 struct member_function_pod_type<R (Cls::*)(Args...) __VA_ARGS__ a_nopropQual a_propQual> \
9 { \
10 using type = R(__VA_ARGS__ Cls*, Args...) a_propQual; \
11 }; \
12 \
13 template < \
14 class R, \
15 class Cls, \
16 class... Args> \
17 struct member_function_pod_type<R (Cls::*)(Args..., ...) __VA_ARGS__ a_nopropQual a_propQual> \
18 { \
19 using type = R(__VA_ARGS__ Cls*, Args..., ...) a_propQual; \
20 };
21
22#define REL_MAKE_MEMBER_FUNCTION_POD_TYPE_HELPER(a_qualifer, ...) \
23 REL_MAKE_MEMBER_FUNCTION_POD_TYPE_HELPER_IMPL(a_qualifer, , ##__VA_ARGS__) \
24 REL_MAKE_MEMBER_FUNCTION_POD_TYPE_HELPER_IMPL(a_qualifer, noexcept, ##__VA_ARGS__)
25
26#define REL_MAKE_MEMBER_FUNCTION_POD_TYPE(...) \
27 REL_MAKE_MEMBER_FUNCTION_POD_TYPE_HELPER(, __VA_ARGS__) \
28 REL_MAKE_MEMBER_FUNCTION_POD_TYPE_HELPER(&, ##__VA_ARGS__) \
29 REL_MAKE_MEMBER_FUNCTION_POD_TYPE_HELPER(&&, ##__VA_ARGS__)
30
31#define REL_MAKE_MEMBER_FUNCTION_NON_POD_TYPE_HELPER_IMPL(a_nopropQual, a_propQual, ...) \
32 template < \
33 class R, \
34 class Cls, \
35 class... Args> \
36 struct member_function_non_pod_type<R (Cls::*)(Args...) __VA_ARGS__ a_nopropQual a_propQual> \
37 { \
38 using type = R&(__VA_ARGS__ Cls*, void*, Args...)a_propQual; \
39 }; \
40 \
41 template < \
42 class R, \
43 class Cls, \
44 class... Args> \
45 struct member_function_non_pod_type<R (Cls::*)(Args..., ...) __VA_ARGS__ a_nopropQual a_propQual> \
46 { \
47 using type = R&(__VA_ARGS__ Cls*, void*, Args..., ...)a_propQual; \
48 };
49
50#define REL_MAKE_MEMBER_FUNCTION_NON_POD_TYPE_HELPER(a_qualifer, ...) \
51 REL_MAKE_MEMBER_FUNCTION_NON_POD_TYPE_HELPER_IMPL(a_qualifer, , ##__VA_ARGS__) \
52 REL_MAKE_MEMBER_FUNCTION_NON_POD_TYPE_HELPER_IMPL(a_qualifer, noexcept, ##__VA_ARGS__)
53
54#define REL_MAKE_MEMBER_FUNCTION_NON_POD_TYPE(...) \
55 REL_MAKE_MEMBER_FUNCTION_NON_POD_TYPE_HELPER(, __VA_ARGS__) \
56 REL_MAKE_MEMBER_FUNCTION_NON_POD_TYPE_HELPER(&, ##__VA_ARGS__) \
57 REL_MAKE_MEMBER_FUNCTION_NON_POD_TYPE_HELPER(&&, ##__VA_ARGS__)
58
59namespace REL
60{
61 namespace detail
62 {
64 {
65 public:
66 memory_map() noexcept = default;
67 memory_map(const memory_map&) = delete;
68
69 memory_map(memory_map&& a_rhs) noexcept :
70 _mapping(a_rhs._mapping),
71 _view(a_rhs._view)
72 {
73 a_rhs._mapping = nullptr;
74 a_rhs._view = nullptr;
75 }
76
78
79 memory_map& operator=(const memory_map&) = delete;
80
81 memory_map& operator=(memory_map&& a_rhs) noexcept
82 {
83 if (this != std::addressof(a_rhs)) {
84 _mapping = a_rhs._mapping;
85 a_rhs._mapping = nullptr;
86
87 _view = a_rhs._view;
88 a_rhs._view = nullptr;
89 }
90 return *this;
91 }
92
93 [[nodiscard]] void* data() noexcept { return _view; }
94
95 bool open(stl::zwstring a_name, std::size_t a_size);
96 bool create(stl::zwstring a_name, std::size_t a_size);
97 void close();
98
99 private:
100 void* _mapping{ nullptr };
101 void* _view{ nullptr };
102 };
103
104 template <class>
106
110 REL_MAKE_MEMBER_FUNCTION_POD_TYPE(const volatile);
111
112 template <class F>
114
115 template <class>
117
122
123 template <class F>
125
126 // https://docs.microsoft.com/en-us/cpp/build/x64-calling-convention
127
128 template <class T>
130 std::disjunction<
131 std::bool_constant<sizeof(T) == 1>,
132 std::bool_constant<sizeof(T) == 2>,
133 std::bool_constant<sizeof(T) == 4>,
134 std::bool_constant<sizeof(T) == 8>>
135 {};
136
137 template <class T>
139 std::conjunction<
140 std::is_trivially_constructible<T>,
141 std::is_trivially_destructible<T>,
142 std::is_trivially_copy_assignable<T>,
143 std::negation<
144 std::is_polymorphic<T>>>
145 {};
146
147 template <class T>
149 std::is_standard_layout<T>
150 {};
151
152 template <class T, class = void>
153 struct is_x64_pod :
154 std::true_type
155 {};
156
157 template <class T>
159 T,
160 std::enable_if_t<
161 std::is_union_v<T>>> :
162 std::false_type
163 {};
164
165 template <class T>
167 T,
168 std::enable_if_t<
169 std::is_class_v<T>>> :
170 std::conjunction<
171 meets_length_req<T>,
172 meets_function_req<T>,
173 meets_member_req<T>>
174 {};
175
176 template <class T>
177 inline constexpr bool is_x64_pod_v = is_x64_pod<T>::value;
178
179 template <
180 class F,
181 class First,
182 class... Rest>
183 decltype(auto) invoke_member_function_non_pod(F&& a_func, First&& a_first, Rest&&... a_rest) //
184 noexcept(std::is_nothrow_invocable_v<F, First, Rest...>)
185 {
186 using result_t = std::invoke_result_t<F, First, Rest...>;
187 std::aligned_storage_t<sizeof(result_t), alignof(result_t)> result;
188
190 auto func = stl::unrestricted_cast<func_t*>(std::forward<F>(a_func));
191
192 return func(std::forward<First>(a_first), std::addressof(result), std::forward<Rest>(a_rest)...);
193 }
194 }
195
196 inline constexpr std::uint8_t NOP = 0x90;
197 inline constexpr std::uint8_t RET = 0xC3;
198 inline constexpr std::uint8_t INT3 = 0xCC;
199
200 template <class F, class... Args>
201 std::invoke_result_t<F, Args...> invoke(F&& a_func, Args&&... a_args) //
202 noexcept(std::is_nothrow_invocable_v<F, Args...>) //
203 requires(std::invocable<F, Args...>)
204 {
205 if constexpr (std::is_member_function_pointer_v<std::decay_t<F>>) {
206 if constexpr (detail::is_x64_pod_v<std::invoke_result_t<F, Args...>>) { // member functions == free functions in x64
208 auto func = stl::unrestricted_cast<func_t*>(std::forward<F>(a_func));
209 return func(std::forward<Args>(a_args)...);
210 } else { // shift args to insert result
211 return detail::invoke_member_function_non_pod(std::forward<F>(a_func), std::forward<Args>(a_args)...);
212 }
213 } else {
214 return std::forward<F>(a_func)(std::forward<Args>(a_args)...);
215 }
216 }
217
218 inline void safe_write(std::uintptr_t a_dst, const void* a_src, std::size_t a_count)
219 {
220 std::uint32_t old{ 0 };
221 auto success =
223 reinterpret_cast<void*>(a_dst),
224 a_count,
226 std::addressof(old));
227 if (success != 0) {
228 std::memcpy(reinterpret_cast<void*>(a_dst), a_src, a_count);
229 success =
231 reinterpret_cast<void*>(a_dst),
232 a_count,
233 old,
234 std::addressof(old));
235 }
236
237 assert(success != 0);
238 }
239
240 template <std::integral T>
241 void safe_write(std::uintptr_t a_dst, const T& a_data)
242 {
243 safe_write(a_dst, std::addressof(a_data), sizeof(T));
244 }
245
246 template <class T>
247 void safe_write(std::uintptr_t a_dst, std::span<T> a_data)
248 {
249 safe_write(a_dst, a_data.data(), a_data.size_bytes());
250 }
251
252 inline void safe_fill(std::uintptr_t a_dst, std::uint8_t a_value, std::size_t a_count)
253 {
254 std::uint32_t old{ 0 };
255 auto success =
257 reinterpret_cast<void*>(a_dst),
258 a_count,
260 std::addressof(old));
261 if (success != 0) {
262 std::fill_n(reinterpret_cast<std::uint8_t*>(a_dst), a_count, a_value);
263 success =
265 reinterpret_cast<void*>(a_dst),
266 a_count,
267 old,
268 std::addressof(old));
269 }
270
271 assert(success != 0);
272 }
273
275 {
276 public:
277 using value_type = std::uint16_t;
280
281 constexpr Version() noexcept = default;
282
283 explicit constexpr Version(std::array<value_type, 4> a_version) noexcept :
284 _impl(a_version)
285 {}
286
287 constexpr Version(value_type a_v1, value_type a_v2 = 0, value_type a_v3 = 0, value_type a_v4 = 0) noexcept :
288 _impl{ a_v1, a_v2, a_v3, a_v4 }
289 {}
290
291 [[nodiscard]] constexpr reference operator[](std::size_t a_idx) noexcept { return _impl[a_idx]; }
292 [[nodiscard]] constexpr const_reference operator[](std::size_t a_idx) const noexcept { return _impl[a_idx]; }
293
294 [[nodiscard]] constexpr decltype(auto) begin() const noexcept { return _impl.begin(); }
295 [[nodiscard]] constexpr decltype(auto) cbegin() const noexcept { return _impl.cbegin(); }
296 [[nodiscard]] constexpr decltype(auto) end() const noexcept { return _impl.end(); }
297 [[nodiscard]] constexpr decltype(auto) cend() const noexcept { return _impl.cend(); }
298
299 [[nodiscard]] std::strong_ordering constexpr compare(const Version& a_rhs) const noexcept
300 {
301 for (std::size_t i = 0; i < _impl.size(); ++i) {
302 if ((*this)[i] != a_rhs[i]) {
303 return (*this)[i] < a_rhs[i] ? std::strong_ordering::less : std::strong_ordering::greater;
304 }
305 }
306 return std::strong_ordering::equal;
307 }
308
309 [[nodiscard]] constexpr std::uint32_t pack() const noexcept
310 {
311 return static_cast<std::uint32_t>(
312 (_impl[0] & 0x0FF) << 24u |
313 (_impl[1] & 0x0FF) << 16u |
314 (_impl[2] & 0xFFF) << 4u |
315 (_impl[3] & 0x00F) << 0u);
316 }
317
318 [[nodiscard]] std::string string() const
319 {
320 std::string result;
321 for (auto&& ver : _impl) {
322 result += std::to_string(ver);
323 result += '-';
324 }
325 result.pop_back();
326 return result;
327 }
328
329 [[nodiscard]] std::wstring wstring() const
330 {
331 std::wstring result;
332 for (auto&& ver : _impl) {
333 result += std::to_wstring(ver);
334 result += L'-';
335 }
336 result.pop_back();
337 return result;
338 }
339
340 private:
341 std::array<value_type, 4> _impl{ 0, 0, 0, 0 };
342 };
343
344 [[nodiscard]] constexpr bool operator==(const Version& a_lhs, const Version& a_rhs) noexcept { return a_lhs.compare(a_rhs) == 0; }
345 [[nodiscard]] constexpr std::strong_ordering operator<=>(const Version& a_lhs, const Version& a_rhs) noexcept { return a_lhs.compare(a_rhs); }
346
347 [[nodiscard]] inline std::optional<Version> get_file_version(stl::zwstring a_filename)
348 {
349 std::uint32_t dummy;
350 std::vector<char> buf(WinAPI::GetFileVersionInfoSize(a_filename.data(), std::addressof(dummy)));
351 if (buf.empty()) {
352 return std::nullopt;
353 }
354
355 if (!WinAPI::GetFileVersionInfo(a_filename.data(), 0, static_cast<std::uint32_t>(buf.size()), buf.data())) {
356 return std::nullopt;
357 }
358
359 void* verBuf{ nullptr };
360 std::uint32_t verLen{ 0 };
361 if (!WinAPI::VerQueryValue(buf.data(), L"\\StringFileInfo\\040904B0\\ProductVersion", std::addressof(verBuf), std::addressof(verLen))) {
362 return std::nullopt;
363 }
364
365 Version version;
366 std::wistringstream ss(
367 std::wstring(static_cast<const wchar_t*>(verBuf), verLen));
368 std::wstring token;
369 for (std::size_t i = 0; i < 4 && std::getline(ss, token, L'.'); ++i) {
370 version[i] = static_cast<std::uint16_t>(std::stoi(token));
371 }
372
373 return version;
374 }
375
377 {
378 public:
379 enum Name : std::size_t
380 {
389 total
390 };
391
392 Segment() noexcept = default;
393
394 Segment(std::uintptr_t a_proxyBase, std::uintptr_t a_address, std::uintptr_t a_size) noexcept :
395 _proxyBase(a_proxyBase),
396 _address(a_address),
397 _size(a_size)
398 {}
399
400 [[nodiscard]] std::uintptr_t address() const noexcept { return _address; }
401 [[nodiscard]] std::size_t offset() const noexcept { return address() - _proxyBase; }
402 [[nodiscard]] std::size_t size() const noexcept { return _size; }
403
404 [[nodiscard]] void* pointer() const noexcept { return reinterpret_cast<void*>(address()); }
405
406 template <class T>
407 [[nodiscard]] T* pointer() const noexcept
408 {
409 return static_cast<T*>(pointer());
410 }
411
412 private:
413 std::uintptr_t _proxyBase{ 0 };
414 std::uintptr_t _address{ 0 };
415 std::size_t _size{ 0 };
416 };
417
418 class Module
419 {
420 public:
421 [[nodiscard]] static Module& get()
422 {
423 static Module singleton;
424 return singleton;
425 }
426
427 [[nodiscard]] std::uintptr_t base() const noexcept { return _base; }
428 [[nodiscard]] stl::zwstring filename() const noexcept { return _filename; }
429 [[nodiscard]] Version version() const noexcept { return _version; }
430
431 [[nodiscard]] Segment segment(Segment::Name a_segment) const noexcept { return _segments[a_segment]; }
432
433 [[nodiscard]] void* pointer() const noexcept { return reinterpret_cast<void*>(base()); }
434
435 template <class T>
436 [[nodiscard]] T* pointer() const noexcept
437 {
438 return static_cast<T*>(pointer());
439 }
440
441 private:
442 Module()
443 {
444 const auto getFilename = [&]() {
446 ENVIRONMENT.data(),
447 _filename.data(),
448 static_cast<std::uint32_t>(_filename.size()));
449 };
450
451 _filename.resize(getFilename());
452 if (const auto result = getFilename();
453 result != _filename.size() - 1 ||
454 result == 0) {
455 _filename = L"SkyrimSE.exe"sv;
456 }
457
458 load();
459 }
460
461 Module(const Module&) = delete;
462 Module(Module&&) = delete;
463
464 ~Module() noexcept = default;
465
466 Module& operator=(const Module&) = delete;
467 Module& operator=(Module&&) = delete;
468
469 void load()
470 {
471 auto handle = WinAPI::GetModuleHandle(_filename.c_str());
472 if (handle == nullptr) {
474 fmt::format(
475 "Failed to obtain module handle for: \"{0}\".\n"
476 "You have likely renamed the executable to something unexpected. "
477 "Renaming the executable back to \"{0}\" may resolve the issue."sv,
478 stl::utf16_to_utf8(_filename).value_or("<unicode conversion error>"s)));
479 }
480 _base = reinterpret_cast<std::uintptr_t>(handle);
481
482 load_version();
483 load_segments();
484 }
485
486 void load_segments();
487
488 void load_version()
489 {
490 const auto version = get_file_version(_filename);
491 if (version) {
492 _version = *version;
493 } else {
495 fmt::format(
496 "Failed to obtain file version info for: {}\n"
497 "Please contact the author of this script extender plugin for further assistance."sv,
498 stl::utf16_to_utf8(_filename).value_or("<unicode conversion error>"s)));
499 }
500 }
501
502 static constexpr std::array SEGMENTS{
503 std::make_pair(".text"sv, WinAPI::IMAGE_SCN_MEM_EXECUTE),
504 std::make_pair(".idata"sv, static_cast<std::uint32_t>(0)),
505 std::make_pair(".rdata"sv, static_cast<std::uint32_t>(0)),
506 std::make_pair(".data"sv, static_cast<std::uint32_t>(0)),
507 std::make_pair(".pdata"sv, static_cast<std::uint32_t>(0)),
508 std::make_pair(".tls"sv, static_cast<std::uint32_t>(0)),
509 std::make_pair(".text"sv, WinAPI::IMAGE_SCN_MEM_WRITE),
510 std::make_pair(".gfids"sv, static_cast<std::uint32_t>(0))
511 };
512
513 static constexpr auto ENVIRONMENT = L"SKSE_RUNTIME"sv;
514
515 std::wstring _filename;
516 std::array<Segment, Segment::total> _segments;
517 Version _version;
518 std::uintptr_t _base{ 0 };
519 };
520
522 {
523 private:
524 struct mapping_t
525 {
526 std::uint64_t id;
527 std::uint64_t offset;
528 };
529
530 public:
532 {
533 public:
534 using value_type = mapping_t;
535 using container_type = std::vector<value_type>;
536 using size_type = typename container_type::size_type;
537 using const_iterator = typename container_type::const_iterator;
538 using const_reverse_iterator = typename container_type::const_reverse_iterator;
539
540 template <class ExecutionPolicy>
541 explicit Offset2ID(ExecutionPolicy&& a_policy) //
542 requires(std::is_execution_policy_v<std::decay_t<ExecutionPolicy>>)
543 {
544 const std::span<const mapping_t> id2offset = IDDatabase::get()._id2offset;
545 _offset2id.reserve(id2offset.size());
546 _offset2id.insert(_offset2id.begin(), id2offset.begin(), id2offset.end());
547 std::sort(
548 a_policy,
549 _offset2id.begin(),
550 _offset2id.end(),
551 [](auto&& a_lhs, auto&& a_rhs) {
552 return a_lhs.offset < a_rhs.offset;
553 });
554 }
555
557 Offset2ID(std::execution::sequenced_policy{})
558 {}
559
560 [[nodiscard]] std::uint64_t operator()(std::size_t a_offset) const
561 {
562 const mapping_t elem{ 0, a_offset };
563 const auto it = std::lower_bound(
564 _offset2id.begin(),
565 _offset2id.end(),
566 elem,
567 [](auto&& a_lhs, auto&& a_rhs) {
568 return a_lhs.offset < a_rhs.offset;
569 });
570 if (it == _offset2id.end()) {
572 fmt::format(
573 "Failed to find the offset within the database: 0x{:08X}"sv,
574 a_offset));
575 }
576
577 return it->id;
578 }
579
580 [[nodiscard]] const_iterator begin() const noexcept { return _offset2id.begin(); }
581 [[nodiscard]] const_iterator cbegin() const noexcept { return _offset2id.cbegin(); }
582
583 [[nodiscard]] const_iterator end() const noexcept { return _offset2id.end(); }
584 [[nodiscard]] const_iterator cend() const noexcept { return _offset2id.cend(); }
585
586 [[nodiscard]] const_reverse_iterator rbegin() const noexcept { return _offset2id.rbegin(); }
587 [[nodiscard]] const_reverse_iterator crbegin() const noexcept { return _offset2id.crbegin(); }
588
589 [[nodiscard]] const_reverse_iterator rend() const noexcept { return _offset2id.rend(); }
590 [[nodiscard]] const_reverse_iterator crend() const noexcept { return _offset2id.crend(); }
591
592 [[nodiscard]] size_type size() const noexcept { return _offset2id.size(); }
593
594 private:
595 container_type _offset2id;
596 };
597
598 [[nodiscard]] static IDDatabase& get()
599 {
600 static IDDatabase singleton;
601 return singleton;
602 }
603
604 [[nodiscard]] inline std::size_t id2offset(std::uint64_t a_id) const
605 {
606 mapping_t elem{ a_id, 0 };
607 const auto it = std::lower_bound(
608 _id2offset.begin(),
609 _id2offset.end(),
610 elem,
611 [](auto&& a_lhs, auto&& a_rhs) {
612 return a_lhs.id < a_rhs.id;
613 });
614 if (it == _id2offset.end()) {
616 fmt::format(
617 "Failed to find the id within the address library: {}\n"
618 "This means this script extender plugin is incompatible with the address "
619 "library for this version of the game, and thus does not support it."sv,
620 a_id));
621 }
622
623 return static_cast<std::size_t>(it->offset);
624 }
625
626 private:
627 friend Offset2ID;
628
629 class header_t
630 {
631 public:
632 void read(binary_io::file_istream& a_in)
633 {
634 const auto [format] = a_in.read<std::int32_t>();
635#ifdef SKYRIM_SUPPORT_AE
636 if (format != 2) {
637#else
638 if (format != 1) {
639#endif
641 fmt::format(
642 "Unsupported address library format: {}\n"
643 "This means this script extender plugin is incompatible with the address "
644 "library available for this version of the game, and thus does not "
645 "support it."sv,
646 format));
647 }
648
649 const auto [major, minor, patch, revision] =
650 a_in.read<std::int32_t, std::int32_t, std::int32_t, std::int32_t>();
651 _version[0] = static_cast<std::uint16_t>(major);
652 _version[1] = static_cast<std::uint16_t>(minor);
653 _version[2] = static_cast<std::uint16_t>(patch);
654 _version[3] = static_cast<std::uint16_t>(revision);
655
656 const auto [nameLen] = a_in.read<std::int32_t>();
657 a_in.seek_relative(nameLen);
658
659 a_in.read(_pointerSize, _addressCount);
660 }
661
662 [[nodiscard]] std::size_t address_count() const noexcept { return static_cast<std::size_t>(_addressCount); }
663 [[nodiscard]] std::uint64_t pointer_size() const noexcept { return static_cast<std::uint64_t>(_pointerSize); }
664 [[nodiscard]] Version version() const noexcept { return _version; }
665
666 private:
667 Version _version;
668 std::int32_t _pointerSize{ 0 };
669 std::int32_t _addressCount{ 0 };
670 };
671
672 IDDatabase() { load(); }
673
674 IDDatabase(const IDDatabase&) = delete;
675 IDDatabase(IDDatabase&&) = delete;
676
677 ~IDDatabase() = default;
678
679 IDDatabase& operator=(const IDDatabase&) = delete;
680 IDDatabase& operator=(IDDatabase&&) = delete;
681
682 void load()
683 {
684 const auto version = Module::get().version();
685 const auto filename =
687 fmt::format(
688#ifdef SKYRIM_SUPPORT_AE
689 "Data/SKSE/Plugins/versionlib-{}.bin"sv,
690#else
691 "Data/SKSE/Plugins/version-{}.bin"sv,
692#endif
693 version.string()))
694 .value_or(L"<unknown filename>"s);
695 load_file(filename, version);
696 }
697
698 void load_file(stl::zwstring a_filename, Version a_version)
699 {
700 try {
701 binary_io::file_istream in(a_filename);
702 header_t header;
703 header.read(in);
704 if (header.version() != a_version) {
705 stl::report_and_fail("version mismatch"sv);
706 }
707
708 auto mapname = L"CommonLibSSEOffsets-v2-"s;
709 mapname += a_version.wstring();
710 const auto byteSize = static_cast<std::size_t>(header.address_count()) * sizeof(mapping_t);
711 if (_mmap.open(mapname, byteSize)) {
712 _id2offset = { static_cast<mapping_t*>(_mmap.data()), header.address_count() };
713 } else if (_mmap.create(mapname, byteSize)) {
714 _id2offset = { static_cast<mapping_t*>(_mmap.data()), header.address_count() };
715 unpack_file(in, header);
716 std::sort(
717 _id2offset.begin(),
718 _id2offset.end(),
719 [](auto&& a_lhs, auto&& a_rhs) {
720 return a_lhs.id < a_rhs.id;
721 });
722 } else {
723 stl::report_and_fail("failed to create shared mapping"sv);
724 }
725 } catch (const std::system_error&) {
727 fmt::format(
728 "Failed to locate an appropriate address library with the path: {}\n"
729 "This means you are missing the address library for this specific version of "
730 "the game. Please continue to the mod page for address library to download "
731 "an appropriate version. If one is not available, then it is likely that "
732 "address library has not yet added support for this version of the game."sv,
733 stl::utf16_to_utf8(a_filename).value_or("<unknown filename>"s)));
734 }
735 }
736
737 void unpack_file(binary_io::file_istream& a_in, header_t a_header)
738 {
739 std::uint8_t type = 0;
740 std::uint64_t id = 0;
741 std::uint64_t offset = 0;
742 std::uint64_t prevID = 0;
743 std::uint64_t prevOffset = 0;
744 for (auto& mapping : _id2offset) {
745 a_in.read(type);
746 const auto lo = static_cast<std::uint8_t>(type & 0xF);
747 const auto hi = static_cast<std::uint8_t>(type >> 4);
748
749 switch (lo) {
750 case 0:
751 a_in.read(id);
752 break;
753 case 1:
754 id = prevID + 1;
755 break;
756 case 2:
757 id = prevID + std::get<0>(a_in.read<std::uint8_t>());
758 break;
759 case 3:
760 id = prevID - std::get<0>(a_in.read<std::uint8_t>());
761 break;
762 case 4:
763 id = prevID + std::get<0>(a_in.read<std::uint16_t>());
764 break;
765 case 5:
766 id = prevID - std::get<0>(a_in.read<std::uint16_t>());
767 break;
768 case 6:
769 std::tie(id) = a_in.read<std::uint16_t>();
770 break;
771 case 7:
772 std::tie(id) = a_in.read<std::uint32_t>();
773 break;
774 default:
775 stl::report_and_fail("unhandled type"sv);
776 }
777
778 const std::uint64_t tmp = (hi & 8) != 0 ? (prevOffset / a_header.pointer_size()) : prevOffset;
779
780 switch (hi & 7) {
781 case 0:
782 a_in.read(offset);
783 break;
784 case 1:
785 offset = tmp + 1;
786 break;
787 case 2:
788 offset = tmp + std::get<0>(a_in.read<std::uint8_t>());
789 break;
790 case 3:
791 offset = tmp - std::get<0>(a_in.read<std::uint8_t>());
792 break;
793 case 4:
794 offset = tmp + std::get<0>(a_in.read<std::uint16_t>());
795 break;
796 case 5:
797 offset = tmp - std::get<0>(a_in.read<std::uint16_t>());
798 break;
799 case 6:
800 std::tie(offset) = a_in.read<std::uint16_t>();
801 break;
802 case 7:
803 std::tie(offset) = a_in.read<std::uint32_t>();
804 break;
805 default:
806 stl::report_and_fail("unhandled type"sv);
807 }
808
809 if ((hi & 8) != 0) {
810 offset *= a_header.pointer_size();
811 }
812
813 mapping = { id, offset };
814
815 prevOffset = offset;
816 prevID = id;
817 }
818 }
819
820 detail::memory_map _mmap;
821 std::span<mapping_t> _id2offset;
822 };
823
824 class Offset
825 {
826 public:
827 constexpr Offset() noexcept = default;
828
829 explicit constexpr Offset(std::size_t a_offset) noexcept :
830 _offset(a_offset)
831 {}
832
833 constexpr Offset& operator=(std::size_t a_offset) noexcept
834 {
835 _offset = a_offset;
836 return *this;
837 }
838
839 [[nodiscard]] std::uintptr_t address() const { return base() + offset(); }
840 [[nodiscard]] constexpr std::size_t offset() const noexcept { return _offset; }
841
842 private:
843 [[nodiscard]] static std::uintptr_t base() { return Module::get().base(); }
844
845 std::size_t _offset{ 0 };
846 };
847
848 class ID
849 {
850 public:
851 constexpr ID() noexcept = default;
852
853 explicit constexpr ID(std::uint64_t a_id) noexcept :
854 _id(a_id)
855 {}
856
857 constexpr ID& operator=(std::uint64_t a_id) noexcept
858 {
859 _id = a_id;
860 return *this;
861 }
862
863 [[nodiscard]] std::uintptr_t address() const { return base() + offset(); }
864 [[nodiscard]] constexpr std::uint64_t id() const noexcept { return _id; }
865 [[nodiscard]] std::size_t offset() const { return IDDatabase::get().id2offset(_id); }
866
867 private:
868 [[nodiscard]] static std::uintptr_t base() { return Module::get().base(); }
869
870 std::uint64_t _id{ 0 };
871 };
872
873 template <class T>
875 {
876 public:
878 std::conditional_t<
879 std::is_member_pointer_v<T> || std::is_function_v<std::remove_pointer_t<T>>,
880 std::decay_t<T>,
881 T>;
882
883 constexpr Relocation() noexcept = default;
884
885 explicit constexpr Relocation(std::uintptr_t a_address) noexcept :
886 _impl{ a_address }
887 {}
888
889 explicit Relocation(Offset a_offset) :
890 _impl{ a_offset.address() }
891 {}
892
893 explicit Relocation(ID a_id) :
894 _impl{ a_id.address() }
895 {}
896
897 explicit Relocation(ID a_id, std::ptrdiff_t a_offset) :
898 _impl{ a_id.address() + a_offset }
899 {}
900
901 constexpr Relocation& operator=(std::uintptr_t a_address) noexcept
902 {
903 _impl = a_address;
904 return *this;
905 }
906
908 {
909 _impl = a_offset.address();
910 return *this;
911 }
912
914 {
915 _impl = a_id.address();
916 return *this;
917 }
918
919 template <class U = value_type>
920 [[nodiscard]] decltype(auto) operator*() const noexcept //
921 requires(std::is_pointer_v<U>)
922 {
923 return *get();
924 }
925
926 template <class U = value_type>
927 [[nodiscard]] auto operator->() const noexcept //
928 requires(std::is_pointer_v<U>)
929 {
930 return get();
931 }
932
933 template <class... Args>
934 std::invoke_result_t<const value_type&, Args...> operator()(Args&&... a_args) const //
935 noexcept(std::is_nothrow_invocable_v<const value_type&, Args...>) //
936 requires(std::invocable<const value_type&, Args...>)
937 {
938 return REL::invoke(get(), std::forward<Args>(a_args)...);
939 }
940
941 [[nodiscard]] constexpr std::uintptr_t address() const noexcept { return _impl; }
942 [[nodiscard]] std::size_t offset() const { return _impl - base(); }
943
944 [[nodiscard]] value_type get() const //
945 noexcept(std::is_nothrow_copy_constructible_v<value_type>)
946 {
947 assert(_impl != 0);
948 return stl::unrestricted_cast<value_type>(_impl);
949 }
950
951 template <class U = value_type>
952 std::uintptr_t write_vfunc(std::size_t a_idx, std::uintptr_t a_newFunc) //
953 requires(std::same_as<U, std::uintptr_t>)
954 {
955 const auto addr = address() + (sizeof(void*) * a_idx);
956 const auto result = *reinterpret_cast<std::uintptr_t*>(addr);
957 safe_write(addr, a_newFunc);
958 return result;
959 }
960
961 template <class F>
962 std::uintptr_t write_vfunc(std::size_t a_idx, F a_newFunc) //
963 requires(std::same_as<value_type, std::uintptr_t>)
964 {
965 return write_vfunc(a_idx, stl::unrestricted_cast<std::uintptr_t>(a_newFunc));
966 }
967
968 private :
969 // clang-format off
970 [[nodiscard]] static std::uintptr_t base() { return Module::get().base(); }
971 // clang-format on
972
973 std::uintptr_t _impl{ 0 };
974 };
975
976 namespace detail
977 {
978 namespace characters
979 {
980 [[nodiscard]] constexpr bool hexadecimal(char a_ch) noexcept
981 {
982 return ('0' <= a_ch && a_ch <= '9') ||
983 ('A' <= a_ch && a_ch <= 'F') ||
984 ('a' <= a_ch && a_ch <= 'f');
985 }
986
987 [[nodiscard]] constexpr bool space(char a_ch) noexcept
988 {
989 return a_ch == ' ';
990 }
991
992 [[nodiscard]] constexpr bool wildcard(char a_ch) noexcept
993 {
994 return a_ch == '?';
995 }
996 }
997
998 namespace rules
999 {
1000 namespace detail
1001 {
1002 [[nodiscard]] consteval std::byte hexacharacters_to_hexadecimal(char a_hi, char a_lo) noexcept
1003 {
1004 constexpr auto lut = []() noexcept {
1005 std::array<std::uint8_t, std::numeric_limits<unsigned char>::max() + 1> a = {};
1006
1007 const auto iterate = [&](std::uint8_t a_iFirst, unsigned char a_cFirst, unsigned char a_cLast) noexcept {
1008 for (; a_cFirst <= a_cLast; ++a_cFirst, ++a_iFirst) {
1009 a[a_cFirst] = a_iFirst;
1010 }
1011 };
1012
1013 iterate(0, '0', '9');
1014 iterate(0xA, 'A', 'F');
1015 iterate(0xa, 'a', 'f');
1016
1017 return a;
1018 }();
1019
1020 return static_cast<std::byte>(
1021 lut[static_cast<unsigned char>(a_hi)] * 0x10u +
1022 lut[static_cast<unsigned char>(a_lo)]);
1023 }
1024 }
1025
1026 template <char HI, char LO>
1028 {
1029 public:
1030 [[nodiscard]] static constexpr bool match(std::byte a_byte) noexcept
1031 {
1032 constexpr auto expected = detail::hexacharacters_to_hexadecimal(HI, LO);
1033 return a_byte == expected;
1034 }
1035 };
1036
1037 static_assert(Hexadecimal<'5', '7'>::match(std::byte{ 0x57 }));
1038 static_assert(Hexadecimal<'6', '5'>::match(std::byte{ 0x65 }));
1039 static_assert(Hexadecimal<'B', 'D'>::match(std::byte{ 0xBD }));
1040 static_assert(Hexadecimal<'1', 'C'>::match(std::byte{ 0x1C }));
1041 static_assert(Hexadecimal<'F', '2'>::match(std::byte{ 0xF2 }));
1042 static_assert(Hexadecimal<'9', 'f'>::match(std::byte{ 0x9f }));
1043
1044 static_assert(!Hexadecimal<'D', '4'>::match(std::byte{ 0xF8 }));
1045 static_assert(!Hexadecimal<'6', '7'>::match(std::byte{ 0xAA }));
1046 static_assert(!Hexadecimal<'7', '8'>::match(std::byte{ 0xE3 }));
1047 static_assert(!Hexadecimal<'6', 'E'>::match(std::byte{ 0x61 }));
1048
1050 {
1051 public:
1052 [[nodiscard]] static constexpr bool match(std::byte) noexcept
1053 {
1054 return true;
1055 }
1056 };
1057
1058 static_assert(Wildcard::match(std::byte{ 0xB9 }));
1059 static_assert(Wildcard::match(std::byte{ 0x96 }));
1060 static_assert(Wildcard::match(std::byte{ 0x35 }));
1061 static_assert(Wildcard::match(std::byte{ 0xE4 }));
1062
1063 template <char, char>
1064 void rule_for() noexcept;
1065
1066 template <char C1, char C2>
1067 Hexadecimal<C1, C2> rule_for() noexcept
1068 requires(characters::hexadecimal(C1) && characters::hexadecimal(C2));
1069
1070 template <char C1, char C2>
1071 Wildcard rule_for() noexcept
1072 requires(characters::wildcard(C1) && characters::wildcard(C2));
1073 }
1074
1075 template <class... Rules>
1077 {
1078 public:
1079 static_assert(sizeof...(Rules) >= 1, "must provide at least 1 rule for the pattern matcher");
1080
1081 [[nodiscard]] constexpr bool match(std::span<const std::byte, sizeof...(Rules)> a_bytes) const noexcept
1082 {
1083 std::size_t i = 0;
1084 return (Rules::match(a_bytes[i++]) && ...);
1085 }
1086
1087 [[nodiscard]] bool match(std::uintptr_t a_address) const noexcept
1088 {
1089 return this->match(*reinterpret_cast<const std::byte(*)[sizeof...(Rules)]>(a_address));
1090 }
1091
1092 void match_or_fail(std::uintptr_t a_address, std::source_location a_loc = std::source_location::current()) const noexcept
1093 {
1094 if (!this->match(a_address)) {
1095 const auto version = Module::get().version();
1097 fmt::format(
1098 "A pattern has failed to match.\n"
1099 "This means the plugin is incompatible with the current version of the game ({}.{}.{}). "
1100 "Head to the mod page of this plugin to see if an update is available."sv,
1101 version[0],
1102 version[1],
1103 version[2]),
1104 a_loc);
1105 }
1106 }
1107 };
1108
1109 void consteval_error(const char* a_error);
1110
1111 template <stl::nttp::string S, class... Rules>
1112 [[nodiscard]] constexpr auto do_make_pattern() noexcept
1113 {
1114 if constexpr (S.length() == 0) {
1115 return PatternMatcher<Rules...>();
1116 } else if constexpr (S.length() == 1) {
1117 constexpr char c = S[0];
1118 if constexpr (characters::hexadecimal(c) || characters::wildcard(c)) {
1119 consteval_error("the given pattern has an unpaired rule (rules are required to be written in pairs of 2)");
1120 } else {
1121 consteval_error("the given pattern has trailing characters at the end (which is not allowed)");
1122 }
1123 } else {
1124 using rule_t = decltype(rules::rule_for<S[0], S[1]>());
1125 if constexpr (std::same_as<rule_t, void>) {
1126 consteval_error("the given pattern failed to match any known rules");
1127 } else {
1128 if constexpr (S.length() <= 3) {
1129 return do_make_pattern<S.template substr<2>(), Rules..., rule_t>();
1130 } else if constexpr (characters::space(S[2])) {
1131 return do_make_pattern<S.template substr<3>(), Rules..., rule_t>();
1132 } else {
1133 consteval_error("a space character is required to split byte patterns");
1134 }
1135 }
1136 }
1137 }
1138
1139 template <class... Bytes>
1140 [[nodiscard]] consteval auto make_byte_array(Bytes... a_bytes) noexcept
1141 -> std::array<std::byte, sizeof...(Bytes)>
1142 {
1143 static_assert((std::integral<Bytes> && ...), "all bytes must be an integral type");
1144 return { static_cast<std::byte>(a_bytes)... };
1145 }
1146 }
1147
1148 template <stl::nttp::string S>
1149 [[nodiscard]] constexpr auto make_pattern() noexcept
1150 {
1151 return detail::do_make_pattern<S>();
1152 }
1153
1154 static_assert(make_pattern<"40 10 F2 ??">().match(
1155 detail::make_byte_array(0x40, 0x10, 0xF2, 0x41)));
1156 static_assert(make_pattern<"B8 D0 ?? ?? D4 6E">().match(
1157 detail::make_byte_array(0xB8, 0xD0, 0x35, 0x2A, 0xD4, 0x6E)));
1158}
1159
1160#undef REL_MAKE_MEMBER_FUNCTION_NON_POD_TYPE
1161#undef REL_MAKE_MEMBER_FUNCTION_NON_POD_TYPE_HELPER
1162#undef REL_MAKE_MEMBER_FUNCTION_NON_POD_TYPE_HELPER_IMPL
1163
1164#undef REL_MAKE_MEMBER_FUNCTION_POD_TYPE
1165#undef REL_MAKE_MEMBER_FUNCTION_POD_TYPE_HELPER
1166#undef REL_MAKE_MEMBER_FUNCTION_POD_TYPE_HELPER_IMPL
Definition: Relocation.h:532
Offset2ID()
Definition: Relocation.h:556
typename container_type::const_iterator const_iterator
Definition: Relocation.h:537
typename container_type::const_reverse_iterator const_reverse_iterator
Definition: Relocation.h:538
const_reverse_iterator crbegin() const noexcept
Definition: Relocation.h:587
const_iterator begin() const noexcept
Definition: Relocation.h:580
std::vector< value_type > container_type
Definition: Relocation.h:535
size_type size() const noexcept
Definition: Relocation.h:592
Offset2ID(ExecutionPolicy &&a_policy)
Definition: Relocation.h:541
const_reverse_iterator rbegin() const noexcept
Definition: Relocation.h:586
const_reverse_iterator crend() const noexcept
Definition: Relocation.h:590
const_iterator cbegin() const noexcept
Definition: Relocation.h:581
typename container_type::size_type size_type
Definition: Relocation.h:536
const_iterator cend() const noexcept
Definition: Relocation.h:584
std::uint64_t operator()(std::size_t a_offset) const
Definition: Relocation.h:560
mapping_t value_type
Definition: Relocation.h:534
const_reverse_iterator rend() const noexcept
Definition: Relocation.h:589
const_iterator end() const noexcept
Definition: Relocation.h:583
Definition: Relocation.h:522
static IDDatabase & get()
Definition: Relocation.h:598
std::size_t id2offset(std::uint64_t a_id) const
Definition: Relocation.h:604
Definition: Relocation.h:849
std::size_t offset() const
Definition: Relocation.h:865
constexpr ID & operator=(std::uint64_t a_id) noexcept
Definition: Relocation.h:857
constexpr std::uint64_t id() const noexcept
Definition: Relocation.h:864
constexpr ID() noexcept=default
std::uintptr_t address() const
Definition: Relocation.h:863
Definition: Relocation.h:419
Version version() const noexcept
Definition: Relocation.h:429
T * pointer() const noexcept
Definition: Relocation.h:436
std::uintptr_t base() const noexcept
Definition: Relocation.h:427
stl::zwstring filename() const noexcept
Definition: Relocation.h:428
static Module & get()
Definition: Relocation.h:421
void * pointer() const noexcept
Definition: Relocation.h:433
Segment segment(Segment::Name a_segment) const noexcept
Definition: Relocation.h:431
Definition: Relocation.h:825
constexpr Offset() noexcept=default
std::uintptr_t address() const
Definition: Relocation.h:839
constexpr Offset & operator=(std::size_t a_offset) noexcept
Definition: Relocation.h:833
constexpr std::size_t offset() const noexcept
Definition: Relocation.h:840
Definition: Relocation.h:875
std::uintptr_t write_vfunc(std::size_t a_idx, std::uintptr_t a_newFunc)
Definition: Relocation.h:952
constexpr std::uintptr_t address() const noexcept
Definition: Relocation.h:941
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:881
Relocation & operator=(ID a_id)
Definition: Relocation.h:913
value_type get() const noexcept(std::is_nothrow_copy_constructible_v< value_type >)
Definition: Relocation.h:944
Relocation(Offset a_offset)
Definition: Relocation.h:889
auto operator->() const noexcept
Definition: Relocation.h:927
Relocation(ID a_id, std::ptrdiff_t a_offset)
Definition: Relocation.h:897
constexpr Relocation & operator=(std::uintptr_t a_address) noexcept
Definition: Relocation.h:901
constexpr Relocation() noexcept=default
Relocation(ID a_id)
Definition: Relocation.h:893
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:934
std::size_t offset() const
Definition: Relocation.h:942
Relocation & operator=(Offset a_offset)
Definition: Relocation.h:907
std::uintptr_t write_vfunc(std::size_t a_idx, F a_newFunc)
Definition: Relocation.h:962
Definition: Relocation.h:377
std::size_t size() const noexcept
Definition: Relocation.h:402
Name
Definition: Relocation.h:380
@ data
Definition: Relocation.h:384
@ tls
Definition: Relocation.h:386
@ textw
Definition: Relocation.h:387
@ gfids
Definition: Relocation.h:388
@ total
Definition: Relocation.h:389
@ idata
Definition: Relocation.h:382
@ textx
Definition: Relocation.h:381
@ pdata
Definition: Relocation.h:385
@ rdata
Definition: Relocation.h:383
void * pointer() const noexcept
Definition: Relocation.h:404
std::uintptr_t address() const noexcept
Definition: Relocation.h:400
Segment() noexcept=default
std::size_t offset() const noexcept
Definition: Relocation.h:401
T * pointer() const noexcept
Definition: Relocation.h:407
Definition: Relocation.h:275
constexpr decltype(auto) end() const noexcept
Definition: Relocation.h:296
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:287
constexpr reference operator[](std::size_t a_idx) noexcept
Definition: Relocation.h:291
std::strong_ordering constexpr compare(const Version &a_rhs) const noexcept
Definition: Relocation.h:299
constexpr decltype(auto) cbegin() const noexcept
Definition: Relocation.h:295
std::string string() const
Definition: Relocation.h:318
constexpr std::uint32_t pack() const noexcept
Definition: Relocation.h:309
constexpr const_reference operator[](std::size_t a_idx) const noexcept
Definition: Relocation.h:292
std::uint16_t value_type
Definition: Relocation.h:277
constexpr decltype(auto) cend() const noexcept
Definition: Relocation.h:297
value_type & reference
Definition: Relocation.h:278
const value_type & const_reference
Definition: Relocation.h:279
constexpr Version() noexcept=default
constexpr decltype(auto) begin() const noexcept
Definition: Relocation.h:294
std::wstring wstring() const
Definition: Relocation.h:329
Definition: Relocation.h:1077
constexpr bool match(std::span< const std::byte, sizeof...(Rules)> a_bytes) const noexcept
Definition: Relocation.h:1081
void match_or_fail(std::uintptr_t a_address, std::source_location a_loc=std::source_location::current()) const noexcept
Definition: Relocation.h:1092
bool match(std::uintptr_t a_address) const noexcept
Definition: Relocation.h:1087
Definition: Relocation.h:64
~memory_map()
Definition: Relocation.h:77
memory_map & operator=(memory_map &&a_rhs) noexcept
Definition: Relocation.h:81
memory_map & operator=(const memory_map &)=delete
memory_map() noexcept=default
void * data() noexcept
Definition: Relocation.h:93
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:1028
static constexpr bool match(std::byte a_byte) noexcept
Definition: Relocation.h:1030
Definition: Relocation.h:1050
static constexpr bool match(std::byte) noexcept
Definition: Relocation.h:1052
constexpr bool space(char a_ch) noexcept
Definition: Relocation.h:987
constexpr bool hexadecimal(char a_ch) noexcept
Definition: Relocation.h:980
constexpr bool wildcard(char a_ch) noexcept
Definition: Relocation.h:992
consteval std::byte hexacharacters_to_hexadecimal(char a_hi, char a_lo) noexcept
Definition: Relocation.h:1002
void rule_for() noexcept
typename member_function_non_pod_type< F >::type member_function_non_pod_type_t
Definition: Relocation.h:124
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:183
consteval auto make_byte_array(Bytes... a_bytes) noexcept -> std::array< std::byte, sizeof...(Bytes)>
Definition: Relocation.h:1140
typename member_function_pod_type< F >::type member_function_pod_type_t
Definition: Relocation.h:113
constexpr bool is_x64_pod_v
Definition: Relocation.h:177
void consteval_error(const char *a_error)
constexpr auto do_make_pattern() noexcept
Definition: Relocation.h:1112
REL_MAKE_MEMBER_FUNCTION_POD_TYPE()
REL_MAKE_MEMBER_FUNCTION_NON_POD_TYPE()
Definition: Relocation.h:60
std::invoke_result_t< F, Args... > invoke(F &&a_func, Args &&... a_args) noexcept(std::is_nothrow_invocable_v< F, Args... >)
Definition: Relocation.h:201
std::optional< Version > get_file_version(stl::zwstring a_filename)
Definition: Relocation.h:347
constexpr std::uint8_t NOP
Definition: Relocation.h:196
constexpr std::strong_ordering operator<=>(const Version &a_lhs, const Version &a_rhs) noexcept
Definition: Relocation.h:345
constexpr std::uint8_t INT3
Definition: Relocation.h:198
constexpr bool operator==(const Version &a_lhs, const Version &a_rhs) noexcept
Definition: Relocation.h:344
void safe_fill(std::uintptr_t a_dst, std::uint8_t a_value, std::size_t a_count)
Definition: Relocation.h:252
void safe_write(std::uintptr_t a_dst, const void *a_src, std::size_t a_count)
Definition: Relocation.h:218
constexpr auto make_pattern() noexcept
Definition: Relocation.h:1149
constexpr std::uint8_t RET
Definition: Relocation.h:197
std::uint32_t GetFileVersionInfoSize(const char *a_filename, std::uint32_t *a_handle) noexcept
std::uint32_t GetEnvironmentVariable(const char *a_name, char *a_buffer, std::uint32_t a_size) noexcept
bool VirtualProtect(void *a_address, std::size_t a_size, std::uint32_t a_newProtect, std::uint32_t *a_oldProtect) noexcept
constexpr auto IMAGE_SCN_MEM_WRITE
Definition: WinAPI.h:7
constexpr auto PAGE_EXECUTE_READWRITE
Definition: WinAPI.h:11
void * GetModuleHandle(const char *a_moduleName) noexcept
bool GetFileVersionInfo(const char *a_filename, std::uint32_t a_handle, std::uint32_t a_len, void *a_data) noexcept
constexpr auto IMAGE_SCN_MEM_EXECUTE
Definition: WinAPI.h:6
bool VerQueryValue(const void *a_block, const char *a_subBlock, void **a_buffer, unsigned int *a_len) noexcept
void report_and_fail(std::string_view a_msg, std::source_location a_loc=std::source_location::current())
Definition: PCH.h:587
auto utf16_to_utf8(std::wstring_view a_in) noexcept -> std::optional< std::string >
Definition: PCH.h:559
auto utf8_to_utf16(std::string_view a_in) noexcept -> std::optional< std::wstring >
Definition: PCH.h:533
basic_zstring< wchar_t > zwstring
Definition: PCH.h:79
Definition: EffectArchetypes.h:65
bool getline(RE::NiBinaryStream &a_input, std::basic_string< CharT, Traits, Allocator > &a_str)
Definition: NiBinaryStream.h:96
std::string to_string(RE::EffectArchetype a_archetype)
Definition: EffectArchetypes.h:66
Definition: Relocation.h:155
Definition: Relocation.h:145
Definition: Relocation.h:135
Definition: Relocation.h:150
Definition: Relocation.h:116
Definition: Relocation.h:105