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 uint32_t type;
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 bool _getIntValue(const struct Configuration* config, const char* section, const char* key, int* value) {
49 const char* strValue = ConfigurationGetValue(config, section, key);
50 if (!strValue) {
51 return false;
52 }
53 char* end;
54 long intValue = strtol(strValue, &end, 10);
55 if (*end) {
56 return false;
57 }
58 *value = intValue;
59 return true;
60}
61
62static struct GBAInputMapImpl* _lookupMap(struct GBAInputMap* map, uint32_t type) {
63 size_t m;
64 struct GBAInputMapImpl* impl = 0;
65 for (m = 0; m < map->numMaps; ++m) {
66 if (map->maps[m].type == type) {
67 impl = &map->maps[m];
68 break;
69 }
70 }
71 return impl;
72}
73
74static const struct GBAInputMapImpl* _lookupMapConst(const struct GBAInputMap* map, uint32_t type) {
75 size_t m;
76 const struct GBAInputMapImpl* impl = 0;
77 for (m = 0; m < map->numMaps; ++m) {
78 if (map->maps[m].type == type) {
79 impl = &map->maps[m];
80 break;
81 }
82 }
83 return impl;
84}
85
86static struct GBAInputMapImpl* _guaranteeMap(struct GBAInputMap* map, uint32_t type) {
87 struct GBAInputMapImpl* impl = 0;
88 if (map->numMaps == 0) {
89 map->maps = malloc(sizeof(*map->maps));
90 map->numMaps = 1;
91 impl = &map->maps[0];
92 impl->type = type;
93 impl->map = calloc(GBA_KEY_MAX, sizeof(enum GBAKey));
94 TableInit(&impl->axes, 2, free);
95 } else {
96 impl = _lookupMap(map, type);
97 }
98 if (!impl) {
99 size_t m;
100 for (m = 0; m < map->numMaps; ++m) {
101 if (!map->maps[m].type) {
102 impl = &map->maps[m];
103 break;
104 }
105 }
106 if (impl) {
107 impl->type = type;
108 impl->map = calloc(GBA_KEY_MAX, sizeof(enum GBAKey));
109 } else {
110 map->maps = realloc(map->maps, sizeof(*map->maps) * map->numMaps * 2);
111 for (m = map->numMaps * 2 - 1; m > map->numMaps; --m) {
112 map->maps[m].type = 0;
113 map->maps[m].map = 0;
114 }
115 map->numMaps *= 2;
116 impl = &map->maps[m];
117 impl->type = type;
118 impl->map = calloc(GBA_KEY_MAX, sizeof(enum GBAKey));
119 }
120 TableInit(&impl->axes, 2, free);
121 }
122 return impl;
123}
124
125static void _loadKey(struct GBAInputMap* map, uint32_t type, const struct Configuration* config, enum GBAKey key, const char* keyName) {
126 char sectionName[SECTION_NAME_MAX];
127 snprintf(sectionName, SECTION_NAME_MAX, "input.%c%c%c%c", type >> 24, type >> 16, type >> 8, type);
128 sectionName[SECTION_NAME_MAX - 1] = '\0';
129
130 char keyKey[KEY_NAME_MAX];
131 snprintf(keyKey, KEY_NAME_MAX, "key%s", keyName);
132 keyKey[KEY_NAME_MAX - 1] = '\0';
133
134 int value;
135 if (!_getIntValue(config, sectionName, keyKey, &value)) {
136 return;
137 }
138 GBAInputBindKey(map, type, value, key);
139}
140
141static void _loadAxis(struct GBAInputMap* map, uint32_t type, const struct Configuration* config, enum GBAKey direction, const char* axisName) {
142 char sectionName[SECTION_NAME_MAX];
143 snprintf(sectionName, SECTION_NAME_MAX, "input.%c%c%c%c", type >> 24, type >> 16, type >> 8, type);
144 sectionName[SECTION_NAME_MAX - 1] = '\0';
145
146 char axisKey[KEY_NAME_MAX];
147 snprintf(axisKey, KEY_NAME_MAX, "axis%sValue", axisName);
148 axisKey[KEY_NAME_MAX - 1] = '\0';
149 int value;
150 if (!_getIntValue(config, sectionName, axisKey, &value)) {
151 return;
152 }
153
154 snprintf(axisKey, KEY_NAME_MAX, "axis%sAxis", axisName);
155 axisKey[KEY_NAME_MAX - 1] = '\0';
156 int axis;
157 const char* strValue = ConfigurationGetValue(config, sectionName, axisKey);
158 if (!strValue || !strValue[0]) {
159 return;
160 }
161 char* end;
162 axis = strtoul(&strValue[1], &end, 10);
163 if (*end) {
164 return;
165 }
166
167 const struct GBAAxis* description = GBAInputQueryAxis(map, type, axis);
168 struct GBAAxis realDescription = { GBA_KEY_NONE, GBA_KEY_NONE, 0, 0 };
169 if (description) {
170 realDescription = *description;
171 }
172 if (strValue[0] == '+') {
173 realDescription.deadHigh = value;
174 realDescription.highDirection = direction;
175 } else if (strValue[0] == '-') {
176 realDescription.deadLow = value;
177 realDescription.lowDirection = direction;
178 }
179 GBAInputBindAxis(map, type, axis, &realDescription);
180}
181
182static void _saveKey(const struct GBAInputMap* map, uint32_t type, struct Configuration* config, enum GBAKey key, const char* keyName) {
183 char sectionName[SECTION_NAME_MAX];
184 snprintf(sectionName, SECTION_NAME_MAX, "input.%c%c%c%c", type >> 24, type >> 16, type >> 8, type);
185 sectionName[SECTION_NAME_MAX - 1] = '\0';
186
187 char keyKey[KEY_NAME_MAX];
188 snprintf(keyKey, KEY_NAME_MAX, "key%s", keyName);
189 keyKey[KEY_NAME_MAX - 1] = '\0';
190
191 int value = GBAInputQueryBinding(map, type, key);
192 char keyValue[KEY_VALUE_MAX];
193 snprintf(keyValue, KEY_VALUE_MAX, "%" PRIi32, value);
194
195 ConfigurationSetValue(config, sectionName, keyKey, keyValue);
196}
197
198static void _clearAxis(uint32_t type, struct Configuration* config, const char* axisName) {
199 char sectionName[SECTION_NAME_MAX];
200 snprintf(sectionName, SECTION_NAME_MAX, "input.%c%c%c%c", type >> 24, type >> 16, type >> 8, type);
201 sectionName[SECTION_NAME_MAX - 1] = '\0';
202
203 char axisKey[KEY_NAME_MAX];
204 snprintf(axisKey, KEY_NAME_MAX, "axis%sValue", axisName);
205 axisKey[KEY_NAME_MAX - 1] = '\0';
206 ConfigurationClearValue(config, sectionName, axisKey);
207
208 snprintf(axisKey, KEY_NAME_MAX, "axis%sAxis", axisName);
209 axisKey[KEY_NAME_MAX - 1] = '\0';
210 ConfigurationClearValue(config, sectionName, axisKey);
211}
212
213static void _saveAxis(uint32_t axis, void* dp, void* up) {
214 struct GBAAxisSave* user = up;
215 const struct GBAAxis* description = dp;
216
217 uint32_t type = user->type;
218 char sectionName[SECTION_NAME_MAX];
219 snprintf(sectionName, SECTION_NAME_MAX, "input.%c%c%c%c", type >> 24, type >> 16, type >> 8, type);
220 sectionName[SECTION_NAME_MAX - 1] = '\0';
221
222 if (description->lowDirection != GBA_KEY_NONE) {
223 const char* keyName = GBAKeyNames[description->lowDirection];
224
225 char axisKey[KEY_NAME_MAX];
226 snprintf(axisKey, KEY_NAME_MAX, "axis%sValue", keyName);
227 axisKey[KEY_NAME_MAX - 1] = '\0';
228 ConfigurationSetIntValue(user->config, sectionName, axisKey, description->deadLow);
229
230 snprintf(axisKey, KEY_NAME_MAX, "axis%sAxis", keyName);
231 axisKey[KEY_NAME_MAX - 1] = '\0';
232
233 char axisInfo[AXIS_INFO_MAX];
234 snprintf(axisInfo, AXIS_INFO_MAX, "-%u", axis);
235 axisInfo[AXIS_INFO_MAX - 1] = '\0';
236 ConfigurationSetValue(user->config, sectionName, axisKey, axisInfo);
237 }
238 if (description->highDirection != GBA_KEY_NONE) {
239 const char* keyName = GBAKeyNames[description->highDirection];
240
241 char axisKey[KEY_NAME_MAX];
242 snprintf(axisKey, KEY_NAME_MAX, "axis%sValue", keyName);
243 axisKey[KEY_NAME_MAX - 1] = '\0';
244 ConfigurationSetIntValue(user->config, sectionName, axisKey, description->deadHigh);
245
246 snprintf(axisKey, KEY_NAME_MAX, "axis%sAxis", keyName);
247 axisKey[KEY_NAME_MAX - 1] = '\0';
248
249 char axisInfo[AXIS_INFO_MAX];
250 snprintf(axisInfo, AXIS_INFO_MAX, "+%u", axis);
251 axisInfo[AXIS_INFO_MAX - 1] = '\0';
252 ConfigurationSetValue(user->config, sectionName, axisKey, axisInfo);
253 }
254}
255
256void _enumerateAxis(uint32_t axis, void* dp, void* ep) {
257 struct GBAAxisEnumerate* enumUser = ep;
258 const struct GBAAxis* description = dp;
259 enumUser->handler(axis, description, enumUser->user);
260}
261
262void _unbindAxis(uint32_t axis, void* dp, void* user) {
263 UNUSED(axis);
264 enum GBAKey* key = user;
265 struct GBAAxis* description = dp;
266 if (description->highDirection == *key) {
267 description->highDirection = GBA_KEY_NONE;
268 }
269 if (description->lowDirection == *key) {
270 description->lowDirection = GBA_KEY_NONE;
271 }
272}
273
274void GBAInputMapInit(struct GBAInputMap* map) {
275 map->maps = 0;
276 map->numMaps = 0;
277}
278
279void GBAInputMapDeinit(struct GBAInputMap* map) {
280 size_t m;
281 for (m = 0; m < map->numMaps; ++m) {
282 if (map->maps[m].type) {
283 free(map->maps[m].map);
284 TableDeinit(&map->maps[m].axes);
285 }
286 }
287 free(map->maps);
288 map->maps = 0;
289 map->numMaps = 0;
290}
291
292enum GBAKey GBAInputMapKey(const struct GBAInputMap* map, uint32_t type, int key) {
293 size_t m;
294 const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
295 if (!impl || !impl->map) {
296 return GBA_KEY_NONE;
297 }
298
299 for (m = 0; m < GBA_KEY_MAX; ++m) {
300 if (impl->map[m] == key) {
301 return m;
302 }
303 }
304 return GBA_KEY_NONE;
305}
306
307void GBAInputBindKey(struct GBAInputMap* map, uint32_t type, int key, enum GBAKey input) {
308 struct GBAInputMapImpl* impl = _guaranteeMap(map, type);
309 GBAInputUnbindKey(map, type, input);
310 impl->map[input] = key;
311}
312
313void GBAInputUnbindKey(struct GBAInputMap* map, uint32_t type, enum GBAKey input) {
314 struct GBAInputMapImpl* impl = _lookupMap(map, type);
315 if (input < 0 || input >= GBA_KEY_MAX) {
316 return;
317 }
318 if (impl) {
319 impl->map[input] = GBA_NO_MAPPING;
320 }
321 TableEnumerate(&impl->axes, _unbindAxis, &input);
322}
323
324int GBAInputQueryBinding(const struct GBAInputMap* map, uint32_t type, enum GBAKey input) {
325 if (input >= GBA_KEY_MAX) {
326 return 0;
327 }
328
329 const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
330 if (!impl || !impl->map) {
331 return 0;
332 }
333
334 return impl->map[input];
335}
336
337enum GBAKey GBAInputMapAxis(const struct GBAInputMap* map, uint32_t type, int axis, int value) {
338 const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
339 if (!impl) {
340 return GBA_KEY_NONE;
341 }
342 struct GBAAxis* description = TableLookup(&impl->axes, axis);
343 if (!description) {
344 return GBA_KEY_NONE;
345 }
346 int state = 0;
347 if (value < description->deadLow) {
348 state = -1;
349 } else if (value > description->deadHigh) {
350 state = 1;
351 }
352 if (state > 0) {
353 return description->highDirection;
354 }
355 if (state < 0) {
356 return description->lowDirection;
357 }
358 return GBA_KEY_NONE;
359}
360
361int GBAInputClearAxis(const struct GBAInputMap* map, uint32_t type, int axis, int keys) {
362 const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
363 if (!impl) {
364 return keys;
365 }
366 struct GBAAxis* description = TableLookup(&impl->axes, axis);
367 if (!description) {
368 return keys;
369 }
370 return keys &= ~((1 << description->highDirection) | (1 << description->lowDirection));
371}
372
373void GBAInputBindAxis(struct GBAInputMap* map, uint32_t type, int axis, const struct GBAAxis* description) {
374 struct GBAInputMapImpl* impl = _guaranteeMap(map, type);
375 struct GBAAxis* dup = malloc(sizeof(struct GBAAxis));
376 GBAInputUnbindKey(map, type, description->lowDirection);
377 GBAInputUnbindKey(map, type, description->highDirection);
378 *dup = *description;
379 TableInsert(&impl->axes, axis, dup);
380}
381
382void GBAInputUnbindAxis(struct GBAInputMap* map, uint32_t type, int axis) {
383 struct GBAInputMapImpl* impl = _lookupMap(map, type);
384 if (impl) {
385 TableRemove(&impl->axes, axis);
386 }
387}
388
389void GBAInputUnbindAllAxes(struct GBAInputMap* map, uint32_t type) {
390 struct GBAInputMapImpl* impl = _lookupMap(map, type);
391 if (impl) {
392 TableClear(&impl->axes);
393 }
394}
395
396const struct GBAAxis* GBAInputQueryAxis(const struct GBAInputMap* map, uint32_t type, int axis) {
397 const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
398 if (!impl) {
399 return 0;
400 }
401 return TableLookup(&impl->axes, axis);
402}
403
404void GBAInputEnumerateAxes(const struct GBAInputMap* map, uint32_t type, void (handler(int axis, const struct GBAAxis* description, void* user)), void* user) {
405 const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
406 if (!impl) {
407 return;
408 }
409 struct GBAAxisEnumerate enumUser = {
410 handler,
411 user
412 };
413 TableEnumerate(&impl->axes, _enumerateAxis, &enumUser);
414}
415
416void GBAInputMapLoad(struct GBAInputMap* map, uint32_t type, const struct Configuration* config) {
417 _loadKey(map, type, config, GBA_KEY_A, "A");
418 _loadKey(map, type, config, GBA_KEY_B, "B");
419 _loadKey(map, type, config, GBA_KEY_L, "L");
420 _loadKey(map, type, config, GBA_KEY_R, "R");
421 _loadKey(map, type, config, GBA_KEY_START, "Start");
422 _loadKey(map, type, config, GBA_KEY_SELECT, "Select");
423 _loadKey(map, type, config, GBA_KEY_UP, "Up");
424 _loadKey(map, type, config, GBA_KEY_DOWN, "Down");
425 _loadKey(map, type, config, GBA_KEY_LEFT, "Left");
426 _loadKey(map, type, config, GBA_KEY_RIGHT, "Right");
427
428 _loadAxis(map, type, config, GBA_KEY_A, "A");
429 _loadAxis(map, type, config, GBA_KEY_B, "B");
430 _loadAxis(map, type, config, GBA_KEY_L, "L");
431 _loadAxis(map, type, config, GBA_KEY_R, "R");
432 _loadAxis(map, type, config, GBA_KEY_START, "Start");
433 _loadAxis(map, type, config, GBA_KEY_SELECT, "Select");
434 _loadAxis(map, type, config, GBA_KEY_UP, "Up");
435 _loadAxis(map, type, config, GBA_KEY_DOWN, "Down");
436 _loadAxis(map, type, config, GBA_KEY_LEFT, "Left");
437 _loadAxis(map, type, config, GBA_KEY_RIGHT, "Right");
438}
439
440void GBAInputMapSave(const struct GBAInputMap* map, uint32_t type, struct Configuration* config) {
441 _saveKey(map, type, config, GBA_KEY_A, "A");
442 _saveKey(map, type, config, GBA_KEY_B, "B");
443 _saveKey(map, type, config, GBA_KEY_L, "L");
444 _saveKey(map, type, config, GBA_KEY_R, "R");
445 _saveKey(map, type, config, GBA_KEY_START, "Start");
446 _saveKey(map, type, config, GBA_KEY_SELECT, "Select");
447 _saveKey(map, type, config, GBA_KEY_UP, "Up");
448 _saveKey(map, type, config, GBA_KEY_DOWN, "Down");
449 _saveKey(map, type, config, GBA_KEY_LEFT, "Left");
450 _saveKey(map, type, config, GBA_KEY_RIGHT, "Right");
451
452 _clearAxis(type, config, "A");
453 _clearAxis(type, config, "B");
454 _clearAxis(type, config, "L");
455 _clearAxis(type, config, "R");
456 _clearAxis(type, config, "Start");
457 _clearAxis(type, config, "Select");
458 _clearAxis(type, config, "Up");
459 _clearAxis(type, config, "Down");
460 _clearAxis(type, config, "Left");
461 _clearAxis(type, config, "Right");
462
463 const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
464 if (!impl) {
465 return;
466 }
467 struct GBAAxisSave save = {
468 config,
469 type
470 };
471 TableEnumerate(&impl->axes, _saveAxis, &save);
472}