CommonLibSSE (Parapets fork)
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 load();
445 }
446
447 Module(const Module&) = delete;
448 Module(Module&&) = delete;
449
450 ~Module() noexcept = default;
451
452 Module& operator=(const Module&) = delete;
453 Module& operator=(Module&&) = delete;
454
455 void load()
456 {
457 auto handle = WinAPI::GetModuleHandle(static_cast<wchar_t*>(nullptr));
458 if (handle == nullptr) {
460 "Failed to obtain module handle for game executable."sv);
461 }
462 _base = reinterpret_cast<std::uintptr_t>(handle);
463
464 std::wstring path;
465 path.resize(4096);
466
467 path.resize(
469 handle,
470 path.data(),
471 static_cast<std::uint32_t>(path.size())));
472
473 _filename = std::filesystem::path(path).filename().c_str();
474
475 load_version();
476 load_segments();
477 }
478
479 void load_segments();
480
481 void load_version()
482 {
483 const auto version = get_file_version(_filename);
484 if (version) {
485 _version = *version;
486 } else {
488 fmt::format(
489 "Failed to obtain file version info for: {}\n"
490 "Please contact the author of this script extender plugin for further assistance."sv,
491 stl::utf16_to_utf8(_filename).value_or("<unicode conversion error>"s)));
492 }
493 }
494
495 static constexpr std::array SEGMENTS{
496 std::make_pair(".text"sv, WinAPI::IMAGE_SCN_MEM_EXECUTE),
497 std::make_pair(".idata"sv, static_cast<std::uint32_t>(0)),
498 std::make_pair(".rdata"sv, static_cast<std::uint32_t>(0)),
499 std::make_pair(".data"sv, static_cast<std::uint32_t>(0)),
500 std::make_pair(".pdata"sv, static_cast<std::uint32_t>(0)),
501 std::make_pair(".tls"sv, static_cast<std::uint32_t>(0)),
502 std::make_pair(".text"sv, WinAPI::IMAGE_SCN_MEM_WRITE),
503 std::make_pair(".gfids"sv, static_cast<std::uint32_t>(0))
504 };
505
506 std::wstring _filename;
507 std::array<Segment, Segment::total> _segments;
508 Version _version;
509 std::uintptr_t _base{ 0 };
510 };
511
513 {
514 private:
515 struct mapping_t
516 {
517 std::uint64_t id;
518 std::uint64_t offset;
519 };
520
521 public:
523 {
524 public:
525 using value_type = mapping_t;
526 using container_type = std::vector<value_type>;
527 using size_type = typename container_type::size_type;
528 using const_iterator = typename container_type::const_iterator;
529 using const_reverse_iterator = typename container_type::const_reverse_iterator;
530
531 template <class ExecutionPolicy>
532 explicit Offset2ID(ExecutionPolicy&& a_policy) //
533 requires(std::is_execution_policy_v<std::decay_t<ExecutionPolicy>>)
534 {
535 const std::span<const mapping_t> id2offset = IDDatabase::get()._id2offset;
536 _offset2id.reserve(id2offset.size());
537 _offset2id.insert(_offset2id.begin(), id2offset.begin(), id2offset.end());
538 std::sort(
539 a_policy,
540 _offset2id.begin(),
541 _offset2id.end(),
542 [](auto&& a_lhs, auto&& a_rhs) {
543 return a_lhs.offset < a_rhs.offset;
544 });
545 }
546
548 Offset2ID(std::execution::sequenced_policy{})
549 {}
550
551 [[nodiscard]] std::uint64_t operator()(std::size_t a_offset) const
552 {
553 const mapping_t elem{ 0, a_offset };
554 const auto it = std::lower_bound(
555 _offset2id.begin(),
556 _offset2id.end(),
557 elem,
558 [](auto&& a_lhs, auto&& a_rhs) {
559 return a_lhs.offset < a_rhs.offset;
560 });
561 if (it == _offset2id.end()) {
563 fmt::format(
564 "Failed to find the offset within the database: 0x{:08X}"sv,
565 a_offset));
566 }
567
568 return it->id;
569 }
570
571 [[nodiscard]] const_iterator begin() const noexcept { return _offset2id.begin(); }
572 [[nodiscard]] const_iterator cbegin() const noexcept { return _offset2id.cbegin(); }
573
574 [[nodiscard]] const_iterator end() const noexcept { return _offset2id.end(); }
575 [[nodiscard]] const_iterator cend() const noexcept { return _offset2id.cend(); }
576
577 [[nodiscard]] const_reverse_iterator rbegin() const noexcept { return _offset2id.rbegin(); }
578 [[nodiscard]] const_reverse_iterator crbegin() const noexcept { return _offset2id.crbegin(); }
579
580 [[nodiscard]] const_reverse_iterator rend() const noexcept { return _offset2id.rend(); }
581 [[nodiscard]] const_reverse_iterator crend() const noexcept { return _offset2id.crend(); }
582
583 [[nodiscard]] size_type size() const noexcept { return _offset2id.size(); }
584
585 private:
586 container_type _offset2id;
587 };
588
589 [[nodiscard]] static IDDatabase& get()
590 {
591 static IDDatabase singleton;
592 return singleton;
593 }
594
595 [[nodiscard]] inline std::size_t id2offset(std::uint64_t a_id) const
596 {
597 mapping_t elem{ a_id, 0 };
598 const auto it = std::lower_bound(
599 _id2offset.begin(),
600 _id2offset.end(),
601 elem,
602 [](auto&& a_lhs, auto&& a_rhs) {
603 return a_lhs.id < a_rhs.id;
604 });
605 if (it == _id2offset.end()) {
607 fmt::format(
608 "Failed to find the id within the address library: {}\n"
609 "This means this script extender plugin is incompatible with the address "
610 "library for this version of the game, and thus does not support it."sv,
611 a_id));
612 }
613
614 return static_cast<std::size_t>(it->offset);
615 }
616
617 private:
618 friend Offset2ID;
619
620 class header_t
621 {
622 public:
623 void read(binary_io::file_istream& a_in)
624 {
625 const auto [format] = a_in.read<std::int32_t>();
626 if (format != 2) {
628 fmt::format(
629 "Unsupported address library format: {}\n"
630 "This means this script extender plugin is incompatible with the address "
631 "library available for this version of the game, and thus does not "
632 "support it."sv,
633 format));
634 }
635
636 const auto [major, minor, patch, revision] =
637 a_in.read<std::int32_t, std::int32_t, std::int32_t, std::int32_t>();
638 _version[0] = static_cast<std::uint16_t>(major);
639 _version[1] = static_cast<std::uint16_t>(minor);
640 _version[2] = static_cast<std::uint16_t>(patch);
641 _version[3] = static_cast<std::uint16_t>(revision);
642
643 const auto [nameLen] = a_in.read<std::int32_t>();
644 a_in.seek_relative(nameLen);
645
646 a_in.read(_pointerSize, _addressCount);
647 }
648
649 [[nodiscard]] std::size_t address_count() const noexcept { return static_cast<std::size_t>(_addressCount); }
650 [[nodiscard]] std::uint64_t pointer_size() const noexcept { return static_cast<std::uint64_t>(_pointerSize); }
651 [[nodiscard]] Version version() const noexcept { return _version; }
652
653 private:
654 Version _version;
655 std::int32_t _pointerSize{ 0 };
656 std::int32_t _addressCount{ 0 };
657 };
658
659 IDDatabase() { load(); }
660
661 IDDatabase(const IDDatabase&) = delete;
662 IDDatabase(IDDatabase&&) = delete;
663
664 ~IDDatabase() = default;
665
666 IDDatabase& operator=(const IDDatabase&) = delete;
667 IDDatabase& operator=(IDDatabase&&) = delete;
668
669 void load()
670 {
671 const auto version = Module::get().version();
672 const auto filename =
674 fmt::format(
675 "Data/SKSE/Plugins/versionlib-{}.bin"sv,
676 version.string()))
677 .value_or(L"<unknown filename>"s);
678 load_file(filename, version);
679 }
680
681 void load_file(stl::zwstring a_filename, Version a_version)
682 {
683 try {
684 binary_io::file_istream in(a_filename);
685 header_t header;
686 header.read(in);
687 if (header.version() != a_version) {
688 stl::report_and_fail("version mismatch"sv);
689 }
690
691 auto mapname = L"CommonLibSSEOffsets-v2-"s;
692 mapname += a_version.wstring();
693 const auto byteSize = static_cast<std::size_t>(header.address_count()) * sizeof(mapping_t);
694 if (_mmap.open(mapname, byteSize)) {
695 _id2offset = { static_cast<mapping_t*>(_mmap.data()), header.address_count() };
696 } else if (_mmap.create(mapname, byteSize)) {
697 _id2offset = { static_cast<mapping_t*>(_mmap.data()), header.address_count() };
698 unpack_file(in, header);
699 std::sort(
700 _id2offset.begin(),
701 _id2offset.end(),
702 [](auto&& a_lhs, auto&& a_rhs) {
703 return a_lhs.id < a_rhs.id;
704 });
705 } else {
706 stl::report_and_fail("failed to create shared mapping"sv);
707 }
708 } catch (const std::system_error&) {
710 fmt::format(
711 "Failed to locate an appropriate address library with the path: {}\n"
712 "This means you are missing the address library for this specific version of "
713 "the game. Please continue to the mod page for address library to download "
714 "an appropriate version. If one is not available, then it is likely that "
715 "address library has not yet added support for this version of the game."sv,
716 stl::utf16_to_utf8(a_filename).value_or("<unknown filename>"s)));
717 }
718 }
719
720 void unpack_file(binary_io::file_istream& a_in, header_t a_header)
721 {
722 std::uint8_t type = 0;
723 std::uint64_t id = 0;
724 std::uint64_t offset = 0;
725 std::uint64_t prevID = 0;
726 std::uint64_t prevOffset = 0;
727 for (auto& mapping : _id2offset) {
728 a_in.read(type);
729 const auto lo = static_cast<std::uint8_t>(type & 0xF);
730 const auto hi = static_cast<std::uint8_t>(type >> 4);
731
732 switch (lo) {
733 case 0:
734 a_in.read(id);
735 break;
736 case 1:
737 id = prevID + 1;
738 break;
739 case 2:
740 id = prevID + std::get<0>(a_in.read<std::uint8_t>());
741 break;
742 case 3:
743 id = prevID - std::get<0>(a_in.read<std::uint8_t>());
744 break;
745 case 4:
746 id = prevID + std::get<0>(a_in.read<std::uint16_t>());
747 break;
748 case 5:
749 id = prevID - std::get<0>(a_in.read<std::uint16_t>());
750 break;
751 case 6:
752 std::tie(id) = a_in.read<std::uint16_t>();
753 break;
754 case 7:
755 std::tie(id) = a_in.read<std::uint32_t>();
756 break;
757 default:
758 stl::report_and_fail("unhandled type"sv);
759 }
760
761 const std::uint64_t tmp = (hi & 8) != 0 ? (prevOffset / a_header.pointer_size()) : prevOffset;
762
763 switch (hi & 7) {
764 case 0:
765 a_in.read(offset);
766 break;
767 case 1:
768 offset = tmp + 1;
769 break;
770 case 2:
771 offset = tmp + std::get<0>(a_in.read<std::uint8_t>());
772 break;
773 case 3:
774 offset = tmp - std::get<0>(a_in.read<std::uint8_t>());
775 break;
776 case 4:
777 offset = tmp + std::get<0>(a_in.read<std::uint16_t>());
778 break;
779 case 5:
780 offset = tmp - std::get<0>(a_in.read<std::uint16_t>());
781 break;
782 case 6:
783 std::tie(offset) = a_in.read<std::uint16_t>();
784 break;
785 case 7:
786 std::tie(offset) = a_in.read<std::uint32_t>();
787 break;
788 default:
789 stl::report_and_fail("unhandled type"sv);
790 }
791
792 if ((hi & 8) != 0) {
793 offset *= a_header.pointer_size();
794 }
795
796 mapping = { id, offset };
797
798 prevOffset = offset;
799 prevID = id;
800 }
801 }
802
803 detail::memory_map _mmap;
804 std::span<mapping_t> _id2offset;
805 };
806
807 class Offset
808 {
809 public:
810 constexpr Offset() noexcept = default;
811
812 explicit constexpr Offset(std::size_t a_offset) noexcept :
813 _offset(a_offset)
814 {}
815
816 constexpr Offset& operator=(std::size_t a_offset) noexcept
817 {
818 _offset = a_offset;
819 return *this;
820 }
821
822 [[nodiscard]] std::uintptr_t address() const { return base() + offset(); }
823 [[nodiscard]] constexpr std::size_t offset() const noexcept { return _offset; }
824
825 private:
826 [[nodiscard]] static std::uintptr_t base() { return Module::get().base(); }
827
828 std::size_t _offset{ 0 };
829 };
830
831 class ID
832 {
833 public:
834 constexpr ID() noexcept = default;
835
836 explicit constexpr ID(std::uint64_t a_id) noexcept :
837 _id(a_id)
838 {}
839
840 constexpr ID& operator=(std::uint64_t a_id) noexcept
841 {
842 _id = a_id;
843 return *this;
844 }
845
846 [[nodiscard]] std::uintptr_t address() const { return base() + offset(); }
847 [[nodiscard]] constexpr std::uint64_t id() const noexcept { return _id; }
848 [[nodiscard]] std::size_t offset() const { return IDDatabase::get().id2offset(_id); }
849
850 private:
851 [[nodiscard]] static std::uintptr_t base() { return Module::get().base(); }
852
853 std::uint64_t _id{ 0 };
854 };
855
856 template <class T>
858 {
859 public:
861 std::conditional_t<
862 std::is_member_pointer_v<T> || std::is_function_v<std::remove_pointer_t<T>>,
863 std::decay_t<T>,
864 T>;
865
866 constexpr Relocation() noexcept = default;
867
868 explicit constexpr Relocation(std::uintptr_t a_address) noexcept :
869 _impl{ a_address }
870 {}
871
872 explicit Relocation(Offset a_offset) :
873 _impl{ a_offset.address() }
874 {}
875
876 explicit Relocation(ID a_id) :
877 _impl{ a_id.address() }
878 {}
879
880 explicit Relocation(ID a_id, std::ptrdiff_t a_offset) :
881 _impl{ a_id.address() + a_offset }
882 {}
883
884 constexpr Relocation& operator=(std::uintptr_t a_address) noexcept
885 {
886 _impl = a_address;
887 return *this;
888 }
889
891 {
892 _impl = a_offset.address();
893 return *this;
894 }
895
897 {
898 _impl = a_id.address();
899 return *this;
900 }
901
902 template <class U = value_type>
903 [[nodiscard]] decltype(auto) operator*() const noexcept //
904 requires(std::is_pointer_v<U>)
905 {
906 return *get();
907 }
908
909 template <class U = value_type>
910 [[nodiscard]] auto operator->() const noexcept //
911 requires(std::is_pointer_v<U>)
912 {
913 return get();
914 }
915
916 template <class... Args>
917 std::invoke_result_t<const value_type&, Args...> operator()(Args&&... a_args) const //
918 noexcept(std::is_nothrow_invocable_v<const value_type&, Args...>) //
919 requires(std::invocable<const value_type&, Args...>)
920 {
921 return REL::invoke(get(), std::forward<Args>(a_args)...);
922 }
923
924 [[nodiscard]] constexpr std::uintptr_t address() const noexcept { return _impl; }
925 [[nodiscard]] std::size_t offset() const { return _impl - base(); }
926
927 [[nodiscard]] value_type get() const //
928 noexcept(std::is_nothrow_copy_constructible_v<value_type>)
929 {
930 assert(_impl != 0);
931 return stl::unrestricted_cast<value_type>(_impl);
932 }
933
934 template <class U = value_type>
935 std::uintptr_t write_vfunc(std::size_t a_idx, std::uintptr_t a_newFunc) //
936 requires(std::same_as<U, std::uintptr_t>)
937 {
938 const auto addr = address() + (sizeof(void*) * a_idx);
939 const auto result = *reinterpret_cast<std::uintptr_t*>(addr);
940 safe_write(addr, a_newFunc);
941 return result;
942 }
943
944 template <class F>
945 std::uintptr_t write_vfunc(std::size_t a_idx, F a_newFunc) //
946 requires(std::same_as<value_type, std::uintptr_t>)
947 {
948 return write_vfunc(a_idx, stl::unrestricted_cast<std::uintptr_t>(a_newFunc));
949 }
950
951 private :
952 // clang-format off
953 [[nodiscard]] static std::uintptr_t base() { return Module::get().base(); }
954 // clang-format on
955
956 std::uintptr_t _impl{ 0 };
957 };
958
959 namespace detail
960 {
961 namespace characters
962 {
963 [[nodiscard]] constexpr bool hexadecimal(char a_ch) noexcept
964 {
965 return ('0' <= a_ch && a_ch <= '9') ||
966 ('A' <= a_ch && a_ch <= 'F') ||
967 ('a' <= a_ch && a_ch <= 'f');
968 }
969
970 [[nodiscard]] constexpr bool space(char a_ch) noexcept
971 {
972 return a_ch == ' ';
973 }
974
975 [[nodiscard]] constexpr bool wildcard(char a_ch) noexcept
976 {
977 return a_ch == '?';
978 }
979 }
980
981 namespace rules
982 {
983 namespace detail
984 {
985 [[nodiscard]] consteval std::byte hexacharacters_to_hexadecimal(char a_hi, char a_lo) noexcept
986 {
987 constexpr auto lut = []() noexcept {
988 std::array<std::uint8_t, std::numeric_limits<unsigned char>::max() + 1> a = {};
989
990 const auto iterate = [&](std::uint8_t a_iFirst, unsigned char a_cFirst, unsigned char a_cLast) noexcept {
991 for (; a_cFirst <= a_cLast; ++a_cFirst, ++a_iFirst) {
992 a[a_cFirst] = a_iFirst;
993 }
994 };
995
996 iterate(0, '0', '9');
997 iterate(0xA, 'A', 'F');
998 iterate(0xa, 'a', 'f');
999
1000 return a;
1001 }();
1002
1003 return static_cast<std::byte>(
1004 lut[static_cast<unsigned char>(a_hi)] * 0x10u +
1005 lut[static_cast<unsigned char>(a_lo)]);
1006 }
1007 }
1008
1009 template <char HI, char LO>
1011 {
1012 public:
1013 [[nodiscard]] static constexpr bool match(std::byte a_byte) noexcept
1014 {
1015 constexpr auto expected = detail::hexacharacters_to_hexadecimal(HI, LO);
1016 return a_byte == expected;
1017 }
1018 };
1019
1020 static_assert(Hexadecimal<'5', '7'>::match(std::byte{ 0x57 }));
1021 static_assert(Hexadecimal<'6', '5'>::match(std::byte{ 0x65 }));
1022 static_assert(Hexadecimal<'B', 'D'>::match(std::byte{ 0xBD }));
1023 static_assert(Hexadecimal<'1', 'C'>::match(std::byte{ 0x1C }));
1024 static_assert(Hexadecimal<'F', '2'>::match(std::byte{ 0xF2 }));
1025 static_assert(Hexadecimal<'9', 'f'>::match(std::byte{ 0x9f }));
1026
1027 static_assert(!Hexadecimal<'D', '4'>::match(std::byte{ 0xF8 }));
1028 static_assert(!Hexadecimal<'6', '7'>::match(std::byte{ 0xAA }));
1029 static_assert(!Hexadecimal<'7', '8'>::match(std::byte{ 0xE3 }));
1030 static_assert(!Hexadecimal<'6', 'E'>::match(std::byte{ 0x61 }));
1031
1033 {
1034 public:
1035 [[nodiscard]] static constexpr bool match(std::byte) noexcept
1036 {
1037 return true;
1038 }
1039 };
1040
1041 static_assert(Wildcard::match(std::byte{ 0xB9 }));
1042 static_assert(Wildcard::match(std::byte{ 0x96 }));
1043 static_assert(Wildcard::match(std::byte{ 0x35 }));
1044 static_assert(Wildcard::match(std::byte{ 0xE4 }));
1045
1046 template <char, char>
1047 void rule_for() noexcept;
1048
1049 template <char C1, char C2>
1050 Hexadecimal<C1, C2> rule_for() noexcept
1051 requires(characters::hexadecimal(C1) && characters::hexadecimal(C2));
1052
1053 template <char C1, char C2>
1055 requires(characters::wildcard(C1) && characters::wildcard(C2));
1056 }
1057
1058 template <class... Rules>
1060 {
1061 public:
1062 static_assert(sizeof...(Rules) >= 1, "must provide at least 1 rule for the pattern matcher");
1063
1064 [[nodiscard]] constexpr bool match(std::span<const std::byte, sizeof...(Rules)> a_bytes) const noexcept
1065 {
1066 std::size_t i = 0;
1067 return (Rules::match(a_bytes[i++]) && ...);
1068 }
1069
1070 [[nodiscard]] bool match(std::uintptr_t a_address) const noexcept
1071 {
1072 return this->match(*reinterpret_cast<const std::byte(*)[sizeof...(Rules)]>(a_address));
1073 }
1074
1075 void match_or_fail(std::uintptr_t a_address, std::source_location a_loc = std::source_location::current()) const noexcept
1076 {
1077 if (!this->match(a_address)) {
1078 const auto version = Module::get().version();
1080 fmt::format(
1081 "A pattern has failed to match.\n"
1082 "This means the plugin is incompatible with the current version of the game ({}.{}.{}). "
1083 "Head to the mod page of this plugin to see if an update is available."sv,
1084 version[0],
1085 version[1],
1086 version[2]),
1087 a_loc);
1088 }
1089 }
1090 };
1091
1092 void consteval_error(const char* a_error);
1093
1094 template <stl::nttp::string S, class... Rules>
1095 [[nodiscard]] constexpr auto do_make_pattern() noexcept
1096 {
1097 if constexpr (S.length() == 0) {
1098 return PatternMatcher<Rules...>();
1099 } else if constexpr (S.length() == 1) {
1100 constexpr char c = S[0];
1101 if constexpr (characters::hexadecimal(c) || characters::wildcard(c)) {
1102 consteval_error("the given pattern has an unpaired rule (rules are required to be written in pairs of 2)");
1103 } else {
1104 consteval_error("the given pattern has trailing characters at the end (which is not allowed)");
1105 }
1106 } else {
1107 using rule_t = decltype(rules::rule_for<S[0], S[1]>());
1108 if constexpr (std::same_as<rule_t, void>) {
1109 consteval_error("the given pattern failed to match any known rules");
1110 } else {
1111 if constexpr (S.length() <= 3) {
1112 return do_make_pattern<S.template substr<2>(), Rules..., rule_t>();
1113 } else if constexpr (characters::space(S[2])) {
1114 return do_make_pattern<S.template substr<3>(), Rules..., rule_t>();
1115 } else {
1116 consteval_error("a space character is required to split byte patterns");
1117 }
1118 }
1119 }
1120 }
1121
1122 template <class... Bytes>
1123 [[nodiscard]] consteval auto make_byte_array(Bytes... a_bytes) noexcept
1124 -> std::array<std::byte, sizeof...(Bytes)>
1125 {
1126 static_assert((std::integral<Bytes> && ...), "all bytes must be an integral type");
1127 return { static_cast<std::byte>(a_bytes)... };
1128 }
1129 }
1130
1131 template <stl::nttp::string S>
1132 [[nodiscard]] constexpr auto make_pattern() noexcept
1133 {
1134 return detail::do_make_pattern<S>();
1135 }
1136
1137 static_assert(make_pattern<"40 10 F2 ??">().match(
1138 detail::make_byte_array(0x40, 0x10, 0xF2, 0x41)));
1139 static_assert(make_pattern<"B8 D0 ?? ?? D4 6E">().match(
1140 detail::make_byte_array(0xB8, 0xD0, 0x35, 0x2A, 0xD4, 0x6E)));
1141}
1142
1143#undef REL_MAKE_MEMBER_FUNCTION_NON_POD_TYPE
1144#undef REL_MAKE_MEMBER_FUNCTION_NON_POD_TYPE_HELPER
1145#undef REL_MAKE_MEMBER_FUNCTION_NON_POD_TYPE_HELPER_IMPL
1146
1147#undef REL_MAKE_MEMBER_FUNCTION_POD_TYPE
1148#undef REL_MAKE_MEMBER_FUNCTION_POD_TYPE_HELPER
1149#undef REL_MAKE_MEMBER_FUNCTION_POD_TYPE_HELPER_IMPL
Definition: Relocation.h:523
Offset2ID()
Definition: Relocation.h:547
typename container_type::const_iterator const_iterator
Definition: Relocation.h:528
typename container_type::const_reverse_iterator const_reverse_iterator
Definition: Relocation.h:529
const_reverse_iterator crbegin() const noexcept
Definition: Relocation.h:578
const_iterator begin() const noexcept
Definition: Relocation.h:571
std::vector< value_type > container_type
Definition: Relocation.h:526
size_type size() const noexcept
Definition: Relocation.h:583
Offset2ID(ExecutionPolicy &&a_policy)
Definition: Relocation.h:532
const_reverse_iterator rbegin() const noexcept
Definition: Relocation.h:577
const_reverse_iterator crend() const noexcept
Definition: Relocation.h:581
const_iterator cbegin() const noexcept
Definition: Relocation.h:572
typename container_type::size_type size_type
Definition: Relocation.h:527
const_iterator cend() const noexcept
Definition: Relocation.h:575
std::uint64_t operator()(std::size_t a_offset) const
Definition: Relocation.h:551
mapping_t value_type
Definition: Relocation.h:525
const_reverse_iterator rend() const noexcept
Definition: Relocation.h:580
const_iterator end() const noexcept
Definition: Relocation.h:574
Definition: Relocation.h:513
static IDDatabase & get()
Definition: Relocation.h:589
std::size_t id2offset(std::uint64_t a_id) const
Definition: Relocation.h:595
Definition: Relocation.h:832
std::size_t offset() const
Definition: Relocation.h:848
constexpr ID & operator=(std::uint64_t a_id) noexcept
Definition: Relocation.h:840
constexpr std::uint64_t id() const noexcept
Definition: Relocation.h:847
constexpr ID() noexcept=default
std::uintptr_t address() const
Definition: Relocation.h:846
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:808
constexpr Offset() noexcept=default
std::uintptr_t address() const
Definition: Relocation.h:822
constexpr Offset & operator=(std::size_t a_offset) noexcept
Definition: Relocation.h:816
constexpr std::size_t offset() const noexcept
Definition: Relocation.h:823
Definition: Relocation.h:858
std::uintptr_t write_vfunc(std::size_t a_idx, std::uintptr_t a_newFunc)
Definition: Relocation.h:935
constexpr std::uintptr_t address() const noexcept
Definition: Relocation.h:924
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:864
Relocation & operator=(ID a_id)
Definition: Relocation.h:896
value_type get() const noexcept(std::is_nothrow_copy_constructible_v< value_type >)
Definition: Relocation.h:927
Relocation(Offset a_offset)
Definition: Relocation.h:872
auto operator->() const noexcept
Definition: Relocation.h:910
Relocation(ID a_id, std::ptrdiff_t a_offset)
Definition: Relocation.h:880
constexpr Relocation & operator=(std::uintptr_t a_address) noexcept
Definition: Relocation.h:884
constexpr Relocation() noexcept=default
Relocation(ID a_id)
Definition: Relocation.h:876
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:917
std::size_t offset() const
Definition: Relocation.h:925
Relocation & operator=(Offset a_offset)
Definition: Relocation.h:890
std::uintptr_t write_vfunc(std::size_t a_idx, F a_newFunc)
Definition: Relocation.h:945
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:1060
constexpr bool match(std::span< const std::byte, sizeof...(Rules)> a_bytes) const noexcept
Definition: Relocation.h:1064
void match_or_fail(std::uintptr_t a_address, std::source_location a_loc=std::source_location::current()) const noexcept
Definition: Relocation.h:1075
bool match(std::uintptr_t a_address) const noexcept
Definition: Relocation.h:1070
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:1011
static constexpr bool match(std::byte a_byte) noexcept
Definition: Relocation.h:1013
Definition: Relocation.h:1033
static constexpr bool match(std::byte) noexcept
Definition: Relocation.h:1035
constexpr bool space(char a_ch) noexcept
Definition: Relocation.h:970
constexpr bool hexadecimal(char a_ch) noexcept
Definition: Relocation.h:963
constexpr bool wildcard(char a_ch) noexcept
Definition: Relocation.h:975
consteval std::byte hexacharacters_to_hexadecimal(char a_hi, char a_lo) noexcept
Definition: Relocation.h:985
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:1123
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:1095
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:1132
constexpr std::uint8_t RET
Definition: Relocation.h:197
std::uint32_t GetFileVersionInfoSize(const char *a_filename, std::uint32_t *a_handle) 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
std::uint32_t GetModuleFileName(void *a_module, char *a_filename, std::uint32_t a_size) noexcept
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:580
auto utf16_to_utf8(std::wstring_view a_in) noexcept -> std::optional< std::string >
Definition: PCH.h:552
auto utf8_to_utf16(std::string_view a_in) noexcept -> std::optional< std::wstring >
Definition: PCH.h:526
basic_zstring< wchar_t > zwstring
Definition: PCH.h:78
Definition: NiBinaryStream.h:94
bool getline(RE::NiBinaryStream &a_input, std::basic_string< CharT, Traits, Allocator > &a_str)
Definition: NiBinaryStream.h:96
Definition: Relocation.h:155
Definition: Relocation.h:145
Definition: Relocation.h:135
Definition: Relocation.h:150
Definition: Relocation.h:116
Definition: Relocation.h:105