CommonLibSSE (Parapets fork)
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 
59 namespace REL
60 {
61  namespace detail
62  {
63  class memory_map
64  {
65  public:
66  constexpr memory_map() noexcept = default;
67  memory_map(const memory_map&) = delete;
68 
69  constexpr 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 
77  ~memory_map() { close(); }
78 
79  memory_map& operator=(const memory_map&) = delete;
80 
81  constexpr 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>
158  struct is_x64_pod<
159  T,
160  std::enable_if_t<
161  std::is_union_v<T>>> :
162  std::false_type
163  {};
164 
165  template <class T>
166  struct is_x64_pod<
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 
189  using func_t = member_function_non_pod_type_t<F>;
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 <std::ranges::contiguous_range R>
247  void safe_write(std::uintptr_t a_dst, R&& a_data)
248  {
249  safe_write(
250  a_dst,
251  std::ranges::data(a_data),
252  std::ranges::size(a_data) * sizeof(std::ranges::range_value_t<R>));
253  }
254 
255  template <class T>
256  void safe_write(std::uintptr_t a_dst, std::span<T> a_data)
257  {
258  safe_write(a_dst, a_data.data(), a_data.size_bytes());
259  }
260 
261  inline void safe_fill(std::uintptr_t a_dst, std::uint8_t a_value, std::size_t a_count)
262  {
263  std::uint32_t old{ 0 };
264  auto success =
266  reinterpret_cast<void*>(a_dst),
267  a_count,
269  std::addressof(old));
270  if (success != 0) {
271  std::fill_n(reinterpret_cast<std::uint8_t*>(a_dst), a_count, a_value);
272  success =
274  reinterpret_cast<void*>(a_dst),
275  a_count,
276  old,
277  std::addressof(old));
278  }
279 
280  assert(success != 0);
281  }
282 
283  class Version
284  {
285  public:
286  using value_type = std::uint16_t;
288  using const_reference = const value_type&;
289 
290  constexpr Version() noexcept = default;
291 
292  explicit constexpr Version(std::array<value_type, 4> a_version) noexcept :
293  _impl(a_version)
294  {}
295 
296  constexpr Version(value_type a_v1, value_type a_v2 = 0, value_type a_v3 = 0, value_type a_v4 = 0) noexcept :
297  _impl{ a_v1, a_v2, a_v3, a_v4 }
298  {}
299 
300  [[nodiscard]] constexpr reference operator[](std::size_t a_idx) noexcept { return _impl[a_idx]; }
301  [[nodiscard]] constexpr const_reference operator[](std::size_t a_idx) const noexcept { return _impl[a_idx]; }
302 
303  [[nodiscard]] constexpr decltype(auto) begin() const noexcept { return _impl.begin(); }
304  [[nodiscard]] constexpr decltype(auto) cbegin() const noexcept { return _impl.cbegin(); }
305  [[nodiscard]] constexpr decltype(auto) end() const noexcept { return _impl.end(); }
306  [[nodiscard]] constexpr decltype(auto) cend() const noexcept { return _impl.cend(); }
307 
308  [[nodiscard]] std::strong_ordering constexpr compare(const Version& a_rhs) const noexcept
309  {
310  for (std::size_t i = 0; i < _impl.size(); ++i) {
311  if ((*this)[i] != a_rhs[i]) {
312  return (*this)[i] < a_rhs[i] ? std::strong_ordering::less : std::strong_ordering::greater;
313  }
314  }
315  return std::strong_ordering::equal;
316  }
317 
318  [[nodiscard]] constexpr std::uint32_t pack() const noexcept
319  {
320  return static_cast<std::uint32_t>(
321  (_impl[0] & 0x0FF) << 24u |
322  (_impl[1] & 0x0FF) << 16u |
323  (_impl[2] & 0xFFF) << 4u |
324  (_impl[3] & 0x00F) << 0u);
325  }
326 
327  [[nodiscard]] std::string string() const
328  {
329  std::string result;
330  for (auto&& ver : _impl) {
331  result += std::to_string(ver);
332  result += '-';
333  }
334  result.pop_back();
335  return result;
336  }
337 
338  [[nodiscard]] std::wstring wstring() const
339  {
340  std::wstring result;
341  for (auto&& ver : _impl) {
342  result += std::to_wstring(ver);
343  result += L'-';
344  }
345  result.pop_back();
346  return result;
347  }
348 
349  private:
350  std::array<value_type, 4> _impl{ 0, 0, 0, 0 };
351  };
352 
353  [[nodiscard]] constexpr bool operator==(const Version& a_lhs, const Version& a_rhs) noexcept { return a_lhs.compare(a_rhs) == 0; }
354  [[nodiscard]] constexpr std::strong_ordering operator<=>(const Version& a_lhs, const Version& a_rhs) noexcept { return a_lhs.compare(a_rhs); }
355 
356  [[nodiscard]] inline std::optional<Version> get_file_version(stl::zwstring a_filename)
357  {
358  std::uint32_t dummy;
359  std::vector<char> buf(WinAPI::GetFileVersionInfoSize(a_filename.data(), std::addressof(dummy)));
360  if (buf.empty()) {
361  return std::nullopt;
362  }
363 
364  if (!WinAPI::GetFileVersionInfo(a_filename.data(), 0, static_cast<std::uint32_t>(buf.size()), buf.data())) {
365  return std::nullopt;
366  }
367 
368  void* verBuf{ nullptr };
369  std::uint32_t verLen{ 0 };
370  if (!WinAPI::VerQueryValue(buf.data(), L"\\StringFileInfo\\040904B0\\ProductVersion", std::addressof(verBuf), std::addressof(verLen))) {
371  return std::nullopt;
372  }
373 
374  Version version;
375  std::wistringstream ss(
376  std::wstring(static_cast<const wchar_t*>(verBuf), verLen));
377  std::wstring token;
378  for (std::size_t i = 0; i < 4 && std::getline(ss, token, L'.'); ++i) {
379  version[i] = static_cast<std::uint16_t>(std::stoi(token));
380  }
381 
382  return version;
383  }
384 
385  class Segment
386  {
387  public:
388  enum Name : std::size_t
389  {
398  total
399  };
400 
401  constexpr Segment() noexcept = default;
402 
403  Segment(std::uintptr_t a_proxyBase, std::uintptr_t a_address, std::uintptr_t a_size) noexcept :
404  _proxyBase(a_proxyBase),
405  _address(a_address),
406  _size(a_size)
407  {}
408 
409  [[nodiscard]] std::uintptr_t address() const noexcept { return _address; }
410  [[nodiscard]] std::size_t offset() const noexcept { return address() - _proxyBase; }
411  [[nodiscard]] std::size_t size() const noexcept { return _size; }
412 
413  [[nodiscard]] void* pointer() const noexcept { return reinterpret_cast<void*>(address()); }
414 
415  template <class T>
416  [[nodiscard]] T* pointer() const noexcept
417  {
418  return static_cast<T*>(pointer());
419  }
420 
421  private:
422  std::uintptr_t _proxyBase{ 0 };
423  std::uintptr_t _address{ 0 };
424  std::size_t _size{ 0 };
425  };
426 
427  class Module
428  {
429  public:
430  [[nodiscard]] static Module& get();
431 
432  static void init()
433  {
434  _singleton.load();
435  _loaded = true;
436  }
437 
438  [[nodiscard]] constexpr std::uintptr_t base() const noexcept { return _base; }
439  [[nodiscard]] constexpr stl::zwstring filename() const noexcept { return { _filename.data(), _filename.size() }; }
440  [[nodiscard]] constexpr Version version() const noexcept { return _version; }
441 
442  [[nodiscard]] constexpr Segment segment(Segment::Name a_segment) const noexcept { return _segments[a_segment]; }
443 
444  [[nodiscard]] void* pointer() const noexcept { return reinterpret_cast<void*>(base()); }
445 
446  template <class T>
447  [[nodiscard]] T* pointer() const noexcept
448  {
449  return static_cast<T*>(pointer());
450  }
451 
452  Module(const Module&) = delete;
453  Module(Module&&) = delete;
454 
455  constexpr ~Module() noexcept = default;
456 
457  Module& operator=(const Module&) = delete;
458  Module& operator=(Module&&) = delete;
459 
460  private:
461  constexpr Module() = default;
462 
463  void load()
464  {
465  auto handle = WinAPI::GetModuleHandle(static_cast<wchar_t*>(nullptr));
466  if (handle == nullptr) {
468  "Failed to obtain module handle for game executable."sv);
469  }
470  _base = reinterpret_cast<std::uintptr_t>(handle);
471 
473  handle,
474  _filename.data(),
475  static_cast<std::uint32_t>(_filename.size()));
476 
477  load_version();
478  load_segments();
479  }
480 
481  void load_segments();
482 
483  void load_version()
484  {
485  const auto version = get_file_version(_filename.data());
486  if (version) {
487  _version = *version;
488  } else {
490  fmt::format(
491  "Failed to obtain file version info for: {}\n"
492  "Please contact the author of this script extender plugin for further assistance."sv,
493  stl::utf16_to_utf8(_filename.data()).value_or("<unicode conversion error>"s)));
494  }
495  }
496 
497  static constexpr std::array SEGMENTS{
499  std::make_pair(".idata"sv, static_cast<std::uint32_t>(0)),
500  std::make_pair(".rdata"sv, static_cast<std::uint32_t>(0)),
501  std::make_pair(".data"sv, static_cast<std::uint32_t>(0)),
502  std::make_pair(".pdata"sv, static_cast<std::uint32_t>(0)),
503  std::make_pair(".tls"sv, static_cast<std::uint32_t>(0)),
505  std::make_pair(".gfids"sv, static_cast<std::uint32_t>(0))
506  };
507 
508  static Module _singleton;
509  inline static bool _loaded{ false };
510  std::array<wchar_t, SKSE::WinAPI::MAX_PATH> _filename{ L"" };
511  std::array<Segment, Segment::total> _segments{};
512  Version _version;
513  std::uintptr_t _base{ 0 };
514  };
515 
516  inline constinit Module Module::_singleton;
517 
518  inline Module& Module::get()
519  {
520 #ifndef NDEBUG
521  if (!_loaded) {
522  stl::report_and_fail("Tried to access uninitialized Module.");
523  }
524 #endif
525  return _singleton;
526  }
527 
529  {
530  private:
531  struct mapping_t
532  {
533  std::uint64_t id;
534  std::uint64_t offset;
535 
536  constexpr auto operator<=>(const mapping_t&) const noexcept = default;
537  };
538 
539  struct comp_offset
540  {
541  constexpr bool operator()(const mapping_t& a_lhs, const mapping_t& a_rhs) const noexcept
542  {
543  return a_lhs.offset < a_rhs.offset;
544  }
545  };
546 
547  struct backup_t
548  {
549  std::uint64_t id;
550  std::uint64_t backup;
551 
552  constexpr auto operator<=>(const backup_t&) const noexcept = default;
553  };
554 
555  public:
556  class Offset2ID
557  {
558  public:
559  using value_type = mapping_t;
560  using container_type = std::vector<value_type>;
561  using size_type = typename container_type::size_type;
562  using const_iterator = typename container_type::const_iterator;
563  using const_reverse_iterator = typename container_type::const_reverse_iterator;
564 
565  template <class ExecutionPolicy>
566  explicit Offset2ID(ExecutionPolicy&& a_policy) //
567  requires(std::is_execution_policy_v<std::decay_t<ExecutionPolicy>>)
568  {
569  const std::span<const mapping_t> id2offset = IDDatabase::get()._id2offset;
570  _offset2id.reserve(id2offset.size());
571  _offset2id.insert(_offset2id.begin(), id2offset.begin(), id2offset.end());
572  std::sort(a_policy, _offset2id.begin(), _offset2id.end(), comp_offset());
573  }
574 
576  Offset2ID(std::execution::sequenced_policy{})
577  {}
578 
579  [[nodiscard]] std::uint64_t operator()(std::size_t a_offset) const
580  {
581  const mapping_t elem{ 0, a_offset };
582  const auto it = std::ranges::lower_bound(_offset2id, elem, comp_offset());
583  if (it == _offset2id.end()) {
585  fmt::format(
586  "Failed to find the offset within the database: 0x{:08X}"sv,
587  a_offset));
588  }
589 
590  return it->id;
591  }
592 
593  [[nodiscard]] const_iterator begin() const noexcept { return _offset2id.begin(); }
594  [[nodiscard]] const_iterator cbegin() const noexcept { return _offset2id.cbegin(); }
595 
596  [[nodiscard]] const_iterator end() const noexcept { return _offset2id.end(); }
597  [[nodiscard]] const_iterator cend() const noexcept { return _offset2id.cend(); }
598 
599  [[nodiscard]] const_reverse_iterator rbegin() const noexcept { return _offset2id.rbegin(); }
600  [[nodiscard]] const_reverse_iterator crbegin() const noexcept { return _offset2id.crbegin(); }
601 
602  [[nodiscard]] const_reverse_iterator rend() const noexcept { return _offset2id.rend(); }
603  [[nodiscard]] const_reverse_iterator crend() const noexcept { return _offset2id.crend(); }
604 
605  [[nodiscard]] size_type size() const noexcept { return _offset2id.size(); }
606 
607  private:
608  container_type _offset2id;
609  };
610 
611  [[nodiscard]] static IDDatabase& get();
612 
613  static void init()
614  {
615 #ifndef SKYRIMVR
616  _singleton.load();
617  _loaded = true;
618 #endif
619  }
620 
621  [[nodiscard]] inline std::size_t id2offset(std::uint64_t a_id) const
622  {
623  auto it = std::ranges::lower_bound(_id2offset, mapping_t{ a_id, 0 });
624  if (it == _id2offset.end() || it->id != a_id) {
625  const auto backup = std::ranges::lower_bound(_backups, backup_t{ a_id, 0 });
626  if (backup != _backups.end() && backup->id == a_id) {
627  a_id = backup->backup;
628  it = std::ranges::lower_bound(_id2offset, mapping_t{ a_id, 0 });
629  }
630  }
631 
632  if (it == _id2offset.end() || it->id != a_id) {
634  fmt::format(
635  "Failed to find the id within the address library: {}\n"
636  "This means this script extender plugin is incompatible with the address "
637  "library for this version of the game, and thus does not support it."sv,
638  a_id));
639  }
640 
641  return static_cast<std::size_t>(it->offset);
642  }
643 
644  IDDatabase(const IDDatabase&) = delete;
645  IDDatabase(IDDatabase&&) = delete;
646 
647  ~IDDatabase() = default;
648 
649  IDDatabase& operator=(const IDDatabase&) = delete;
651 
652  private:
653  friend Offset2ID;
654 
655  class header_t
656  {
657  public:
658  void read(binary_io::file_istream& a_in)
659  {
660  const auto [format] = a_in.read<std::int32_t>();
661  if (format != 2) {
663  fmt::format(
664  "Unsupported address library format: {}\n"
665  "This means this script extender plugin is incompatible with the address "
666  "library available for this version of the game, and thus does not "
667  "support it."sv,
668  format));
669  }
670 
671  const auto [major, minor, patch, revision] =
672  a_in.read<std::int32_t, std::int32_t, std::int32_t, std::int32_t>();
673  _version[0] = static_cast<std::uint16_t>(major);
674  _version[1] = static_cast<std::uint16_t>(minor);
675  _version[2] = static_cast<std::uint16_t>(patch);
676  _version[3] = static_cast<std::uint16_t>(revision);
677 
678  const auto [nameLen] = a_in.read<std::int32_t>();
679  a_in.seek_relative(nameLen);
680 
681  a_in.read(_pointerSize, _addressCount);
682  }
683 
684  [[nodiscard]] std::size_t address_count() const noexcept { return static_cast<std::size_t>(_addressCount); }
685  [[nodiscard]] std::uint64_t pointer_size() const noexcept { return static_cast<std::uint64_t>(_pointerSize); }
686  [[nodiscard]] Version version() const noexcept { return _version; }
687 
688  private:
689  Version _version;
690  std::int32_t _pointerSize{ 0 };
691  std::int32_t _addressCount{ 0 };
692  };
693 
694  constexpr IDDatabase() = default;
695 
696  void load()
697  {
698  const auto version = Module::get().version();
699  const auto filename =
701  fmt::format(
702  "Data/SKSE/Plugins/versionlib-{}.bin"sv,
703  version.string()))
704  .value_or(L"<unknown filename>"s);
705  load_file(filename, version);
706  }
707 
708  void load_file(stl::zwstring a_filename, Version a_version)
709  {
710  try {
711  binary_io::file_istream in(a_filename);
712  header_t header;
713  header.read(in);
714  if (header.version() != a_version) {
715  stl::report_and_fail("version mismatch"sv);
716  }
717 
718  auto mapname = L"CommonLibSSEOffsets-v2-"s;
719  mapname += a_version.wstring();
720  const auto byteSize = static_cast<std::size_t>(header.address_count()) * sizeof(mapping_t);
721  if (_mmap.open(mapname, byteSize)) {
722  _id2offset = { static_cast<mapping_t*>(_mmap.data()), header.address_count() };
723  } else if (_mmap.create(mapname, byteSize)) {
724  _id2offset = { static_cast<mapping_t*>(_mmap.data()), header.address_count() };
725  unpack_file(in, header);
726  std::sort(
727  _id2offset.begin(),
728  _id2offset.end(),
729  [](auto&& a_lhs, auto&& a_rhs) {
730  return a_lhs.id < a_rhs.id;
731  });
732  } else {
733  stl::report_and_fail("failed to create shared mapping"sv);
734  }
735  } catch (const std::system_error&) {
737  fmt::format(
738  "Failed to locate an appropriate address library with the path: {}\n"
739  "This means you are missing the address library for this specific version of "
740  "the game. Please continue to the mod page for address library to download "
741  "an appropriate version. If one is not available, then it is likely that "
742  "address library has not yet added support for this version of the game."sv,
743  stl::utf16_to_utf8(a_filename).value_or("<unknown filename>"s)));
744  }
745  }
746 
747  void unpack_file(binary_io::file_istream& a_in, header_t a_header)
748  {
749  std::uint8_t type = 0;
750  std::uint64_t id = 0;
751  std::uint64_t offset = 0;
752  std::uint64_t prevID = 0;
753  std::uint64_t prevOffset = 0;
754  for (auto& mapping : _id2offset) {
755  a_in.read(type);
756  const auto lo = static_cast<std::uint8_t>(type & 0xF);
757  const auto hi = static_cast<std::uint8_t>(type >> 4);
758 
759  switch (lo) {
760  case 0:
761  a_in.read(id);
762  break;
763  case 1:
764  id = prevID + 1;
765  break;
766  case 2:
767  id = prevID + std::get<0>(a_in.read<std::uint8_t>());
768  break;
769  case 3:
770  id = prevID - std::get<0>(a_in.read<std::uint8_t>());
771  break;
772  case 4:
773  id = prevID + std::get<0>(a_in.read<std::uint16_t>());
774  break;
775  case 5:
776  id = prevID - std::get<0>(a_in.read<std::uint16_t>());
777  break;
778  case 6:
779  std::tie(id) = a_in.read<std::uint16_t>();
780  break;
781  case 7:
782  std::tie(id) = a_in.read<std::uint32_t>();
783  break;
784  default:
785  stl::report_and_fail("unhandled type"sv);
786  }
787 
788  const std::uint64_t tmp = (hi & 8) != 0 ? (prevOffset / a_header.pointer_size()) : prevOffset;
789 
790  switch (hi & 7) {
791  case 0:
792  a_in.read(offset);
793  break;
794  case 1:
795  offset = tmp + 1;
796  break;
797  case 2:
798  offset = tmp + std::get<0>(a_in.read<std::uint8_t>());
799  break;
800  case 3:
801  offset = tmp - std::get<0>(a_in.read<std::uint8_t>());
802  break;
803  case 4:
804  offset = tmp + std::get<0>(a_in.read<std::uint16_t>());
805  break;
806  case 5:
807  offset = tmp - std::get<0>(a_in.read<std::uint16_t>());
808  break;
809  case 6:
810  std::tie(offset) = a_in.read<std::uint16_t>();
811  break;
812  case 7:
813  std::tie(offset) = a_in.read<std::uint32_t>();
814  break;
815  default:
816  stl::report_and_fail("unhandled type"sv);
817  }
818 
819  if ((hi & 8) != 0) {
820  offset *= a_header.pointer_size();
821  }
822 
823  mapping = { id, offset };
824 
825  prevOffset = offset;
826  prevID = id;
827  }
828  }
829 
830  static IDDatabase _singleton;
831  inline static bool _loaded{ false };
832  detail::memory_map _mmap;
833  std::span<mapping_t> _id2offset;
834 
835  // Patch for known Address Library bugs
836  inline static constexpr auto _backups =
837  std::to_array<backup_t>({
838  { 441582, 21890 },
839  { 443410, 69188 },
840  { 453511, 109206 },
841  { 502114, 380738 },
842  });
843  static_assert(std::ranges::is_sorted(_backups));
844  };
845 
846  inline constinit IDDatabase IDDatabase::_singleton;
847 
849  {
850 #ifndef NDEBUG
851  if (!_loaded) {
852  stl::report_and_fail("Tried to access uninitialized IDDatabase.");
853  }
854 #endif
855  return _singleton;
856  }
857 
858  class Offset
859  {
860  public:
861  constexpr Offset() noexcept = default;
862 
863  explicit constexpr Offset(std::size_t a_offset) noexcept :
864  _offset(a_offset)
865  {}
866 
867  constexpr Offset& operator=(std::size_t a_offset) noexcept
868  {
869  _offset = a_offset;
870  return *this;
871  }
872 
873  [[nodiscard]] std::uintptr_t address() const { return base() + offset(); }
874  [[nodiscard]] constexpr std::size_t offset() const noexcept { return _offset; }
875 
876  private:
877  [[nodiscard]] static std::uintptr_t base() { return Module::get().base(); }
878 
879  std::size_t _offset{ 0 };
880  };
881 
882  class ID
883  {
884  public:
885  constexpr ID() noexcept = default;
886 
887  explicit constexpr ID(std::uint64_t a_id) noexcept :
888  _id(a_id)
889  {}
890 
891  constexpr ID& operator=(std::uint64_t a_id) noexcept
892  {
893  _id = a_id;
894  return *this;
895  }
896 
897  [[nodiscard]] std::uintptr_t address() const { return base() + offset(); }
898  [[nodiscard]] constexpr std::uint64_t id() const noexcept { return _id; }
899  [[nodiscard]] std::size_t offset() const { return IDDatabase::get().id2offset(_id); }
900 
901  private:
902  [[nodiscard]] static std::uintptr_t base() { return Module::get().base(); }
903 
904  std::uint64_t _id{ 0 };
905  };
906 
908  {
909  public:
910  using reg_t = std::pair<std::uint64_t, std::uintptr_t*>;
911 
912  constexpr AddressManager() noexcept = default;
913 
914  [[nodiscard]] static AddressManager& get();
915 
916  void flush()
917  {
918  for (const auto& [id, offsetAddr] : std::span(_buffer.get(), _size)) {
919  *offsetAddr = ID(id).offset();
920  }
921  clear();
922  }
923 
924  [[msvc::noinline]] void register_address(std::uint64_t a_ID, std::uintptr_t* a_offsetAddr)
925  {
926  if (_size == _capacity) {
927  grow();
928  }
929  std::construct_at(&_buffer[_size++], a_ID, a_offsetAddr);
930  }
931 
932  void clear()
933  {
934  _buffer.reset();
935  _size = 0;
936  _capacity = 0;
937  }
938 
939  private:
940  void grow()
941  {
942  const std::size_t capacity = _capacity > 0 ? _capacity * 2 : 1;
943  auto buffer = std::make_unique<reg_t[]>(capacity);
944  std::memcpy(buffer.get(), _buffer.get(), _capacity * sizeof(reg_t));
945  _buffer.swap(buffer);
946  _capacity = capacity;
947  }
948 
949  private:
950  static AddressManager _singleton;
951  std::unique_ptr<reg_t[]> _buffer{ nullptr };
952  std::size_t _size{ 0 };
953  std::size_t _capacity{ 0 };
954  };
955 
956  inline constinit AddressManager AddressManager::_singleton;
957 
959  {
960  return _singleton;
961  }
962 
963  template <std::uint64_t ID>
964  class StaticID
965  {
966  public:
967  operator Offset() const noexcept { return Offset(_cache._offset); }
968 
969  private:
970  struct Cache
971  {
972  Cache()
973  {
975  }
976 
977  std::uintptr_t _offset;
978  };
979 
980  inline static Cache _cache;
981  };
982 
983  template <class T>
985  {
986  public:
987  using value_type =
988  std::conditional_t<
989  std::is_member_pointer_v<T> || std::is_function_v<std::remove_pointer_t<T>>,
990  std::decay_t<T>,
991  T>;
992 
993  constexpr Relocation() noexcept = default;
994 
995  explicit constexpr Relocation(std::uintptr_t a_address) noexcept :
996  _impl{ a_address }
997  {}
998 
999  explicit Relocation(Offset a_offset) :
1000  _impl{ a_offset.address() }
1001  {}
1002 
1003  explicit Relocation(ID a_id) :
1004  _impl{ a_id.address() }
1005  {}
1006 
1007  explicit Relocation(ID a_id, std::ptrdiff_t a_offset) :
1008  _impl{ a_id.address() + a_offset }
1009  {}
1010 
1011  constexpr Relocation& operator=(std::uintptr_t a_address) noexcept
1012  {
1013  _impl = a_address;
1014  return *this;
1015  }
1016 
1018  {
1019  _impl = a_offset.address();
1020  return *this;
1021  }
1022 
1024  {
1025  _impl = a_id.address();
1026  return *this;
1027  }
1028 
1029  template <class U = value_type>
1030  [[nodiscard]] decltype(auto) operator*() const noexcept //
1031  requires(std::is_pointer_v<U>)
1032  {
1033  return *get();
1034  }
1035 
1036  template <class U = value_type>
1037  [[nodiscard]] auto operator->() const noexcept //
1038  requires(std::is_pointer_v<U>)
1039  {
1040  return get();
1041  }
1042 
1043  template <class... Args>
1044  std::invoke_result_t<const value_type&, Args...> operator()(Args&&... a_args) const //
1045  noexcept(std::is_nothrow_invocable_v<const value_type&, Args...>) //
1046  requires(std::invocable<const value_type&, Args...>)
1047  {
1048  return REL::invoke(get(), std::forward<Args>(a_args)...);
1049  }
1050 
1051  [[nodiscard]] constexpr std::uintptr_t address() const noexcept { return _impl; }
1052  [[nodiscard]] std::size_t offset() const { return _impl - base(); }
1053 
1054  [[nodiscard]] value_type get() const //
1055  noexcept(std::is_nothrow_copy_constructible_v<value_type>)
1056  {
1057  assert(_impl != 0);
1058  return stl::unrestricted_cast<value_type>(_impl);
1059  }
1060 
1061  template <class U = value_type>
1062  std::uintptr_t write_vfunc(std::size_t a_idx, std::uintptr_t a_newFunc) //
1063  requires(std::same_as<U, std::uintptr_t>)
1064  {
1065  const auto addr = address() + (sizeof(void*) * a_idx);
1066  const auto result = *reinterpret_cast<std::uintptr_t*>(addr);
1067  safe_write(addr, a_newFunc);
1068  return result;
1069  }
1070 
1071  template <class F>
1072  std::uintptr_t write_vfunc(std::size_t a_idx, F a_newFunc) //
1073  requires(std::same_as<value_type, std::uintptr_t>)
1074  {
1075  return write_vfunc(a_idx, stl::unrestricted_cast<std::uintptr_t>(a_newFunc));
1076  }
1077 
1078  private:
1079  // clang-format off
1080  [[nodiscard]] static std::uintptr_t base() { return Module::get().base(); }
1081  // clang-format on
1082 
1083  std::uintptr_t _impl{ 0 };
1084  };
1085 
1086  namespace detail
1087  {
1088  namespace characters
1089  {
1090  [[nodiscard]] constexpr bool hexadecimal(char a_ch) noexcept
1091  {
1092  return ('0' <= a_ch && a_ch <= '9') ||
1093  ('A' <= a_ch && a_ch <= 'F') ||
1094  ('a' <= a_ch && a_ch <= 'f');
1095  }
1096 
1097  [[nodiscard]] constexpr bool space(char a_ch) noexcept
1098  {
1099  return a_ch == ' ';
1100  }
1101 
1102  [[nodiscard]] constexpr bool wildcard(char a_ch) noexcept
1103  {
1104  return a_ch == '?';
1105  }
1106  }
1107 
1108  namespace rules
1109  {
1110  namespace detail
1111  {
1112  [[nodiscard]] consteval std::byte hexacharacters_to_hexadecimal(char a_hi, char a_lo) noexcept
1113  {
1114  constexpr auto lut = []() noexcept {
1115  std::array<std::uint8_t, std::numeric_limits<unsigned char>::max() + 1> a = {};
1116 
1117  const auto iterate = [&](std::uint8_t a_iFirst, unsigned char a_cFirst, unsigned char a_cLast) noexcept {
1118  for (; a_cFirst <= a_cLast; ++a_cFirst, ++a_iFirst) {
1119  a[a_cFirst] = a_iFirst;
1120  }
1121  };
1122 
1123  iterate(0, '0', '9');
1124  iterate(0xA, 'A', 'F');
1125  iterate(0xa, 'a', 'f');
1126 
1127  return a;
1128  }();
1129 
1130  return static_cast<std::byte>(
1131  lut[static_cast<unsigned char>(a_hi)] * 0x10u +
1132  lut[static_cast<unsigned char>(a_lo)]);
1133  }
1134  }
1135 
1136  template <char HI, char LO>
1138  {
1139  public:
1140  [[nodiscard]] static constexpr bool match(std::byte a_byte) noexcept
1141  {
1142  constexpr auto expected = detail::hexacharacters_to_hexadecimal(HI, LO);
1143  return a_byte == expected;
1144  }
1145  };
1146 
1147  static_assert(Hexadecimal<'5', '7'>::match(std::byte{ 0x57 }));
1148  static_assert(Hexadecimal<'6', '5'>::match(std::byte{ 0x65 }));
1149  static_assert(Hexadecimal<'B', 'D'>::match(std::byte{ 0xBD }));
1150  static_assert(Hexadecimal<'1', 'C'>::match(std::byte{ 0x1C }));
1151  static_assert(Hexadecimal<'F', '2'>::match(std::byte{ 0xF2 }));
1152  static_assert(Hexadecimal<'9', 'f'>::match(std::byte{ 0x9f }));
1153 
1154  static_assert(!Hexadecimal<'D', '4'>::match(std::byte{ 0xF8 }));
1155  static_assert(!Hexadecimal<'6', '7'>::match(std::byte{ 0xAA }));
1156  static_assert(!Hexadecimal<'7', '8'>::match(std::byte{ 0xE3 }));
1157  static_assert(!Hexadecimal<'6', 'E'>::match(std::byte{ 0x61 }));
1158 
1159  class Wildcard
1160  {
1161  public:
1162  [[nodiscard]] static constexpr bool match(std::byte) noexcept
1163  {
1164  return true;
1165  }
1166  };
1167 
1168  static_assert(Wildcard::match(std::byte{ 0xB9 }));
1169  static_assert(Wildcard::match(std::byte{ 0x96 }));
1170  static_assert(Wildcard::match(std::byte{ 0x35 }));
1171  static_assert(Wildcard::match(std::byte{ 0xE4 }));
1172 
1173  template <char, char>
1174  void rule_for() noexcept;
1175 
1176  template <char C1, char C2>
1177  Hexadecimal<C1, C2> rule_for() noexcept
1178  requires(characters::hexadecimal(C1) && characters::hexadecimal(C2));
1179 
1180  template <char C1, char C2>
1181  Wildcard rule_for() noexcept
1182  requires(characters::wildcard(C1) && characters::wildcard(C2));
1183  }
1184 
1185  template <class... Rules>
1187  {
1188  public:
1189  static_assert(sizeof...(Rules) >= 1, "must provide at least 1 rule for the pattern matcher");
1190 
1191  [[nodiscard]] constexpr bool match(std::span<const std::byte, sizeof...(Rules)> a_bytes) const noexcept
1192  {
1193  std::size_t i = 0;
1194  return (Rules::match(a_bytes[i++]) && ...);
1195  }
1196 
1197  [[nodiscard]] bool match(std::uintptr_t a_address) const noexcept
1198  {
1199  return this->match(*reinterpret_cast<const std::byte(*)[sizeof...(Rules)]>(a_address));
1200  }
1201 
1202  void match_or_fail(std::uintptr_t a_address, std::source_location a_loc = std::source_location::current()) const noexcept
1203  {
1204  if (!this->match(a_address)) {
1205  const auto version = Module::get().version();
1207  fmt::format(
1208  "A pattern has failed to match.\n"
1209  "This means the plugin is incompatible with the current version of the game ({}.{}.{}). "
1210  "Head to the mod page of this plugin to see if an update is available."sv,
1211  version[0],
1212  version[1],
1213  version[2]),
1214  a_loc);
1215  }
1216  }
1217  };
1218 
1219  void consteval_error(const char* a_error);
1220 
1221  template <stl::nttp::string S, class... Rules>
1222  [[nodiscard]] constexpr auto do_make_pattern() noexcept
1223  {
1224  if constexpr (S.length() == 0) {
1225  return PatternMatcher<Rules...>();
1226  } else if constexpr (S.length() == 1) {
1227  constexpr char c = S[0];
1228  if constexpr (characters::hexadecimal(c) || characters::wildcard(c)) {
1229  consteval_error("the given pattern has an unpaired rule (rules are required to be written in pairs of 2)");
1230  } else {
1231  consteval_error("the given pattern has trailing characters at the end (which is not allowed)");
1232  }
1233  } else {
1234  using rule_t = decltype(rules::rule_for<S[0], S[1]>());
1235  if constexpr (std::same_as<rule_t, void>) {
1236  consteval_error("the given pattern failed to match any known rules");
1237  } else {
1238  if constexpr (S.length() <= 3) {
1239  return do_make_pattern<S.template substr<2>(), Rules..., rule_t>();
1240  } else if constexpr (characters::space(S[2])) {
1241  return do_make_pattern<S.template substr<3>(), Rules..., rule_t>();
1242  } else {
1243  consteval_error("a space character is required to split byte patterns");
1244  }
1245  }
1246  }
1247  }
1248 
1249  template <class... Bytes>
1250  [[nodiscard]] consteval auto make_byte_array(Bytes... a_bytes) noexcept
1251  -> std::array<std::byte, sizeof...(Bytes)>
1252  {
1253  static_assert((std::integral<Bytes> && ...), "all bytes must be an integral type");
1254  return { static_cast<std::byte>(a_bytes)... };
1255  }
1256  }
1257 
1258  template <stl::nttp::string S>
1259  [[nodiscard]] constexpr auto make_pattern() noexcept
1260  {
1261  return detail::do_make_pattern<S>();
1262  }
1263 
1264  static_assert(make_pattern<"40 10 F2 ??">().match(
1265  detail::make_byte_array(0x40, 0x10, 0xF2, 0x41)));
1266  static_assert(make_pattern<"B8 D0 ?? ?? D4 6E">().match(
1267  detail::make_byte_array(0xB8, 0xD0, 0x35, 0x2A, 0xD4, 0x6E)));
1268 }
1269 
1270 #ifndef SKYRIMVR
1271 # define STATIC_OFFSET(name) ::REL::StaticID<::RE::Offset::name.id()>()
1272 #else
1273 # define STATIC_OFFSET(name) ::RE::Offset::name
1274 #endif
1275 
1276 #undef REL_MAKE_MEMBER_FUNCTION_NON_POD_TYPE
1277 #undef REL_MAKE_MEMBER_FUNCTION_NON_POD_TYPE_HELPER
1278 #undef REL_MAKE_MEMBER_FUNCTION_NON_POD_TYPE_HELPER_IMPL
1279 
1280 #undef REL_MAKE_MEMBER_FUNCTION_POD_TYPE
1281 #undef REL_MAKE_MEMBER_FUNCTION_POD_TYPE_HELPER
1282 #undef REL_MAKE_MEMBER_FUNCTION_POD_TYPE_HELPER_IMPL
Definition: Relocation.h:908
static AddressManager & get()
Definition: Relocation.h:958
void flush()
Definition: Relocation.h:916
std::pair< std::uint64_t, std::uintptr_t * > reg_t
Definition: Relocation.h:910
void clear()
Definition: Relocation.h:932
void register_address(std::uint64_t a_ID, std::uintptr_t *a_offsetAddr)
Definition: Relocation.h:924
constexpr AddressManager() noexcept=default
Definition: Relocation.h:557
Offset2ID()
Definition: Relocation.h:575
typename container_type::const_iterator const_iterator
Definition: Relocation.h:562
typename container_type::const_reverse_iterator const_reverse_iterator
Definition: Relocation.h:563
const_reverse_iterator crbegin() const noexcept
Definition: Relocation.h:600
Offset2ID(ExecutionPolicy &&a_policy) requires(std
Definition: Relocation.h:566
const_iterator begin() const noexcept
Definition: Relocation.h:593
std::vector< value_type > container_type
Definition: Relocation.h:560
size_type size() const noexcept
Definition: Relocation.h:605
const_reverse_iterator rbegin() const noexcept
Definition: Relocation.h:599
const_reverse_iterator crend() const noexcept
Definition: Relocation.h:603
const_iterator cbegin() const noexcept
Definition: Relocation.h:594
typename container_type::size_type size_type
Definition: Relocation.h:561
const_iterator cend() const noexcept
Definition: Relocation.h:597
std::uint64_t operator()(std::size_t a_offset) const
Definition: Relocation.h:579
mapping_t value_type
Definition: Relocation.h:559
const_reverse_iterator rend() const noexcept
Definition: Relocation.h:602
const_iterator end() const noexcept
Definition: Relocation.h:596
Definition: Relocation.h:529
IDDatabase(IDDatabase &&)=delete
std::size_t id2offset(std::uint64_t a_id) const
Definition: Relocation.h:621
static IDDatabase & get()
Definition: Relocation.h:848
IDDatabase(const IDDatabase &)=delete
IDDatabase & operator=(const IDDatabase &)=delete
~IDDatabase()=default
static void init()
Definition: Relocation.h:613
IDDatabase & operator=(IDDatabase &&)=delete
Definition: Relocation.h:883
std::size_t offset() const
Definition: Relocation.h:899
constexpr ID & operator=(std::uint64_t a_id) noexcept
Definition: Relocation.h:891
constexpr std::uint64_t id() const noexcept
Definition: Relocation.h:898
constexpr ID() noexcept=default
std::uintptr_t address() const
Definition: Relocation.h:897
Definition: Relocation.h:428
Module(Module &&)=delete
constexpr Version version() const noexcept
Definition: Relocation.h:440
constexpr Segment segment(Segment::Name a_segment) const noexcept
Definition: Relocation.h:442
constexpr stl::zwstring filename() const noexcept
Definition: Relocation.h:439
static void init()
Definition: Relocation.h:432
Module(const Module &)=delete
constexpr ~Module() noexcept=default
T * pointer() const noexcept
Definition: Relocation.h:447
void * pointer() const noexcept
Definition: Relocation.h:444
constexpr std::uintptr_t base() const noexcept
Definition: Relocation.h:438
static Module & get()
Definition: Relocation.h:518
Definition: Relocation.h:859
constexpr Offset() noexcept=default
std::uintptr_t address() const
Definition: Relocation.h:873
constexpr Offset & operator=(std::size_t a_offset) noexcept
Definition: Relocation.h:867
constexpr std::size_t offset() const noexcept
Definition: Relocation.h:874
Definition: Relocation.h:985
constexpr std::uintptr_t address() const noexcept
Definition: Relocation.h:1051
auto operator->() const noexcept requires(std
Definition: Relocation.h:1037
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:991
constexpr Relocation & operator=(std::uintptr_t a_address) noexcept
Definition: Relocation.h:1011
std::invoke_result_t< const value_type &, Args... > operator()(Args &&... a_args) const noexcept(std::is_nothrow_invocable_v< const value_type &, Args... >) requires(std
Definition: Relocation.h:1044
value_type get() const noexcept(std::is_nothrow_copy_constructible_v< value_type >)
Definition: Relocation.h:1054
Relocation(Offset a_offset)
Definition: Relocation.h:999
Relocation(ID a_id, std::ptrdiff_t a_offset)
Definition: Relocation.h:1007
std::uintptr_t write_vfunc(std::size_t a_idx, std::uintptr_t a_newFunc) requires(std
Definition: Relocation.h:1062
std::uintptr_t write_vfunc(std::size_t a_idx, F a_newFunc) requires(std
Definition: Relocation.h:1072
Relocation & operator=(Offset a_offset)
Definition: Relocation.h:1017
constexpr Relocation() noexcept=default
Relocation(ID a_id)
Definition: Relocation.h:1003
Relocation & operator=(ID a_id)
Definition: Relocation.h:1023
std::size_t offset() const
Definition: Relocation.h:1052
Definition: Relocation.h:386
std::size_t size() const noexcept
Definition: Relocation.h:411
void * pointer() const noexcept
Definition: Relocation.h:413
Name
Definition: Relocation.h:389
@ data
Definition: Relocation.h:393
@ tls
Definition: Relocation.h:395
@ textw
Definition: Relocation.h:396
@ gfids
Definition: Relocation.h:397
@ total
Definition: Relocation.h:398
@ idata
Definition: Relocation.h:391
@ textx
Definition: Relocation.h:390
@ pdata
Definition: Relocation.h:394
@ rdata
Definition: Relocation.h:392
T * pointer() const noexcept
Definition: Relocation.h:416
constexpr Segment() noexcept=default
std::uintptr_t address() const noexcept
Definition: Relocation.h:409
std::size_t offset() const noexcept
Definition: Relocation.h:410
Definition: Relocation.h:965
Definition: Relocation.h:284
constexpr decltype(auto) end() const noexcept
Definition: Relocation.h:305
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:296
constexpr reference operator[](std::size_t a_idx) noexcept
Definition: Relocation.h:300
constexpr decltype(auto) cbegin() const noexcept
Definition: Relocation.h:304
std::string string() const
Definition: Relocation.h:327
constexpr std::uint32_t pack() const noexcept
Definition: Relocation.h:318
constexpr const_reference operator[](std::size_t a_idx) const noexcept
Definition: Relocation.h:301
std::uint16_t value_type
Definition: Relocation.h:286
constexpr decltype(auto) cend() const noexcept
Definition: Relocation.h:306
value_type & reference
Definition: Relocation.h:287
const value_type & const_reference
Definition: Relocation.h:288
constexpr Version() noexcept=default
constexpr std::strong_ordering compare(const Version &a_rhs) const noexcept
Definition: Relocation.h:308
constexpr decltype(auto) begin() const noexcept
Definition: Relocation.h:303
std::wstring wstring() const
Definition: Relocation.h:338
Definition: Relocation.h:1187
constexpr bool match(std::span< const std::byte, sizeof...(Rules)> a_bytes) const noexcept
Definition: Relocation.h:1191
void match_or_fail(std::uintptr_t a_address, std::source_location a_loc=std::source_location::current()) const noexcept
Definition: Relocation.h:1202
bool match(std::uintptr_t a_address) const noexcept
Definition: Relocation.h:1197
Definition: Relocation.h:64
~memory_map()
Definition: Relocation.h:77
void * data() noexcept
Definition: Relocation.h:93
constexpr memory_map & operator=(memory_map &&a_rhs) noexcept
Definition: Relocation.h:81
constexpr memory_map() noexcept=default
bool create(stl::zwstring a_name, std::size_t a_size)
memory_map & operator=(const memory_map &)=delete
bool open(stl::zwstring a_name, std::size_t a_size)
Definition: Relocation.h:1138
static constexpr bool match(std::byte a_byte) noexcept
Definition: Relocation.h:1140
Definition: Relocation.h:1160
static constexpr bool match(std::byte) noexcept
Definition: Relocation.h:1162
constexpr bool space(char a_ch) noexcept
Definition: Relocation.h:1097
constexpr bool hexadecimal(char a_ch) noexcept
Definition: Relocation.h:1090
constexpr bool wildcard(char a_ch) noexcept
Definition: Relocation.h:1102
consteval std::byte hexacharacters_to_hexadecimal(char a_hi, char a_lo) noexcept
Definition: Relocation.h:1112
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:1250
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:1222
REL_MAKE_MEMBER_FUNCTION_POD_TYPE()
REL_MAKE_MEMBER_FUNCTION_NON_POD_TYPE()
Definition: Relocation.h:60
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:354
constexpr std::uint8_t INT3
Definition: Relocation.h:198
constexpr bool operator==(const Version &a_lhs, const Version &a_rhs) noexcept
Definition: Relocation.h:353
void safe_fill(std::uintptr_t a_dst, std::uint8_t a_value, std::size_t a_count)
Definition: Relocation.h:261
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:1259
std::optional< Version > get_file_version(stl::zwstring a_filename)
Definition: Relocation.h:356
std::invoke_result_t< F, Args... > invoke(F &&a_func, Args &&... a_args) noexcept(std::is_nothrow_invocable_v< F, Args... >) requires(std
Definition: Relocation.h:201
constexpr std::uint8_t RET
Definition: Relocation.h:197
auto make_pair(T1 &&a_first, T2 &&a_second)
Definition: BSTTuple.h:177
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
string(const CharT(&)[N]) -> string< CharT, N - 1 >
void report_and_fail(std::string_view a_msg, std::source_location a_loc=std::source_location::current())
Definition: PCH.h:621
auto utf16_to_utf8(std::wstring_view a_in) noexcept -> std::optional< std::string >
Definition: PCH.h:593
auto utf8_to_utf16(std::string_view a_in) noexcept -> std::optional< std::wstring >
Definition: PCH.h:567
requires(std::invocable< std::remove_reference_t< EF >>) class scope_exit
Definition: PCH.h:190
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