src/gba/gba-input.c (view raw)
1/* Copyright (c) 2013-2014 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 "gba-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 (impl) {
316 impl->map[input] = GBA_NO_MAPPING;
317 }
318 TableEnumerate(&impl->axes, _unbindAxis, &input);
319}
320
321int GBAInputQueryBinding(const struct GBAInputMap* map, uint32_t type, enum GBAKey input) {
322 if (input >= GBA_KEY_MAX) {
323 return 0;
324 }
325
326 const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
327 if (!impl || !impl->map) {
328 return 0;
329 }
330
331 return impl->map[input];
332}
333
334enum GBAKey GBAInputMapAxis(const struct GBAInputMap* map, uint32_t type, int axis, int value) {
335 const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
336 if (!impl) {
337 return GBA_KEY_NONE;
338 }
339 struct GBAAxis* description = TableLookup(&impl->axes, axis);
340 if (!description) {
341 return GBA_KEY_NONE;
342 }
343 int state = 0;
344 if (value < description->deadLow) {
345 state = -1;
346 } else if (value > description->deadHigh) {
347 state = 1;
348 }
349 if (state > 0) {
350 return description->highDirection;
351 }
352 if (state < 0) {
353 return description->lowDirection;
354 }
355 return GBA_KEY_NONE;
356}
357
358int GBAInputClearAxis(const struct GBAInputMap* map, uint32_t type, int axis, int keys) {
359 const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
360 if (!impl) {
361 return keys;
362 }
363 struct GBAAxis* description = TableLookup(&impl->axes, axis);
364 if (!description) {
365 return keys;
366 }
367 return keys &= ~((1 << description->highDirection) | (1 << description->lowDirection));
368}
369
370void GBAInputBindAxis(struct GBAInputMap* map, uint32_t type, int axis, const struct GBAAxis* description) {
371 struct GBAInputMapImpl* impl = _guaranteeMap(map, type);
372 struct GBAAxis* dup = malloc(sizeof(struct GBAAxis));
373 GBAInputUnbindKey(map, type, description->lowDirection);
374 GBAInputUnbindKey(map, type, description->highDirection);
375 *dup = *description;
376 TableInsert(&impl->axes, axis, dup);
377}
378
379void GBAInputUnbindAxis(struct GBAInputMap* map, uint32_t type, int axis) {
380 struct GBAInputMapImpl* impl = _lookupMap(map, type);
381 if (impl) {
382 TableRemove(&impl->axes, axis);
383 }
384}
385
386void GBAInputUnbindAllAxes(struct GBAInputMap* map, uint32_t type) {
387 struct GBAInputMapImpl* impl = _lookupMap(map, type);
388 if (impl) {
389 TableClear(&impl->axes);
390 }
391}
392
393const struct GBAAxis* GBAInputQueryAxis(const struct GBAInputMap* map, uint32_t type, int axis) {
394 const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
395 if (!impl) {
396 return 0;
397 }
398 return TableLookup(&impl->axes, axis);
399}
400
401void GBAInputEnumerateAxes(const struct GBAInputMap* map, uint32_t type, void (handler(int axis, const struct GBAAxis* description, void* user)), void* user) {
402 const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
403 if (!impl) {
404 return;
405 }
406 struct GBAAxisEnumerate enumUser = {
407 handler,
408 user
409 };
410 TableEnumerate(&impl->axes, _enumerateAxis, &enumUser);
411}
412
413void GBAInputMapLoad(struct GBAInputMap* map, uint32_t type, const struct Configuration* config) {
414 _loadKey(map, type, config, GBA_KEY_A, "A");
415 _loadKey(map, type, config, GBA_KEY_B, "B");
416 _loadKey(map, type, config, GBA_KEY_L, "L");
417 _loadKey(map, type, config, GBA_KEY_R, "R");
418 _loadKey(map, type, config, GBA_KEY_START, "Start");
419 _loadKey(map, type, config, GBA_KEY_SELECT, "Select");
420 _loadKey(map, type, config, GBA_KEY_UP, "Up");
421 _loadKey(map, type, config, GBA_KEY_DOWN, "Down");
422 _loadKey(map, type, config, GBA_KEY_LEFT, "Left");
423 _loadKey(map, type, config, GBA_KEY_RIGHT, "Right");
424
425 _loadAxis(map, type, config, GBA_KEY_A, "A");
426 _loadAxis(map, type, config, GBA_KEY_B, "B");
427 _loadAxis(map, type, config, GBA_KEY_L, "L");
428 _loadAxis(map, type, config, GBA_KEY_R, "R");
429 _loadAxis(map, type, config, GBA_KEY_START, "Start");
430 _loadAxis(map, type, config, GBA_KEY_SELECT, "Select");
431 _loadAxis(map, type, config, GBA_KEY_UP, "Up");
432 _loadAxis(map, type, config, GBA_KEY_DOWN, "Down");
433 _loadAxis(map, type, config, GBA_KEY_LEFT, "Left");
434 _loadAxis(map, type, config, GBA_KEY_RIGHT, "Right");
435}
436
437void GBAInputMapSave(const struct GBAInputMap* map, uint32_t type, struct Configuration* config) {
438 _saveKey(map, type, config, GBA_KEY_A, "A");
439 _saveKey(map, type, config, GBA_KEY_B, "B");
440 _saveKey(map, type, config, GBA_KEY_L, "L");
441 _saveKey(map, type, config, GBA_KEY_R, "R");
442 _saveKey(map, type, config, GBA_KEY_START, "Start");
443 _saveKey(map, type, config, GBA_KEY_SELECT, "Select");
444 _saveKey(map, type, config, GBA_KEY_UP, "Up");
445 _saveKey(map, type, config, GBA_KEY_DOWN, "Down");
446 _saveKey(map, type, config, GBA_KEY_LEFT, "Left");
447 _saveKey(map, type, config, GBA_KEY_RIGHT, "Right");
448
449 _clearAxis(type, config, "A");
450 _clearAxis(type, config, "B");
451 _clearAxis(type, config, "L");
452 _clearAxis(type, config, "R");
453 _clearAxis(type, config, "Start");
454 _clearAxis(type, config, "Select");
455 _clearAxis(type, config, "Up");
456 _clearAxis(type, config, "Down");
457 _clearAxis(type, config, "Left");
458 _clearAxis(type, config, "Right");
459
460 const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
461 if (!impl) {
462 return;
463 }
464 struct GBAAxisSave save = {
465 config,
466 type
467 };
468 TableEnumerate(&impl->axes, _saveAxis, &save);
469}