GCC Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 100.0% 57 / 0 / 57
Functions: 100.0% 10 / 0 / 10
Branches: 71.2% 37 / 0 / 52

src/input_codes.cpp
Line Branch Exec Source
1 /**
2 * @file input_codes.cpp
3 * @brief Implementation of named input code resolution and formatting.
4 *
5 * Provides the name lookup table and resolution functions for converting between
6 * human-readable input names (e.g., "Ctrl", "Mouse4", "Gamepad_A") and their
7 * corresponding InputCode values.
8 */
9
10 #include "DetourModKit/input_codes.hpp"
11 #include "DetourModKit/format.hpp"
12
13 #include <algorithm>
14 #include <cctype>
15 #include <unordered_map>
16
17 namespace DetourModKit
18 {
19 namespace
20 {
21 struct NameEntry
22 {
23 const char *name;
24 InputCode code;
25 };
26
27 // clang-format off
28 constexpr NameEntry name_table[] = {
29 // --- Keyboard: Modifiers ---
30 {"Ctrl", {InputSource::Keyboard, 0x11}},
31 {"LCtrl", {InputSource::Keyboard, 0xA2}},
32 {"RCtrl", {InputSource::Keyboard, 0xA3}},
33 {"Shift", {InputSource::Keyboard, 0x10}},
34 {"LShift", {InputSource::Keyboard, 0xA0}},
35 {"RShift", {InputSource::Keyboard, 0xA1}},
36 {"Alt", {InputSource::Keyboard, 0x12}},
37 {"LAlt", {InputSource::Keyboard, 0xA4}},
38 {"RAlt", {InputSource::Keyboard, 0xA5}},
39
40 // --- Keyboard: Function keys ---
41 {"F1", {InputSource::Keyboard, 0x70}},
42 {"F2", {InputSource::Keyboard, 0x71}},
43 {"F3", {InputSource::Keyboard, 0x72}},
44 {"F4", {InputSource::Keyboard, 0x73}},
45 {"F5", {InputSource::Keyboard, 0x74}},
46 {"F6", {InputSource::Keyboard, 0x75}},
47 {"F7", {InputSource::Keyboard, 0x76}},
48 {"F8", {InputSource::Keyboard, 0x77}},
49 {"F9", {InputSource::Keyboard, 0x78}},
50 {"F10", {InputSource::Keyboard, 0x79}},
51 {"F11", {InputSource::Keyboard, 0x7A}},
52 {"F12", {InputSource::Keyboard, 0x7B}},
53 {"F13", {InputSource::Keyboard, 0x7C}},
54 {"F14", {InputSource::Keyboard, 0x7D}},
55 {"F15", {InputSource::Keyboard, 0x7E}},
56 {"F16", {InputSource::Keyboard, 0x7F}},
57 {"F17", {InputSource::Keyboard, 0x80}},
58 {"F18", {InputSource::Keyboard, 0x81}},
59 {"F19", {InputSource::Keyboard, 0x82}},
60 {"F20", {InputSource::Keyboard, 0x83}},
61 {"F21", {InputSource::Keyboard, 0x84}},
62 {"F22", {InputSource::Keyboard, 0x85}},
63 {"F23", {InputSource::Keyboard, 0x86}},
64 {"F24", {InputSource::Keyboard, 0x87}},
65
66 // --- Keyboard: Letters ---
67 {"A", {InputSource::Keyboard, 0x41}},
68 {"B", {InputSource::Keyboard, 0x42}},
69 {"C", {InputSource::Keyboard, 0x43}},
70 {"D", {InputSource::Keyboard, 0x44}},
71 {"E", {InputSource::Keyboard, 0x45}},
72 {"F", {InputSource::Keyboard, 0x46}},
73 {"G", {InputSource::Keyboard, 0x47}},
74 {"H", {InputSource::Keyboard, 0x48}},
75 {"I", {InputSource::Keyboard, 0x49}},
76 {"J", {InputSource::Keyboard, 0x4A}},
77 {"K", {InputSource::Keyboard, 0x4B}},
78 {"L", {InputSource::Keyboard, 0x4C}},
79 {"M", {InputSource::Keyboard, 0x4D}},
80 {"N", {InputSource::Keyboard, 0x4E}},
81 {"O", {InputSource::Keyboard, 0x4F}},
82 {"P", {InputSource::Keyboard, 0x50}},
83 {"Q", {InputSource::Keyboard, 0x51}},
84 {"R", {InputSource::Keyboard, 0x52}},
85 {"S", {InputSource::Keyboard, 0x53}},
86 {"T", {InputSource::Keyboard, 0x54}},
87 {"U", {InputSource::Keyboard, 0x55}},
88 {"V", {InputSource::Keyboard, 0x56}},
89 {"W", {InputSource::Keyboard, 0x57}},
90 {"X", {InputSource::Keyboard, 0x58}},
91 {"Y", {InputSource::Keyboard, 0x59}},
92 {"Z", {InputSource::Keyboard, 0x5A}},
93
94 // --- Keyboard: Digits ---
95 {"0", {InputSource::Keyboard, 0x30}},
96 {"1", {InputSource::Keyboard, 0x31}},
97 {"2", {InputSource::Keyboard, 0x32}},
98 {"3", {InputSource::Keyboard, 0x33}},
99 {"4", {InputSource::Keyboard, 0x34}},
100 {"5", {InputSource::Keyboard, 0x35}},
101 {"6", {InputSource::Keyboard, 0x36}},
102 {"7", {InputSource::Keyboard, 0x37}},
103 {"8", {InputSource::Keyboard, 0x38}},
104 {"9", {InputSource::Keyboard, 0x39}},
105
106 // --- Keyboard: Navigation ---
107 {"Up", {InputSource::Keyboard, 0x26}},
108 {"Down", {InputSource::Keyboard, 0x28}},
109 {"Left", {InputSource::Keyboard, 0x25}},
110 {"Right", {InputSource::Keyboard, 0x27}},
111 {"Home", {InputSource::Keyboard, 0x24}},
112 {"End", {InputSource::Keyboard, 0x23}},
113 {"PageUp", {InputSource::Keyboard, 0x21}},
114 {"PageDown", {InputSource::Keyboard, 0x22}},
115 {"Insert", {InputSource::Keyboard, 0x2D}},
116 {"Delete", {InputSource::Keyboard, 0x2E}},
117
118 // --- Keyboard: Common keys ---
119 {"Space", {InputSource::Keyboard, 0x20}},
120 {"Enter", {InputSource::Keyboard, 0x0D}},
121 {"Escape", {InputSource::Keyboard, 0x1B}},
122 {"Tab", {InputSource::Keyboard, 0x09}},
123 {"Backspace", {InputSource::Keyboard, 0x08}},
124 {"CapsLock", {InputSource::Keyboard, 0x14}},
125 {"NumLock", {InputSource::Keyboard, 0x90}},
126 {"ScrollLock", {InputSource::Keyboard, 0x91}},
127 {"PrintScreen", {InputSource::Keyboard, 0x2C}},
128 {"Pause", {InputSource::Keyboard, 0x13}},
129
130 // --- Keyboard: Numpad ---
131 {"Numpad0", {InputSource::Keyboard, 0x60}},
132 {"Numpad1", {InputSource::Keyboard, 0x61}},
133 {"Numpad2", {InputSource::Keyboard, 0x62}},
134 {"Numpad3", {InputSource::Keyboard, 0x63}},
135 {"Numpad4", {InputSource::Keyboard, 0x64}},
136 {"Numpad5", {InputSource::Keyboard, 0x65}},
137 {"Numpad6", {InputSource::Keyboard, 0x66}},
138 {"Numpad7", {InputSource::Keyboard, 0x67}},
139 {"Numpad8", {InputSource::Keyboard, 0x68}},
140 {"Numpad9", {InputSource::Keyboard, 0x69}},
141 {"NumpadAdd", {InputSource::Keyboard, 0x6B}},
142 {"NumpadSubtract", {InputSource::Keyboard, 0x6D}},
143 {"NumpadMultiply", {InputSource::Keyboard, 0x6A}},
144 {"NumpadDivide", {InputSource::Keyboard, 0x6F}},
145 {"NumpadDecimal", {InputSource::Keyboard, 0x6E}},
146
147 // --- Mouse buttons ---
148 {"Mouse1", {InputSource::Mouse, 0x01}},
149 {"Mouse2", {InputSource::Mouse, 0x02}},
150 {"Mouse3", {InputSource::Mouse, 0x04}},
151 {"Mouse4", {InputSource::Mouse, 0x05}},
152 {"Mouse5", {InputSource::Mouse, 0x06}},
153
154 // --- Gamepad buttons ---
155 {"Gamepad_A", {InputSource::Gamepad, GamepadCode::A}},
156 {"Gamepad_B", {InputSource::Gamepad, GamepadCode::B}},
157 {"Gamepad_X", {InputSource::Gamepad, GamepadCode::X}},
158 {"Gamepad_Y", {InputSource::Gamepad, GamepadCode::Y}},
159 {"Gamepad_LB", {InputSource::Gamepad, GamepadCode::LeftBumper}},
160 {"Gamepad_RB", {InputSource::Gamepad, GamepadCode::RightBumper}},
161 {"Gamepad_LT", {InputSource::Gamepad, GamepadCode::LeftTrigger}},
162 {"Gamepad_RT", {InputSource::Gamepad, GamepadCode::RightTrigger}},
163 {"Gamepad_Start", {InputSource::Gamepad, GamepadCode::Start}},
164 {"Gamepad_Back", {InputSource::Gamepad, GamepadCode::Back}},
165 {"Gamepad_LS", {InputSource::Gamepad, GamepadCode::LeftStick}},
166 {"Gamepad_RS", {InputSource::Gamepad, GamepadCode::RightStick}},
167 {"Gamepad_DpadUp", {InputSource::Gamepad, GamepadCode::DpadUp}},
168 {"Gamepad_DpadDown", {InputSource::Gamepad, GamepadCode::DpadDown}},
169 {"Gamepad_DpadLeft", {InputSource::Gamepad, GamepadCode::DpadLeft}},
170 {"Gamepad_DpadRight", {InputSource::Gamepad, GamepadCode::DpadRight}},
171
172 // --- Gamepad thumbstick axes (digital) ---
173 {"Gamepad_LSUp", {InputSource::Gamepad, GamepadCode::LeftStickUp}},
174 {"Gamepad_LSDown", {InputSource::Gamepad, GamepadCode::LeftStickDown}},
175 {"Gamepad_LSLeft", {InputSource::Gamepad, GamepadCode::LeftStickLeft}},
176 {"Gamepad_LSRight", {InputSource::Gamepad, GamepadCode::LeftStickRight}},
177 {"Gamepad_RSUp", {InputSource::Gamepad, GamepadCode::RightStickUp}},
178 {"Gamepad_RSDown", {InputSource::Gamepad, GamepadCode::RightStickDown}},
179 {"Gamepad_RSLeft", {InputSource::Gamepad, GamepadCode::RightStickLeft}},
180 {"Gamepad_RSRight", {InputSource::Gamepad, GamepadCode::RightStickRight}},
181 };
182 // clang-format on
183
184 constexpr size_t name_table_size = sizeof(name_table) / sizeof(name_table[0]);
185
186 1695 int icompare(std::string_view a, std::string_view b) noexcept
187 {
188 1695 const size_t len = std::min(a.size(), b.size());
189
2/2
✓ Branch 11 → 6 taken 4706 times.
✓ Branch 11 → 12 taken 211 times.
4917 for (size_t i = 0; i < len; ++i)
190 {
191 4706 const int ca = std::tolower(static_cast<unsigned char>(a[i]));
192 4706 const int cb = std::tolower(static_cast<unsigned char>(b[i]));
193
2/2
✓ Branch 8 → 9 taken 1484 times.
✓ Branch 8 → 10 taken 3222 times.
4706 if (ca != cb)
194 {
195 1484 return ca - cb;
196 }
197 }
198
2/2
✓ Branch 14 → 15 taken 86 times.
✓ Branch 14 → 16 taken 125 times.
211 if (a.size() < b.size())
199 {
200 86 return -1;
201 }
202
2/2
✓ Branch 18 → 19 taken 52 times.
✓ Branch 18 → 20 taken 73 times.
125 if (a.size() > b.size())
203 {
204 52 return 1;
205 }
206 73 return 0;
207 }
208
209 struct SortedNameIndex
210 {
211 size_t indices[name_table_size]{};
212 size_t count = name_table_size;
213
214 1 SortedNameIndex()
215 1 {
216
2/2
✓ Branch 4 → 3 taken 133 times.
✓ Branch 4 → 5 taken 1 time.
134 for (size_t i = 0; i < name_table_size; ++i)
217 {
218 133 indices[i] = i;
219 }
220 1 std::sort(indices, indices + count, [](size_t a, size_t b)
221 1020 { return icompare(name_table[a].name, name_table[b].name) < 0; });
222 1 }
223 };
224
225 112 const SortedNameIndex &get_sorted_index()
226 {
227
4/8
✓ Branch 2 → 3 taken 1 time.
✓ Branch 2 → 7 taken 111 times.
✓ Branch 4 → 5 taken 1 time.
✗ Branch 4 → 7 not taken.
✓ Branch 5 → 6 taken 1 time.
✗ Branch 5 → 9 not taken.
✗ Branch 9 → 10 not taken.
✗ Branch 9 → 11 not taken.
112 static const SortedNameIndex instance;
228 112 return instance;
229 }
230 struct InputCodeHash
231 {
232 286 size_t operator()(const InputCode &c) const noexcept
233 {
234 286 return std::hash<int>{}(c.code) ^ (std::hash<uint8_t>{}(static_cast<uint8_t>(c.source)) << 16);
235 }
236 };
237
238 struct CodeNameMap
239 {
240 std::unordered_map<InputCode, std::string_view, InputCodeHash> map;
241
242 1 CodeNameMap()
243 1 {
244
1/2
✓ Branch 3 → 4 taken 1 time.
✗ Branch 3 → 10 not taken.
1 map.reserve(name_table_size);
245
2/2
✓ Branch 7 → 5 taken 133 times.
✓ Branch 7 → 8 taken 1 time.
134 for (size_t i = 0; i < name_table_size; ++i)
246 {
247
1/2
✓ Branch 5 → 6 taken 133 times.
✗ Branch 5 → 9 not taken.
133 map.emplace(name_table[i].code, name_table[i].name);
248 }
249 1 }
250 };
251
252 16 const CodeNameMap &get_code_name_map()
253 {
254
4/8
✓ Branch 2 → 3 taken 1 time.
✓ Branch 2 → 8 taken 15 times.
✓ Branch 4 → 5 taken 1 time.
✗ Branch 4 → 8 not taken.
✓ Branch 5 → 6 taken 1 time.
✗ Branch 5 → 10 not taken.
✗ Branch 10 → 11 not taken.
✗ Branch 10 → 12 not taken.
16 static const CodeNameMap instance;
255 16 return instance;
256 }
257 } // anonymous namespace
258
259 112 std::optional<InputCode> parse_input_name(std::string_view name)
260 {
261 112 const auto &idx = get_sorted_index();
262 112 size_t lo = 0;
263 112 size_t hi = idx.count;
264
2/2
✓ Branch 12 → 4 taken 675 times.
✓ Branch 12 → 13 taken 39 times.
714 while (lo < hi)
265 {
266 675 const size_t mid = lo + (hi - lo) / 2;
267 675 const int cmp = icompare(name_table[idx.indices[mid]].name, name);
268
2/2
✓ Branch 6 → 7 taken 171 times.
✓ Branch 6 → 8 taken 504 times.
675 if (cmp < 0)
269 {
270 171 lo = mid + 1;
271 }
272
2/2
✓ Branch 8 → 9 taken 431 times.
✓ Branch 8 → 10 taken 73 times.
504 else if (cmp > 0)
273 {
274 431 hi = mid;
275 }
276 else
277 {
278 73 return name_table[idx.indices[mid]].code;
279 }
280 }
281 39 return std::nullopt;
282 }
283
284 16 std::string_view input_code_to_name(const InputCode &code)
285 {
286
1/2
✓ Branch 2 → 3 taken 16 times.
✗ Branch 2 → 12 not taken.
16 const auto &map = get_code_name_map().map;
287
1/2
✓ Branch 3 → 4 taken 16 times.
✗ Branch 3 → 12 not taken.
16 const auto it = map.find(code);
288
2/2
✓ Branch 6 → 7 taken 14 times.
✓ Branch 6 → 9 taken 2 times.
16 if (it != map.end())
289 {
290 14 return it->second;
291 }
292 2 return {};
293 }
294
295 10 std::string format_input_code(const InputCode &code)
296 {
297
1/2
✓ Branch 2 → 3 taken 10 times.
✗ Branch 2 → 17 not taken.
10 auto name = input_code_to_name(code);
298
2/2
✓ Branch 4 → 5 taken 9 times.
✓ Branch 4 → 10 taken 1 time.
10 if (!name.empty())
299 {
300
1/2
✓ Branch 7 → 8 taken 9 times.
✗ Branch 7 → 14 not taken.
18 return std::string(name);
301 }
302
1/2
✓ Branch 10 → 11 taken 1 time.
✗ Branch 10 → 17 not taken.
1 return Format::format_hex(code.code, 2);
303 }
304
305 } // namespace DetourModKit
306