all repos — mgba @ 9dc49df0bca23925cfcc8193a1770463fd9916cf

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	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 (input < 0 || input >= GBA_KEY_MAX) {
316		return;
317	}
318	if (impl) {
319		impl->map[input] = GBA_NO_MAPPING;
320	}
321	TableEnumerate(&impl->axes, _unbindAxis, &input);
322}
323
324int GBAInputQueryBinding(const struct GBAInputMap* map, uint32_t type, enum GBAKey input) {
325	if (input >= GBA_KEY_MAX) {
326		return 0;
327	}
328
329	const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
330	if (!impl || !impl->map) {
331		return 0;
332	}
333
334	return impl->map[input];
335}
336
337enum GBAKey GBAInputMapAxis(const struct GBAInputMap* map, uint32_t type, int axis, int value) {
338	const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
339	if (!impl) {
340		return GBA_KEY_NONE;
341	}
342	struct GBAAxis* description = TableLookup(&impl->axes, axis);
343	if (!description) {
344		return GBA_KEY_NONE;
345	}
346	int state = 0;
347	if (value < description->deadLow) {
348		state = -1;
349	} else if (value > description->deadHigh) {
350		state = 1;
351	}
352	if (state > 0) {
353		return description->highDirection;
354	}
355	if (state < 0) {
356		return description->lowDirection;
357	}
358	return GBA_KEY_NONE;
359}
360
361int GBAInputClearAxis(const struct GBAInputMap* map, uint32_t type, int axis, int keys) {
362	const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
363	if (!impl) {
364		return keys;
365	}
366	struct GBAAxis* description = TableLookup(&impl->axes, axis);
367	if (!description) {
368		return keys;
369	}
370	return keys &= ~((1 << description->highDirection) | (1 << description->lowDirection));
371}
372
373void GBAInputBindAxis(struct GBAInputMap* map, uint32_t type, int axis, const struct GBAAxis* description) {
374	struct GBAInputMapImpl* impl = _guaranteeMap(map, type);
375	struct GBAAxis* dup = malloc(sizeof(struct GBAAxis));
376	GBAInputUnbindKey(map, type, description->lowDirection);
377	GBAInputUnbindKey(map, type, description->highDirection);
378	*dup = *description;
379	TableInsert(&impl->axes, axis, dup);
380}
381
382void GBAInputUnbindAxis(struct GBAInputMap* map, uint32_t type, int axis) {
383	struct GBAInputMapImpl* impl = _lookupMap(map, type);
384	if (impl) {
385		TableRemove(&impl->axes, axis);
386	}
387}
388
389void GBAInputUnbindAllAxes(struct GBAInputMap* map, uint32_t type) {
390	struct GBAInputMapImpl* impl = _lookupMap(map, type);
391	if (impl) {
392		TableClear(&impl->axes);
393	}
394}
395
396const struct GBAAxis* GBAInputQueryAxis(const struct GBAInputMap* map, uint32_t type, int axis) {
397	const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
398	if (!impl) {
399		return 0;
400	}
401	return TableLookup(&impl->axes, axis);
402}
403
404void GBAInputEnumerateAxes(const struct GBAInputMap* map, uint32_t type, void (handler(int axis, const struct GBAAxis* description, void* user)), void* user) {
405	const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
406	if (!impl) {
407		return;
408	}
409	struct GBAAxisEnumerate enumUser = {
410		handler,
411		user
412	};
413	TableEnumerate(&impl->axes, _enumerateAxis, &enumUser);
414}
415
416void GBAInputMapLoad(struct GBAInputMap* map, uint32_t type, const struct Configuration* config) {
417	_loadKey(map, type, config, GBA_KEY_A, "A");
418	_loadKey(map, type, config, GBA_KEY_B, "B");
419	_loadKey(map, type, config, GBA_KEY_L, "L");
420	_loadKey(map, type, config, GBA_KEY_R, "R");
421	_loadKey(map, type, config, GBA_KEY_START, "Start");
422	_loadKey(map, type, config, GBA_KEY_SELECT, "Select");
423	_loadKey(map, type, config, GBA_KEY_UP, "Up");
424	_loadKey(map, type, config, GBA_KEY_DOWN, "Down");
425	_loadKey(map, type, config, GBA_KEY_LEFT, "Left");
426	_loadKey(map, type, config, GBA_KEY_RIGHT, "Right");
427
428	_loadAxis(map, type, config, GBA_KEY_A, "A");
429	_loadAxis(map, type, config, GBA_KEY_B, "B");
430	_loadAxis(map, type, config, GBA_KEY_L, "L");
431	_loadAxis(map, type, config, GBA_KEY_R, "R");
432	_loadAxis(map, type, config, GBA_KEY_START, "Start");
433	_loadAxis(map, type, config, GBA_KEY_SELECT, "Select");
434	_loadAxis(map, type, config, GBA_KEY_UP, "Up");
435	_loadAxis(map, type, config, GBA_KEY_DOWN, "Down");
436	_loadAxis(map, type, config, GBA_KEY_LEFT, "Left");
437	_loadAxis(map, type, config, GBA_KEY_RIGHT, "Right");
438}
439
440void GBAInputMapSave(const struct GBAInputMap* map, uint32_t type, struct Configuration* config) {
441	_saveKey(map, type, config, GBA_KEY_A, "A");
442	_saveKey(map, type, config, GBA_KEY_B, "B");
443	_saveKey(map, type, config, GBA_KEY_L, "L");
444	_saveKey(map, type, config, GBA_KEY_R, "R");
445	_saveKey(map, type, config, GBA_KEY_START, "Start");
446	_saveKey(map, type, config, GBA_KEY_SELECT, "Select");
447	_saveKey(map, type, config, GBA_KEY_UP, "Up");
448	_saveKey(map, type, config, GBA_KEY_DOWN, "Down");
449	_saveKey(map, type, config, GBA_KEY_LEFT, "Left");
450	_saveKey(map, type, config, GBA_KEY_RIGHT, "Right");
451
452	_clearAxis(type, config, "A");
453	_clearAxis(type, config, "B");
454	_clearAxis(type, config, "L");
455	_clearAxis(type, config, "R");
456	_clearAxis(type, config, "Start");
457	_clearAxis(type, config, "Select");
458	_clearAxis(type, config, "Up");
459	_clearAxis(type, config, "Down");
460	_clearAxis(type, config, "Left");
461	_clearAxis(type, config, "Right");
462
463	const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
464	if (!impl) {
465		return;
466	}
467	struct GBAAxisSave save = {
468		config,
469		type
470	};
471	TableEnumerate(&impl->axes, _saveAxis, &save);
472}