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