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