all repos — mgba @ d4ef56cd16bf35a47dfcd26d52fbd0b1909c163c

mGBA Game Boy Advance Emulator

src/gba/input.c (view raw)

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