src/gba/input.c (view raw)
1/* Copyright (c) 2013-2015 Jeffrey Pfau
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6#include "input.h"
7
8#include "util/configuration.h"
9#include "util/table.h"
10
11#include <inttypes.h>
12
13#define SECTION_NAME_MAX 128
14#define KEY_NAME_MAX 32
15#define KEY_VALUE_MAX 16
16#define AXIS_INFO_MAX 12
17
18struct GBAInputMapImpl {
19 int* map;
20 uint32_t type;
21
22 struct Table axes;
23};
24
25struct GBAAxisSave {
26 struct Configuration* config;
27 const char* sectionName;
28};
29
30struct GBAAxisEnumerate {
31 void (*handler)(int axis, const struct GBAAxis* description, void* user);
32 void* user;
33};
34
35const char* GBAKeyNames[] = {
36 "A",
37 "B",
38 "Select",
39 "Start",
40 "Right",
41 "Left",
42 "Up",
43 "Down",
44 "R",
45 "L"
46};
47
48static void _makeSectionName(char* sectionName, size_t len, uint32_t type) {
49 snprintf(sectionName, len, "input.%c%c%c%c", type >> 24, type >> 16, type >> 8, type);
50 sectionName[len - 1] = '\0';
51}
52
53static bool _getIntValue(const struct Configuration* config, const char* section, const char* key, int* value) {
54 const char* strValue = ConfigurationGetValue(config, section, key);
55 if (!strValue) {
56 return false;
57 }
58 char* end;
59 long intValue = strtol(strValue, &end, 10);
60 if (*end) {
61 return false;
62 }
63 *value = intValue;
64 return true;
65}
66
67static struct GBAInputMapImpl* _lookupMap(struct GBAInputMap* map, uint32_t type) {
68 size_t m;
69 struct GBAInputMapImpl* impl = 0;
70 for (m = 0; m < map->numMaps; ++m) {
71 if (map->maps[m].type == type) {
72 impl = &map->maps[m];
73 break;
74 }
75 }
76 return impl;
77}
78
79static const struct GBAInputMapImpl* _lookupMapConst(const struct GBAInputMap* map, uint32_t type) {
80 size_t m;
81 const struct GBAInputMapImpl* impl = 0;
82 for (m = 0; m < map->numMaps; ++m) {
83 if (map->maps[m].type == type) {
84 impl = &map->maps[m];
85 break;
86 }
87 }
88 return impl;
89}
90
91static struct GBAInputMapImpl* _guaranteeMap(struct GBAInputMap* map, uint32_t type) {
92 struct GBAInputMapImpl* impl = 0;
93 if (map->numMaps == 0) {
94 map->maps = malloc(sizeof(*map->maps));
95 map->numMaps = 1;
96 impl = &map->maps[0];
97 impl->type = type;
98 impl->map = calloc(GBA_KEY_MAX, sizeof(int));
99 TableInit(&impl->axes, 2, free);
100 } else {
101 impl = _lookupMap(map, type);
102 }
103 if (!impl) {
104 size_t m;
105 for (m = 0; m < map->numMaps; ++m) {
106 if (!map->maps[m].type) {
107 impl = &map->maps[m];
108 break;
109 }
110 }
111 if (impl) {
112 impl->type = type;
113 impl->map = calloc(GBA_KEY_MAX, sizeof(int));
114 } else {
115 map->maps = realloc(map->maps, sizeof(*map->maps) * map->numMaps * 2);
116 for (m = map->numMaps * 2 - 1; m > map->numMaps; --m) {
117 map->maps[m].type = 0;
118 map->maps[m].map = 0;
119 }
120 map->numMaps *= 2;
121 impl = &map->maps[m];
122 impl->type = type;
123 impl->map = calloc(GBA_KEY_MAX, sizeof(int));
124 }
125 TableInit(&impl->axes, 2, free);
126 }
127 return impl;
128}
129
130static void _loadKey(struct GBAInputMap* map, uint32_t type, const char* sectionName, const struct Configuration* config, enum GBAKey key, const char* keyName) {
131 char keyKey[KEY_NAME_MAX];
132 snprintf(keyKey, KEY_NAME_MAX, "key%s", keyName);
133 keyKey[KEY_NAME_MAX - 1] = '\0';
134
135 int value;
136 if (!_getIntValue(config, sectionName, keyKey, &value)) {
137 return;
138 }
139 GBAInputBindKey(map, type, value, key);
140}
141
142static void _loadAxis(struct GBAInputMap* map, uint32_t type, const char* sectionName, const struct Configuration* config, enum GBAKey direction, const char* axisName) {
143 char axisKey[KEY_NAME_MAX];
144 snprintf(axisKey, KEY_NAME_MAX, "axis%sValue", axisName);
145 axisKey[KEY_NAME_MAX - 1] = '\0';
146 int value;
147 if (!_getIntValue(config, sectionName, axisKey, &value)) {
148 return;
149 }
150
151 snprintf(axisKey, KEY_NAME_MAX, "axis%sAxis", axisName);
152 axisKey[KEY_NAME_MAX - 1] = '\0';
153 int axis;
154 const char* strValue = ConfigurationGetValue(config, sectionName, axisKey);
155 if (!strValue || !strValue[0]) {
156 return;
157 }
158 char* end;
159 axis = strtoul(&strValue[1], &end, 10);
160 if (*end) {
161 return;
162 }
163
164 const struct GBAAxis* description = GBAInputQueryAxis(map, type, axis);
165 struct GBAAxis realDescription = { GBA_KEY_NONE, GBA_KEY_NONE, 0, 0 };
166 if (description) {
167 realDescription = *description;
168 }
169 if (strValue[0] == '+') {
170 realDescription.deadHigh = value;
171 realDescription.highDirection = direction;
172 } else if (strValue[0] == '-') {
173 realDescription.deadLow = value;
174 realDescription.lowDirection = direction;
175 }
176 GBAInputBindAxis(map, type, axis, &realDescription);
177}
178
179static void _saveKey(const struct GBAInputMap* map, uint32_t type, const char* sectionName, struct Configuration* config, enum GBAKey key, const char* keyName) {
180 char keyKey[KEY_NAME_MAX];
181 snprintf(keyKey, KEY_NAME_MAX, "key%s", keyName);
182 keyKey[KEY_NAME_MAX - 1] = '\0';
183
184 int value = GBAInputQueryBinding(map, type, key);
185 char keyValue[KEY_VALUE_MAX];
186 snprintf(keyValue, KEY_VALUE_MAX, "%" PRIi32, value);
187
188 ConfigurationSetValue(config, sectionName, keyKey, keyValue);
189}
190
191static void _clearAxis(const char* sectionName, struct Configuration* config, const char* axisName) {
192 char axisKey[KEY_NAME_MAX];
193 snprintf(axisKey, KEY_NAME_MAX, "axis%sValue", axisName);
194 axisKey[KEY_NAME_MAX - 1] = '\0';
195 ConfigurationClearValue(config, sectionName, axisKey);
196
197 snprintf(axisKey, KEY_NAME_MAX, "axis%sAxis", axisName);
198 axisKey[KEY_NAME_MAX - 1] = '\0';
199 ConfigurationClearValue(config, sectionName, axisKey);
200}
201
202static void _saveAxis(uint32_t axis, void* dp, void* up) {
203 struct GBAAxisSave* user = up;
204 const struct GBAAxis* description = dp;
205
206 const char* sectionName = user->sectionName;
207
208 if (description->lowDirection != GBA_KEY_NONE) {
209 const char* keyName = GBAKeyNames[description->lowDirection];
210
211 char axisKey[KEY_NAME_MAX];
212 snprintf(axisKey, KEY_NAME_MAX, "axis%sValue", keyName);
213 axisKey[KEY_NAME_MAX - 1] = '\0';
214 ConfigurationSetIntValue(user->config, sectionName, axisKey, description->deadLow);
215
216 snprintf(axisKey, KEY_NAME_MAX, "axis%sAxis", keyName);
217 axisKey[KEY_NAME_MAX - 1] = '\0';
218
219 char axisInfo[AXIS_INFO_MAX];
220 snprintf(axisInfo, AXIS_INFO_MAX, "-%u", axis);
221 axisInfo[AXIS_INFO_MAX - 1] = '\0';
222 ConfigurationSetValue(user->config, sectionName, axisKey, axisInfo);
223 }
224 if (description->highDirection != GBA_KEY_NONE) {
225 const char* keyName = GBAKeyNames[description->highDirection];
226
227 char axisKey[KEY_NAME_MAX];
228 snprintf(axisKey, KEY_NAME_MAX, "axis%sValue", keyName);
229 axisKey[KEY_NAME_MAX - 1] = '\0';
230 ConfigurationSetIntValue(user->config, sectionName, axisKey, description->deadHigh);
231
232 snprintf(axisKey, KEY_NAME_MAX, "axis%sAxis", keyName);
233 axisKey[KEY_NAME_MAX - 1] = '\0';
234
235 char axisInfo[AXIS_INFO_MAX];
236 snprintf(axisInfo, AXIS_INFO_MAX, "+%u", axis);
237 axisInfo[AXIS_INFO_MAX - 1] = '\0';
238 ConfigurationSetValue(user->config, sectionName, axisKey, axisInfo);
239 }
240}
241
242void _enumerateAxis(uint32_t axis, void* dp, void* ep) {
243 struct GBAAxisEnumerate* enumUser = ep;
244 const struct GBAAxis* description = dp;
245 enumUser->handler(axis, description, enumUser->user);
246}
247
248void _unbindAxis(uint32_t axis, void* dp, void* user) {
249 UNUSED(axis);
250 enum GBAKey* key = user;
251 struct GBAAxis* description = dp;
252 if (description->highDirection == *key) {
253 description->highDirection = GBA_KEY_NONE;
254 }
255 if (description->lowDirection == *key) {
256 description->lowDirection = GBA_KEY_NONE;
257 }
258}
259
260static bool _loadAll(struct GBAInputMap* map, uint32_t type, const char* sectionName, const struct Configuration* config) {
261 if (!ConfigurationHasSection(config, sectionName)) {
262 return false;
263 }
264 _loadKey(map, type, sectionName, config, GBA_KEY_A, "A");
265 _loadKey(map, type, sectionName, config, GBA_KEY_B, "B");
266 _loadKey(map, type, sectionName, config, GBA_KEY_L, "L");
267 _loadKey(map, type, sectionName, config, GBA_KEY_R, "R");
268 _loadKey(map, type, sectionName, config, GBA_KEY_START, "Start");
269 _loadKey(map, type, sectionName, config, GBA_KEY_SELECT, "Select");
270 _loadKey(map, type, sectionName, config, GBA_KEY_UP, "Up");
271 _loadKey(map, type, sectionName, config, GBA_KEY_DOWN, "Down");
272 _loadKey(map, type, sectionName, config, GBA_KEY_LEFT, "Left");
273 _loadKey(map, type, sectionName, config, GBA_KEY_RIGHT, "Right");
274
275 _loadAxis(map, type, sectionName, config, GBA_KEY_A, "A");
276 _loadAxis(map, type, sectionName, config, GBA_KEY_B, "B");
277 _loadAxis(map, type, sectionName, config, GBA_KEY_L, "L");
278 _loadAxis(map, type, sectionName, config, GBA_KEY_R, "R");
279 _loadAxis(map, type, sectionName, config, GBA_KEY_START, "Start");
280 _loadAxis(map, type, sectionName, config, GBA_KEY_SELECT, "Select");
281 _loadAxis(map, type, sectionName, config, GBA_KEY_UP, "Up");
282 _loadAxis(map, type, sectionName, config, GBA_KEY_DOWN, "Down");
283 _loadAxis(map, type, sectionName, config, GBA_KEY_LEFT, "Left");
284 _loadAxis(map, type, sectionName, config, GBA_KEY_RIGHT, "Right");
285 return true;
286}
287
288static void _saveAll(const struct GBAInputMap* map, uint32_t type, const char* sectionName, struct Configuration* config) {
289 _saveKey(map, type, sectionName, config, GBA_KEY_A, "A");
290 _saveKey(map, type, sectionName, config, GBA_KEY_B, "B");
291 _saveKey(map, type, sectionName, config, GBA_KEY_L, "L");
292 _saveKey(map, type, sectionName, config, GBA_KEY_R, "R");
293 _saveKey(map, type, sectionName, config, GBA_KEY_START, "Start");
294 _saveKey(map, type, sectionName, config, GBA_KEY_SELECT, "Select");
295 _saveKey(map, type, sectionName, config, GBA_KEY_UP, "Up");
296 _saveKey(map, type, sectionName, config, GBA_KEY_DOWN, "Down");
297 _saveKey(map, type, sectionName, config, GBA_KEY_LEFT, "Left");
298 _saveKey(map, type, sectionName, config, GBA_KEY_RIGHT, "Right");
299
300 _clearAxis(sectionName, config, "A");
301 _clearAxis(sectionName, config, "B");
302 _clearAxis(sectionName, config, "L");
303 _clearAxis(sectionName, config, "R");
304 _clearAxis(sectionName, config, "Start");
305 _clearAxis(sectionName, config, "Select");
306 _clearAxis(sectionName, config, "Up");
307 _clearAxis(sectionName, config, "Down");
308 _clearAxis(sectionName, config, "Left");
309 _clearAxis(sectionName, config, "Right");
310
311 const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
312 if (!impl) {
313 return;
314 }
315 struct GBAAxisSave save = {
316 config,
317 sectionName
318 };
319 TableEnumerate(&impl->axes, _saveAxis, &save);
320}
321
322void GBAInputMapInit(struct GBAInputMap* map) {
323 map->maps = 0;
324 map->numMaps = 0;
325}
326
327void GBAInputMapDeinit(struct GBAInputMap* map) {
328 size_t m;
329 for (m = 0; m < map->numMaps; ++m) {
330 if (map->maps[m].type) {
331 free(map->maps[m].map);
332 TableDeinit(&map->maps[m].axes);
333 }
334 }
335 free(map->maps);
336 map->maps = 0;
337 map->numMaps = 0;
338}
339
340enum GBAKey GBAInputMapKey(const struct GBAInputMap* map, uint32_t type, int key) {
341 size_t m;
342 const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
343 if (!impl || !impl->map) {
344 return GBA_KEY_NONE;
345 }
346
347 for (m = 0; m < GBA_KEY_MAX; ++m) {
348 if (impl->map[m] == key) {
349 return m;
350 }
351 }
352 return GBA_KEY_NONE;
353}
354
355int GBAInputMapKeyBits(const struct GBAInputMap* map, uint32_t type, uint32_t bits, unsigned offset) {
356 int keys = 0;
357 for (; bits; bits >>= 1, ++offset) {
358 if (bits & 1) {
359 enum GBAKey key = GBAInputMapKey(map, type, offset);
360 if (key == GBA_KEY_NONE) {
361 continue;
362 }
363 keys |= 1 << key;
364 }
365 }
366 return keys;
367}
368
369void GBAInputBindKey(struct GBAInputMap* map, uint32_t type, int key, enum GBAKey input) {
370 struct GBAInputMapImpl* impl = _guaranteeMap(map, type);
371 GBAInputUnbindKey(map, type, input);
372 impl->map[input] = key;
373}
374
375void GBAInputUnbindKey(struct GBAInputMap* map, uint32_t type, enum GBAKey input) {
376 struct GBAInputMapImpl* impl = _lookupMap(map, type);
377 if (input < 0 || input >= GBA_KEY_MAX) {
378 return;
379 }
380 if (impl) {
381 impl->map[input] = GBA_NO_MAPPING;
382 }
383}
384
385int GBAInputQueryBinding(const struct GBAInputMap* map, uint32_t type, enum GBAKey input) {
386 if (input >= GBA_KEY_MAX) {
387 return 0;
388 }
389
390 const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
391 if (!impl || !impl->map) {
392 return 0;
393 }
394
395 return impl->map[input];
396}
397
398enum GBAKey GBAInputMapAxis(const struct GBAInputMap* map, uint32_t type, int axis, int value) {
399 const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
400 if (!impl) {
401 return GBA_KEY_NONE;
402 }
403 struct GBAAxis* description = TableLookup(&impl->axes, axis);
404 if (!description) {
405 return GBA_KEY_NONE;
406 }
407 int state = 0;
408 if (value < description->deadLow) {
409 state = -1;
410 } else if (value > description->deadHigh) {
411 state = 1;
412 }
413 if (state > 0) {
414 return description->highDirection;
415 }
416 if (state < 0) {
417 return description->lowDirection;
418 }
419 return GBA_KEY_NONE;
420}
421
422int GBAInputClearAxis(const struct GBAInputMap* map, uint32_t type, int axis, int keys) {
423 const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
424 if (!impl) {
425 return keys;
426 }
427 struct GBAAxis* description = TableLookup(&impl->axes, axis);
428 if (!description) {
429 return keys;
430 }
431 return keys &= ~((1 << description->highDirection) | (1 << description->lowDirection));
432}
433
434void GBAInputBindAxis(struct GBAInputMap* map, uint32_t type, int axis, const struct GBAAxis* description) {
435 struct GBAInputMapImpl* impl = _guaranteeMap(map, type);
436 struct GBAAxis d2 = *description;
437 TableEnumerate(&impl->axes, _unbindAxis, &d2.highDirection);
438 TableEnumerate(&impl->axes, _unbindAxis, &d2.lowDirection);
439 struct GBAAxis* dup = malloc(sizeof(struct GBAAxis));
440 *dup = *description;
441 TableInsert(&impl->axes, axis, dup);
442}
443
444void GBAInputUnbindAxis(struct GBAInputMap* map, uint32_t type, int axis) {
445 struct GBAInputMapImpl* impl = _lookupMap(map, type);
446 if (impl) {
447 TableRemove(&impl->axes, axis);
448 }
449}
450
451void GBAInputUnbindAllAxes(struct GBAInputMap* map, uint32_t type) {
452 struct GBAInputMapImpl* impl = _lookupMap(map, type);
453 if (impl) {
454 TableClear(&impl->axes);
455 }
456}
457
458const struct GBAAxis* GBAInputQueryAxis(const struct GBAInputMap* map, uint32_t type, int axis) {
459 const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
460 if (!impl) {
461 return 0;
462 }
463 return TableLookup(&impl->axes, axis);
464}
465
466void GBAInputEnumerateAxes(const struct GBAInputMap* map, uint32_t type, void (handler(int axis, const struct GBAAxis* description, void* user)), void* user) {
467 const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
468 if (!impl) {
469 return;
470 }
471 struct GBAAxisEnumerate enumUser = {
472 handler,
473 user
474 };
475 TableEnumerate(&impl->axes, _enumerateAxis, &enumUser);
476}
477
478void GBAInputMapLoad(struct GBAInputMap* map, uint32_t type, const struct Configuration* config) {
479 char sectionName[SECTION_NAME_MAX];
480 _makeSectionName(sectionName, SECTION_NAME_MAX, type);
481 _loadAll(map, type, sectionName, config);
482}
483
484void GBAInputMapSave(const struct GBAInputMap* map, uint32_t type, struct Configuration* config) {
485 char sectionName[SECTION_NAME_MAX];
486 _makeSectionName(sectionName, SECTION_NAME_MAX, type);
487 _saveAll(map, type, sectionName, config);
488}
489
490bool GBAInputProfileLoad(struct GBAInputMap* map, uint32_t type, const struct Configuration* config, const char* profile) {
491 char sectionName[SECTION_NAME_MAX];
492 snprintf(sectionName, SECTION_NAME_MAX, "input-profile.%s", profile);
493 sectionName[SECTION_NAME_MAX - 1] = '\0';
494 return _loadAll(map, type, sectionName, config);
495}
496
497void GBAInputProfileSave(const struct GBAInputMap* map, uint32_t type, struct Configuration* config, const char* profile) {
498 char sectionName[SECTION_NAME_MAX];
499 snprintf(sectionName, SECTION_NAME_MAX, "input-profile.%s", profile);
500 sectionName[SECTION_NAME_MAX - 1] = '\0';
501 _saveAll(map, type, sectionName, config);
502}
503
504const char* GBAInputGetPreferredDevice(const struct Configuration* config, uint32_t type, int playerId) {
505 char sectionName[SECTION_NAME_MAX];
506 _makeSectionName(sectionName, SECTION_NAME_MAX, type);
507
508 char deviceId[KEY_NAME_MAX];
509 snprintf(deviceId, sizeof(deviceId), "device%i", playerId);
510 return ConfigurationGetValue(config, sectionName, deviceId);
511}
512
513void GBAInputSetPreferredDevice(struct Configuration* config, uint32_t type, int playerId, const char* deviceName) {
514 char sectionName[SECTION_NAME_MAX];
515 _makeSectionName(sectionName, SECTION_NAME_MAX, type);
516
517 char deviceId[KEY_NAME_MAX];
518 snprintf(deviceId, sizeof(deviceId), "device%i", playerId);
519 return ConfigurationSetValue(config, sectionName, deviceId, deviceName);
520}
521
522const char* GBAInputGetCustomValue(const struct Configuration* config, uint32_t type, const char* key, const char* profile) {
523 char sectionName[SECTION_NAME_MAX];
524 if (profile) {
525 snprintf(sectionName, SECTION_NAME_MAX, "input-profile.%s", profile);
526 const char* value = ConfigurationGetValue(config, sectionName, key);
527 if (value) {
528 return value;
529 }
530 }
531 _makeSectionName(sectionName, SECTION_NAME_MAX, type);
532 return ConfigurationGetValue(config, sectionName, key);
533}
534
535void GBAInputSetCustomValue(struct Configuration* config, uint32_t type, const char* key, const char* value, const char* profile) {
536 char sectionName[SECTION_NAME_MAX];
537 if (profile) {
538 snprintf(sectionName, SECTION_NAME_MAX, "input-profile.%s", profile);
539 ConfigurationSetValue(config, sectionName, key, value);
540 }
541 _makeSectionName(sectionName, SECTION_NAME_MAX, type);
542 ConfigurationSetValue(config, sectionName, key, value);
543}