CommonLibVR
Loading...
Searching...
No Matches
Pattern.h
Go to the documentation of this file.
1#pragma once
2
3#include "REL/Module.h"
4
5namespace REL
6{
7 namespace detail
8 {
9 namespace characters
10 {
11 [[nodiscard]] constexpr bool hexadecimal(char a_ch) noexcept
12 {
13 return ('0' <= a_ch && a_ch <= '9') ||
14 ('A' <= a_ch && a_ch <= 'F') ||
15 ('a' <= a_ch && a_ch <= 'f');
16 }
17
18 [[nodiscard]] constexpr bool space(char a_ch) noexcept
19 {
20 return a_ch == ' ';
21 }
22
23 [[nodiscard]] constexpr bool wildcard(char a_ch) noexcept
24 {
25 return a_ch == '?';
26 }
27 }
28
29 namespace rules
30 {
31 namespace detail
32 {
33 [[nodiscard]] consteval std::byte hexacharacters_to_hexadecimal(char a_hi, char a_lo) noexcept
34 {
35 constexpr auto lut = []() noexcept {
36 std::array<std::uint8_t, std::numeric_limits<unsigned char>::max() + 1> a = {};
37
38 const auto iterate = [&](std::uint8_t a_iFirst, unsigned char a_cFirst, unsigned char a_cLast) noexcept {
39 for (; a_cFirst <= a_cLast; ++a_cFirst, ++a_iFirst) {
40 a[a_cFirst] = a_iFirst;
41 }
42 };
43
44 iterate(0, '0', '9');
45 iterate(0xA, 'A', 'F');
46 iterate(0xa, 'a', 'f');
47
48 return a;
49 }();
50
51 return static_cast<std::byte>(
52 lut[static_cast<unsigned char>(a_hi)] * 0x10u +
53 lut[static_cast<unsigned char>(a_lo)]);
54 }
55 }
56
57 template <char HI, char LO>
59 {
60 public:
61 [[nodiscard]] static constexpr bool match(std::byte a_byte) noexcept
62 {
63 constexpr auto expected = detail::hexacharacters_to_hexadecimal(HI, LO);
64 return a_byte == expected;
65 }
66 };
67
68 static_assert(Hexadecimal<'5', '7'>::match(std::byte{ 0x57 }));
69 static_assert(Hexadecimal<'6', '5'>::match(std::byte{ 0x65 }));
70 static_assert(Hexadecimal<'B', 'D'>::match(std::byte{ 0xBD }));
71 static_assert(Hexadecimal<'1', 'C'>::match(std::byte{ 0x1C }));
72 static_assert(Hexadecimal<'F', '2'>::match(std::byte{ 0xF2 }));
73 static_assert(Hexadecimal<'9', 'f'>::match(std::byte{ 0x9f }));
74
75 static_assert(!Hexadecimal<'D', '4'>::match(std::byte{ 0xF8 }));
76 static_assert(!Hexadecimal<'6', '7'>::match(std::byte{ 0xAA }));
77 static_assert(!Hexadecimal<'7', '8'>::match(std::byte{ 0xE3 }));
78 static_assert(!Hexadecimal<'6', 'E'>::match(std::byte{ 0x61 }));
79
81 {
82 public:
83 [[nodiscard]] static constexpr bool match(std::byte) noexcept
84 {
85 return true;
86 }
87 };
88
89 static_assert(Wildcard::match(std::byte{ 0xB9 }));
90 static_assert(Wildcard::match(std::byte{ 0x96 }));
91 static_assert(Wildcard::match(std::byte{ 0x35 }));
92 static_assert(Wildcard::match(std::byte{ 0xE4 }));
93
94 template <char, char>
95 void rule_for() noexcept;
96
97 template <char C1, char C2>
98 Hexadecimal<C1, C2> rule_for() noexcept
99 requires(characters::hexadecimal(C1) && characters::hexadecimal(C2));
100
101 template <char C1, char C2>
103 requires(characters::wildcard(C1) && characters::wildcard(C2));
104 }
105
106 template <class... Rules>
108 {
109 public:
110 static_assert(sizeof...(Rules) >= 1, "must provide at least 1 rule for the pattern matcher");
111
112 [[nodiscard]] constexpr bool match(std::span<const std::byte, sizeof...(Rules)> a_bytes) const noexcept
113 {
114 std::size_t i = 0;
115 return (Rules::match(a_bytes[i++]) && ...);
116 }
117
118 [[nodiscard]] bool match(std::uintptr_t a_address) const noexcept
119 {
120 return this->match(*reinterpret_cast<const std::byte(*)[sizeof...(Rules)]>(a_address));
121 }
122
123 void match_or_fail(std::uintptr_t a_address, std::source_location a_loc = std::source_location::current()) const noexcept
124 {
125 if (!this->match(a_address)) {
126 const auto version = Module::get().version();
128 std::format(
129 "A pattern has failed to match.\n"
130 "This means the plugin is incompatible with the current version of the game ({}.{}.{}). "
131 "Head to the mod page of this plugin to see if an update is available."sv,
132 version[0],
133 version[1],
134 version[2]),
135 a_loc);
136 }
137 }
138 };
139
140 void consteval_error(const char* a_error);
141
142 template <stl::nttp::string S, class... Rules>
143 [[nodiscard]] constexpr auto do_make_pattern() noexcept
144 {
145 if constexpr (S.length() == 0) {
146 return PatternMatcher<Rules...>();
147 } else if constexpr (S.length() == 1) {
148 constexpr char c = S[0];
149 if constexpr (characters::hexadecimal(c) || characters::wildcard(c)) {
150 consteval_error("the given pattern has an unpaired rule (rules are required to be written in pairs of 2)");
151 } else {
152 consteval_error("the given pattern has trailing characters at the end (which is not allowed)");
153 }
154 } else {
155 using rule_t = decltype(rules::rule_for<S[0], S[1]>());
156 if constexpr (std::same_as<rule_t, void>) {
157 consteval_error("the given pattern failed to match any known rules");
158 } else {
159 if constexpr (S.length() <= 3) {
160 return do_make_pattern<S.template substr<2>(), Rules..., rule_t>();
161 } else if constexpr (characters::space(S[2])) {
162 return do_make_pattern<S.template substr<3>(), Rules..., rule_t>();
163 } else {
164 consteval_error("a space character is required to split byte patterns");
165 }
166 }
167 }
168 }
169
170 template <class... Bytes>
171 [[nodiscard]] consteval auto make_byte_array(Bytes... a_bytes) noexcept
172 -> std::array<std::byte, sizeof...(Bytes)>
173 {
174 static_assert((std::integral<Bytes> && ...), "all bytes must be an integral type");
175 return { static_cast<std::byte>(a_bytes)... };
176 }
177 }
178
179 template <stl::nttp::string S>
180 [[nodiscard]] constexpr auto make_pattern() noexcept
181 {
182 return detail::do_make_pattern<S>();
183 }
184
185 static_assert(make_pattern<"40 10 F2 ??">().match(
186 detail::make_byte_array(0x40, 0x10, 0xF2, 0x41)));
187 static_assert(make_pattern<"B8 D0 ?? ?? D4 6E">().match(
188 detail::make_byte_array(0xB8, 0xD0, 0x35, 0x2A, 0xD4, 0x6E)));
189}
Version version() const noexcept
Definition Module.h:62
static Module & get()
Definition Module.h:54
Definition Pattern.h:108
constexpr bool match(std::span< const std::byte, sizeof...(Rules)> a_bytes) const noexcept
Definition Pattern.h:112
void match_or_fail(std::uintptr_t a_address, std::source_location a_loc=std::source_location::current()) const noexcept
Definition Pattern.h:123
bool match(std::uintptr_t a_address) const noexcept
Definition Pattern.h:118
Definition Pattern.h:59
static constexpr bool match(std::byte a_byte) noexcept
Definition Pattern.h:61
Definition Pattern.h:81
static constexpr bool match(std::byte) noexcept
Definition Pattern.h:83
constexpr bool space(char a_ch) noexcept
Definition Pattern.h:18
constexpr bool hexadecimal(char a_ch) noexcept
Definition Pattern.h:11
constexpr bool wildcard(char a_ch) noexcept
Definition Pattern.h:23
consteval std::byte hexacharacters_to_hexadecimal(char a_hi, char a_lo) noexcept
Definition Pattern.h:33
void rule_for() noexcept
consteval auto make_byte_array(Bytes... a_bytes) noexcept -> std::array< std::byte, sizeof...(Bytes)>
Definition Pattern.h:171
void consteval_error(const char *a_error)
constexpr auto do_make_pattern() noexcept
Definition Pattern.h:143
Definition ID.h:9
constexpr auto make_pattern() noexcept
Definition Pattern.h:180
void report_and_fail(std::string_view a_msg, std::source_location a_loc=std::source_location::current())
Definition PCH.h:397