diff --git a/include/SDL_gamecontroller.h b/include/SDL_gamecontroller.h index e55749ce3..84d58d89e 100644 --- a/include/SDL_gamecontroller.h +++ b/include/SDL_gamecontroller.h @@ -108,6 +108,14 @@ typedef struct SDL_GameControllerButtonBind * */ +/** + * Load a set of mappings from a file, filtered by the current SDL_GetPlatform() + * A community sourced database of controllers is available at https://raw.github.com/gabomdq/SDL_GameControllerDB/master/gamecontrollerdb.txt + * + * \return number of mappings added, -1 on error + */ +extern DECLSPEC int SDLCALL SDL_GameControllerAddMappingsFromFile( const char* mapDB ); + /** * Add or update an existing mapping configuration * diff --git a/src/joystick/SDL_gamecontroller.c b/src/joystick/SDL_gamecontroller.c index 26769b8e1..1563f5141 100644 --- a/src/joystick/SDL_gamecontroller.c +++ b/src/joystick/SDL_gamecontroller.c @@ -33,6 +33,7 @@ #endif #define ABS(_x) ((_x) < 0 ? -(_x) : (_x)) +#define SDL_CONTROLLER_PLATFORM_FIELD "platform:" /* a list of currently opened game controllers */ static SDL_GameController *SDL_gamecontrollers = NULL; @@ -653,6 +654,73 @@ void SDL_PrivateGameControllerRefreshMapping( ControllerMapping_t *pControllerMa } } +/* + * Add or update an entry into the Mappings Database + */ +int +SDL_GameControllerAddMappingsFromFile( const char* mapDB ) +{ + const char *platform = SDL_GetPlatform(); + SDL_RWops *rw; + int controllers = 0; + char *buf, *line, *line_end, *tmp, *comma, line_platform[64]; + size_t db_size, platform_len; + + rw = SDL_RWFromFile(mapDB, "rb"); + if (rw == NULL) { + return SDL_SetError("Could not open %s", mapDB); + } + db_size = SDL_RWsize(rw); + + buf = (char *) SDL_malloc(db_size + 1); + if (buf == NULL) { + SDL_RWclose(rw); + return SDL_SetError("Could allocate space to not read DB into memory"); + } + + if (SDL_RWread(rw, buf, db_size, 1) != 1) { + SDL_RWclose(rw); + SDL_free(buf); + return SDL_SetError("Could not read DB"); + } + SDL_RWclose(rw); + + buf[db_size] = '\0'; + line = buf; + + while (line < buf + db_size) { + line_end = SDL_strchr( line, '\n' ); + if (line_end != NULL) { + *line_end = '\0'; + } + else { + line_end = buf + db_size; + } + + /* Extract and verify the platform */ + tmp = SDL_strstr(line, SDL_CONTROLLER_PLATFORM_FIELD); + if ( tmp != NULL ) { + tmp += SDL_strlen(SDL_CONTROLLER_PLATFORM_FIELD); + comma = SDL_strchr(tmp, ','); + if (comma != NULL) { + platform_len = comma - tmp + 1; + if (platform_len + 1 < SDL_arraysize(line_platform)) { + SDL_strlcpy(line_platform, tmp, platform_len); + if(SDL_strncasecmp(line_platform, platform, platform_len) == 0 + && SDL_GameControllerAddMapping(line) > 0) { + controllers++; + } + } + } + } + + line = line_end + 1; + } + + SDL_free(buf); + return controllers; +} + /* * Add or update an entry into the Mappings Database */