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 _saveAxis(uint32_t axis, void* dp, void* up) {
199 struct GBAAxisSave* user = up;
200 const struct GBAAxis* description = dp;
201
202 uint32_t type = user->type;
203 char sectionName[SECTION_NAME_MAX];
204 snprintf(sectionName, SECTION_NAME_MAX, "input.%c%c%c%c", type >> 24, type >> 16, type >> 8, type);
205 sectionName[SECTION_NAME_MAX - 1] = '\0';
206
207 if (description->lowDirection != GBA_KEY_NONE) {
208 const char* keyName = GBAKeyNames[description->lowDirection];
209
210 char axisKey[KEY_NAME_MAX];
211 snprintf(axisKey, KEY_NAME_MAX, "axis%sValue", keyName);
212 axisKey[KEY_NAME_MAX - 1] = '\0';
213 ConfigurationSetIntValue(user->config, sectionName, axisKey, description->deadLow);
214
215 snprintf(axisKey, KEY_NAME_MAX, "axis%sAxis", keyName);
216 axisKey[KEY_NAME_MAX - 1] = '\0';
217
218 char axisInfo[AXIS_INFO_MAX];
219 snprintf(axisInfo, AXIS_INFO_MAX, "-%u", axis);
220 axisInfo[AXIS_INFO_MAX - 1] = '\0';
221 ConfigurationSetValue(user->config, sectionName, axisKey, axisInfo);
222 }
223 if (description->highDirection != GBA_KEY_NONE) {
224 const char* keyName = GBAKeyNames[description->highDirection];
225
226 char axisKey[KEY_NAME_MAX];
227 snprintf(axisKey, KEY_NAME_MAX, "axis%sValue", keyName);
228 axisKey[KEY_NAME_MAX - 1] = '\0';
229 ConfigurationSetIntValue(user->config, sectionName, axisKey, description->deadHigh);
230
231 snprintf(axisKey, KEY_NAME_MAX, "axis%sAxis", keyName);
232 axisKey[KEY_NAME_MAX - 1] = '\0';
233
234 char axisInfo[AXIS_INFO_MAX];
235 snprintf(axisInfo, AXIS_INFO_MAX, "+%u", axis);
236 axisInfo[AXIS_INFO_MAX - 1] = '\0';
237 ConfigurationSetValue(user->config, sectionName, axisKey, axisInfo);
238 }
239}
240
241void _enumerateAxis(uint32_t axis, void* dp, void* ep) {
242 struct GBAAxisEnumerate* enumUser = ep;
243 const struct GBAAxis* description = dp;
244 enumUser->handler(axis, description, enumUser->user);
245}
246
247void GBAInputMapInit(struct GBAInputMap* map) {
248 map->maps = 0;
249 map->numMaps = 0;
250}
251
252void GBAInputMapDeinit(struct GBAInputMap* map) {
253 size_t m;
254 for (m = 0; m < map->numMaps; ++m) {
255 if (map->maps[m].type) {
256 free(map->maps[m].map);
257 TableDeinit(&map->maps[m].axes);
258 }
259 }
260 free(map->maps);
261 map->maps = 0;
262 map->numMaps = 0;
263}
264
265enum GBAKey GBAInputMapKey(const struct GBAInputMap* map, uint32_t type, int key) {
266 size_t m;
267 const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
268 if (!impl || !impl->map) {
269 return GBA_KEY_NONE;
270 }
271
272 for (m = 0; m < GBA_KEY_MAX; ++m) {
273 if (impl->map[m] == key) {
274 return m;
275 }
276 }
277 return GBA_KEY_NONE;
278}
279
280void GBAInputBindKey(struct GBAInputMap* map, uint32_t type, int key, enum GBAKey input) {
281 struct GBAInputMapImpl* impl = _guaranteeMap(map, type);
282 impl->map[input] = key;
283}
284
285void GBAInputUnbindKey(struct GBAInputMap* map, uint32_t type, enum GBAKey input) {
286 struct GBAInputMapImpl* impl = _lookupMap(map, type);
287 if (impl) {
288 impl->map[input] = GBA_NO_MAPPING;
289 }
290}
291
292int GBAInputQueryBinding(const struct GBAInputMap* map, uint32_t type, enum GBAKey input) {
293 if (input >= GBA_KEY_MAX) {
294 return 0;
295 }
296
297 const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
298 if (!impl || !impl->map) {
299 return 0;
300 }
301
302 return impl->map[input];
303}
304
305enum GBAKey GBAInputMapAxis(const struct GBAInputMap* map, uint32_t type, int axis, int value) {
306 const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
307 if (!impl) {
308 return GBA_KEY_NONE;
309 }
310 struct GBAAxis* description = TableLookup(&impl->axes, axis);
311 if (!description) {
312 return GBA_KEY_NONE;
313 }
314 int state = 0;
315 if (value < description->deadLow) {
316 state = -1;
317 } else if (value > description->deadHigh) {
318 state = 1;
319 }
320 if (state > 0) {
321 return description->highDirection;
322 }
323 if (state < 0) {
324 return description->lowDirection;
325 }
326 return GBA_KEY_NONE;
327}
328
329int GBAInputClearAxis(const struct GBAInputMap* map, uint32_t type, int axis, int keys) {
330 const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
331 if (!impl) {
332 return keys;
333 }
334 struct GBAAxis* description = TableLookup(&impl->axes, axis);
335 if (!description) {
336 return keys;
337 }
338 return keys &= ~((1 << description->highDirection) | (1 << description->lowDirection));
339}
340
341void GBAInputBindAxis(struct GBAInputMap* map, uint32_t type, int axis, const struct GBAAxis* description) {
342 struct GBAInputMapImpl* impl = _guaranteeMap(map, type);
343 struct GBAAxis* dup = malloc(sizeof(struct GBAAxis));
344 *dup = *description;
345 TableInsert(&impl->axes, axis, dup);
346}
347
348void GBAInputUnbindAxis(struct GBAInputMap* map, uint32_t type, int axis) {
349 struct GBAInputMapImpl* impl = _lookupMap(map, type);
350 if (impl) {
351 TableRemove(&impl->axes, axis);
352 }
353}
354
355void GBAInputUnbindAllAxes(struct GBAInputMap* map, uint32_t type) {
356 struct GBAInputMapImpl* impl = _lookupMap(map, type);
357 if (impl) {
358 TableClear(&impl->axes);
359 }
360}
361
362const struct GBAAxis* GBAInputQueryAxis(const struct GBAInputMap* map, uint32_t type, int axis) {
363 const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
364 if (!impl) {
365 return 0;
366 }
367 return TableLookup(&impl->axes, axis);
368}
369
370void GBAInputEnumerateAxes(const struct GBAInputMap* map, uint32_t type, void (handler(int axis, const struct GBAAxis* description, void* user)), void* user) {
371 const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
372 if (!impl) {
373 return;
374 }
375 struct GBAAxisEnumerate enumUser = {
376 handler,
377 user
378 };
379 TableEnumerate(&impl->axes, _enumerateAxis, &enumUser);
380}
381
382void GBAInputMapLoad(struct GBAInputMap* map, uint32_t type, const struct Configuration* config) {
383 _loadKey(map, type, config, GBA_KEY_A, "A");
384 _loadKey(map, type, config, GBA_KEY_B, "B");
385 _loadKey(map, type, config, GBA_KEY_L, "L");
386 _loadKey(map, type, config, GBA_KEY_R, "R");
387 _loadKey(map, type, config, GBA_KEY_START, "Start");
388 _loadKey(map, type, config, GBA_KEY_SELECT, "Select");
389 _loadKey(map, type, config, GBA_KEY_UP, "Up");
390 _loadKey(map, type, config, GBA_KEY_DOWN, "Down");
391 _loadKey(map, type, config, GBA_KEY_LEFT, "Left");
392 _loadKey(map, type, config, GBA_KEY_RIGHT, "Right");
393
394 _loadAxis(map, type, config, GBA_KEY_UP, "Up");
395 _loadAxis(map, type, config, GBA_KEY_DOWN, "Down");
396 _loadAxis(map, type, config, GBA_KEY_LEFT, "Left");
397 _loadAxis(map, type, config, GBA_KEY_RIGHT, "Right");
398}
399
400void GBAInputMapSave(const struct GBAInputMap* map, uint32_t type, struct Configuration* config) {
401 _saveKey(map, type, config, GBA_KEY_A, "A");
402 _saveKey(map, type, config, GBA_KEY_B, "B");
403 _saveKey(map, type, config, GBA_KEY_L, "L");
404 _saveKey(map, type, config, GBA_KEY_R, "R");
405 _saveKey(map, type, config, GBA_KEY_START, "Start");
406 _saveKey(map, type, config, GBA_KEY_SELECT, "Select");
407 _saveKey(map, type, config, GBA_KEY_UP, "Up");
408 _saveKey(map, type, config, GBA_KEY_DOWN, "Down");
409 _saveKey(map, type, config, GBA_KEY_LEFT, "Left");
410 _saveKey(map, type, config, GBA_KEY_RIGHT, "Right");
411
412 const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
413 if (!impl) {
414 return;
415 }
416 struct GBAAxisSave save = {
417 config,
418 type
419 };
420 TableEnumerate(&impl->axes, _saveAxis, &save);
421}