all repos — mgba @ c732ea6a71d410b234ecf76e86c5e4d8bbd98e44

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 = malloc(GBA_KEY_MAX * sizeof(int));
 99		int i;
100		for (i = 0; i < GBA_KEY_MAX; ++i) {
101			impl->map[i] = GBA_KEY_NONE;
102		}
103		TableInit(&impl->axes, 2, free);
104	} else {
105		impl = _lookupMap(map, type);
106	}
107	if (!impl) {
108		size_t m;
109		for (m = 0; m < map->numMaps; ++m) {
110			if (!map->maps[m].type) {
111				impl = &map->maps[m];
112				break;
113			}
114		}
115		if (impl) {
116			impl->type = type;
117			impl->map = malloc(GBA_KEY_MAX * sizeof(int));
118			int i;
119			for (i = 0; i < GBA_KEY_MAX; ++i) {
120				impl->map[i] = GBA_KEY_NONE;
121			}
122		} else {
123			map->maps = realloc(map->maps, sizeof(*map->maps) * map->numMaps * 2);
124			for (m = map->numMaps * 2 - 1; m > map->numMaps; --m) {
125				map->maps[m].type = 0;
126				map->maps[m].map = 0;
127			}
128			map->numMaps *= 2;
129			impl = &map->maps[m];
130			impl->type = type;
131			impl->map = malloc(GBA_KEY_MAX * sizeof(int));
132			int i;
133			for (i = 0; i < GBA_KEY_MAX; ++i) {
134				impl->map[i] = GBA_KEY_NONE;
135			}
136		}
137		TableInit(&impl->axes, 2, free);
138	}
139	return impl;
140}
141
142static void _loadKey(struct GBAInputMap* map, uint32_t type, const char* sectionName, const struct Configuration* config, enum GBAKey key, const char* keyName) {
143	char keyKey[KEY_NAME_MAX];
144	snprintf(keyKey, KEY_NAME_MAX, "key%s", keyName);
145	keyKey[KEY_NAME_MAX - 1] = '\0';
146
147	int value;
148	if (!_getIntValue(config, sectionName, keyKey, &value)) {
149		return;
150	}
151	GBAInputBindKey(map, type, value, key);
152}
153
154static void _loadAxis(struct GBAInputMap* map, uint32_t type, const char* sectionName, const struct Configuration* config, enum GBAKey direction, const char* axisName) {
155	char axisKey[KEY_NAME_MAX];
156	snprintf(axisKey, KEY_NAME_MAX, "axis%sValue", axisName);
157	axisKey[KEY_NAME_MAX - 1] = '\0';
158	int value;
159	if (!_getIntValue(config, sectionName, axisKey, &value)) {
160		return;
161	}
162
163	snprintf(axisKey, KEY_NAME_MAX, "axis%sAxis", axisName);
164	axisKey[KEY_NAME_MAX - 1] = '\0';
165	int axis;
166	const char* strValue = ConfigurationGetValue(config, sectionName, axisKey);
167	if (!strValue || !strValue[0]) {
168		return;
169	}
170	char* end;
171	axis = strtoul(&strValue[1], &end, 10);
172	if (*end) {
173		return;
174	}
175
176	const struct GBAAxis* description = GBAInputQueryAxis(map, type, axis);
177	struct GBAAxis realDescription = { GBA_KEY_NONE, GBA_KEY_NONE, 0, 0 };
178	if (description) {
179		realDescription = *description;
180	}
181	if (strValue[0] == '+') {
182		realDescription.deadHigh = value;
183		realDescription.highDirection = direction;
184	} else if (strValue[0] == '-') {
185		realDescription.deadLow = value;
186		realDescription.lowDirection = direction;
187	}
188	GBAInputBindAxis(map, type, axis, &realDescription);
189}
190
191static void _saveKey(const struct GBAInputMap* map, uint32_t type, const char* sectionName, struct Configuration* config, enum GBAKey key, const char* keyName) {
192	char keyKey[KEY_NAME_MAX];
193	snprintf(keyKey, KEY_NAME_MAX, "key%s", keyName);
194	keyKey[KEY_NAME_MAX - 1] = '\0';
195
196	int value = GBAInputQueryBinding(map, type, key);
197	char keyValue[KEY_VALUE_MAX];
198	snprintf(keyValue, KEY_VALUE_MAX, "%" PRIi32, value);
199
200	ConfigurationSetValue(config, sectionName, keyKey, keyValue);
201}
202
203static void _clearAxis(const char* sectionName, struct Configuration* config, const char* axisName) {
204	char axisKey[KEY_NAME_MAX];
205	snprintf(axisKey, KEY_NAME_MAX, "axis%sValue", axisName);
206	axisKey[KEY_NAME_MAX - 1] = '\0';
207	ConfigurationClearValue(config, sectionName, axisKey);
208
209	snprintf(axisKey, KEY_NAME_MAX, "axis%sAxis", axisName);
210	axisKey[KEY_NAME_MAX - 1] = '\0';
211	ConfigurationClearValue(config, sectionName, axisKey);
212}
213
214static void _saveAxis(uint32_t axis, void* dp, void* up) {
215	struct GBAAxisSave* user = up;
216	const struct GBAAxis* description = dp;
217
218	const char* sectionName = user->sectionName;
219
220	if (description->lowDirection != GBA_KEY_NONE) {
221		const char* keyName = GBAKeyNames[description->lowDirection];
222
223		char axisKey[KEY_NAME_MAX];
224		snprintf(axisKey, KEY_NAME_MAX, "axis%sValue", keyName);
225		axisKey[KEY_NAME_MAX - 1] = '\0';
226		ConfigurationSetIntValue(user->config, sectionName, axisKey, description->deadLow);
227
228		snprintf(axisKey, KEY_NAME_MAX, "axis%sAxis", keyName);
229		axisKey[KEY_NAME_MAX - 1] = '\0';
230
231		char axisInfo[AXIS_INFO_MAX];
232		snprintf(axisInfo, AXIS_INFO_MAX, "-%u", axis);
233		axisInfo[AXIS_INFO_MAX - 1] = '\0';
234		ConfigurationSetValue(user->config, sectionName, axisKey, axisInfo);
235	}
236	if (description->highDirection != GBA_KEY_NONE) {
237		const char* keyName = GBAKeyNames[description->highDirection];
238
239		char axisKey[KEY_NAME_MAX];
240		snprintf(axisKey, KEY_NAME_MAX, "axis%sValue", keyName);
241		axisKey[KEY_NAME_MAX - 1] = '\0';
242		ConfigurationSetIntValue(user->config, sectionName, axisKey, description->deadHigh);
243
244		snprintf(axisKey, KEY_NAME_MAX, "axis%sAxis", keyName);
245		axisKey[KEY_NAME_MAX - 1] = '\0';
246
247		char axisInfo[AXIS_INFO_MAX];
248		snprintf(axisInfo, AXIS_INFO_MAX, "+%u", axis);
249		axisInfo[AXIS_INFO_MAX - 1] = '\0';
250		ConfigurationSetValue(user->config, sectionName, axisKey, axisInfo);
251	}
252}
253
254void _enumerateAxis(uint32_t axis, void* dp, void* ep) {
255	struct GBAAxisEnumerate* enumUser = ep;
256	const struct GBAAxis* description = dp;
257	enumUser->handler(axis, description, enumUser->user);
258}
259
260void _unbindAxis(uint32_t axis, void* dp, void* user) {
261	UNUSED(axis);
262	enum GBAKey* key = user;
263	struct GBAAxis* description = dp;
264	if (description->highDirection == *key) {
265		description->highDirection = GBA_KEY_NONE;
266	}
267	if (description->lowDirection == *key) {
268		description->lowDirection = GBA_KEY_NONE;
269	}
270}
271
272static bool _loadAll(struct GBAInputMap* map, uint32_t type, const char* sectionName, const struct Configuration* config) {
273	if (!ConfigurationHasSection(config, sectionName)) {
274		return false;
275	}
276	_loadKey(map, type, sectionName, config, GBA_KEY_A, "A");
277	_loadKey(map, type, sectionName, config, GBA_KEY_B, "B");
278	_loadKey(map, type, sectionName, config, GBA_KEY_L, "L");
279	_loadKey(map, type, sectionName, config, GBA_KEY_R, "R");
280	_loadKey(map, type, sectionName, config, GBA_KEY_START, "Start");
281	_loadKey(map, type, sectionName, config, GBA_KEY_SELECT, "Select");
282	_loadKey(map, type, sectionName, config, GBA_KEY_UP, "Up");
283	_loadKey(map, type, sectionName, config, GBA_KEY_DOWN, "Down");
284	_loadKey(map, type, sectionName, config, GBA_KEY_LEFT, "Left");
285	_loadKey(map, type, sectionName, config, GBA_KEY_RIGHT, "Right");
286
287	_loadAxis(map, type, sectionName, config, GBA_KEY_A, "A");
288	_loadAxis(map, type, sectionName, config, GBA_KEY_B, "B");
289	_loadAxis(map, type, sectionName, config, GBA_KEY_L, "L");
290	_loadAxis(map, type, sectionName, config, GBA_KEY_R, "R");
291	_loadAxis(map, type, sectionName, config, GBA_KEY_START, "Start");
292	_loadAxis(map, type, sectionName, config, GBA_KEY_SELECT, "Select");
293	_loadAxis(map, type, sectionName, config, GBA_KEY_UP, "Up");
294	_loadAxis(map, type, sectionName, config, GBA_KEY_DOWN, "Down");
295	_loadAxis(map, type, sectionName, config, GBA_KEY_LEFT, "Left");
296	_loadAxis(map, type, sectionName, config, GBA_KEY_RIGHT, "Right");
297	return true;
298}
299
300static void _saveAll(const struct GBAInputMap* map, uint32_t type, const char* sectionName, struct Configuration* config) {
301	_saveKey(map, type, sectionName, config, GBA_KEY_A, "A");
302	_saveKey(map, type, sectionName, config, GBA_KEY_B, "B");
303	_saveKey(map, type, sectionName, config, GBA_KEY_L, "L");
304	_saveKey(map, type, sectionName, config, GBA_KEY_R, "R");
305	_saveKey(map, type, sectionName, config, GBA_KEY_START, "Start");
306	_saveKey(map, type, sectionName, config, GBA_KEY_SELECT, "Select");
307	_saveKey(map, type, sectionName, config, GBA_KEY_UP, "Up");
308	_saveKey(map, type, sectionName, config, GBA_KEY_DOWN, "Down");
309	_saveKey(map, type, sectionName, config, GBA_KEY_LEFT, "Left");
310	_saveKey(map, type, sectionName, config, GBA_KEY_RIGHT, "Right");
311
312	_clearAxis(sectionName, config, "A");
313	_clearAxis(sectionName, config, "B");
314	_clearAxis(sectionName, config, "L");
315	_clearAxis(sectionName, config, "R");
316	_clearAxis(sectionName, config, "Start");
317	_clearAxis(sectionName, config, "Select");
318	_clearAxis(sectionName, config, "Up");
319	_clearAxis(sectionName, config, "Down");
320	_clearAxis(sectionName, config, "Left");
321	_clearAxis(sectionName, config, "Right");
322
323	const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
324	if (!impl) {
325		return;
326	}
327	struct GBAAxisSave save = {
328		config,
329		sectionName
330	};
331	TableEnumerate(&impl->axes, _saveAxis, &save);
332}
333
334void GBAInputMapInit(struct GBAInputMap* map) {
335	map->maps = 0;
336	map->numMaps = 0;
337}
338
339void GBAInputMapDeinit(struct GBAInputMap* map) {
340	size_t m;
341	for (m = 0; m < map->numMaps; ++m) {
342		if (map->maps[m].type) {
343			free(map->maps[m].map);
344			TableDeinit(&map->maps[m].axes);
345		}
346	}
347	free(map->maps);
348	map->maps = 0;
349	map->numMaps = 0;
350}
351
352enum GBAKey GBAInputMapKey(const struct GBAInputMap* map, uint32_t type, int key) {
353	size_t m;
354	const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
355	if (!impl || !impl->map) {
356		return GBA_KEY_NONE;
357	}
358
359	for (m = 0; m < GBA_KEY_MAX; ++m) {
360		if (impl->map[m] == key) {
361			return m;
362		}
363	}
364	return GBA_KEY_NONE;
365}
366
367int GBAInputMapKeyBits(const struct GBAInputMap* map, uint32_t type, uint32_t bits, unsigned offset) {
368	int keys = 0;
369	for (; bits; bits >>= 1, ++offset) {
370		if (bits & 1) {
371			enum GBAKey key = GBAInputMapKey(map, type, offset);
372			if (key == GBA_KEY_NONE) {
373				continue;
374			}
375			keys |= 1 << key;
376		}
377	}
378	return keys;
379}
380
381void GBAInputBindKey(struct GBAInputMap* map, uint32_t type, int key, enum GBAKey input) {
382	struct GBAInputMapImpl* impl = _guaranteeMap(map, type);
383	GBAInputUnbindKey(map, type, input);
384	impl->map[input] = key;
385}
386
387void GBAInputUnbindKey(struct GBAInputMap* map, uint32_t type, enum GBAKey input) {
388	struct GBAInputMapImpl* impl = _lookupMap(map, type);
389	if (input < 0 || input >= GBA_KEY_MAX) {
390		return;
391	}
392	if (impl) {
393		impl->map[input] = GBA_KEY_NONE;
394	}
395}
396
397int GBAInputQueryBinding(const struct GBAInputMap* map, uint32_t type, enum GBAKey input) {
398	if (input >= GBA_KEY_MAX) {
399		return GBA_KEY_NONE;
400	}
401
402	const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
403	if (!impl || !impl->map) {
404		return GBA_KEY_NONE;
405	}
406
407	return impl->map[input];
408}
409
410enum GBAKey GBAInputMapAxis(const struct GBAInputMap* map, uint32_t type, int axis, int value) {
411	const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
412	if (!impl) {
413		return GBA_KEY_NONE;
414	}
415	struct GBAAxis* description = TableLookup(&impl->axes, axis);
416	if (!description) {
417		return GBA_KEY_NONE;
418	}
419	int state = 0;
420	if (value < description->deadLow) {
421		state = -1;
422	} else if (value > description->deadHigh) {
423		state = 1;
424	}
425	if (state > 0) {
426		return description->highDirection;
427	}
428	if (state < 0) {
429		return description->lowDirection;
430	}
431	return GBA_KEY_NONE;
432}
433
434int GBAInputClearAxis(const struct GBAInputMap* map, uint32_t type, int axis, int keys) {
435	const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
436	if (!impl) {
437		return keys;
438	}
439	struct GBAAxis* description = TableLookup(&impl->axes, axis);
440	if (!description) {
441		return keys;
442	}
443	return keys &= ~((1 << description->highDirection) | (1 << description->lowDirection));
444}
445
446void GBAInputBindAxis(struct GBAInputMap* map, uint32_t type, int axis, const struct GBAAxis* description) {
447	struct GBAInputMapImpl* impl = _guaranteeMap(map, type);
448	struct GBAAxis d2 = *description;
449	TableEnumerate(&impl->axes, _unbindAxis, &d2.highDirection);
450	TableEnumerate(&impl->axes, _unbindAxis, &d2.lowDirection);
451	struct GBAAxis* dup = malloc(sizeof(struct GBAAxis));
452	*dup = *description;
453	TableInsert(&impl->axes, axis, dup);
454}
455
456void GBAInputUnbindAxis(struct GBAInputMap* map, uint32_t type, int axis) {
457	struct GBAInputMapImpl* impl = _lookupMap(map, type);
458	if (impl) {
459		TableRemove(&impl->axes, axis);
460	}
461}
462
463void GBAInputUnbindAllAxes(struct GBAInputMap* map, uint32_t type) {
464	struct GBAInputMapImpl* impl = _lookupMap(map, type);
465	if (impl) {
466		TableClear(&impl->axes);
467	}
468}
469
470const struct GBAAxis* GBAInputQueryAxis(const struct GBAInputMap* map, uint32_t type, int axis) {
471	const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
472	if (!impl) {
473		return 0;
474	}
475	return TableLookup(&impl->axes, axis);
476}
477
478void GBAInputEnumerateAxes(const struct GBAInputMap* map, uint32_t type, void (handler(int axis, const struct GBAAxis* description, void* user)), void* user) {
479	const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
480	if (!impl) {
481		return;
482	}
483	struct GBAAxisEnumerate enumUser = {
484		handler,
485		user
486	};
487	TableEnumerate(&impl->axes, _enumerateAxis, &enumUser);
488}
489
490void GBAInputMapLoad(struct GBAInputMap* map, uint32_t type, const struct Configuration* config) {
491	char sectionName[SECTION_NAME_MAX];
492	_makeSectionName(sectionName, SECTION_NAME_MAX, type);
493	_loadAll(map, type, sectionName, config);
494}
495
496void GBAInputMapSave(const struct GBAInputMap* map, uint32_t type, struct Configuration* config) {
497	char sectionName[SECTION_NAME_MAX];
498	_makeSectionName(sectionName, SECTION_NAME_MAX, type);
499	_saveAll(map, type, sectionName, config);
500}
501
502bool GBAInputProfileLoad(struct GBAInputMap* map, uint32_t type, const struct Configuration* config, const char* profile) {
503	char sectionName[SECTION_NAME_MAX];
504	snprintf(sectionName, SECTION_NAME_MAX, "input-profile.%s", profile);
505	sectionName[SECTION_NAME_MAX - 1] = '\0';
506	return _loadAll(map, type, sectionName, config);
507}
508
509void GBAInputProfileSave(const struct GBAInputMap* map, uint32_t type, struct Configuration* config, const char* profile) {
510	char sectionName[SECTION_NAME_MAX];
511	snprintf(sectionName, SECTION_NAME_MAX, "input-profile.%s", profile);
512	sectionName[SECTION_NAME_MAX - 1] = '\0';
513	_saveAll(map, type, sectionName, config);
514}
515
516const char* GBAInputGetPreferredDevice(const struct Configuration* config, uint32_t type, int playerId) {
517	char sectionName[SECTION_NAME_MAX];
518	_makeSectionName(sectionName, SECTION_NAME_MAX, type);
519
520	char deviceId[KEY_NAME_MAX];
521	snprintf(deviceId, sizeof(deviceId), "device%i", playerId);
522	return ConfigurationGetValue(config, sectionName, deviceId);
523}
524
525void GBAInputSetPreferredDevice(struct Configuration* config, uint32_t type, int playerId, const char* deviceName) {
526	char sectionName[SECTION_NAME_MAX];
527	_makeSectionName(sectionName, SECTION_NAME_MAX, type);
528
529	char deviceId[KEY_NAME_MAX];
530	snprintf(deviceId, sizeof(deviceId), "device%i", playerId);
531	return ConfigurationSetValue(config, sectionName, deviceId, deviceName);
532}
533
534const char* GBAInputGetCustomValue(const struct Configuration* config, uint32_t type, const char* key, const char* profile) {
535	char sectionName[SECTION_NAME_MAX];
536	if (profile) {
537		snprintf(sectionName, SECTION_NAME_MAX, "input-profile.%s", profile);
538		const char* value = ConfigurationGetValue(config, sectionName, key);
539		if (value) {
540			return value;
541		}
542	}
543	_makeSectionName(sectionName, SECTION_NAME_MAX, type);
544	return ConfigurationGetValue(config, sectionName, key);
545}
546
547void GBAInputSetCustomValue(struct Configuration* config, uint32_t type, const char* key, const char* value, const char* profile) {
548	char sectionName[SECTION_NAME_MAX];
549	if (profile) {
550		snprintf(sectionName, SECTION_NAME_MAX, "input-profile.%s", profile);
551		ConfigurationSetValue(config, sectionName, key, value);
552	}
553	_makeSectionName(sectionName, SECTION_NAME_MAX, type);
554	ConfigurationSetValue(config, sectionName, key, value);
555}