1 /*
2     Copyright (c) 2015, Cong Xu
3 
4     Redistribution and use in source and binary forms, with or without
5     modification, are permitted provided that the following conditions are met:
6 
7     Redistributions of source code must retain the above copyright notice, this
8     list of conditions and the following disclaimer.
9     Redistributions in binary form must reproduce the above copyright notice,
10     this list of conditions and the following disclaimer in the documentation
11     and/or other materials provided with the distribution.
12 
13     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
14     AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15     IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16     ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
17     LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18     CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19     SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20     INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21     CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22     ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
23     POSSIBILITY OF SUCH DAMAGE.
24 */
25 #include "SDL_joystickbuttonnames.h"
26 
27 #include <stdbool.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 
32 #include <SDL_pixels.h>
33 
34 
35 typedef struct
36 {
37 	SDL_JoystickGUID guid;
38 	char joystickName[256];
39 	char buttonNames[SDL_CONTROLLER_BUTTON_MAX][256];
40 	SDL_Color buttonColors[SDL_CONTROLLER_BUTTON_MAX];
41 	char axisNames[SDL_CONTROLLER_AXIS_MAX][256];
42 	SDL_Color axisColors[SDL_CONTROLLER_AXIS_MAX];
43 } JoystickButtonNames;
44 
45 const char *err = NULL;
46 JoystickButtonNames *jbn = NULL;
47 int nJBN = 0;
48 JoystickButtonNames jDefault;
49 #include "db.h"
50 
51 
52 static JoystickButtonNames DefaultJoystickButtonNames(void);
53 static int ReadMappingsString(const char *s);
54 
SDLJBN_Init(void)55 int SDLJBN_Init(void)
56 {
57 	// Don't reinitialise
58 	if (jbn != NULL) return 0;
59 
60 	jDefault = DefaultJoystickButtonNames();
61 
62 	ReadMappingsString(db);
63 
64 	return 0;
65 }
66 
SDLJBN_Quit(void)67 void SDLJBN_Quit(void)
68 {
69 	SDL_free(jbn);
70 	jbn = NULL;
71 }
72 
SDLJBN_AddMappingsFromFile(const char * file)73 int SDLJBN_AddMappingsFromFile(const char *file)
74 {
75 	int ret = 0;
76 	char *s = NULL;
77 
78 	SDLJBN_Init();
79 	SDL_RWops *rwops = SDL_RWFromFile(file, "r");
80 	if (rwops == NULL)
81 	{
82 		err = "Cannot open file";
83 		ret = -1;
84 		goto bail;
85 	}
86 
87 	// Read file into memory
88 	const Sint64 fsize = rwops->size(rwops);
89 	if (fsize == -1)
90 	{
91 		err = "Cannot find file size";
92 		ret = -1;
93 		goto bail;
94 	}
95 	s = SDL_malloc((size_t)fsize + 1);
96 	if (s == NULL)
97 	{
98 		err = "Out of memory";
99 		ret = -1;
100 		goto bail;
101 	}
102 	if (SDL_RWread(rwops, s, (size_t)fsize, 1) == 0)
103 	{
104 		err = "Cannot read file";
105 		ret = -1;
106 		goto bail;
107 	}
108 	s[fsize] = '\0';
109 
110 	ret = ReadMappingsString(s);
111 
112 bail:
113 	if (rwops != NULL)
114 	{
115 		SDL_RWclose(rwops);
116 	}
117 	SDL_free(s);
118 	return ret;
119 }
120 
ReadMappingsString(const char * s)121 static int ReadMappingsString(const char *s)
122 {
123 #define READ_TOKEN(buf, p, end)\
124 	if (end == NULL)\
125 	{\
126 		strcpy(buf, p);\
127 		p = NULL;\
128 	}\
129 	else\
130 	{\
131 		strncpy(buf, p, end - p);\
132 		buf[end - p] = '\0';\
133 		p = end + 1;\
134 	}
135 
136 	// Read compiled string button names into memory
137 	// Search for a matching GUID + joystickName in the db
138 	int read = 0;
139 	for (const char *cur = s; cur;)
140 	{
141 		const char *nl = strchr(cur, '\n');
142 		char line[2048];
143 		READ_TOKEN(line, cur, nl);
144 
145 		char buf[256];
146 
147 		// Check for the platform string
148 		sprintf(buf, "platform:%s", SDL_GetPlatform());
149 		if (strstr(line, buf) == NULL) continue;
150 
151 #define STR_NOT_EQ(expected, actualP, actualEnd)\
152 	strlen(expected) != (actualEnd) - (actualP) || \
153 	strncmp(expected, actualP, (actualEnd) - (actualP)) != 0
154 		const char *curL = line;
155 		// Ignore hash comments
156 		if (*curL == '#') continue;
157 
158 		const char *nextComma;
159 		JoystickButtonNames j = jDefault;
160 
161 		// Read GUID
162 		nextComma = strchr(curL, ',');
163 		if (nextComma == NULL || cur == nextComma) continue;
164 		READ_TOKEN(buf, curL, nextComma);
165 		j.guid = SDL_JoystickGetGUIDFromString(buf);
166 
167 		// Read joystick name
168 		nextComma = strchr(curL, ',');
169 		if (nextComma == NULL || curL == nextComma) continue;
170 		READ_TOKEN(j.joystickName, curL, nextComma);
171 
172 		// Check if GUID+joystick name already exists
173 		bool exists = false;
174 		for (int i = 0; i < nJBN; i++)
175 		{
176 			const JoystickButtonNames *jp = jbn + i;
177 			if (memcmp(&jp->guid, &j.guid, sizeof j.guid) == 0 &&
178 				strcmp(jp->joystickName, j.joystickName) == 0)
179 			{
180 				exists = true;
181 				break;
182 			}
183 		}
184 		if (exists) continue;
185 
186 		// Read name and colors
187 		for (;; curL = nextComma + 1)
188 		{
189 			nextComma = strchr(curL, ',');
190 			if (nextComma == NULL) break;
191 
192 			const char *nextColon;
193 
194 			nextColon = strchr(curL, ':');
195 			if (nextColon == NULL || curL == nextColon) continue;
196 			READ_TOKEN(buf, curL, nextColon);
197 			const SDL_GameControllerButton button =
198 				SDL_GameControllerGetButtonFromString(buf);
199 			const SDL_GameControllerAxis axis =
200 				SDL_GameControllerGetAxisFromString(buf);
201 			char *name;
202 			SDL_Color *color;
203 			if (button != SDL_CONTROLLER_BUTTON_INVALID)
204 			{
205 				name = j.buttonNames[(int)button];
206 				color = &j.buttonColors[(int)button];
207 			}
208 			else if (axis != SDL_CONTROLLER_AXIS_INVALID)
209 			{
210 				name = j.axisNames[(int)axis];
211 				color = &j.axisColors[(int)axis];
212 			}
213 			else
214 			{
215 				continue;
216 			}
217 			// Read the real button/axis name
218 			nextColon = strchr(curL, ':');
219 			if (nextColon == NULL) continue;
220 			READ_TOKEN(name, curL, nextColon);
221 			// R
222 			nextColon = strchr(curL, ':');
223 			if (nextColon == NULL) continue;
224 			READ_TOKEN(buf, curL, nextColon);
225 			color->r = (Uint8)atoi(buf);
226 			// G
227 			nextColon = strchr(curL, ':');
228 			if (nextColon == NULL) continue;
229 			READ_TOKEN(buf, curL, nextColon);
230 			color->g = (Uint8)atoi(buf);
231 			// B
232 			READ_TOKEN(buf, curL, nextComma);
233 			color->b = (Uint8)atoi(buf);
234 
235 			color->a = 255;
236 		}
237 		nJBN++;
238 		read++;
239 		jbn = SDL_realloc(jbn, nJBN * sizeof *jbn);
240 		memcpy(jbn + nJBN - 1, &j, sizeof j);
241 	}
242 	return read;
243 }
244 
245 static const char *DefaultButtonName(SDL_GameControllerButton button);
246 static const char *DefaultAxisName(SDL_GameControllerAxis axis);
247 static SDL_Color DefaultButtonColor(SDL_GameControllerButton button);
248 static SDL_Color DefaultAxisColor(SDL_GameControllerAxis axis);
249 
DefaultJoystickButtonNames(void)250 static JoystickButtonNames DefaultJoystickButtonNames(void)
251 {
252 	JoystickButtonNames j;
253 	memset(&j, 0, sizeof j);
254 	for (SDL_GameControllerButton button = SDL_CONTROLLER_BUTTON_A;
255 		button < SDL_CONTROLLER_BUTTON_MAX;
256 		button++)
257 	{
258 		strcpy(j.buttonNames[(int)button], DefaultButtonName(button));
259 		j.buttonColors[(int)button] = DefaultButtonColor(button);
260 	}
261 	for (SDL_GameControllerAxis axis = SDL_CONTROLLER_AXIS_LEFTX;
262 		axis < SDL_CONTROLLER_AXIS_MAX;
263 		axis++)
264 	{
265 		strcpy(j.axisNames[(int)axis], DefaultAxisName(axis));
266 		j.axisColors[(int)axis] = DefaultAxisColor(axis);
267 	}
268 	return j;
269 }
270 
DefaultButtonName(SDL_GameControllerButton button)271 static const char *DefaultButtonName(SDL_GameControllerButton button)
272 {
273 	switch (button)
274 	{
275 	case SDL_CONTROLLER_BUTTON_A: return "A";
276 	case SDL_CONTROLLER_BUTTON_B: return "B";
277 	case SDL_CONTROLLER_BUTTON_X: return "X";
278 	case SDL_CONTROLLER_BUTTON_Y: return "Y";
279 	case SDL_CONTROLLER_BUTTON_BACK: return "Back";
280 	case SDL_CONTROLLER_BUTTON_GUIDE: return "Guide";
281 	case SDL_CONTROLLER_BUTTON_START: return "Start";
282 	case SDL_CONTROLLER_BUTTON_LEFTSTICK: return "Left Stick";
283 	case SDL_CONTROLLER_BUTTON_RIGHTSTICK: return "Right Stick";
284 	case SDL_CONTROLLER_BUTTON_LEFTSHOULDER: return "LB";
285 	case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: return "RB";
286 	case SDL_CONTROLLER_BUTTON_DPAD_UP: return "D-pad Up";
287 	case SDL_CONTROLLER_BUTTON_DPAD_DOWN: return "D-pad Down";
288 	case SDL_CONTROLLER_BUTTON_DPAD_LEFT: return "D-pad Left";
289 	case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: return "D-pad Right";
290 	default: return "";
291 	}
292 }
293 
DefaultAxisName(SDL_GameControllerAxis axis)294 static const char *DefaultAxisName(SDL_GameControllerAxis axis)
295 {
296 	switch (axis)
297 	{
298 	case SDL_CONTROLLER_AXIS_LEFTX: return "Left Stick X";
299 	case SDL_CONTROLLER_AXIS_LEFTY: return "Left Stick Y";
300 	case SDL_CONTROLLER_AXIS_RIGHTX: return "Right Stick X";
301 	case SDL_CONTROLLER_AXIS_RIGHTY: return "Right Stick Y";
302 	case SDL_CONTROLLER_AXIS_TRIGGERLEFT: return "LT";
303 	case SDL_CONTROLLER_AXIS_TRIGGERRIGHT: return "RT";
304 	default: return "";
305 	}
306 }
307 
308 static SDL_Color NewColor(Uint8 r, Uint8 g, Uint8 b);
309 
DefaultButtonColor(SDL_GameControllerButton button)310 static SDL_Color DefaultButtonColor(SDL_GameControllerButton button)
311 {
312 	// Default colors for Xbox 360 controller
313 	switch (button)
314 	{
315 	case SDL_CONTROLLER_BUTTON_A: return NewColor(96, 160, 0);
316 	case SDL_CONTROLLER_BUTTON_B: return NewColor(240, 0, 0);
317 	case SDL_CONTROLLER_BUTTON_X: return NewColor(0, 96, 208);
318 	case SDL_CONTROLLER_BUTTON_Y: return NewColor(255, 160, 0);
319 	case SDL_CONTROLLER_BUTTON_BACK: return NewColor(224, 224, 224);
320 	case SDL_CONTROLLER_BUTTON_GUIDE: return NewColor(128, 176, 0);
321 	case SDL_CONTROLLER_BUTTON_START: return NewColor(224, 224, 224);
322 	case SDL_CONTROLLER_BUTTON_LEFTSTICK: return NewColor(96, 128, 128);
323 	case SDL_CONTROLLER_BUTTON_RIGHTSTICK: return NewColor(96, 128, 128);
324 	case SDL_CONTROLLER_BUTTON_LEFTSHOULDER: return NewColor(224, 224, 224);
325 	case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: return NewColor(224, 224, 224);
326 	case SDL_CONTROLLER_BUTTON_DPAD_UP: return NewColor(96, 128, 128);
327 	case SDL_CONTROLLER_BUTTON_DPAD_DOWN: return NewColor(96, 128, 128);
328 	case SDL_CONTROLLER_BUTTON_DPAD_LEFT: return NewColor(96, 128, 128);
329 	case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: return NewColor(96, 128, 128);
330 	default: return NewColor(0, 0, 0);
331 	}
332 }
333 
DefaultAxisColor(SDL_GameControllerAxis axis)334 static SDL_Color DefaultAxisColor(SDL_GameControllerAxis axis)
335 {
336 	// Default colors for Xbox 360 controller
337 	switch (axis)
338 	{
339 	case SDL_CONTROLLER_AXIS_LEFTX: return NewColor(96, 128, 128);
340 	case SDL_CONTROLLER_AXIS_LEFTY: return NewColor(96, 128, 128);
341 	case SDL_CONTROLLER_AXIS_RIGHTX: return NewColor(96, 128, 128);
342 	case SDL_CONTROLLER_AXIS_RIGHTY: return NewColor(96, 128, 128);
343 	case SDL_CONTROLLER_AXIS_TRIGGERLEFT: return NewColor(224, 224, 224);
344 	case SDL_CONTROLLER_AXIS_TRIGGERRIGHT: return NewColor(224, 224, 224);
345 	default: return NewColor(0, 0, 0);
346 	}
347 }
348 
SDLJBN_GetButtonNameAndColor(SDL_Joystick * joystick,SDL_GameControllerButton button,char * s,Uint8 * r,Uint8 * g,Uint8 * b)349 int SDLJBN_GetButtonNameAndColor(SDL_Joystick *joystick,
350                                  SDL_GameControllerButton button,
351                                  char *s, Uint8 *r, Uint8 *g, Uint8 *b)
352 {
353 	SDLJBN_Init();
354 
355 	if (joystick == NULL)
356 	{
357 		err = "joystick is NULL";
358 		return -1;
359 	}
360 	if (button < SDL_CONTROLLER_BUTTON_A ||
361 		button >= SDL_CONTROLLER_BUTTON_MAX)
362 	{
363 		err = "button is invalid";
364 		return -1;
365 	}
366 	// Use defaults first
367 	if (s) strcpy(s, jDefault.buttonNames[(int)button]);
368 	if (r) *r = jDefault.buttonColors[(int)button].r;
369 	if (g) *g = jDefault.buttonColors[(int)button].g;
370 	if (b) *b = jDefault.buttonColors[(int)button].b;
371 
372 	const SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick);
373 	const char *joystickName = SDL_JoystickName(joystick);
374 	// Search for a matching GUID + joystickName in the db
375 	for (int i = 0; i < nJBN; i++)
376 	{
377 		const JoystickButtonNames *j = jbn + i;
378 		if (memcmp(&j->guid, &guid, sizeof guid) == 0 &&
379 			strcmp(j->joystickName, joystickName) == 0)
380 		{
381 			// GUID and joystick name match; read name and colors
382 			if (s && strlen(j->buttonNames[(int)button]) > 0)
383 				strcpy(s, j->buttonNames[(int)button]);
384 			if (r) *r = j->buttonColors[(int)button].r;
385 			if (g) *g = j->buttonColors[(int)button].g;
386 			if (b) *b = j->buttonColors[(int)button].b;
387 			break;
388 		}
389 	}
390 
391 	return 0;
392 }
393 
394 
SDLJBN_GetAxisNameAndColor(SDL_Joystick * joystick,SDL_GameControllerAxis axis,char * s,Uint8 * r,Uint8 * g,Uint8 * b)395 int SDLJBN_GetAxisNameAndColor(SDL_Joystick *joystick,
396                                SDL_GameControllerAxis axis,
397                                char *s, Uint8 *r, Uint8 *g, Uint8 *b)
398 {
399 	SDLJBN_Init();
400 
401 	if (joystick == NULL)
402 	{
403 		err = "joystick is NULL";
404 		return -1;
405 	}
406 	if (axis < SDL_CONTROLLER_AXIS_LEFTX || axis >= SDL_CONTROLLER_AXIS_MAX)
407 	{
408 		err = "axis is invalid";
409 		return -1;
410 	}
411 	// Use defaults first
412 	if (s) strcpy(s, jDefault.axisNames[(int)axis]);
413 	if (r) *r = jDefault.axisColors[(int)axis].r;
414 	if (g) *g = jDefault.axisColors[(int)axis].g;
415 	if (b) *b = jDefault.axisColors[(int)axis].b;
416 
417 	const SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick);
418 	const char *joystickName = SDL_JoystickName(joystick);
419 	// Search for a matching GUID + joystickName in the db
420 	for (int i = 0; i < nJBN; i++)
421 	{
422 		const JoystickButtonNames *j = jbn + i;
423 		if (memcmp(&j->guid, &guid, sizeof guid) == 0 &&
424 			strcmp(j->joystickName, joystickName) == 0)
425 		{
426 			// GUID and joystick name match; read name and colors
427 			if (s && strlen(j->axisNames[(int)axis]) > 0)
428 				strcpy(s, j->axisNames[(int)axis]);
429 			if (r) *r = j->axisColors[(int)axis].r;
430 			if (g) *g = j->axisColors[(int)axis].g;
431 			if (b) *b = j->axisColors[(int)axis].b;
432 			break;
433 		}
434 	}
435 
436 	return 0;
437 }
438 
NewColor(Uint8 r,Uint8 g,Uint8 b)439 static SDL_Color NewColor(Uint8 r, Uint8 g, Uint8 b)
440 {
441 	SDL_Color c;
442 	c.r = r;
443 	c.g = g;
444 	c.b = b;
445 	c.a = 255;
446 	return c;
447 }
448 
SDLJBN_GetError(void)449 const char *SDLJBN_GetError(void)
450 {
451 	return err;
452 }
453