all repos — mgba @ 1b6389164c6b083ac1c0f25c34c439d763a1c82b

mGBA Game Boy Advance Emulator

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}