CommonLibSSE (powerof3)
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  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 
77  ~memory_map() { close(); }
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>
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 <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 
274  class Version
275  {
276  public:
277  using value_type = std::uint16_t;
279  using const_reference = const value_type&;
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 
376  class Segment
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{
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)),
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:
531  class Offset2ID
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  if (format != 2) {
637  fmt::format(
638  "Unsupported address library format: {}\n"
639  "This means this script extender plugin is incompatible with the address "
640  "library available for this version of the game, and thus does not "
641  "support it."sv,
642  format));
643  }
644 
645  const auto [major, minor, patch, revision] =
646  a_in.read<std::int32_t, std::int32_t, std::int32_t, std::int32_t>();
647  _version[0] = static_cast<std::uint16_t>(major);
648  _version[1] = static_cast<std::uint16_t>(minor);
649  _version[2] = static_cast<std::uint16_t>(patch);
650  _version[3] = static_cast<std::uint16_t>(revision);
651 
652  const auto [nameLen] = a_in.read<std::int32_t>();
653  a_in.seek_relative(nameLen);
654 
655  a_in.read(_pointerSize, _addressCount);
656  }
657 
658  [[nodiscard]] std::size_t address_count() const noexcept { return static_cast<std::size_t>(_addressCount); }
659  [[nodiscard]] std::uint64_t pointer_size() const noexcept { return static_cast<std::uint64_t>(_pointerSize); }
660  [[nodiscard]] Version version() const noexcept { return _version; }
661 
662  private:
663  Version _version;
664  std::int32_t _pointerSize{ 0 };
665  std::int32_t _addressCount{ 0 };
666  };
667 
668  IDDatabase() { load(); }
669 
670  IDDatabase(const IDDatabase&) = delete;
671  IDDatabase(IDDatabase&&) = delete;
672 
673  ~IDDatabase() = default;
674 
675  IDDatabase& operator=(const IDDatabase&) = delete;
676  IDDatabase& operator=(IDDatabase&&) = delete;
677 
678  void load()
679  {
680  const auto version = Module::get().version();
681  const auto filename =
683  fmt::format(
684  "Data/SKSE/Plugins/versionlib-{}.bin"sv,
685  version.string()))
686  .value_or(L"<unknown filename>"s);
687  load_file(filename, version);
688  }
689 
690  void load_file(stl::zwstring a_filename, Version a_version)
691  {
692  try {
693  binary_io::file_istream in(a_filename);
694  header_t header;
695  header.read(in);
696  if (header.version() != a_version) {
697  stl::report_and_fail("version mismatch"sv);
698  }
699 
700  auto mapname = L"CommonLibSSEOffsets-v2-"s;
701  mapname += a_version.wstring();
702  const auto byteSize = static_cast<std::size_t>(header.address_count()) * sizeof(mapping_t);
703  if (_mmap.open(mapname, byteSize)) {
704  _id2offset = { static_cast<mapping_t*>(_mmap.data()), header.address_count() };
705  } else if (_mmap.create(mapname, byteSize)) {
706  _id2offset = { static_cast<mapping_t*>(_mmap.data()), header.address_count() };
707  unpack_file(in, header);
708  std::sort(
709  _id2offset.begin(),
710  _id2offset.end(),
711  [](auto&& a_lhs, auto&& a_rhs) {
712  return a_lhs.id < a_rhs.id;
713  });
714  } else {
715  stl::report_and_fail("failed to create shared mapping"sv);
716  }
717  } catch (const std::system_error&) {
719  fmt::format(
720  "Failed to locate an appropriate address library with the path: {}\n"
721  "This means you are missing the address library for this specific version of "
722  "the game. Please continue to the mod page for address library to download "
723  "an appropriate version. If one is not available, then it is likely that "
724  "address library has not yet added support for this version of the game."sv,
725  stl::utf16_to_utf8(a_filename).value_or("<unknown filename>"s)));
726  }
727  }
728 
729  void unpack_file(binary_io::file_istream& a_in, header_t a_header)
730  {
731  std::uint8_t type = 0;
732  std::uint64_t id = 0;
733  std::uint64_t offset = 0;
734  std::uint64_t prevID = 0;
735  std::uint64_t prevOffset = 0;
736  for (auto& mapping : _id2offset) {
737  a_in.read(type);
738  const auto lo = static_cast<std::uint8_t>(type & 0xF);
739  const auto hi = static_cast<std::uint8_t>(type >> 4);
740 
741  switch (lo) {
742  case 0:
743  a_in.read(id);
744  break;
745  case 1:
746  id = prevID + 1;
747  break;
748  case 2:
749  id = prevID + std::get<0>(a_in.read<std::uint8_t>());
750  break;
751  case 3:
752  id = prevID - std::get<0>(a_in.read<std::uint8_t>());
753  break;
754  case 4:
755  id = prevID + std::get<0>(a_in.read<std::uint16_t>());
756  break;
757  case 5:
758  id = prevID - std::get<0>(a_in.read<std::uint16_t>());
759  break;
760  case 6:
761  std::tie(id) = a_in.read<std::uint16_t>();
762  break;
763  case 7:
764  std::tie(id) = a_in.read<std::uint32_t>();
765  break;
766  default:
767  stl::report_and_fail("unhandled type"sv);
768  }
769 
770  const std::uint64_t tmp = (hi & 8) != 0 ? (prevOffset / a_header.pointer_size()) : prevOffset;
771 
772  switch (hi & 7) {
773  case 0:
774  a_in.read(offset);
775  break;
776  case 1:
777  offset = tmp + 1;
778  break;
779  case 2:
780  offset = tmp + std::get<0>(a_in.read<std::uint8_t>());
781  break;
782  case 3:
783  offset = tmp - std::get<0>(a_in.read<std::uint8_t>());
784  break;
785  case 4:
786  offset = tmp + std::get<0>(a_in.read<std::uint16_t>());
787  break;
788  case 5:
789  offset = tmp - std::get<0>(a_in.read<std::uint16_t>());
790  break;
791  case 6:
792  std::tie(offset) = a_in.read<std::uint16_t>();
793  break;
794  case 7:
795  std::tie(offset) = a_in.read<std::uint32_t>();
796  break;
797  default:
798  stl::report_and_fail("unhandled type"sv);
799  }
800 
801  if ((hi & 8) != 0) {
802  offset *= a_header.pointer_size();
803  }
804 
805  mapping = { id, offset };
806 
807  prevOffset = offset;
808  prevID = id;
809  }
810  }
811 
812  detail::memory_map _mmap;
813  std::span<mapping_t> _id2offset;
814  };
815 
816  class Offset
817  {
818  public:
819  constexpr Offset() noexcept = default;
820 
821  explicit constexpr Offset(std::size_t a_offset) noexcept :
822  _offset(a_offset)
823  {}
824 
825  constexpr Offset& operator=(std::size_t a_offset) noexcept
826  {
827  _offset = a_offset;
828  return *this;
829  }
830 
831  [[nodiscard]] std::uintptr_t address() const { return base() + offset(); }
832  [[nodiscard]] constexpr std::size_t offset() const noexcept { return _offset; }
833 
834  private:
835  [[nodiscard]] static std::uintptr_t base() { return Module::get().base(); }
836 
837  std::size_t _offset{ 0 };
838  };
839 
840  class ID
841  {
842  public:
843  constexpr ID() noexcept = default;
844 
845  explicit constexpr ID(std::uint64_t a_id) noexcept :
846  _id(a_id)
847  {}
848 
849  constexpr ID& operator=(std::uint64_t a_id) noexcept
850  {
851  _id = a_id;
852  return *this;
853  }
854 
855  [[nodiscard]] std::uintptr_t address() const { return base() + offset(); }
856  [[nodiscard]] constexpr std::uint64_t id() const noexcept { return _id; }
857  [[nodiscard]] std::size_t offset() const { return IDDatabase::get().id2offset(_id); }
858 
859  private:
860  [[nodiscard]] static std::uintptr_t base() { return Module::get().base(); }
861 
862  std::uint64_t _id{ 0 };
863  };
864 
865  template <class T>
867  {
868  public:
869  using value_type =
870  std::conditional_t<
871  std::is_member_pointer_v<T> || std::is_function_v<std::remove_pointer_t<T>>,
872  std::decay_t<T>,
873  T>;
874 
875  constexpr Relocation() noexcept = default;
876 
877  explicit constexpr Relocation(std::uintptr_t a_address) noexcept :
878  _impl{ a_address }
879  {}
880 
881  explicit Relocation(Offset a_offset) :
882  _impl{ a_offset.address() }
883  {}
884 
885  explicit Relocation(ID a_id) :
886  _impl{ a_id.address() }
887  {}
888 
889  explicit Relocation(ID a_id, std::ptrdiff_t a_offset) :
890  _impl{ a_id.address() + a_offset }
891  {}
892 
893  constexpr Relocation& operator=(std::uintptr_t a_address) noexcept
894  {
895  _impl = a_address;
896  return *this;
897  }
898 
900  {
901  _impl = a_offset.address();
902  return *this;
903  }
904 
906  {
907  _impl = a_id.address();
908  return *this;
909  }
910 
911  template <class U = value_type>
912  [[nodiscard]] decltype(auto) operator*() const noexcept //
913  requires(std::is_pointer_v<U>)
914  {
915  return *get();
916  }
917 
918  template <class U = value_type>
919  [[nodiscard]] auto operator->() const noexcept //
920  requires(std::is_pointer_v<U>)
921  {
922  return get();
923  }
924 
925  template <class... Args>
926  std::invoke_result_t<const value_type&, Args...> operator()(Args&&... a_args) const //
927  noexcept(std::is_nothrow_invocable_v<const value_type&, Args...>) //
928  requires(std::invocable<const value_type&, Args...>)
929  {
930  return REL::invoke(get(), std::forward<Args>(a_args)...);
931  }
932 
933  [[nodiscard]] constexpr std::uintptr_t address() const noexcept { return _impl; }
934  [[nodiscard]] std::size_t offset() const { return _impl - base(); }
935 
936  [[nodiscard]] value_type get() const //
937  noexcept(std::is_nothrow_copy_constructible_v<value_type>)
938  {
939  assert(_impl != 0);
940  return stl::unrestricted_cast<value_type>(_impl);
941  }
942 
943  template <class U = value_type>
944  std::uintptr_t write_vfunc(std::size_t a_idx, std::uintptr_t a_newFunc) //
945  requires(std::same_as<U, std::uintptr_t>)
946  {
947  const auto addr = address() + (sizeof(void*) * a_idx);
948  const auto result = *reinterpret_cast<std::uintptr_t*>(addr);
949  safe_write(addr, a_newFunc);
950  return result;
951  }
952 
953  template <class F>
954  std::uintptr_t write_vfunc(std::size_t a_idx, F a_newFunc) //
955  requires(std::same_as<value_type, std::uintptr_t>)
956  {
957  return write_vfunc(a_idx, stl::unrestricted_cast<std::uintptr_t>(a_newFunc));
958  }
959 
960  private :
961  // clang-format off
962  [[nodiscard]] static std::uintptr_t base() { return Module::get().base(); }
963  // clang-format on
964 
965  std::uintptr_t _impl{ 0 };
966  };
967 
968  namespace detail
969  {
970  namespace characters
971  {
972  [[nodiscard]] constexpr bool hexadecimal(char a_ch) noexcept
973  {
974  return ('0' <= a_ch && a_ch <= '9') ||
975  ('A' <= a_ch && a_ch <= 'F') ||
976  ('a' <= a_ch && a_ch <= 'f');
977  }
978 
979  [[nodiscard]] constexpr bool space(char a_ch) noexcept
980  {
981  return a_ch == ' ';
982  }
983 
984  [[nodiscard]] constexpr bool wildcard(char a_ch) noexcept
985  {
986  return a_ch == '?';
987  }
988  }
989 
990  namespace rules
991  {
992  namespace detail
993  {
994  [[nodiscard]] consteval std::byte hexacharacters_to_hexadecimal(char a_hi, char a_lo) noexcept
995  {
996  constexpr auto lut = []() noexcept {
997  std::array<std::uint8_t, std::numeric_limits<unsigned char>::max() + 1> a = {};
998 
999  const auto iterate = [&](std::uint8_t a_iFirst, unsigned char a_cFirst, unsigned char a_cLast) noexcept {
1000  for (; a_cFirst <= a_cLast; ++a_cFirst, ++a_iFirst) {
1001  a[a_cFirst] = a_iFirst;
1002  }
1003  };
1004 
1005  iterate(0, '0', '9');
1006  iterate(0xA, 'A', 'F');
1007  iterate(0xa, 'a', 'f');
1008 
1009  return a;
1010  }();
1011 
1012  return static_cast<std::byte>(
1013  lut[static_cast<unsigned char>(a_hi)] * 0x10u +
1014  lut[static_cast<unsigned char>(a_lo)]);
1015  }
1016  }
1017 
1018  template <char HI, char LO>
1020  {
1021  public:
1022  [[nodiscard]] static constexpr bool match(std::byte a_byte) noexcept
1023  {
1024  constexpr auto expected = detail::hexacharacters_to_hexadecimal(HI, LO);
1025  return a_byte == expected;
1026  }
1027  };
1028 
1029  static_assert(Hexadecimal<'5', '7'>::match(std::byte{ 0x57 }));
1030  static_assert(Hexadecimal<'6', '5'>::match(std::byte{ 0x65 }));
1031  static_assert(Hexadecimal<'B', 'D'>::match(std::byte{ 0xBD }));
1032  static_assert(Hexadecimal<'1', 'C'>::match(std::byte{ 0x1C }));
1033  static_assert(Hexadecimal<'F', '2'>::match(std::byte{ 0xF2 }));
1034  static_assert(Hexadecimal<'9', 'f'>::match(std::byte{ 0x9f }));
1035 
1036  static_assert(!Hexadecimal<'D', '4'>::match(std::byte{ 0xF8 }));
1037  static_assert(!Hexadecimal<'6', '7'>::match(std::byte{ 0xAA }));
1038  static_assert(!Hexadecimal<'7', '8'>::match(std::byte{ 0xE3 }));
1039  static_assert(!Hexadecimal<'6', 'E'>::match(std::byte{ 0x61 }));
1040 
1041  class Wildcard
1042  {
1043  public:
1044  [[nodiscard]] static constexpr bool match(std::byte) noexcept
1045  {
1046  return true;
1047  }
1048  };
1049 
1050  static_assert(Wildcard::match(std::byte{ 0xB9 }));
1051  static_assert(Wildcard::match(std::byte{ 0x96 }));
1052  static_assert(Wildcard::match(std::byte{ 0x35 }));
1053  static_assert(Wildcard::match(std::byte{ 0xE4 }));
1054 
1055  template <char, char>
1056  void rule_for() noexcept;
1057 
1058  template <char C1, char C2>
1059  Hexadecimal<C1, C2> rule_for() noexcept
1060  requires(characters::hexadecimal(C1) && characters::hexadecimal(C2));
1061 
1062  template <char C1, char C2>
1063  Wildcard rule_for() noexcept
1064  requires(characters::wildcard(C1) && characters::wildcard(C2));
1065  }
1066 
1067  template <class... Rules>
1069  {
1070  public:
1071  static_assert(sizeof...(Rules) >= 1, "must provide at least 1 rule for the pattern matcher");
1072 
1073  [[nodiscard]] constexpr bool match(std::span<const std::byte, sizeof...(Rules)> a_bytes) const noexcept
1074  {
1075  std::size_t i = 0;
1076  return (Rules::match(a_bytes[i++]) && ...);
1077  }
1078 
1079  [[nodiscard]] bool match(std::uintptr_t a_address) const noexcept
1080  {
1081  return this->match(*reinterpret_cast<const std::byte(*)[sizeof...(Rules)]>(a_address));
1082  }
1083 
1084  void match_or_fail(std::uintptr_t a_address, std::source_location a_loc = std::source_location::current()) const noexcept
1085  {
1086  if (!this->match(a_address)) {
1087  const auto version = Module::get().version();
1089  fmt::format(
1090  "A pattern has failed to match.\n"
1091  "This means the plugin is incompatible with the current version of the game ({}.{}.{}). "
1092  "Head to the mod page of this plugin to see if an update is available."sv,
1093  version[0],
1094  version[1],
1095  version[2]),
1096  a_loc);
1097  }
1098  }
1099  };
1100 
1101  void consteval_error(const char* a_error);
1102 
1103  template <stl::nttp::string S, class... Rules>
1104  [[nodiscard]] constexpr auto do_make_pattern() noexcept
1105  {
1106  if constexpr (S.length() == 0) {
1107  return PatternMatcher<Rules...>();
1108  } else if constexpr (S.length() == 1) {
1109  constexpr char c = S[0];
1110  if constexpr (characters::hexadecimal(c) || characters::wildcard(c)) {
1111  consteval_error("the given pattern has an unpaired rule (rules are required to be written in pairs of 2)");
1112  } else {
1113  consteval_error("the given pattern has trailing characters at the end (which is not allowed)");
1114  }
1115  } else {
1116  using rule_t = decltype(rules::rule_for<S[0], S[1]>());
1117  if constexpr (std::same_as<rule_t, void>) {
1118  consteval_error("the given pattern failed to match any known rules");
1119  } else {
1120  if constexpr (S.length() <= 3) {
1121  return do_make_pattern<S.template substr<2>(), Rules..., rule_t>();
1122  } else if constexpr (characters::space(S[2])) {
1123  return do_make_pattern<S.template substr<3>(), Rules..., rule_t>();
1124  } else {
1125  consteval_error("a space character is required to split byte patterns");
1126  }
1127  }
1128  }
1129  }
1130 
1131  template <class... Bytes>
1132  [[nodiscard]] consteval auto make_byte_array(Bytes... a_bytes) noexcept
1133  -> std::array<std::byte, sizeof...(Bytes)>
1134  {
1135  static_assert((std::integral<Bytes> && ...), "all bytes must be an integral type");
1136  return { static_cast<std::byte>(a_bytes)... };
1137  }
1138  }
1139 
1140  template <stl::nttp::string S>
1141  [[nodiscard]] constexpr auto make_pattern() noexcept
1142  {
1143  return detail::do_make_pattern<S>();
1144  }
1145 
1146  static_assert(make_pattern<"40 10 F2 ??">().match(
1147  detail::make_byte_array(0x40, 0x10, 0xF2, 0x41)));
1148  static_assert(make_pattern<"B8 D0 ?? ?? D4 6E">().match(
1149  detail::make_byte_array(0xB8, 0xD0, 0x35, 0x2A, 0xD4, 0x6E)));
1150 }
1151 
1152 #undef REL_MAKE_MEMBER_FUNCTION_NON_POD_TYPE
1153 #undef REL_MAKE_MEMBER_FUNCTION_NON_POD_TYPE_HELPER
1154 #undef REL_MAKE_MEMBER_FUNCTION_NON_POD_TYPE_HELPER_IMPL
1155 
1156 #undef REL_MAKE_MEMBER_FUNCTION_POD_TYPE
1157 #undef REL_MAKE_MEMBER_FUNCTION_POD_TYPE_HELPER
1158 #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
Offset2ID(ExecutionPolicy &&a_policy) requires(std
Definition: Relocation.h:541
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
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
std::size_t id2offset(std::uint64_t a_id) const
Definition: Relocation.h:604
static IDDatabase & get()
Definition: Relocation.h:598
Definition: Relocation.h:841
std::size_t offset() const
Definition: Relocation.h:857
constexpr ID & operator=(std::uint64_t a_id) noexcept
Definition: Relocation.h:849
constexpr std::uint64_t id() const noexcept
Definition: Relocation.h:856
constexpr ID() noexcept=default
std::uintptr_t address() const
Definition: Relocation.h:855
Definition: Relocation.h:419
static Module & get()
Definition: Relocation.h:421
Version version() const noexcept
Definition: Relocation.h:429
std::uintptr_t base() const noexcept
Definition: Relocation.h:427
stl::zwstring filename() const noexcept
Definition: Relocation.h:428
T * pointer() const noexcept
Definition: Relocation.h:436
void * pointer() const noexcept
Definition: Relocation.h:433
Segment segment(Segment::Name a_segment) const noexcept
Definition: Relocation.h:431
Definition: Relocation.h:817
constexpr Offset() noexcept=default
std::uintptr_t address() const
Definition: Relocation.h:831
constexpr Offset & operator=(std::size_t a_offset) noexcept
Definition: Relocation.h:825
constexpr std::size_t offset() const noexcept
Definition: Relocation.h:832
Definition: Relocation.h:867
constexpr std::uintptr_t address() const noexcept
Definition: Relocation.h:933
auto operator->() const noexcept requires(std
Definition: Relocation.h:919
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:873
constexpr Relocation & operator=(std::uintptr_t a_address) noexcept
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... >) requires(std
Definition: Relocation.h:926
value_type get() const noexcept(std::is_nothrow_copy_constructible_v< value_type >)
Definition: Relocation.h:936
Relocation(Offset a_offset)
Definition: Relocation.h:881
Relocation(ID a_id, std::ptrdiff_t a_offset)
Definition: Relocation.h:889
std::uintptr_t write_vfunc(std::size_t a_idx, std::uintptr_t a_newFunc) requires(std
Definition: Relocation.h:944
std::uintptr_t write_vfunc(std::size_t a_idx, F a_newFunc) requires(std
Definition: Relocation.h:954
Relocation & operator=(Offset a_offset)
Definition: Relocation.h:899
constexpr Relocation() noexcept=default
Relocation(ID a_id)
Definition: Relocation.h:885
Relocation & operator=(ID a_id)
Definition: Relocation.h:905
std::size_t offset() const
Definition: Relocation.h:934
Definition: Relocation.h:377
std::size_t size() const noexcept
Definition: Relocation.h:402
void * pointer() const noexcept
Definition: Relocation.h:404
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
T * pointer() const noexcept
Definition: Relocation.h:407
std::uintptr_t address() const noexcept
Definition: Relocation.h:400
Segment() noexcept=default
std::size_t offset() const noexcept
Definition: Relocation.h:401
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
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 std::strong_ordering compare(const Version &a_rhs) const noexcept
Definition: Relocation.h:299
constexpr decltype(auto) begin() const noexcept
Definition: Relocation.h:294
std::wstring wstring() const
Definition: Relocation.h:329
Definition: Relocation.h:1069
constexpr bool match(std::span< const std::byte, sizeof...(Rules)> a_bytes) const noexcept
Definition: Relocation.h:1073
void match_or_fail(std::uintptr_t a_address, std::source_location a_loc=std::source_location::current()) const noexcept
Definition: Relocation.h:1084
bool match(std::uintptr_t a_address) const noexcept
Definition: Relocation.h:1079
Definition: Relocation.h:64
~memory_map()
Definition: Relocation.h:77
void * data() noexcept
Definition: Relocation.h:93
memory_map & operator=(memory_map &&a_rhs) noexcept
Definition: Relocation.h:81
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:1020
static constexpr bool match(std::byte a_byte) noexcept
Definition: Relocation.h:1022
Definition: Relocation.h:1042
static constexpr bool match(std::byte) noexcept
Definition: Relocation.h:1044
constexpr bool space(char a_ch) noexcept
Definition: Relocation.h:979
constexpr bool hexadecimal(char a_ch) noexcept
Definition: Relocation.h:972
constexpr bool wildcard(char a_ch) noexcept
Definition: Relocation.h:984
consteval std::byte hexacharacters_to_hexadecimal(char a_hi, char a_lo) noexcept
Definition: Relocation.h:994
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:1132
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:1104
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: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:1141
std::optional< Version > get_file_version(stl::zwstring a_filename)
Definition: Relocation.h:347
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
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
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:579
auto utf16_to_utf8(std::wstring_view a_in) noexcept -> std::optional< std::string >
Definition: PCH.h:551
auto utf8_to_utf16(std::string_view a_in) noexcept -> std::optional< std::wstring >
Definition: PCH.h:525
requires(std::invocable< std::remove_reference_t< EF >>) class scope_exit
Definition: PCH.h:148
basic_zstring< wchar_t > zwstring
Definition: PCH.h:77
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