all repos — mgba @ 33b66e5d44fb54f81acd6f2f5a6d84b805360057

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(int));
 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(int));
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(int));
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
351int GBAInputMapKeyBits(const struct GBAInputMap* map, uint32_t type, uint32_t bits, unsigned offset) {
352	int keys = 0;
353	for (; bits; bits >>= 1, ++offset) {
354		if (bits & 1) {
355			enum GBAKey key = GBAInputMapKey(map, type, offset);
356			if (key == GBA_KEY_NONE) {
357				continue;
358			}
359			keys |= 1 << key;
360		}
361	}
362	return keys;
363}
364
365void GBAInputBindKey(struct GBAInputMap* map, uint32_t type, int key, enum GBAKey input) {
366	struct GBAInputMapImpl* impl = _guaranteeMap(map, type);
367	GBAInputUnbindKey(map, type, input);
368	impl->map[input] = key;
369}
370
371void GBAInputUnbindKey(struct GBAInputMap* map, uint32_t type, enum GBAKey input) {
372	struct GBAInputMapImpl* impl = _lookupMap(map, type);
373	if (input < 0 || input >= GBA_KEY_MAX) {
374		return;
375	}
376	if (impl) {
377		impl->map[input] = GBA_NO_MAPPING;
378	}
379	TableEnumerate(&impl->axes, _unbindAxis, &input);
380}
381
382int GBAInputQueryBinding(const struct GBAInputMap* map, uint32_t type, enum GBAKey input) {
383	if (input >= GBA_KEY_MAX) {
384		return 0;
385	}
386
387	const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
388	if (!impl || !impl->map) {
389		return 0;
390	}
391
392	return impl->map[input];
393}
394
395enum GBAKey GBAInputMapAxis(const struct GBAInputMap* map, uint32_t type, int axis, int value) {
396	const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
397	if (!impl) {
398		return GBA_KEY_NONE;
399	}
400	struct GBAAxis* description = TableLookup(&impl->axes, axis);
401	if (!description) {
402		return GBA_KEY_NONE;
403	}
404	int state = 0;
405	if (value < description->deadLow) {
406		state = -1;
407	} else if (value > description->deadHigh) {
408		state = 1;
409	}
410	if (state > 0) {
411		return description->highDirection;
412	}
413	if (state < 0) {
414		return description->lowDirection;
415	}
416	return GBA_KEY_NONE;
417}
418
419int GBAInputClearAxis(const struct GBAInputMap* map, uint32_t type, int axis, int keys) {
420	const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
421	if (!impl) {
422		return keys;
423	}
424	struct GBAAxis* description = TableLookup(&impl->axes, axis);
425	if (!description) {
426		return keys;
427	}
428	return keys &= ~((1 << description->highDirection) | (1 << description->lowDirection));
429}
430
431void GBAInputBindAxis(struct GBAInputMap* map, uint32_t type, int axis, const struct GBAAxis* description) {
432	struct GBAInputMapImpl* impl = _guaranteeMap(map, type);
433	struct GBAAxis* dup = malloc(sizeof(struct GBAAxis));
434	GBAInputUnbindKey(map, type, description->lowDirection);
435	GBAInputUnbindKey(map, type, description->highDirection);
436	*dup = *description;
437	TableInsert(&impl->axes, axis, dup);
438}
439
440void GBAInputUnbindAxis(struct GBAInputMap* map, uint32_t type, int axis) {
441	struct GBAInputMapImpl* impl = _lookupMap(map, type);
442	if (impl) {
443		TableRemove(&impl->axes, axis);
444	}
445}
446
447void GBAInputUnbindAllAxes(struct GBAInputMap* map, uint32_t type) {
448	struct GBAInputMapImpl* impl = _lookupMap(map, type);
449	if (impl) {
450		TableClear(&impl->axes);
451	}
452}
453
454const struct GBAAxis* GBAInputQueryAxis(const struct GBAInputMap* map, uint32_t type, int axis) {
455	const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
456	if (!impl) {
457		return 0;
458	}
459	return TableLookup(&impl->axes, axis);
460}
461
462void GBAInputEnumerateAxes(const struct GBAInputMap* map, uint32_t type, void (handler(int axis, const struct GBAAxis* description, void* user)), void* user) {
463	const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
464	if (!impl) {
465		return;
466	}
467	struct GBAAxisEnumerate enumUser = {
468		handler,
469		user
470	};
471	TableEnumerate(&impl->axes, _enumerateAxis, &enumUser);
472}
473
474void GBAInputMapLoad(struct GBAInputMap* map, uint32_t type, const struct Configuration* config) {
475	char sectionName[SECTION_NAME_MAX];
476	_makeSectionName(sectionName, SECTION_NAME_MAX, type);
477	_loadAll(map, type, sectionName, config);
478}
479
480void GBAInputMapSave(const struct GBAInputMap* map, uint32_t type, struct Configuration* config) {
481	char sectionName[SECTION_NAME_MAX];
482	_makeSectionName(sectionName, SECTION_NAME_MAX, type);
483	_saveAll(map, type, sectionName, config);
484}
485
486void GBAInputProfileLoad(struct GBAInputMap* map, uint32_t type, const struct Configuration* config, const char* profile) {
487	char sectionName[SECTION_NAME_MAX];
488	snprintf(sectionName, SECTION_NAME_MAX, "input-profile.%s", profile);
489	sectionName[SECTION_NAME_MAX - 1] = '\0';
490	_loadAll(map, type, sectionName, config);
491}
492
493void GBAInputProfileSave(const struct GBAInputMap* map, uint32_t type, struct Configuration* config, const char* profile) {
494	char sectionName[SECTION_NAME_MAX];
495	snprintf(sectionName, SECTION_NAME_MAX, "input-profile.%s", profile);
496	sectionName[SECTION_NAME_MAX - 1] = '\0';
497	_saveAll(map, type, sectionName, config);
498}
499
500const char* GBAInputGetPreferredDevice(const struct Configuration* config, uint32_t type, int playerId) {
501	char sectionName[SECTION_NAME_MAX];
502	_makeSectionName(sectionName, SECTION_NAME_MAX, type);
503
504	char deviceId[KEY_NAME_MAX];
505	snprintf(deviceId, sizeof(deviceId), "device%i", playerId);
506	return ConfigurationGetValue(config, sectionName, deviceId);
507}
508
509void GBAInputSetPreferredDevice(struct Configuration* config, uint32_t type, int playerId, const char* deviceName) {
510	char sectionName[SECTION_NAME_MAX];
511	_makeSectionName(sectionName, SECTION_NAME_MAX, type);
512
513	char deviceId[KEY_NAME_MAX];
514	snprintf(deviceId, sizeof(deviceId), "device%i", playerId);
515	return ConfigurationSetValue(config, sectionName, deviceId, deviceName);
516}
517
518const char* GBAInputGetCustomValue(const struct Configuration* config, uint32_t type, const char* key, const char* profile) {
519	char sectionName[SECTION_NAME_MAX];
520	if (profile) {
521		snprintf(sectionName, SECTION_NAME_MAX, "input-profile.%s", profile);
522		const char* value = ConfigurationGetValue(config, sectionName, key);
523		if (value) {
524			return value;
525		}
526	}
527	_makeSectionName(sectionName, SECTION_NAME_MAX, type);
528	return ConfigurationGetValue(config, sectionName, key);
529}
530
531void GBAInputSetCustomValue(struct Configuration* config, uint32_t type, const char* key, const char* value, const char* profile) {
532	char sectionName[SECTION_NAME_MAX];
533	if (profile) {
534		snprintf(sectionName, SECTION_NAME_MAX, "input-profile.%s", profile);
535		ConfigurationSetValue(config, sectionName, key, value);
536	}
537	_makeSectionName(sectionName, SECTION_NAME_MAX, type);
538	ConfigurationSetValue(config, sectionName, key, value);
539}