all repos — mgba @ 2f066a979031136e7686ceee91af9e8a2e7cc754

mGBA Game Boy Advance Emulator

src/core/input.c (view raw)

  1/* Copyright (c) 2013-2016 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 <mgba/core/input.h>
  7
  8#include <mgba-util/configuration.h>
  9#include <mgba-util/table.h>
 10#include <mgba-util/vector.h>
 11
 12#include <inttypes.h>
 13
 14#define SECTION_NAME_MAX 128
 15#define KEY_NAME_MAX 32
 16#define KEY_VALUE_MAX 16
 17#define AXIS_INFO_MAX 12
 18
 19DECLARE_VECTOR(mInputHatList, struct mInputHatBindings);
 20DEFINE_VECTOR(mInputHatList, struct mInputHatBindings);
 21
 22struct mInputMapImpl {
 23	int* map;
 24	uint32_t type;
 25
 26	struct Table axes;
 27	struct mInputHatList hats;
 28};
 29
 30struct mInputAxisSave {
 31	struct Configuration* config;
 32	const char* sectionName;
 33	const struct mInputPlatformInfo* info;
 34};
 35
 36struct mInputAxisEnumerate {
 37	void (*handler)(int axis, const struct mInputAxis* description, void* user);
 38	void* user;
 39};
 40
 41static void _makeSectionName(const char* platform, char* sectionName, size_t len, uint32_t type) {
 42	snprintf(sectionName, len, "%s.input.%c%c%c%c", platform, type >> 24, type >> 16, type >> 8, type);
 43	sectionName[len - 1] = '\0';
 44}
 45
 46static bool _getIntValue(const struct Configuration* config, const char* section, const char* key, int* value) {
 47	const char* strValue = ConfigurationGetValue(config, section, key);
 48	if (!strValue) {
 49		return false;
 50	}
 51	char* end;
 52	long intValue = strtol(strValue, &end, 10);
 53	if (*end) {
 54		return false;
 55	}
 56	*value = intValue;
 57	return true;
 58}
 59
 60static struct mInputMapImpl* _lookupMap(struct mInputMap* map, uint32_t type) {
 61	size_t m;
 62	struct mInputMapImpl* impl = 0;
 63	for (m = 0; m < map->numMaps; ++m) {
 64		if (map->maps[m].type == type) {
 65			impl = &map->maps[m];
 66			break;
 67		}
 68	}
 69	return impl;
 70}
 71
 72static const struct mInputMapImpl* _lookupMapConst(const struct mInputMap* map, uint32_t type) {
 73	size_t m;
 74	const struct mInputMapImpl* impl = 0;
 75	for (m = 0; m < map->numMaps; ++m) {
 76		if (map->maps[m].type == type) {
 77			impl = &map->maps[m];
 78			break;
 79		}
 80	}
 81	return impl;
 82}
 83
 84static struct mInputMapImpl* _guaranteeMap(struct mInputMap* map, uint32_t type) {
 85	struct mInputMapImpl* impl = 0;
 86	if (map->numMaps == 0) {
 87		map->maps = malloc(sizeof(*map->maps));
 88		map->numMaps = 1;
 89		impl = &map->maps[0];
 90		impl->type = type;
 91		impl->map = malloc(map->info->nKeys * sizeof(int));
 92		size_t i;
 93		for (i = 0; i < map->info->nKeys; ++i) {
 94			impl->map[i] = -1;
 95		}
 96		TableInit(&impl->axes, 2, free);
 97		mInputHatListInit(&impl->hats, 1);
 98	} else {
 99		impl = _lookupMap(map, type);
100	}
101	if (!impl) {
102		size_t m;
103		for (m = 0; m < map->numMaps; ++m) {
104			if (!map->maps[m].type) {
105				impl = &map->maps[m];
106				break;
107			}
108		}
109		if (impl) {
110			impl->type = type;
111			impl->map = malloc(map->info->nKeys * sizeof(int));
112			size_t i;
113			for (i = 0; i < map->info->nKeys; ++i) {
114				impl->map[i] = -1;
115			}
116		} else {
117			map->maps = realloc(map->maps, sizeof(*map->maps) * map->numMaps * 2);
118			for (m = map->numMaps * 2 - 1; m > map->numMaps; --m) {
119				map->maps[m].type = 0;
120				map->maps[m].map = 0;
121			}
122			map->numMaps *= 2;
123			impl = &map->maps[m];
124			impl->type = type;
125			impl->map = malloc(map->info->nKeys * sizeof(int));
126			size_t i;
127			for (i = 0; i < map->info->nKeys; ++i) {
128				impl->map[i] = -1;
129			}
130		}
131		TableInit(&impl->axes, 2, free);
132		mInputHatListInit(&impl->hats, 1);
133	}
134	return impl;
135}
136
137static void _loadKey(struct mInputMap* map, uint32_t type, const char* sectionName, const struct Configuration* config, int key, const char* keyName) {
138	char keyKey[KEY_NAME_MAX];
139	snprintf(keyKey, KEY_NAME_MAX, "key%s", keyName);
140	keyKey[KEY_NAME_MAX - 1] = '\0';
141
142	int value;
143	if (!_getIntValue(config, sectionName, keyKey, &value)) {
144		return;
145	}
146	mInputBindKey(map, type, value, key);
147}
148
149static void _loadAxis(struct mInputMap* map, uint32_t type, const char* sectionName, const struct Configuration* config, int direction, const char* axisName) {
150	char axisKey[KEY_NAME_MAX];
151	snprintf(axisKey, KEY_NAME_MAX, "axis%sValue", axisName);
152	axisKey[KEY_NAME_MAX - 1] = '\0';
153	int value;
154	if (!_getIntValue(config, sectionName, axisKey, &value)) {
155		return;
156	}
157
158	snprintf(axisKey, KEY_NAME_MAX, "axis%sAxis", axisName);
159	axisKey[KEY_NAME_MAX - 1] = '\0';
160	int axis;
161	const char* strValue = ConfigurationGetValue(config, sectionName, axisKey);
162	if (!strValue || !strValue[0]) {
163		return;
164	}
165	char* end;
166	axis = strtoul(&strValue[1], &end, 10);
167	if (*end) {
168		return;
169	}
170
171	const struct mInputAxis* description = mInputQueryAxis(map, type, axis);
172	struct mInputAxis realDescription = { -1, -1, 0, 0 };
173	if (description) {
174		realDescription = *description;
175	}
176	if (strValue[0] == '+') {
177		realDescription.deadHigh = value;
178		realDescription.highDirection = direction;
179	} else if (strValue[0] == '-') {
180		realDescription.deadLow = value;
181		realDescription.lowDirection = direction;
182	}
183	mInputBindAxis(map, type, axis, &realDescription);
184}
185
186static bool _loadHat(struct mInputMap* map, uint32_t type, const char* sectionName, const struct Configuration* config, int hatId) {
187	char hatKey[KEY_NAME_MAX];
188
189	struct mInputHatBindings hatBindings = { -1, -1, -1, -1 };
190
191	bool found = false;
192	snprintf(hatKey, KEY_NAME_MAX, "hat%iUp", hatId);
193	found = _getIntValue(config, sectionName, hatKey, &hatBindings.up) || found;
194	snprintf(hatKey, KEY_NAME_MAX, "hat%iRight", hatId);
195	found = _getIntValue(config, sectionName, hatKey, &hatBindings.right) || found;
196	snprintf(hatKey, KEY_NAME_MAX, "hat%iDown", hatId);
197	found = _getIntValue(config, sectionName, hatKey, &hatBindings.down) || found;
198	snprintf(hatKey, KEY_NAME_MAX, "hat%iLeft", hatId);
199	found = _getIntValue(config, sectionName, hatKey, &hatBindings.left) || found;
200
201	if (!found) {
202		return false;
203	}
204	mInputBindHat(map, type, hatId, &hatBindings);
205	return true;
206}
207
208static void _saveKey(const struct mInputMap* map, uint32_t type, const char* sectionName, struct Configuration* config, int key, const char* keyName) {
209	char keyKey[KEY_NAME_MAX];
210	snprintf(keyKey, KEY_NAME_MAX, "key%s", keyName);
211	keyKey[KEY_NAME_MAX - 1] = '\0';
212
213	int value = mInputQueryBinding(map, type, key);
214	char keyValue[KEY_VALUE_MAX];
215	snprintf(keyValue, KEY_VALUE_MAX, "%" PRIi32, value);
216
217	ConfigurationSetValue(config, sectionName, keyKey, keyValue);
218}
219
220static void _clearAxis(const char* sectionName, struct Configuration* config, const char* axisName) {
221	char axisKey[KEY_NAME_MAX];
222	snprintf(axisKey, KEY_NAME_MAX, "axis%sValue", axisName);
223	axisKey[KEY_NAME_MAX - 1] = '\0';
224	ConfigurationClearValue(config, sectionName, axisKey);
225
226	snprintf(axisKey, KEY_NAME_MAX, "axis%sAxis", axisName);
227	axisKey[KEY_NAME_MAX - 1] = '\0';
228	ConfigurationClearValue(config, sectionName, axisKey);
229}
230
231static void _saveAxis(uint32_t axis, void* dp, void* up) {
232	struct mInputAxisSave* user = up;
233	const struct mInputAxis* description = dp;
234
235	const char* sectionName = user->sectionName;
236
237	if (description->lowDirection != -1) {
238		const char* keyName = user->info->keyId[description->lowDirection];
239
240		char axisKey[KEY_NAME_MAX];
241		snprintf(axisKey, KEY_NAME_MAX, "axis%sValue", keyName);
242		axisKey[KEY_NAME_MAX - 1] = '\0';
243		ConfigurationSetIntValue(user->config, sectionName, axisKey, description->deadLow);
244
245		snprintf(axisKey, KEY_NAME_MAX, "axis%sAxis", keyName);
246		axisKey[KEY_NAME_MAX - 1] = '\0';
247
248		char axisInfo[AXIS_INFO_MAX];
249		snprintf(axisInfo, AXIS_INFO_MAX, "-%u", axis);
250		axisInfo[AXIS_INFO_MAX - 1] = '\0';
251		ConfigurationSetValue(user->config, sectionName, axisKey, axisInfo);
252	}
253	if (description->highDirection != -1) {
254		const char* keyName = user->info->keyId[description->highDirection];
255
256		char axisKey[KEY_NAME_MAX];
257		snprintf(axisKey, KEY_NAME_MAX, "axis%sValue", keyName);
258		axisKey[KEY_NAME_MAX - 1] = '\0';
259		ConfigurationSetIntValue(user->config, sectionName, axisKey, description->deadHigh);
260
261		snprintf(axisKey, KEY_NAME_MAX, "axis%sAxis", keyName);
262		axisKey[KEY_NAME_MAX - 1] = '\0';
263
264		char axisInfo[AXIS_INFO_MAX];
265		snprintf(axisInfo, AXIS_INFO_MAX, "+%u", axis);
266		axisInfo[AXIS_INFO_MAX - 1] = '\0';
267		ConfigurationSetValue(user->config, sectionName, axisKey, axisInfo);
268	}
269}
270
271static void _saveHat(const char* sectionName, struct Configuration* config, int hatId, const struct mInputHatBindings* hat) {
272	char hatKey[KEY_NAME_MAX];
273	char hatValue[KEY_VALUE_MAX];
274
275	snprintf(hatKey, KEY_NAME_MAX, "hat%iUp", hatId);
276	snprintf(hatValue, KEY_VALUE_MAX, "%i", hat->up);
277	ConfigurationSetValue(config, sectionName, hatKey, hatValue);
278	snprintf(hatKey, KEY_NAME_MAX, "hat%iRight", hatId);
279	snprintf(hatValue, KEY_VALUE_MAX, "%i", hat->right);
280	ConfigurationSetValue(config, sectionName, hatKey, hatValue);
281	snprintf(hatKey, KEY_NAME_MAX, "hat%iDown", hatId);
282	snprintf(hatValue, KEY_VALUE_MAX, "%i", hat->down);
283	ConfigurationSetValue(config, sectionName, hatKey, hatValue);
284	snprintf(hatKey, KEY_NAME_MAX, "hat%iLeft", hatId);
285	snprintf(hatValue, KEY_VALUE_MAX, "%i", hat->left);
286	ConfigurationSetValue(config, sectionName, hatKey, hatValue);
287}
288
289void _enumerateAxis(uint32_t axis, void* dp, void* ep) {
290	struct mInputAxisEnumerate* enumUser = ep;
291	const struct mInputAxis* description = dp;
292	enumUser->handler(axis, description, enumUser->user);
293}
294
295void _unbindAxis(uint32_t axis, void* dp, void* user) {
296	UNUSED(axis);
297	int* key = user;
298	struct mInputAxis* description = dp;
299	if (description->highDirection == *key) {
300		description->highDirection = -1;
301	}
302	if (description->lowDirection == *key) {
303		description->lowDirection = -1;
304	}
305}
306
307static bool _loadAll(struct mInputMap* map, uint32_t type, const char* sectionName, const struct Configuration* config) {
308	if (!ConfigurationHasSection(config, sectionName)) {
309		return false;
310	}
311	size_t i;
312	for (i = 0; i < map->info->nKeys; ++i) {
313		_loadKey(map, type, sectionName, config, i, map->info->keyId[i]);
314		_loadAxis(map, type, sectionName, config, i, map->info->keyId[i]);
315	}
316	i = 0;
317	while (_loadHat(map, type, sectionName, config, i)) {
318		++i;
319	}
320	return true;
321}
322
323static void _saveAll(const struct mInputMap* map, uint32_t type, const char* sectionName, struct Configuration* config) {
324	size_t i;
325	for (i = 0; i < map->info->nKeys; ++i) {
326		if (!map->info->keyId[i]) {
327			continue;
328		}
329		_saveKey(map, type, sectionName, config, i, map->info->keyId[i]);
330		_clearAxis(sectionName, config, map->info->keyId[i]);
331	}
332
333	const struct mInputMapImpl* impl = _lookupMapConst(map, type);
334	if (!impl) {
335		return;
336	}
337	struct mInputAxisSave save = {
338		config,
339		sectionName,
340		map->info
341	};
342	TableEnumerate(&impl->axes, _saveAxis, &save);
343
344	for (i = 0; i < mInputHatListSize(&impl->hats); ++i) {
345		const struct mInputHatBindings* hat = mInputHatListGetConstPointer(&impl->hats, i);
346		_saveHat(sectionName, config, i, hat);
347	}
348}
349
350void mInputMapInit(struct mInputMap* map, const struct mInputPlatformInfo* info) {
351	map->maps = 0;
352	map->numMaps = 0;
353	map->info = info;
354}
355
356void mInputMapDeinit(struct mInputMap* map) {
357	size_t m;
358	for (m = 0; m < map->numMaps; ++m) {
359		if (map->maps[m].type) {
360			free(map->maps[m].map);
361			TableDeinit(&map->maps[m].axes);
362			mInputHatListDeinit(&map->maps[m].hats);
363		}
364	}
365	free(map->maps);
366	map->maps = 0;
367	map->numMaps = 0;
368}
369
370int mInputMapKey(const struct mInputMap* map, uint32_t type, int key) {
371	size_t m;
372	const struct mInputMapImpl* impl = _lookupMapConst(map, type);
373	if (!impl || !impl->map) {
374		return -1;
375	}
376
377	for (m = 0; m < map->info->nKeys; ++m) {
378		if (impl->map[m] == key) {
379			return m;
380		}
381	}
382	return -1;
383}
384
385int mInputMapKeyBits(const struct mInputMap* map, uint32_t type, uint32_t bits, unsigned offset) {
386	int keys = 0;
387	for (; bits; bits >>= 1, ++offset) {
388		if (bits & 1) {
389			int key = mInputMapKey(map, type, offset);
390			if (key == -1) {
391				continue;
392			}
393			keys |= 1 << key;
394		}
395	}
396	return keys;
397}
398
399void mInputBindKey(struct mInputMap* map, uint32_t type, int key, int input) {
400	struct mInputMapImpl* impl = _guaranteeMap(map, type);
401	if (input < 0 || (size_t) input >= map->info->nKeys) {
402		return;
403	}
404	mInputUnbindKey(map, type, input);
405	impl->map[input] = key;
406}
407
408void mInputUnbindKey(struct mInputMap* map, uint32_t type, int input) {
409	struct mInputMapImpl* impl = _lookupMap(map, type);
410	if (input < 0 || (size_t) input >= map->info->nKeys) {
411		return;
412	}
413	if (impl) {
414		impl->map[input] = -1;
415	}
416}
417
418int mInputQueryBinding(const struct mInputMap* map, uint32_t type, int input) {
419	if (input < 0 || (size_t) input >= map->info->nKeys) {
420		return -1;
421	}
422
423	const struct mInputMapImpl* impl = _lookupMapConst(map, type);
424	if (!impl || !impl->map) {
425		return -1;
426	}
427
428	return impl->map[input];
429}
430
431int mInputMapAxis(const struct mInputMap* map, uint32_t type, int axis, int value) {
432	const struct mInputMapImpl* impl = _lookupMapConst(map, type);
433	if (!impl) {
434		return -1;
435	}
436	struct mInputAxis* description = TableLookup(&impl->axes, axis);
437	if (!description) {
438		return -1;
439	}
440	int state = 0;
441	if (value < description->deadLow) {
442		state = -1;
443	} else if (value > description->deadHigh) {
444		state = 1;
445	}
446	if (state > 0) {
447		return description->highDirection;
448	}
449	if (state < 0) {
450		return description->lowDirection;
451	}
452	return -1;
453}
454
455int mInputClearAxis(const struct mInputMap* map, uint32_t type, int axis, int keys) {
456	const struct mInputMapImpl* impl = _lookupMapConst(map, type);
457	if (!impl) {
458		return keys;
459	}
460	struct mInputAxis* description = TableLookup(&impl->axes, axis);
461	if (!description) {
462		return keys;
463	}
464	return keys &= ~((1 << description->highDirection) | (1 << description->lowDirection));
465}
466
467void mInputBindAxis(struct mInputMap* map, uint32_t type, int axis, const struct mInputAxis* description) {
468	struct mInputMapImpl* impl = _guaranteeMap(map, type);
469	struct mInputAxis d2 = *description;
470	TableEnumerate(&impl->axes, _unbindAxis, &d2.highDirection);
471	TableEnumerate(&impl->axes, _unbindAxis, &d2.lowDirection);
472	struct mInputAxis* dup = malloc(sizeof(struct mInputAxis));
473	*dup = *description;
474	TableInsert(&impl->axes, axis, dup);
475}
476
477void mInputUnbindAxis(struct mInputMap* map, uint32_t type, int axis) {
478	struct mInputMapImpl* impl = _lookupMap(map, type);
479	if (impl) {
480		TableRemove(&impl->axes, axis);
481	}
482}
483
484void mInputUnbindAllAxes(struct mInputMap* map, uint32_t type) {
485	struct mInputMapImpl* impl = _lookupMap(map, type);
486	if (impl) {
487		TableClear(&impl->axes);
488	}
489}
490
491const struct mInputAxis* mInputQueryAxis(const struct mInputMap* map, uint32_t type, int axis) {
492	const struct mInputMapImpl* impl = _lookupMapConst(map, type);
493	if (!impl) {
494		return 0;
495	}
496	return TableLookup(&impl->axes, axis);
497}
498
499void mInputEnumerateAxes(const struct mInputMap* map, uint32_t type, void (handler(int axis, const struct mInputAxis* description, void* user)), void* user) {
500	const struct mInputMapImpl* impl = _lookupMapConst(map, type);
501	if (!impl) {
502		return;
503	}
504	struct mInputAxisEnumerate enumUser = {
505		handler,
506		user
507	};
508	TableEnumerate(&impl->axes, _enumerateAxis, &enumUser);
509}
510
511int mInputMapHat(const struct mInputMap* map, uint32_t type, int id, int direction) {
512	const struct mInputMapImpl* impl = _lookupMapConst(map, type);
513	if (!impl) {
514		return 0;
515	}
516	if (id >= (ssize_t) mInputHatListSize(&impl->hats)) {
517		return 0;
518	}
519	const struct mInputHatBindings* description = mInputHatListGetConstPointer(&impl->hats, id);
520	int mapping = 0;
521	if (direction & M_INPUT_HAT_UP && description->up >= 0) {
522		mapping |= 1 << description->up;
523	}
524	if (direction & M_INPUT_HAT_RIGHT && description->right >= 0) {
525		mapping |= 1 << description->right;
526	}
527	if (direction & M_INPUT_HAT_DOWN && description->down >= 0) {
528		mapping |= 1 << description->down;
529	}
530	if (direction & M_INPUT_HAT_LEFT && description->left >= 0) {
531		mapping |= 1 << description->left;
532	}
533	return mapping;
534}
535
536void mInputBindHat(struct mInputMap* map, uint32_t type, int id, const struct mInputHatBindings* bindings) {
537	struct mInputMapImpl* impl = _guaranteeMap(map, type);
538	while (id >= (ssize_t) mInputHatListSize(&impl->hats)) {
539		*mInputHatListAppend(&impl->hats) = (struct mInputHatBindings) { -1, -1, -1, -1 };
540	}
541	*mInputHatListGetPointer(&impl->hats, id) = *bindings;
542}
543
544
545bool mInputQueryHat(const struct mInputMap* map, uint32_t type, int id, struct mInputHatBindings* bindings) {
546	const struct mInputMapImpl* impl = _lookupMapConst(map, type);
547	if (!impl) {
548		return false;
549	}
550	if (id >= (ssize_t) mInputHatListSize(&impl->hats)) {
551		return false;
552	}
553	*bindings = *mInputHatListGetConstPointer(&impl->hats, id);
554	return true;
555}
556
557void mInputUnbindHat(struct mInputMap* map, uint32_t type, int id) {
558	struct mInputMapImpl* impl = _lookupMap(map, type);
559	if (!impl) {
560		return;
561	}
562	if (mInputHatListSize(&impl->hats) && id + 1 == (ssize_t) mInputHatListSize(&impl->hats)) {
563		mInputHatListResize(&impl->hats, -1);
564	} else {
565		struct mInputHatBindings* description = mInputHatListGetPointer(&impl->hats, id);
566		memset(description, -1, sizeof(*description));
567	}
568}
569
570void mInputUnbindAllHats(struct mInputMap* map, uint32_t type) {
571	struct mInputMapImpl* impl = _lookupMap(map, type);
572	if (impl) {
573		mInputHatListClear(&impl->hats);
574	}
575}
576
577void mInputMapLoad(struct mInputMap* map, uint32_t type, const struct Configuration* config) {
578	char sectionName[SECTION_NAME_MAX];
579	_makeSectionName(map->info->platformName, sectionName, SECTION_NAME_MAX, type);
580	_loadAll(map, type, sectionName, config);
581}
582
583void mInputMapSave(const struct mInputMap* map, uint32_t type, struct Configuration* config) {
584	char sectionName[SECTION_NAME_MAX];
585	_makeSectionName(map->info->platformName, sectionName, SECTION_NAME_MAX, type);
586	_saveAll(map, type, sectionName, config);
587}
588
589bool mInputProfileLoad(struct mInputMap* map, uint32_t type, const struct Configuration* config, const char* profile) {
590	char sectionName[SECTION_NAME_MAX];
591	snprintf(sectionName, SECTION_NAME_MAX, "%s.input-profile.%s", map->info->platformName, profile);
592	sectionName[SECTION_NAME_MAX - 1] = '\0';
593	return _loadAll(map, type, sectionName, config);
594}
595
596void mInputProfileSave(const struct mInputMap* map, uint32_t type, struct Configuration* config, const char* profile) {
597	char sectionName[SECTION_NAME_MAX];
598	snprintf(sectionName, SECTION_NAME_MAX, "%s.input-profile.%s", map->info->platformName, profile);
599	sectionName[SECTION_NAME_MAX - 1] = '\0';
600	_saveAll(map, type, sectionName, config);
601}
602
603const char* mInputGetPreferredDevice(const struct Configuration* config, const char* platformName, uint32_t type, int playerId) {
604	char sectionName[SECTION_NAME_MAX];
605	_makeSectionName(platformName, sectionName, SECTION_NAME_MAX, type);
606
607	char deviceId[KEY_NAME_MAX];
608	snprintf(deviceId, sizeof(deviceId), "device%i", playerId);
609	return ConfigurationGetValue(config, sectionName, deviceId);
610}
611
612void mInputSetPreferredDevice(struct Configuration* config, const char* platformName, uint32_t type, int playerId, const char* deviceName) {
613	char sectionName[SECTION_NAME_MAX];
614	_makeSectionName(platformName, sectionName, SECTION_NAME_MAX, type);
615
616	char deviceId[KEY_NAME_MAX];
617	snprintf(deviceId, sizeof(deviceId), "device%i", playerId);
618	return ConfigurationSetValue(config, sectionName, deviceId, deviceName);
619}
620
621const char* mInputGetCustomValue(const struct Configuration* config, const char* platformName, uint32_t type, const char* key, const char* profile) {
622	char sectionName[SECTION_NAME_MAX];
623	if (profile) {
624		snprintf(sectionName, SECTION_NAME_MAX, "%s.input-profile.%s", platformName, profile);
625		const char* value = ConfigurationGetValue(config, sectionName, key);
626		if (value) {
627			return value;
628		}
629	}
630	_makeSectionName(platformName, sectionName, SECTION_NAME_MAX, type);
631	return ConfigurationGetValue(config, sectionName, key);
632}
633
634void mInputSetCustomValue(struct Configuration* config, const char* platformName, uint32_t type, const char* key, const char* value, const char* profile) {
635	char sectionName[SECTION_NAME_MAX];
636	if (profile) {
637		snprintf(sectionName, SECTION_NAME_MAX, "%s.input-profile.%s", platformName, profile);
638		ConfigurationSetValue(config, sectionName, key, value);
639	}
640	_makeSectionName(platformName, sectionName, SECTION_NAME_MAX, type);
641	ConfigurationSetValue(config, sectionName, key, value);
642}