all repos — mgba @ 1c1fbfe163964cb9a6744b134bda71464dd232f4

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