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
355void GBAInputBindKey(struct GBAInputMap* map, uint32_t type, int key, enum GBAKey input) {
356 struct GBAInputMapImpl* impl = _guaranteeMap(map, type);
357 GBAInputUnbindKey(map, type, input);
358 impl->map[input] = key;
359}
360
361void GBAInputUnbindKey(struct GBAInputMap* map, uint32_t type, enum GBAKey input) {
362 struct GBAInputMapImpl* impl = _lookupMap(map, type);
363 if (input < 0 || input >= GBA_KEY_MAX) {
364 return;
365 }
366 if (impl) {
367 impl->map[input] = GBA_NO_MAPPING;
368 }
369}
370
371int GBAInputQueryBinding(const struct GBAInputMap* map, uint32_t type, enum GBAKey input) {
372 if (input >= GBA_KEY_MAX) {
373 return 0;
374 }
375
376 const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
377 if (!impl || !impl->map) {
378 return 0;
379 }
380
381 return impl->map[input];
382}
383
384enum GBAKey GBAInputMapAxis(const struct GBAInputMap* map, uint32_t type, int axis, int value) {
385 const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
386 if (!impl) {
387 return GBA_KEY_NONE;
388 }
389 struct GBAAxis* description = TableLookup(&impl->axes, axis);
390 if (!description) {
391 return GBA_KEY_NONE;
392 }
393 int state = 0;
394 if (value < description->deadLow) {
395 state = -1;
396 } else if (value > description->deadHigh) {
397 state = 1;
398 }
399 if (state > 0) {
400 return description->highDirection;
401 }
402 if (state < 0) {
403 return description->lowDirection;
404 }
405 return GBA_KEY_NONE;
406}
407
408int GBAInputClearAxis(const struct GBAInputMap* map, uint32_t type, int axis, int keys) {
409 const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
410 if (!impl) {
411 return keys;
412 }
413 struct GBAAxis* description = TableLookup(&impl->axes, axis);
414 if (!description) {
415 return keys;
416 }
417 return keys &= ~((1 << description->highDirection) | (1 << description->lowDirection));
418}
419
420void GBAInputBindAxis(struct GBAInputMap* map, uint32_t type, int axis, const struct GBAAxis* description) {
421 struct GBAInputMapImpl* impl = _guaranteeMap(map, type);
422 TableEnumerate(&impl->axes, _unbindAxis, &description->highDirection);
423 TableEnumerate(&impl->axes, _unbindAxis, &description->lowDirection);
424 struct GBAAxis* dup = malloc(sizeof(struct GBAAxis));
425 *dup = *description;
426 TableInsert(&impl->axes, axis, dup);
427}
428
429void GBAInputUnbindAxis(struct GBAInputMap* map, uint32_t type, int axis) {
430 struct GBAInputMapImpl* impl = _lookupMap(map, type);
431 if (impl) {
432 TableRemove(&impl->axes, axis);
433 }
434}
435
436void GBAInputUnbindAllAxes(struct GBAInputMap* map, uint32_t type) {
437 struct GBAInputMapImpl* impl = _lookupMap(map, type);
438 if (impl) {
439 TableClear(&impl->axes);
440 }
441}
442
443const struct GBAAxis* GBAInputQueryAxis(const struct GBAInputMap* map, uint32_t type, int axis) {
444 const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
445 if (!impl) {
446 return 0;
447 }
448 return TableLookup(&impl->axes, axis);
449}
450
451void GBAInputEnumerateAxes(const struct GBAInputMap* map, uint32_t type, void (handler(int axis, const struct GBAAxis* description, void* user)), void* user) {
452 const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
453 if (!impl) {
454 return;
455 }
456 struct GBAAxisEnumerate enumUser = {
457 handler,
458 user
459 };
460 TableEnumerate(&impl->axes, _enumerateAxis, &enumUser);
461}
462
463void GBAInputMapLoad(struct GBAInputMap* map, uint32_t type, const struct Configuration* config) {
464 char sectionName[SECTION_NAME_MAX];
465 _makeSectionName(sectionName, SECTION_NAME_MAX, type);
466 _loadAll(map, type, sectionName, config);
467}
468
469void GBAInputMapSave(const struct GBAInputMap* map, uint32_t type, struct Configuration* config) {
470 char sectionName[SECTION_NAME_MAX];
471 _makeSectionName(sectionName, SECTION_NAME_MAX, type);
472 _saveAll(map, type, sectionName, config);
473}
474
475bool GBAInputProfileLoad(struct GBAInputMap* map, uint32_t type, const struct Configuration* config, const char* profile) {
476 char sectionName[SECTION_NAME_MAX];
477 snprintf(sectionName, SECTION_NAME_MAX, "input-profile.%s", profile);
478 sectionName[SECTION_NAME_MAX - 1] = '\0';
479 return _loadAll(map, type, sectionName, config);
480}
481
482void GBAInputProfileSave(const struct GBAInputMap* map, uint32_t type, struct Configuration* config, const char* profile) {
483 char sectionName[SECTION_NAME_MAX];
484 snprintf(sectionName, SECTION_NAME_MAX, "input-profile.%s", profile);
485 sectionName[SECTION_NAME_MAX - 1] = '\0';
486 _saveAll(map, type, sectionName, config);
487}
488
489const char* GBAInputGetPreferredDevice(const struct Configuration* config, uint32_t type, int playerId) {
490 char sectionName[SECTION_NAME_MAX];
491 _makeSectionName(sectionName, SECTION_NAME_MAX, type);
492
493 char deviceId[KEY_NAME_MAX];
494 snprintf(deviceId, sizeof(deviceId), "device%i", playerId);
495 return ConfigurationGetValue(config, sectionName, deviceId);
496}
497
498void GBAInputSetPreferredDevice(struct Configuration* config, uint32_t type, int playerId, const char* deviceName) {
499 char sectionName[SECTION_NAME_MAX];
500 _makeSectionName(sectionName, SECTION_NAME_MAX, type);
501
502 char deviceId[KEY_NAME_MAX];
503 snprintf(deviceId, sizeof(deviceId), "device%i", playerId);
504 return ConfigurationSetValue(config, sectionName, deviceId, deviceName);
505}
506
507const char* GBAInputGetCustomValue(const struct Configuration* config, uint32_t type, const char* key, const char* profile) {
508 char sectionName[SECTION_NAME_MAX];
509 if (profile) {
510 snprintf(sectionName, SECTION_NAME_MAX, "input-profile.%s", profile);
511 const char* value = ConfigurationGetValue(config, sectionName, key);
512 if (value) {
513 return value;
514 }
515 }
516 _makeSectionName(sectionName, SECTION_NAME_MAX, type);
517 return ConfigurationGetValue(config, sectionName, key);
518}
519
520void GBAInputSetCustomValue(struct Configuration* config, uint32_t type, const char* key, const char* value, const char* profile) {
521 char sectionName[SECTION_NAME_MAX];
522 if (profile) {
523 snprintf(sectionName, SECTION_NAME_MAX, "input-profile.%s", profile);
524 ConfigurationSetValue(config, sectionName, key, value);
525 }
526 _makeSectionName(sectionName, SECTION_NAME_MAX, type);
527 ConfigurationSetValue(config, sectionName, key, value);
528}