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 void _loadAll(struct GBAInputMap* map, uint32_t type, const char* sectionName, const struct Configuration* config) {
261 _loadKey(map, type, sectionName, config, GBA_KEY_A, "A");
262 _loadKey(map, type, sectionName, config, GBA_KEY_B, "B");
263 _loadKey(map, type, sectionName, config, GBA_KEY_L, "L");
264 _loadKey(map, type, sectionName, config, GBA_KEY_R, "R");
265 _loadKey(map, type, sectionName, config, GBA_KEY_START, "Start");
266 _loadKey(map, type, sectionName, config, GBA_KEY_SELECT, "Select");
267 _loadKey(map, type, sectionName, config, GBA_KEY_UP, "Up");
268 _loadKey(map, type, sectionName, config, GBA_KEY_DOWN, "Down");
269 _loadKey(map, type, sectionName, config, GBA_KEY_LEFT, "Left");
270 _loadKey(map, type, sectionName, config, GBA_KEY_RIGHT, "Right");
271
272 _loadAxis(map, type, sectionName, config, GBA_KEY_A, "A");
273 _loadAxis(map, type, sectionName, config, GBA_KEY_B, "B");
274 _loadAxis(map, type, sectionName, config, GBA_KEY_L, "L");
275 _loadAxis(map, type, sectionName, config, GBA_KEY_R, "R");
276 _loadAxis(map, type, sectionName, config, GBA_KEY_START, "Start");
277 _loadAxis(map, type, sectionName, config, GBA_KEY_SELECT, "Select");
278 _loadAxis(map, type, sectionName, config, GBA_KEY_UP, "Up");
279 _loadAxis(map, type, sectionName, config, GBA_KEY_DOWN, "Down");
280 _loadAxis(map, type, sectionName, config, GBA_KEY_LEFT, "Left");
281 _loadAxis(map, type, sectionName, config, GBA_KEY_RIGHT, "Right");
282}
283
284static void _saveAll(const struct GBAInputMap* map, uint32_t type, const char* sectionName, struct Configuration* config) {
285 _saveKey(map, type, sectionName, config, GBA_KEY_A, "A");
286 _saveKey(map, type, sectionName, config, GBA_KEY_B, "B");
287 _saveKey(map, type, sectionName, config, GBA_KEY_L, "L");
288 _saveKey(map, type, sectionName, config, GBA_KEY_R, "R");
289 _saveKey(map, type, sectionName, config, GBA_KEY_START, "Start");
290 _saveKey(map, type, sectionName, config, GBA_KEY_SELECT, "Select");
291 _saveKey(map, type, sectionName, config, GBA_KEY_UP, "Up");
292 _saveKey(map, type, sectionName, config, GBA_KEY_DOWN, "Down");
293 _saveKey(map, type, sectionName, config, GBA_KEY_LEFT, "Left");
294 _saveKey(map, type, sectionName, config, GBA_KEY_RIGHT, "Right");
295
296 _clearAxis(sectionName, config, "A");
297 _clearAxis(sectionName, config, "B");
298 _clearAxis(sectionName, config, "L");
299 _clearAxis(sectionName, config, "R");
300 _clearAxis(sectionName, config, "Start");
301 _clearAxis(sectionName, config, "Select");
302 _clearAxis(sectionName, config, "Up");
303 _clearAxis(sectionName, config, "Down");
304 _clearAxis(sectionName, config, "Left");
305 _clearAxis(sectionName, config, "Right");
306
307 const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
308 if (!impl) {
309 return;
310 }
311 struct GBAAxisSave save = {
312 config,
313 sectionName
314 };
315 TableEnumerate(&impl->axes, _saveAxis, &save);
316}
317
318void GBAInputMapInit(struct GBAInputMap* map) {
319 map->maps = 0;
320 map->numMaps = 0;
321}
322
323void GBAInputMapDeinit(struct GBAInputMap* map) {
324 size_t m;
325 for (m = 0; m < map->numMaps; ++m) {
326 if (map->maps[m].type) {
327 free(map->maps[m].map);
328 TableDeinit(&map->maps[m].axes);
329 }
330 }
331 free(map->maps);
332 map->maps = 0;
333 map->numMaps = 0;
334}
335
336enum GBAKey GBAInputMapKey(const struct GBAInputMap* map, uint32_t type, int key) {
337 size_t m;
338 const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
339 if (!impl || !impl->map) {
340 return GBA_KEY_NONE;
341 }
342
343 for (m = 0; m < GBA_KEY_MAX; ++m) {
344 if (impl->map[m] == key) {
345 return m;
346 }
347 }
348 return GBA_KEY_NONE;
349}
350
351int GBAInputMapKeyBits(const struct GBAInputMap* map, uint32_t type, uint32_t bits, unsigned offset) {
352 int keys = 0;
353 for (; bits; bits >>= 1, ++offset) {
354 if (bits & 1) {
355 enum GBAKey key = GBAInputMapKey(map, type, offset);
356 if (key == GBA_KEY_NONE) {
357 continue;
358 }
359 keys |= 1 << key;
360 }
361 }
362 return keys;
363}
364
365void GBAInputBindKey(struct GBAInputMap* map, uint32_t type, int key, enum GBAKey input) {
366 struct GBAInputMapImpl* impl = _guaranteeMap(map, type);
367 GBAInputUnbindKey(map, type, input);
368 impl->map[input] = key;
369}
370
371void GBAInputUnbindKey(struct GBAInputMap* map, uint32_t type, enum GBAKey input) {
372 struct GBAInputMapImpl* impl = _lookupMap(map, type);
373 if (input < 0 || input >= GBA_KEY_MAX) {
374 return;
375 }
376 if (impl) {
377 impl->map[input] = GBA_NO_MAPPING;
378 }
379 TableEnumerate(&impl->axes, _unbindAxis, &input);
380}
381
382int GBAInputQueryBinding(const struct GBAInputMap* map, uint32_t type, enum GBAKey input) {
383 if (input >= GBA_KEY_MAX) {
384 return 0;
385 }
386
387 const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
388 if (!impl || !impl->map) {
389 return 0;
390 }
391
392 return impl->map[input];
393}
394
395enum GBAKey GBAInputMapAxis(const struct GBAInputMap* map, uint32_t type, int axis, int value) {
396 const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
397 if (!impl) {
398 return GBA_KEY_NONE;
399 }
400 struct GBAAxis* description = TableLookup(&impl->axes, axis);
401 if (!description) {
402 return GBA_KEY_NONE;
403 }
404 int state = 0;
405 if (value < description->deadLow) {
406 state = -1;
407 } else if (value > description->deadHigh) {
408 state = 1;
409 }
410 if (state > 0) {
411 return description->highDirection;
412 }
413 if (state < 0) {
414 return description->lowDirection;
415 }
416 return GBA_KEY_NONE;
417}
418
419int GBAInputClearAxis(const struct GBAInputMap* map, uint32_t type, int axis, int keys) {
420 const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
421 if (!impl) {
422 return keys;
423 }
424 struct GBAAxis* description = TableLookup(&impl->axes, axis);
425 if (!description) {
426 return keys;
427 }
428 return keys &= ~((1 << description->highDirection) | (1 << description->lowDirection));
429}
430
431void GBAInputBindAxis(struct GBAInputMap* map, uint32_t type, int axis, const struct GBAAxis* description) {
432 struct GBAInputMapImpl* impl = _guaranteeMap(map, type);
433 struct GBAAxis* dup = malloc(sizeof(struct GBAAxis));
434 GBAInputUnbindKey(map, type, description->lowDirection);
435 GBAInputUnbindKey(map, type, description->highDirection);
436 *dup = *description;
437 TableInsert(&impl->axes, axis, dup);
438}
439
440void GBAInputUnbindAxis(struct GBAInputMap* map, uint32_t type, int axis) {
441 struct GBAInputMapImpl* impl = _lookupMap(map, type);
442 if (impl) {
443 TableRemove(&impl->axes, axis);
444 }
445}
446
447void GBAInputUnbindAllAxes(struct GBAInputMap* map, uint32_t type) {
448 struct GBAInputMapImpl* impl = _lookupMap(map, type);
449 if (impl) {
450 TableClear(&impl->axes);
451 }
452}
453
454const struct GBAAxis* GBAInputQueryAxis(const struct GBAInputMap* map, uint32_t type, int axis) {
455 const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
456 if (!impl) {
457 return 0;
458 }
459 return TableLookup(&impl->axes, axis);
460}
461
462void GBAInputEnumerateAxes(const struct GBAInputMap* map, uint32_t type, void (handler(int axis, const struct GBAAxis* description, void* user)), void* user) {
463 const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
464 if (!impl) {
465 return;
466 }
467 struct GBAAxisEnumerate enumUser = {
468 handler,
469 user
470 };
471 TableEnumerate(&impl->axes, _enumerateAxis, &enumUser);
472}
473
474void GBAInputMapLoad(struct GBAInputMap* map, uint32_t type, const struct Configuration* config) {
475 char sectionName[SECTION_NAME_MAX];
476 _makeSectionName(sectionName, SECTION_NAME_MAX, type);
477 _loadAll(map, type, sectionName, config);
478}
479
480void GBAInputMapSave(const struct GBAInputMap* map, uint32_t type, struct Configuration* config) {
481 char sectionName[SECTION_NAME_MAX];
482 _makeSectionName(sectionName, SECTION_NAME_MAX, type);
483 _saveAll(map, type, sectionName, config);
484}
485
486void GBAInputProfileLoad(struct GBAInputMap* map, uint32_t type, const struct Configuration* config, const char* profile) {
487 char sectionName[SECTION_NAME_MAX];
488 snprintf(sectionName, SECTION_NAME_MAX, "input-profile.%s", profile);
489 sectionName[SECTION_NAME_MAX - 1] = '\0';
490 _loadAll(map, type, sectionName, config);
491}
492
493void GBAInputProfileSave(const struct GBAInputMap* map, uint32_t type, struct Configuration* config, const char* profile) {
494 char sectionName[SECTION_NAME_MAX];
495 snprintf(sectionName, SECTION_NAME_MAX, "input-profile.%s", profile);
496 sectionName[SECTION_NAME_MAX - 1] = '\0';
497 _saveAll(map, type, sectionName, config);
498}
499
500const char* GBAInputGetPreferredDevice(const struct Configuration* config, uint32_t type, int playerId) {
501 char sectionName[SECTION_NAME_MAX];
502 _makeSectionName(sectionName, SECTION_NAME_MAX, type);
503
504 char deviceId[KEY_NAME_MAX];
505 snprintf(deviceId, sizeof(deviceId), "device%i", playerId);
506 return ConfigurationGetValue(config, sectionName, deviceId);
507}
508
509void GBAInputSetPreferredDevice(struct Configuration* config, uint32_t type, int playerId, const char* deviceName) {
510 char sectionName[SECTION_NAME_MAX];
511 _makeSectionName(sectionName, SECTION_NAME_MAX, type);
512
513 char deviceId[KEY_NAME_MAX];
514 snprintf(deviceId, sizeof(deviceId), "device%i", playerId);
515 return ConfigurationSetValue(config, sectionName, deviceId, deviceName);
516}
517
518const char* GBAInputGetCustomValue(const struct Configuration* config, uint32_t type, const char* key, const char* profile) {
519 char sectionName[SECTION_NAME_MAX];
520 if (profile) {
521 snprintf(sectionName, SECTION_NAME_MAX, "input-profile.%s", profile);
522 const char* value = ConfigurationGetValue(config, sectionName, key);
523 if (value) {
524 return value;
525 }
526 }
527 _makeSectionName(sectionName, SECTION_NAME_MAX, type);
528 return ConfigurationGetValue(config, sectionName, key);
529}
530
531void GBAInputSetCustomValue(struct Configuration* config, uint32_t type, const char* key, const char* value, const char* profile) {
532 char sectionName[SECTION_NAME_MAX];
533 if (profile) {
534 snprintf(sectionName, SECTION_NAME_MAX, "input-profile.%s", profile);
535 ConfigurationSetValue(config, sectionName, key, value);
536 }
537 _makeSectionName(sectionName, SECTION_NAME_MAX, type);
538 ConfigurationSetValue(config, sectionName, key, value);
539}