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