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