all repos — mgba @ e17e4fd19003209ff9db1f0ac1394e463c7b4a47

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 bool _loadAll(struct GBAInputMap* map, uint32_t type, const char* sectionName, const struct Configuration* config) {
261	if (!ConfigurationHasSection(config, sectionName)) {
262		return false;
263	}
264	_loadKey(map, type, sectionName, config, GBA_KEY_A, "A");
265	_loadKey(map, type, sectionName, config, GBA_KEY_B, "B");
266	_loadKey(map, type, sectionName, config, GBA_KEY_L, "L");
267	_loadKey(map, type, sectionName, config, GBA_KEY_R, "R");
268	_loadKey(map, type, sectionName, config, GBA_KEY_START, "Start");
269	_loadKey(map, type, sectionName, config, GBA_KEY_SELECT, "Select");
270	_loadKey(map, type, sectionName, config, GBA_KEY_UP, "Up");
271	_loadKey(map, type, sectionName, config, GBA_KEY_DOWN, "Down");
272	_loadKey(map, type, sectionName, config, GBA_KEY_LEFT, "Left");
273	_loadKey(map, type, sectionName, config, GBA_KEY_RIGHT, "Right");
274
275	_loadAxis(map, type, sectionName, config, GBA_KEY_A, "A");
276	_loadAxis(map, type, sectionName, config, GBA_KEY_B, "B");
277	_loadAxis(map, type, sectionName, config, GBA_KEY_L, "L");
278	_loadAxis(map, type, sectionName, config, GBA_KEY_R, "R");
279	_loadAxis(map, type, sectionName, config, GBA_KEY_START, "Start");
280	_loadAxis(map, type, sectionName, config, GBA_KEY_SELECT, "Select");
281	_loadAxis(map, type, sectionName, config, GBA_KEY_UP, "Up");
282	_loadAxis(map, type, sectionName, config, GBA_KEY_DOWN, "Down");
283	_loadAxis(map, type, sectionName, config, GBA_KEY_LEFT, "Left");
284	_loadAxis(map, type, sectionName, config, GBA_KEY_RIGHT, "Right");
285	return true;
286}
287
288static void _saveAll(const struct GBAInputMap* map, uint32_t type, const char* sectionName, struct Configuration* config) {
289	_saveKey(map, type, sectionName, config, GBA_KEY_A, "A");
290	_saveKey(map, type, sectionName, config, GBA_KEY_B, "B");
291	_saveKey(map, type, sectionName, config, GBA_KEY_L, "L");
292	_saveKey(map, type, sectionName, config, GBA_KEY_R, "R");
293	_saveKey(map, type, sectionName, config, GBA_KEY_START, "Start");
294	_saveKey(map, type, sectionName, config, GBA_KEY_SELECT, "Select");
295	_saveKey(map, type, sectionName, config, GBA_KEY_UP, "Up");
296	_saveKey(map, type, sectionName, config, GBA_KEY_DOWN, "Down");
297	_saveKey(map, type, sectionName, config, GBA_KEY_LEFT, "Left");
298	_saveKey(map, type, sectionName, config, GBA_KEY_RIGHT, "Right");
299
300	_clearAxis(sectionName, config, "A");
301	_clearAxis(sectionName, config, "B");
302	_clearAxis(sectionName, config, "L");
303	_clearAxis(sectionName, config, "R");
304	_clearAxis(sectionName, config, "Start");
305	_clearAxis(sectionName, config, "Select");
306	_clearAxis(sectionName, config, "Up");
307	_clearAxis(sectionName, config, "Down");
308	_clearAxis(sectionName, config, "Left");
309	_clearAxis(sectionName, config, "Right");
310
311	const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
312	if (!impl) {
313		return;
314	}
315	struct GBAAxisSave save = {
316		config,
317		sectionName
318	};
319	TableEnumerate(&impl->axes, _saveAxis, &save);
320}
321
322void GBAInputMapInit(struct GBAInputMap* map) {
323	map->maps = 0;
324	map->numMaps = 0;
325}
326
327void GBAInputMapDeinit(struct GBAInputMap* map) {
328	size_t m;
329	for (m = 0; m < map->numMaps; ++m) {
330		if (map->maps[m].type) {
331			free(map->maps[m].map);
332			TableDeinit(&map->maps[m].axes);
333		}
334	}
335	free(map->maps);
336	map->maps = 0;
337	map->numMaps = 0;
338}
339
340enum GBAKey GBAInputMapKey(const struct GBAInputMap* map, uint32_t type, int key) {
341	size_t m;
342	const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
343	if (!impl || !impl->map) {
344		return GBA_KEY_NONE;
345	}
346
347	for (m = 0; m < GBA_KEY_MAX; ++m) {
348		if (impl->map[m] == key) {
349			return m;
350		}
351	}
352	return GBA_KEY_NONE;
353}
354
355int GBAInputMapKeyBits(const struct GBAInputMap* map, uint32_t type, uint32_t bits, unsigned offset) {
356	int keys = 0;
357	for (; bits; bits >>= 1, ++offset) {
358		if (bits & 1) {
359			enum GBAKey key = GBAInputMapKey(map, type, offset);
360			if (key == GBA_KEY_NONE) {
361				continue;
362			}
363			keys |= 1 << key;
364		}
365	}
366	return keys;
367}
368
369void GBAInputBindKey(struct GBAInputMap* map, uint32_t type, int key, enum GBAKey input) {
370	struct GBAInputMapImpl* impl = _guaranteeMap(map, type);
371	GBAInputUnbindKey(map, type, input);
372	impl->map[input] = key;
373}
374
375void GBAInputUnbindKey(struct GBAInputMap* map, uint32_t type, enum GBAKey input) {
376	struct GBAInputMapImpl* impl = _lookupMap(map, type);
377	if (input < 0 || input >= GBA_KEY_MAX) {
378		return;
379	}
380	if (impl) {
381		impl->map[input] = GBA_NO_MAPPING;
382	}
383}
384
385int GBAInputQueryBinding(const struct GBAInputMap* map, uint32_t type, enum GBAKey input) {
386	if (input >= GBA_KEY_MAX) {
387		return 0;
388	}
389
390	const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
391	if (!impl || !impl->map) {
392		return 0;
393	}
394
395	return impl->map[input];
396}
397
398enum GBAKey GBAInputMapAxis(const struct GBAInputMap* map, uint32_t type, int axis, int value) {
399	const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
400	if (!impl) {
401		return GBA_KEY_NONE;
402	}
403	struct GBAAxis* description = TableLookup(&impl->axes, axis);
404	if (!description) {
405		return GBA_KEY_NONE;
406	}
407	int state = 0;
408	if (value < description->deadLow) {
409		state = -1;
410	} else if (value > description->deadHigh) {
411		state = 1;
412	}
413	if (state > 0) {
414		return description->highDirection;
415	}
416	if (state < 0) {
417		return description->lowDirection;
418	}
419	return GBA_KEY_NONE;
420}
421
422int GBAInputClearAxis(const struct GBAInputMap* map, uint32_t type, int axis, int keys) {
423	const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
424	if (!impl) {
425		return keys;
426	}
427	struct GBAAxis* description = TableLookup(&impl->axes, axis);
428	if (!description) {
429		return keys;
430	}
431	return keys &= ~((1 << description->highDirection) | (1 << description->lowDirection));
432}
433
434void GBAInputBindAxis(struct GBAInputMap* map, uint32_t type, int axis, const struct GBAAxis* description) {
435	struct GBAInputMapImpl* impl = _guaranteeMap(map, type);
436	struct GBAAxis d2 = *description;
437	TableEnumerate(&impl->axes, _unbindAxis, &d2.highDirection);
438	TableEnumerate(&impl->axes, _unbindAxis, &d2.lowDirection);
439	struct GBAAxis* dup = malloc(sizeof(struct GBAAxis));
440	*dup = *description;
441	TableInsert(&impl->axes, axis, dup);
442}
443
444void GBAInputUnbindAxis(struct GBAInputMap* map, uint32_t type, int axis) {
445	struct GBAInputMapImpl* impl = _lookupMap(map, type);
446	if (impl) {
447		TableRemove(&impl->axes, axis);
448	}
449}
450
451void GBAInputUnbindAllAxes(struct GBAInputMap* map, uint32_t type) {
452	struct GBAInputMapImpl* impl = _lookupMap(map, type);
453	if (impl) {
454		TableClear(&impl->axes);
455	}
456}
457
458const struct GBAAxis* GBAInputQueryAxis(const struct GBAInputMap* map, uint32_t type, int axis) {
459	const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
460	if (!impl) {
461		return 0;
462	}
463	return TableLookup(&impl->axes, axis);
464}
465
466void GBAInputEnumerateAxes(const struct GBAInputMap* map, uint32_t type, void (handler(int axis, const struct GBAAxis* description, void* user)), void* user) {
467	const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
468	if (!impl) {
469		return;
470	}
471	struct GBAAxisEnumerate enumUser = {
472		handler,
473		user
474	};
475	TableEnumerate(&impl->axes, _enumerateAxis, &enumUser);
476}
477
478void GBAInputMapLoad(struct GBAInputMap* map, uint32_t type, const struct Configuration* config) {
479	char sectionName[SECTION_NAME_MAX];
480	_makeSectionName(sectionName, SECTION_NAME_MAX, type);
481	_loadAll(map, type, sectionName, config);
482}
483
484void GBAInputMapSave(const struct GBAInputMap* map, uint32_t type, struct Configuration* config) {
485	char sectionName[SECTION_NAME_MAX];
486	_makeSectionName(sectionName, SECTION_NAME_MAX, type);
487	_saveAll(map, type, sectionName, config);
488}
489
490bool GBAInputProfileLoad(struct GBAInputMap* map, uint32_t type, const struct Configuration* config, const char* profile) {
491	char sectionName[SECTION_NAME_MAX];
492	snprintf(sectionName, SECTION_NAME_MAX, "input-profile.%s", profile);
493	sectionName[SECTION_NAME_MAX - 1] = '\0';
494	return _loadAll(map, type, sectionName, config);
495}
496
497void GBAInputProfileSave(const struct GBAInputMap* map, uint32_t type, struct Configuration* config, const char* profile) {
498	char sectionName[SECTION_NAME_MAX];
499	snprintf(sectionName, SECTION_NAME_MAX, "input-profile.%s", profile);
500	sectionName[SECTION_NAME_MAX - 1] = '\0';
501	_saveAll(map, type, sectionName, config);
502}
503
504const char* GBAInputGetPreferredDevice(const struct Configuration* config, uint32_t type, int playerId) {
505	char sectionName[SECTION_NAME_MAX];
506	_makeSectionName(sectionName, SECTION_NAME_MAX, type);
507
508	char deviceId[KEY_NAME_MAX];
509	snprintf(deviceId, sizeof(deviceId), "device%i", playerId);
510	return ConfigurationGetValue(config, sectionName, deviceId);
511}
512
513void GBAInputSetPreferredDevice(struct Configuration* config, uint32_t type, int playerId, const char* deviceName) {
514	char sectionName[SECTION_NAME_MAX];
515	_makeSectionName(sectionName, SECTION_NAME_MAX, type);
516
517	char deviceId[KEY_NAME_MAX];
518	snprintf(deviceId, sizeof(deviceId), "device%i", playerId);
519	return ConfigurationSetValue(config, sectionName, deviceId, deviceName);
520}
521
522const char* GBAInputGetCustomValue(const struct Configuration* config, uint32_t type, const char* key, const char* profile) {
523	char sectionName[SECTION_NAME_MAX];
524	if (profile) {
525		snprintf(sectionName, SECTION_NAME_MAX, "input-profile.%s", profile);
526		const char* value = ConfigurationGetValue(config, sectionName, key);
527		if (value) {
528			return value;
529		}
530	}
531	_makeSectionName(sectionName, SECTION_NAME_MAX, type);
532	return ConfigurationGetValue(config, sectionName, key);
533}
534
535void GBAInputSetCustomValue(struct Configuration* config, uint32_t type, const char* key, const char* value, const char* profile) {
536	char sectionName[SECTION_NAME_MAX];
537	if (profile) {
538		snprintf(sectionName, SECTION_NAME_MAX, "input-profile.%s", profile);
539		ConfigurationSetValue(config, sectionName, key, value);
540	}
541	_makeSectionName(sectionName, SECTION_NAME_MAX, type);
542	ConfigurationSetValue(config, sectionName, key, value);
543}