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 = malloc(GBA_KEY_MAX * sizeof(int));
99 int i;
100 for (i = 0; i < GBA_KEY_MAX; ++i) {
101 impl->map[i] = GBA_KEY_NONE;
102 }
103 TableInit(&impl->axes, 2, free);
104 } else {
105 impl = _lookupMap(map, type);
106 }
107 if (!impl) {
108 size_t m;
109 for (m = 0; m < map->numMaps; ++m) {
110 if (!map->maps[m].type) {
111 impl = &map->maps[m];
112 break;
113 }
114 }
115 if (impl) {
116 impl->type = type;
117 impl->map = malloc(GBA_KEY_MAX * sizeof(int));
118 int i;
119 for (i = 0; i < GBA_KEY_MAX; ++i) {
120 impl->map[i] = GBA_KEY_NONE;
121 }
122 } else {
123 map->maps = realloc(map->maps, sizeof(*map->maps) * map->numMaps * 2);
124 for (m = map->numMaps * 2 - 1; m > map->numMaps; --m) {
125 map->maps[m].type = 0;
126 map->maps[m].map = 0;
127 }
128 map->numMaps *= 2;
129 impl = &map->maps[m];
130 impl->type = type;
131 impl->map = malloc(GBA_KEY_MAX * sizeof(int));
132 int i;
133 for (i = 0; i < GBA_KEY_MAX; ++i) {
134 impl->map[i] = GBA_KEY_NONE;
135 }
136 }
137 TableInit(&impl->axes, 2, free);
138 }
139 return impl;
140}
141
142static void _loadKey(struct GBAInputMap* map, uint32_t type, const char* sectionName, const struct Configuration* config, enum GBAKey key, const char* keyName) {
143 char keyKey[KEY_NAME_MAX];
144 snprintf(keyKey, KEY_NAME_MAX, "key%s", keyName);
145 keyKey[KEY_NAME_MAX - 1] = '\0';
146
147 int value;
148 if (!_getIntValue(config, sectionName, keyKey, &value)) {
149 return;
150 }
151 GBAInputBindKey(map, type, value, key);
152}
153
154static void _loadAxis(struct GBAInputMap* map, uint32_t type, const char* sectionName, const struct Configuration* config, enum GBAKey direction, const char* axisName) {
155 char axisKey[KEY_NAME_MAX];
156 snprintf(axisKey, KEY_NAME_MAX, "axis%sValue", axisName);
157 axisKey[KEY_NAME_MAX - 1] = '\0';
158 int value;
159 if (!_getIntValue(config, sectionName, axisKey, &value)) {
160 return;
161 }
162
163 snprintf(axisKey, KEY_NAME_MAX, "axis%sAxis", axisName);
164 axisKey[KEY_NAME_MAX - 1] = '\0';
165 int axis;
166 const char* strValue = ConfigurationGetValue(config, sectionName, axisKey);
167 if (!strValue || !strValue[0]) {
168 return;
169 }
170 char* end;
171 axis = strtoul(&strValue[1], &end, 10);
172 if (*end) {
173 return;
174 }
175
176 const struct GBAAxis* description = GBAInputQueryAxis(map, type, axis);
177 struct GBAAxis realDescription = { GBA_KEY_NONE, GBA_KEY_NONE, 0, 0 };
178 if (description) {
179 realDescription = *description;
180 }
181 if (strValue[0] == '+') {
182 realDescription.deadHigh = value;
183 realDescription.highDirection = direction;
184 } else if (strValue[0] == '-') {
185 realDescription.deadLow = value;
186 realDescription.lowDirection = direction;
187 }
188 GBAInputBindAxis(map, type, axis, &realDescription);
189}
190
191static void _saveKey(const struct GBAInputMap* map, uint32_t type, const char* sectionName, struct Configuration* config, enum GBAKey key, const char* keyName) {
192 char keyKey[KEY_NAME_MAX];
193 snprintf(keyKey, KEY_NAME_MAX, "key%s", keyName);
194 keyKey[KEY_NAME_MAX - 1] = '\0';
195
196 int value = GBAInputQueryBinding(map, type, key);
197 char keyValue[KEY_VALUE_MAX];
198 snprintf(keyValue, KEY_VALUE_MAX, "%" PRIi32, value);
199
200 ConfigurationSetValue(config, sectionName, keyKey, keyValue);
201}
202
203static void _clearAxis(const char* sectionName, struct Configuration* config, const char* axisName) {
204 char axisKey[KEY_NAME_MAX];
205 snprintf(axisKey, KEY_NAME_MAX, "axis%sValue", axisName);
206 axisKey[KEY_NAME_MAX - 1] = '\0';
207 ConfigurationClearValue(config, sectionName, axisKey);
208
209 snprintf(axisKey, KEY_NAME_MAX, "axis%sAxis", axisName);
210 axisKey[KEY_NAME_MAX - 1] = '\0';
211 ConfigurationClearValue(config, sectionName, axisKey);
212}
213
214static void _saveAxis(uint32_t axis, void* dp, void* up) {
215 struct GBAAxisSave* user = up;
216 const struct GBAAxis* description = dp;
217
218 const char* sectionName = user->sectionName;
219
220 if (description->lowDirection != GBA_KEY_NONE) {
221 const char* keyName = GBAKeyNames[description->lowDirection];
222
223 char axisKey[KEY_NAME_MAX];
224 snprintf(axisKey, KEY_NAME_MAX, "axis%sValue", keyName);
225 axisKey[KEY_NAME_MAX - 1] = '\0';
226 ConfigurationSetIntValue(user->config, sectionName, axisKey, description->deadLow);
227
228 snprintf(axisKey, KEY_NAME_MAX, "axis%sAxis", keyName);
229 axisKey[KEY_NAME_MAX - 1] = '\0';
230
231 char axisInfo[AXIS_INFO_MAX];
232 snprintf(axisInfo, AXIS_INFO_MAX, "-%u", axis);
233 axisInfo[AXIS_INFO_MAX - 1] = '\0';
234 ConfigurationSetValue(user->config, sectionName, axisKey, axisInfo);
235 }
236 if (description->highDirection != GBA_KEY_NONE) {
237 const char* keyName = GBAKeyNames[description->highDirection];
238
239 char axisKey[KEY_NAME_MAX];
240 snprintf(axisKey, KEY_NAME_MAX, "axis%sValue", keyName);
241 axisKey[KEY_NAME_MAX - 1] = '\0';
242 ConfigurationSetIntValue(user->config, sectionName, axisKey, description->deadHigh);
243
244 snprintf(axisKey, KEY_NAME_MAX, "axis%sAxis", keyName);
245 axisKey[KEY_NAME_MAX - 1] = '\0';
246
247 char axisInfo[AXIS_INFO_MAX];
248 snprintf(axisInfo, AXIS_INFO_MAX, "+%u", axis);
249 axisInfo[AXIS_INFO_MAX - 1] = '\0';
250 ConfigurationSetValue(user->config, sectionName, axisKey, axisInfo);
251 }
252}
253
254void _enumerateAxis(uint32_t axis, void* dp, void* ep) {
255 struct GBAAxisEnumerate* enumUser = ep;
256 const struct GBAAxis* description = dp;
257 enumUser->handler(axis, description, enumUser->user);
258}
259
260void _unbindAxis(uint32_t axis, void* dp, void* user) {
261 UNUSED(axis);
262 enum GBAKey* key = user;
263 struct GBAAxis* description = dp;
264 if (description->highDirection == *key) {
265 description->highDirection = GBA_KEY_NONE;
266 }
267 if (description->lowDirection == *key) {
268 description->lowDirection = GBA_KEY_NONE;
269 }
270}
271
272static bool _loadAll(struct GBAInputMap* map, uint32_t type, const char* sectionName, const struct Configuration* config) {
273 if (!ConfigurationHasSection(config, sectionName)) {
274 return false;
275 }
276 _loadKey(map, type, sectionName, config, GBA_KEY_A, "A");
277 _loadKey(map, type, sectionName, config, GBA_KEY_B, "B");
278 _loadKey(map, type, sectionName, config, GBA_KEY_L, "L");
279 _loadKey(map, type, sectionName, config, GBA_KEY_R, "R");
280 _loadKey(map, type, sectionName, config, GBA_KEY_START, "Start");
281 _loadKey(map, type, sectionName, config, GBA_KEY_SELECT, "Select");
282 _loadKey(map, type, sectionName, config, GBA_KEY_UP, "Up");
283 _loadKey(map, type, sectionName, config, GBA_KEY_DOWN, "Down");
284 _loadKey(map, type, sectionName, config, GBA_KEY_LEFT, "Left");
285 _loadKey(map, type, sectionName, config, GBA_KEY_RIGHT, "Right");
286
287 _loadAxis(map, type, sectionName, config, GBA_KEY_A, "A");
288 _loadAxis(map, type, sectionName, config, GBA_KEY_B, "B");
289 _loadAxis(map, type, sectionName, config, GBA_KEY_L, "L");
290 _loadAxis(map, type, sectionName, config, GBA_KEY_R, "R");
291 _loadAxis(map, type, sectionName, config, GBA_KEY_START, "Start");
292 _loadAxis(map, type, sectionName, config, GBA_KEY_SELECT, "Select");
293 _loadAxis(map, type, sectionName, config, GBA_KEY_UP, "Up");
294 _loadAxis(map, type, sectionName, config, GBA_KEY_DOWN, "Down");
295 _loadAxis(map, type, sectionName, config, GBA_KEY_LEFT, "Left");
296 _loadAxis(map, type, sectionName, config, GBA_KEY_RIGHT, "Right");
297 return true;
298}
299
300static void _saveAll(const struct GBAInputMap* map, uint32_t type, const char* sectionName, struct Configuration* config) {
301 _saveKey(map, type, sectionName, config, GBA_KEY_A, "A");
302 _saveKey(map, type, sectionName, config, GBA_KEY_B, "B");
303 _saveKey(map, type, sectionName, config, GBA_KEY_L, "L");
304 _saveKey(map, type, sectionName, config, GBA_KEY_R, "R");
305 _saveKey(map, type, sectionName, config, GBA_KEY_START, "Start");
306 _saveKey(map, type, sectionName, config, GBA_KEY_SELECT, "Select");
307 _saveKey(map, type, sectionName, config, GBA_KEY_UP, "Up");
308 _saveKey(map, type, sectionName, config, GBA_KEY_DOWN, "Down");
309 _saveKey(map, type, sectionName, config, GBA_KEY_LEFT, "Left");
310 _saveKey(map, type, sectionName, config, GBA_KEY_RIGHT, "Right");
311
312 _clearAxis(sectionName, config, "A");
313 _clearAxis(sectionName, config, "B");
314 _clearAxis(sectionName, config, "L");
315 _clearAxis(sectionName, config, "R");
316 _clearAxis(sectionName, config, "Start");
317 _clearAxis(sectionName, config, "Select");
318 _clearAxis(sectionName, config, "Up");
319 _clearAxis(sectionName, config, "Down");
320 _clearAxis(sectionName, config, "Left");
321 _clearAxis(sectionName, config, "Right");
322
323 const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
324 if (!impl) {
325 return;
326 }
327 struct GBAAxisSave save = {
328 config,
329 sectionName
330 };
331 TableEnumerate(&impl->axes, _saveAxis, &save);
332}
333
334void GBAInputMapInit(struct GBAInputMap* map) {
335 map->maps = 0;
336 map->numMaps = 0;
337}
338
339void GBAInputMapDeinit(struct GBAInputMap* map) {
340 size_t m;
341 for (m = 0; m < map->numMaps; ++m) {
342 if (map->maps[m].type) {
343 free(map->maps[m].map);
344 TableDeinit(&map->maps[m].axes);
345 }
346 }
347 free(map->maps);
348 map->maps = 0;
349 map->numMaps = 0;
350}
351
352enum GBAKey GBAInputMapKey(const struct GBAInputMap* map, uint32_t type, int key) {
353 size_t m;
354 const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
355 if (!impl || !impl->map) {
356 return GBA_KEY_NONE;
357 }
358
359 for (m = 0; m < GBA_KEY_MAX; ++m) {
360 if (impl->map[m] == key) {
361 return m;
362 }
363 }
364 return GBA_KEY_NONE;
365}
366
367int GBAInputMapKeyBits(const struct GBAInputMap* map, uint32_t type, uint32_t bits, unsigned offset) {
368 int keys = 0;
369 for (; bits; bits >>= 1, ++offset) {
370 if (bits & 1) {
371 enum GBAKey key = GBAInputMapKey(map, type, offset);
372 if (key == GBA_KEY_NONE) {
373 continue;
374 }
375 keys |= 1 << key;
376 }
377 }
378 return keys;
379}
380
381void GBAInputBindKey(struct GBAInputMap* map, uint32_t type, int key, enum GBAKey input) {
382 struct GBAInputMapImpl* impl = _guaranteeMap(map, type);
383 GBAInputUnbindKey(map, type, input);
384 impl->map[input] = key;
385}
386
387void GBAInputUnbindKey(struct GBAInputMap* map, uint32_t type, enum GBAKey input) {
388 struct GBAInputMapImpl* impl = _lookupMap(map, type);
389 if (input < 0 || input >= GBA_KEY_MAX) {
390 return;
391 }
392 if (impl) {
393 impl->map[input] = GBA_KEY_NONE;
394 }
395}
396
397int GBAInputQueryBinding(const struct GBAInputMap* map, uint32_t type, enum GBAKey input) {
398 if (input >= GBA_KEY_MAX) {
399 return GBA_KEY_NONE;
400 }
401
402 const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
403 if (!impl || !impl->map) {
404 return GBA_KEY_NONE;
405 }
406
407 return impl->map[input];
408}
409
410enum GBAKey GBAInputMapAxis(const struct GBAInputMap* map, uint32_t type, int axis, int value) {
411 const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
412 if (!impl) {
413 return GBA_KEY_NONE;
414 }
415 struct GBAAxis* description = TableLookup(&impl->axes, axis);
416 if (!description) {
417 return GBA_KEY_NONE;
418 }
419 int state = 0;
420 if (value < description->deadLow) {
421 state = -1;
422 } else if (value > description->deadHigh) {
423 state = 1;
424 }
425 if (state > 0) {
426 return description->highDirection;
427 }
428 if (state < 0) {
429 return description->lowDirection;
430 }
431 return GBA_KEY_NONE;
432}
433
434int GBAInputClearAxis(const struct GBAInputMap* map, uint32_t type, int axis, int keys) {
435 const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
436 if (!impl) {
437 return keys;
438 }
439 struct GBAAxis* description = TableLookup(&impl->axes, axis);
440 if (!description) {
441 return keys;
442 }
443 return keys &= ~((1 << description->highDirection) | (1 << description->lowDirection));
444}
445
446void GBAInputBindAxis(struct GBAInputMap* map, uint32_t type, int axis, const struct GBAAxis* description) {
447 struct GBAInputMapImpl* impl = _guaranteeMap(map, type);
448 struct GBAAxis d2 = *description;
449 TableEnumerate(&impl->axes, _unbindAxis, &d2.highDirection);
450 TableEnumerate(&impl->axes, _unbindAxis, &d2.lowDirection);
451 struct GBAAxis* dup = malloc(sizeof(struct GBAAxis));
452 *dup = *description;
453 TableInsert(&impl->axes, axis, dup);
454}
455
456void GBAInputUnbindAxis(struct GBAInputMap* map, uint32_t type, int axis) {
457 struct GBAInputMapImpl* impl = _lookupMap(map, type);
458 if (impl) {
459 TableRemove(&impl->axes, axis);
460 }
461}
462
463void GBAInputUnbindAllAxes(struct GBAInputMap* map, uint32_t type) {
464 struct GBAInputMapImpl* impl = _lookupMap(map, type);
465 if (impl) {
466 TableClear(&impl->axes);
467 }
468}
469
470const struct GBAAxis* GBAInputQueryAxis(const struct GBAInputMap* map, uint32_t type, int axis) {
471 const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
472 if (!impl) {
473 return 0;
474 }
475 return TableLookup(&impl->axes, axis);
476}
477
478void GBAInputEnumerateAxes(const struct GBAInputMap* map, uint32_t type, void (handler(int axis, const struct GBAAxis* description, void* user)), void* user) {
479 const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
480 if (!impl) {
481 return;
482 }
483 struct GBAAxisEnumerate enumUser = {
484 handler,
485 user
486 };
487 TableEnumerate(&impl->axes, _enumerateAxis, &enumUser);
488}
489
490void GBAInputMapLoad(struct GBAInputMap* map, uint32_t type, const struct Configuration* config) {
491 char sectionName[SECTION_NAME_MAX];
492 _makeSectionName(sectionName, SECTION_NAME_MAX, type);
493 _loadAll(map, type, sectionName, config);
494}
495
496void GBAInputMapSave(const struct GBAInputMap* map, uint32_t type, struct Configuration* config) {
497 char sectionName[SECTION_NAME_MAX];
498 _makeSectionName(sectionName, SECTION_NAME_MAX, type);
499 _saveAll(map, type, sectionName, config);
500}
501
502bool GBAInputProfileLoad(struct GBAInputMap* map, uint32_t type, const struct Configuration* config, const char* profile) {
503 char sectionName[SECTION_NAME_MAX];
504 snprintf(sectionName, SECTION_NAME_MAX, "input-profile.%s", profile);
505 sectionName[SECTION_NAME_MAX - 1] = '\0';
506 return _loadAll(map, type, sectionName, config);
507}
508
509void GBAInputProfileSave(const struct GBAInputMap* map, uint32_t type, struct Configuration* config, const char* profile) {
510 char sectionName[SECTION_NAME_MAX];
511 snprintf(sectionName, SECTION_NAME_MAX, "input-profile.%s", profile);
512 sectionName[SECTION_NAME_MAX - 1] = '\0';
513 _saveAll(map, type, sectionName, config);
514}
515
516const char* GBAInputGetPreferredDevice(const struct Configuration* config, uint32_t type, int playerId) {
517 char sectionName[SECTION_NAME_MAX];
518 _makeSectionName(sectionName, SECTION_NAME_MAX, type);
519
520 char deviceId[KEY_NAME_MAX];
521 snprintf(deviceId, sizeof(deviceId), "device%i", playerId);
522 return ConfigurationGetValue(config, sectionName, deviceId);
523}
524
525void GBAInputSetPreferredDevice(struct Configuration* config, uint32_t type, int playerId, const char* deviceName) {
526 char sectionName[SECTION_NAME_MAX];
527 _makeSectionName(sectionName, SECTION_NAME_MAX, type);
528
529 char deviceId[KEY_NAME_MAX];
530 snprintf(deviceId, sizeof(deviceId), "device%i", playerId);
531 return ConfigurationSetValue(config, sectionName, deviceId, deviceName);
532}
533
534const char* GBAInputGetCustomValue(const struct Configuration* config, uint32_t type, const char* key, const char* profile) {
535 char sectionName[SECTION_NAME_MAX];
536 if (profile) {
537 snprintf(sectionName, SECTION_NAME_MAX, "input-profile.%s", profile);
538 const char* value = ConfigurationGetValue(config, sectionName, key);
539 if (value) {
540 return value;
541 }
542 }
543 _makeSectionName(sectionName, SECTION_NAME_MAX, type);
544 return ConfigurationGetValue(config, sectionName, key);
545}
546
547void GBAInputSetCustomValue(struct Configuration* config, uint32_t type, const char* key, const char* value, const char* profile) {
548 char sectionName[SECTION_NAME_MAX];
549 if (profile) {
550 snprintf(sectionName, SECTION_NAME_MAX, "input-profile.%s", profile);
551 ConfigurationSetValue(config, sectionName, key, value);
552 }
553 _makeSectionName(sectionName, SECTION_NAME_MAX, type);
554 ConfigurationSetValue(config, sectionName, key, value);
555}