all repos — mgba @ c50b228243bb6c381c13b290e2d864075dc34194

mGBA Game Boy Advance Emulator

src/gba/gba-input.c (view raw)

  1/* Copyright (c) 2013-2014 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 "gba-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	uint32_t type;
 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 bool _getIntValue(const struct Configuration* config, const char* section, const char* key, int* value) {
 49	const char* strValue = ConfigurationGetValue(config, section, key);
 50	if (!strValue) {
 51		return false;
 52	}
 53	char* end;
 54	long intValue = strtol(strValue, &end, 10);
 55	if (*end) {
 56		return false;
 57	}
 58	*value = intValue;
 59	return true;
 60}
 61
 62static struct GBAInputMapImpl* _lookupMap(struct GBAInputMap* map, uint32_t type) {
 63	size_t m;
 64	struct GBAInputMapImpl* impl = 0;
 65	for (m = 0; m < map->numMaps; ++m) {
 66		if (map->maps[m].type == type) {
 67			impl = &map->maps[m];
 68			break;
 69		}
 70	}
 71	return impl;
 72}
 73
 74static const struct GBAInputMapImpl* _lookupMapConst(const struct GBAInputMap* map, uint32_t type) {
 75	size_t m;
 76	const struct GBAInputMapImpl* impl = 0;
 77	for (m = 0; m < map->numMaps; ++m) {
 78		if (map->maps[m].type == type) {
 79			impl = &map->maps[m];
 80			break;
 81		}
 82	}
 83	return impl;
 84}
 85
 86static struct GBAInputMapImpl* _guaranteeMap(struct GBAInputMap* map, uint32_t type) {
 87	struct GBAInputMapImpl* impl = 0;
 88	if (map->numMaps == 0) {
 89		map->maps = malloc(sizeof(*map->maps));
 90		map->numMaps = 1;
 91		impl = &map->maps[0];
 92		impl->type = type;
 93		impl->map = calloc(GBA_KEY_MAX, sizeof(enum GBAKey));
 94		TableInit(&impl->axes, 2, free);
 95	} else {
 96		impl = _lookupMap(map, type);
 97	}
 98	if (!impl) {
 99		size_t m;
100		for (m = 0; m < map->numMaps; ++m) {
101			if (!map->maps[m].type) {
102				impl = &map->maps[m];
103				break;
104			}
105		}
106		if (impl) {
107			impl->type = type;
108			impl->map = calloc(GBA_KEY_MAX, sizeof(enum GBAKey));
109		} else {
110			map->maps = realloc(map->maps, sizeof(*map->maps) * map->numMaps * 2);
111			for (m = map->numMaps * 2 - 1; m > map->numMaps; --m) {
112				map->maps[m].type = 0;
113				map->maps[m].map = 0;
114			}
115			map->numMaps *= 2;
116			impl = &map->maps[m];
117			impl->type = type;
118			impl->map = calloc(GBA_KEY_MAX, sizeof(enum GBAKey));
119		}
120		TableInit(&impl->axes, 2, free);
121	}
122	return impl;
123}
124
125static void _loadKey(struct GBAInputMap* map, uint32_t type, const struct Configuration* config, enum GBAKey key, const char* keyName) {
126	char sectionName[SECTION_NAME_MAX];
127	snprintf(sectionName, SECTION_NAME_MAX, "input.%c%c%c%c", type >> 24, type >> 16, type >> 8, type);
128	sectionName[SECTION_NAME_MAX - 1] = '\0';
129
130	char keyKey[KEY_NAME_MAX];
131	snprintf(keyKey, KEY_NAME_MAX, "key%s", keyName);
132	keyKey[KEY_NAME_MAX - 1] = '\0';
133
134	int value;
135	if (!_getIntValue(config, sectionName, keyKey, &value)) {
136		return;
137	}
138	GBAInputBindKey(map, type, value, key);
139}
140
141static void _loadAxis(struct GBAInputMap* map, uint32_t type, const struct Configuration* config, enum GBAKey direction, const char* axisName) {
142	char sectionName[SECTION_NAME_MAX];
143	snprintf(sectionName, SECTION_NAME_MAX, "input.%c%c%c%c", type >> 24, type >> 16, type >> 8, type);
144	sectionName[SECTION_NAME_MAX - 1] = '\0';
145
146	char axisKey[KEY_NAME_MAX];
147	snprintf(axisKey, KEY_NAME_MAX, "axis%sValue", axisName);
148	axisKey[KEY_NAME_MAX - 1] = '\0';
149	int value;
150	if (!_getIntValue(config, sectionName, axisKey, &value)) {
151		return;
152	}
153
154	snprintf(axisKey, KEY_NAME_MAX, "axis%sAxis", axisName);
155	axisKey[KEY_NAME_MAX - 1] = '\0';
156	int axis;
157	const char* strValue = ConfigurationGetValue(config, sectionName, axisKey);
158	if (!strValue || !strValue[0]) {
159		return;
160	}
161	char* end;
162	axis = strtoul(&strValue[1], &end, 10);
163	if (*end) {
164		return;
165	}
166
167	const struct GBAAxis* description = GBAInputQueryAxis(map, type, axis);
168	struct GBAAxis realDescription = { GBA_KEY_NONE, GBA_KEY_NONE, 0, 0 };
169	if (description) {
170		realDescription = *description;
171	}
172	if (strValue[0] == '+') {
173		realDescription.deadHigh = value;
174		realDescription.highDirection = direction;
175	} else if (strValue[0] == '-') {
176		realDescription.deadLow = value;
177		realDescription.lowDirection = direction;
178	}
179	GBAInputBindAxis(map, type, axis, &realDescription);
180}
181
182static void _saveKey(const struct GBAInputMap* map, uint32_t type, struct Configuration* config, enum GBAKey key, const char* keyName) {
183	char sectionName[SECTION_NAME_MAX];
184	snprintf(sectionName, SECTION_NAME_MAX, "input.%c%c%c%c", type >> 24, type >> 16, type >> 8, type);
185	sectionName[SECTION_NAME_MAX - 1] = '\0';
186
187	char keyKey[KEY_NAME_MAX];
188	snprintf(keyKey, KEY_NAME_MAX, "key%s", keyName);
189	keyKey[KEY_NAME_MAX - 1] = '\0';
190
191	int value = GBAInputQueryBinding(map, type, key);
192	char keyValue[KEY_VALUE_MAX];
193	snprintf(keyValue, KEY_VALUE_MAX, "%" PRIi32, value);
194
195	ConfigurationSetValue(config, sectionName, keyKey, keyValue);
196}
197
198static void _clearAxis(uint32_t type, struct Configuration* config, const char* axisName) {
199	char sectionName[SECTION_NAME_MAX];
200	snprintf(sectionName, SECTION_NAME_MAX, "input.%c%c%c%c", type >> 24, type >> 16, type >> 8, type);
201	sectionName[SECTION_NAME_MAX - 1] = '\0';
202
203	char axisKey[KEY_NAME_MAX];
204	snprintf(axisKey, KEY_NAME_MAX, "axis%sValue", axisName);
205	axisKey[KEY_NAME_MAX - 1] = '\0';
206	ConfigurationClearValue(config, sectionName, axisKey);
207
208	snprintf(axisKey, KEY_NAME_MAX, "axis%sAxis", axisName);
209	axisKey[KEY_NAME_MAX - 1] = '\0';
210	ConfigurationClearValue(config, sectionName, axisKey);
211}
212
213static void _saveAxis(uint32_t axis, void* dp, void* up) {
214	struct GBAAxisSave* user = up;
215	const struct GBAAxis* description = dp;
216
217	uint32_t type = user->type;
218	char sectionName[SECTION_NAME_MAX];
219	snprintf(sectionName, SECTION_NAME_MAX, "input.%c%c%c%c", type >> 24, type >> 16, type >> 8, type);
220	sectionName[SECTION_NAME_MAX - 1] = '\0';
221
222	if (description->lowDirection != GBA_KEY_NONE) {
223		const char* keyName = GBAKeyNames[description->lowDirection];
224
225		char axisKey[KEY_NAME_MAX];
226		snprintf(axisKey, KEY_NAME_MAX, "axis%sValue", keyName);
227		axisKey[KEY_NAME_MAX - 1] = '\0';
228		ConfigurationSetIntValue(user->config, sectionName, axisKey, description->deadLow);
229
230		snprintf(axisKey, KEY_NAME_MAX, "axis%sAxis", keyName);
231		axisKey[KEY_NAME_MAX - 1] = '\0';
232
233		char axisInfo[AXIS_INFO_MAX];
234		snprintf(axisInfo, AXIS_INFO_MAX, "-%u", axis);
235		axisInfo[AXIS_INFO_MAX - 1] = '\0';
236		ConfigurationSetValue(user->config, sectionName, axisKey, axisInfo);
237	}
238	if (description->highDirection != GBA_KEY_NONE) {
239		const char* keyName = GBAKeyNames[description->highDirection];
240
241		char axisKey[KEY_NAME_MAX];
242		snprintf(axisKey, KEY_NAME_MAX, "axis%sValue", keyName);
243		axisKey[KEY_NAME_MAX - 1] = '\0';
244		ConfigurationSetIntValue(user->config, sectionName, axisKey, description->deadHigh);
245
246		snprintf(axisKey, KEY_NAME_MAX, "axis%sAxis", keyName);
247		axisKey[KEY_NAME_MAX - 1] = '\0';
248
249		char axisInfo[AXIS_INFO_MAX];
250		snprintf(axisInfo, AXIS_INFO_MAX, "+%u", axis);
251		axisInfo[AXIS_INFO_MAX - 1] = '\0';
252		ConfigurationSetValue(user->config, sectionName, axisKey, axisInfo);
253	}
254}
255
256void _enumerateAxis(uint32_t axis, void* dp, void* ep) {
257	struct GBAAxisEnumerate* enumUser = ep;
258	const struct GBAAxis* description = dp;
259	enumUser->handler(axis, description, enumUser->user);
260}
261
262void _unbindAxis(uint32_t axis, void* dp, void* user) {
263	UNUSED(axis);
264	enum GBAKey* key = user;
265	struct GBAAxis* description = dp;
266	if (description->highDirection == *key) {
267		description->highDirection = GBA_KEY_NONE;
268	}
269	if (description->lowDirection == *key) {
270		description->lowDirection = GBA_KEY_NONE;
271	}
272}
273
274void GBAInputMapInit(struct GBAInputMap* map) {
275	map->maps = 0;
276	map->numMaps = 0;
277}
278
279void GBAInputMapDeinit(struct GBAInputMap* map) {
280	size_t m;
281	for (m = 0; m < map->numMaps; ++m) {
282		if (map->maps[m].type) {
283			free(map->maps[m].map);
284			TableDeinit(&map->maps[m].axes);
285		}
286	}
287	free(map->maps);
288	map->maps = 0;
289	map->numMaps = 0;
290}
291
292enum GBAKey GBAInputMapKey(const struct GBAInputMap* map, uint32_t type, int key) {
293	size_t m;
294	const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
295	if (!impl || !impl->map) {
296		return GBA_KEY_NONE;
297	}
298
299	for (m = 0; m < GBA_KEY_MAX; ++m) {
300		if (impl->map[m] == key) {
301			return m;
302		}
303	}
304	return GBA_KEY_NONE;
305}
306
307void GBAInputBindKey(struct GBAInputMap* map, uint32_t type, int key, enum GBAKey input) {
308	struct GBAInputMapImpl* impl = _guaranteeMap(map, type);
309	GBAInputUnbindKey(map, type, input);
310	impl->map[input] = key;
311}
312
313void GBAInputUnbindKey(struct GBAInputMap* map, uint32_t type, enum GBAKey input) {
314	struct GBAInputMapImpl* impl = _lookupMap(map, type);
315	if (impl) {
316		impl->map[input] = GBA_NO_MAPPING;
317	}
318	TableEnumerate(&impl->axes, _unbindAxis, &input);
319}
320
321int GBAInputQueryBinding(const struct GBAInputMap* map, uint32_t type, enum GBAKey input) {
322	if (input >= GBA_KEY_MAX) {
323		return 0;
324	}
325
326	const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
327	if (!impl || !impl->map) {
328		return 0;
329	}
330
331	return impl->map[input];
332}
333
334enum GBAKey GBAInputMapAxis(const struct GBAInputMap* map, uint32_t type, int axis, int value) {
335	const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
336	if (!impl) {
337		return GBA_KEY_NONE;
338	}
339	struct GBAAxis* description = TableLookup(&impl->axes, axis);
340	if (!description) {
341		return GBA_KEY_NONE;
342	}
343	int state = 0;
344	if (value < description->deadLow) {
345		state = -1;
346	} else if (value > description->deadHigh) {
347		state = 1;
348	}
349	if (state > 0) {
350		return description->highDirection;
351	}
352	if (state < 0) {
353		return description->lowDirection;
354	}
355	return GBA_KEY_NONE;
356}
357
358int GBAInputClearAxis(const struct GBAInputMap* map, uint32_t type, int axis, int keys) {
359	const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
360	if (!impl) {
361		return keys;
362	}
363	struct GBAAxis* description = TableLookup(&impl->axes, axis);
364	if (!description) {
365		return keys;
366	}
367	return keys &= ~((1 << description->highDirection) | (1 << description->lowDirection));
368}
369
370void GBAInputBindAxis(struct GBAInputMap* map, uint32_t type, int axis, const struct GBAAxis* description) {
371	struct GBAInputMapImpl* impl = _guaranteeMap(map, type);
372	struct GBAAxis* dup = malloc(sizeof(struct GBAAxis));
373	GBAInputUnbindKey(map, type, description->lowDirection);
374	GBAInputUnbindKey(map, type, description->highDirection);
375	*dup = *description;
376	TableInsert(&impl->axes, axis, dup);
377}
378
379void GBAInputUnbindAxis(struct GBAInputMap* map, uint32_t type, int axis) {
380	struct GBAInputMapImpl* impl = _lookupMap(map, type);
381	if (impl) {
382		TableRemove(&impl->axes, axis);
383	}
384}
385
386void GBAInputUnbindAllAxes(struct GBAInputMap* map, uint32_t type) {
387	struct GBAInputMapImpl* impl = _lookupMap(map, type);
388	if (impl) {
389		TableClear(&impl->axes);
390	}
391}
392
393const struct GBAAxis* GBAInputQueryAxis(const struct GBAInputMap* map, uint32_t type, int axis) {
394	const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
395	if (!impl) {
396		return 0;
397	}
398	return TableLookup(&impl->axes, axis);
399}
400
401void GBAInputEnumerateAxes(const struct GBAInputMap* map, uint32_t type, void (handler(int axis, const struct GBAAxis* description, void* user)), void* user) {
402	const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
403	if (!impl) {
404		return;
405	}
406	struct GBAAxisEnumerate enumUser = {
407		handler,
408		user
409	};
410	TableEnumerate(&impl->axes, _enumerateAxis, &enumUser);
411}
412
413void GBAInputMapLoad(struct GBAInputMap* map, uint32_t type, const struct Configuration* config) {
414	_loadKey(map, type, config, GBA_KEY_A, "A");
415	_loadKey(map, type, config, GBA_KEY_B, "B");
416	_loadKey(map, type, config, GBA_KEY_L, "L");
417	_loadKey(map, type, config, GBA_KEY_R, "R");
418	_loadKey(map, type, config, GBA_KEY_START, "Start");
419	_loadKey(map, type, config, GBA_KEY_SELECT, "Select");
420	_loadKey(map, type, config, GBA_KEY_UP, "Up");
421	_loadKey(map, type, config, GBA_KEY_DOWN, "Down");
422	_loadKey(map, type, config, GBA_KEY_LEFT, "Left");
423	_loadKey(map, type, config, GBA_KEY_RIGHT, "Right");
424
425	_loadAxis(map, type, config, GBA_KEY_A, "A");
426	_loadAxis(map, type, config, GBA_KEY_B, "B");
427	_loadAxis(map, type, config, GBA_KEY_L, "L");
428	_loadAxis(map, type, config, GBA_KEY_R, "R");
429	_loadAxis(map, type, config, GBA_KEY_START, "Start");
430	_loadAxis(map, type, config, GBA_KEY_SELECT, "Select");
431	_loadAxis(map, type, config, GBA_KEY_UP, "Up");
432	_loadAxis(map, type, config, GBA_KEY_DOWN, "Down");
433	_loadAxis(map, type, config, GBA_KEY_LEFT, "Left");
434	_loadAxis(map, type, config, GBA_KEY_RIGHT, "Right");
435}
436
437void GBAInputMapSave(const struct GBAInputMap* map, uint32_t type, struct Configuration* config) {
438	_saveKey(map, type, config, GBA_KEY_A, "A");
439	_saveKey(map, type, config, GBA_KEY_B, "B");
440	_saveKey(map, type, config, GBA_KEY_L, "L");
441	_saveKey(map, type, config, GBA_KEY_R, "R");
442	_saveKey(map, type, config, GBA_KEY_START, "Start");
443	_saveKey(map, type, config, GBA_KEY_SELECT, "Select");
444	_saveKey(map, type, config, GBA_KEY_UP, "Up");
445	_saveKey(map, type, config, GBA_KEY_DOWN, "Down");
446	_saveKey(map, type, config, GBA_KEY_LEFT, "Left");
447	_saveKey(map, type, config, GBA_KEY_RIGHT, "Right");
448
449	_clearAxis(type, config, "A");
450	_clearAxis(type, config, "B");
451	_clearAxis(type, config, "L");
452	_clearAxis(type, config, "R");
453	_clearAxis(type, config, "Start");
454	_clearAxis(type, config, "Select");
455	_clearAxis(type, config, "Up");
456	_clearAxis(type, config, "Down");
457	_clearAxis(type, config, "Left");
458	_clearAxis(type, config, "Right");
459
460	const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
461	if (!impl) {
462		return;
463	}
464	struct GBAAxisSave save = {
465		config,
466		type
467	};
468	TableEnumerate(&impl->axes, _saveAxis, &save);
469}