all repos — mgba @ 1c6d87f578edee720b4e9263403b39c7182917ed

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