all repos — mgba @ 9b74e27d7ae51dd7d84cdecb3f311e17c291ffca

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