all repos — mgba @ 61467cacd9569e0f56f3c01663edc40d9712d1b9

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 _saveAxis(uint32_t axis, void* dp, void* up) {
199	struct GBAAxisSave* user = up;
200	const struct GBAAxis* description = dp;
201
202	uint32_t type = user->type;
203	char sectionName[SECTION_NAME_MAX];
204	snprintf(sectionName, SECTION_NAME_MAX, "input.%c%c%c%c", type >> 24, type >> 16, type >> 8, type);
205	sectionName[SECTION_NAME_MAX - 1] = '\0';
206
207	if (description->lowDirection != GBA_KEY_NONE) {
208		const char* keyName = GBAKeyNames[description->lowDirection];
209
210		char axisKey[KEY_NAME_MAX];
211		snprintf(axisKey, KEY_NAME_MAX, "axis%sValue", keyName);
212		axisKey[KEY_NAME_MAX - 1] = '\0';
213		ConfigurationSetIntValue(user->config, sectionName, axisKey, description->deadLow);
214
215		snprintf(axisKey, KEY_NAME_MAX, "axis%sAxis", keyName);
216		axisKey[KEY_NAME_MAX - 1] = '\0';
217
218		char axisInfo[AXIS_INFO_MAX];
219		snprintf(axisInfo, AXIS_INFO_MAX, "-%u", axis);
220		axisInfo[AXIS_INFO_MAX - 1] = '\0';
221		ConfigurationSetValue(user->config, sectionName, axisKey, axisInfo);
222	}
223	if (description->highDirection != GBA_KEY_NONE) {
224		const char* keyName = GBAKeyNames[description->highDirection];
225
226		char axisKey[KEY_NAME_MAX];
227		snprintf(axisKey, KEY_NAME_MAX, "axis%sValue", keyName);
228		axisKey[KEY_NAME_MAX - 1] = '\0';
229		ConfigurationSetIntValue(user->config, sectionName, axisKey, description->deadHigh);
230
231		snprintf(axisKey, KEY_NAME_MAX, "axis%sAxis", keyName);
232		axisKey[KEY_NAME_MAX - 1] = '\0';
233
234		char axisInfo[AXIS_INFO_MAX];
235		snprintf(axisInfo, AXIS_INFO_MAX, "+%u", axis);
236		axisInfo[AXIS_INFO_MAX - 1] = '\0';
237		ConfigurationSetValue(user->config, sectionName, axisKey, axisInfo);
238	}
239}
240
241void _enumerateAxis(uint32_t axis, void* dp, void* ep) {
242	struct GBAAxisEnumerate* enumUser = ep;
243	const struct GBAAxis* description = dp;
244	enumUser->handler(axis, description, enumUser->user);
245}
246
247void GBAInputMapInit(struct GBAInputMap* map) {
248	map->maps = 0;
249	map->numMaps = 0;
250}
251
252void GBAInputMapDeinit(struct GBAInputMap* map) {
253	size_t m;
254	for (m = 0; m < map->numMaps; ++m) {
255		if (map->maps[m].type) {
256			free(map->maps[m].map);
257			TableDeinit(&map->maps[m].axes);
258		}
259	}
260	free(map->maps);
261	map->maps = 0;
262	map->numMaps = 0;
263}
264
265enum GBAKey GBAInputMapKey(const struct GBAInputMap* map, uint32_t type, int key) {
266	size_t m;
267	const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
268	if (!impl || !impl->map) {
269		return GBA_KEY_NONE;
270	}
271
272	for (m = 0; m < GBA_KEY_MAX; ++m) {
273		if (impl->map[m] == key) {
274			return m;
275		}
276	}
277	return GBA_KEY_NONE;
278}
279
280void GBAInputBindKey(struct GBAInputMap* map, uint32_t type, int key, enum GBAKey input) {
281	struct GBAInputMapImpl* impl = _guaranteeMap(map, type);
282	impl->map[input] = key;
283}
284
285void GBAInputUnbindKey(struct GBAInputMap* map, uint32_t type, enum GBAKey input) {
286	struct GBAInputMapImpl* impl = _lookupMap(map, type);
287	if (impl) {
288		impl->map[input] = GBA_NO_MAPPING;
289	}
290}
291
292int GBAInputQueryBinding(const struct GBAInputMap* map, uint32_t type, enum GBAKey input) {
293	if (input >= GBA_KEY_MAX) {
294		return 0;
295	}
296
297	const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
298	if (!impl || !impl->map) {
299		return 0;
300	}
301
302	return impl->map[input];
303}
304
305enum GBAKey GBAInputMapAxis(const struct GBAInputMap* map, uint32_t type, int axis, int value) {
306	const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
307	if (!impl) {
308		return GBA_KEY_NONE;
309	}
310	struct GBAAxis* description = TableLookup(&impl->axes, axis);
311	if (!description) {
312		return GBA_KEY_NONE;
313	}
314	int state = 0;
315	if (value < description->deadLow) {
316		state = -1;
317	} else if (value > description->deadHigh) {
318		state = 1;
319	}
320	if (state > 0) {
321		return description->highDirection;
322	}
323	if (state < 0) {
324		return description->lowDirection;
325	}
326	return GBA_KEY_NONE;
327}
328
329int GBAInputClearAxis(const struct GBAInputMap* map, uint32_t type, int axis, int keys) {
330	const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
331	if (!impl) {
332		return keys;
333	}
334	struct GBAAxis* description = TableLookup(&impl->axes, axis);
335	if (!description) {
336		return keys;
337	}
338	return keys &= ~((1 << description->highDirection) | (1 << description->lowDirection));
339}
340
341void GBAInputBindAxis(struct GBAInputMap* map, uint32_t type, int axis, const struct GBAAxis* description) {
342	struct GBAInputMapImpl* impl = _guaranteeMap(map, type);
343	struct GBAAxis* dup = malloc(sizeof(struct GBAAxis));
344	*dup = *description;
345	TableInsert(&impl->axes, axis, dup);
346}
347
348void GBAInputUnbindAxis(struct GBAInputMap* map, uint32_t type, int axis) {
349	struct GBAInputMapImpl* impl = _lookupMap(map, type);
350	if (impl) {
351		TableRemove(&impl->axes, axis);
352	}
353}
354
355void GBAInputUnbindAllAxes(struct GBAInputMap* map, uint32_t type) {
356	struct GBAInputMapImpl* impl = _lookupMap(map, type);
357	if (impl) {
358		TableClear(&impl->axes);
359	}
360}
361
362const struct GBAAxis* GBAInputQueryAxis(const struct GBAInputMap* map, uint32_t type, int axis) {
363	const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
364	if (!impl) {
365		return 0;
366	}
367	return TableLookup(&impl->axes, axis);
368}
369
370void GBAInputEnumerateAxes(const struct GBAInputMap* map, uint32_t type, void (handler(int axis, const struct GBAAxis* description, void* user)), void* user) {
371	const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
372	if (!impl) {
373		return;
374	}
375	struct GBAAxisEnumerate enumUser = {
376		handler,
377		user
378	};
379	TableEnumerate(&impl->axes, _enumerateAxis, &enumUser);
380}
381
382void GBAInputMapLoad(struct GBAInputMap* map, uint32_t type, const struct Configuration* config) {
383	_loadKey(map, type, config, GBA_KEY_A, "A");
384	_loadKey(map, type, config, GBA_KEY_B, "B");
385	_loadKey(map, type, config, GBA_KEY_L, "L");
386	_loadKey(map, type, config, GBA_KEY_R, "R");
387	_loadKey(map, type, config, GBA_KEY_START, "Start");
388	_loadKey(map, type, config, GBA_KEY_SELECT, "Select");
389	_loadKey(map, type, config, GBA_KEY_UP, "Up");
390	_loadKey(map, type, config, GBA_KEY_DOWN, "Down");
391	_loadKey(map, type, config, GBA_KEY_LEFT, "Left");
392	_loadKey(map, type, config, GBA_KEY_RIGHT, "Right");
393
394	_loadAxis(map, type, config, GBA_KEY_UP, "Up");
395	_loadAxis(map, type, config, GBA_KEY_DOWN, "Down");
396	_loadAxis(map, type, config, GBA_KEY_LEFT, "Left");
397	_loadAxis(map, type, config, GBA_KEY_RIGHT, "Right");
398}
399
400void GBAInputMapSave(const struct GBAInputMap* map, uint32_t type, struct Configuration* config) {
401	_saveKey(map, type, config, GBA_KEY_A, "A");
402	_saveKey(map, type, config, GBA_KEY_B, "B");
403	_saveKey(map, type, config, GBA_KEY_L, "L");
404	_saveKey(map, type, config, GBA_KEY_R, "R");
405	_saveKey(map, type, config, GBA_KEY_START, "Start");
406	_saveKey(map, type, config, GBA_KEY_SELECT, "Select");
407	_saveKey(map, type, config, GBA_KEY_UP, "Up");
408	_saveKey(map, type, config, GBA_KEY_DOWN, "Down");
409	_saveKey(map, type, config, GBA_KEY_LEFT, "Left");
410	_saveKey(map, type, config, GBA_KEY_RIGHT, "Right");
411
412	const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
413	if (!impl) {
414		return;
415	}
416	struct GBAAxisSave save = {
417		config,
418		type
419	};
420	TableEnumerate(&impl->axes, _saveAxis, &save);
421}