diff --git a/configuration.c b/configuration.c index b8d38837d2..81b10d30a7 100644 --- a/configuration.c +++ b/configuration.c @@ -2132,6 +2132,7 @@ static struct config_bool_setting *populate_settings_bool( SETTING_BOOL("input_allow_turbo_dpad", &settings->bools.input_allow_turbo_dpad, true, DEFAULT_ALLOW_TURBO_DPAD, false); SETTING_BOOL("input_auto_mouse_grab", &settings->bools.input_auto_mouse_grab, true, DEFAULT_INPUT_AUTO_MOUSE_GRAB, false); SETTING_BOOL("input_remap_binds_enable", &settings->bools.input_remap_binds_enable, true, true, false); + SETTING_BOOL("input_remap_sort_by_controller_enable", &settings->bools.input_remap_sort_by_controller_enable, true, false, false); SETTING_BOOL("input_hotkey_device_merge", &settings->bools.input_hotkey_device_merge, true, DEFAULT_INPUT_HOTKEY_DEVICE_MERGE, false); SETTING_BOOL("all_users_control_menu", &settings->bools.input_all_users_control_menu, true, DEFAULT_ALL_USERS_CONTROL_MENU, false); #ifdef HAVE_MENU @@ -4601,6 +4602,7 @@ bool config_load_remap(const char *directory_input_remapping, char game_path[PATH_MAX_LENGTH]; /* final path for content-dir-specific configuration (prefix+suffix) */ char content_path[PATH_MAX_LENGTH]; + config_file_t *new_conf = NULL; rarch_system_info_t *sys_info = (rarch_system_info_t*)data; const char *core_name = sys_info ? sys_info->info.library_name : NULL; @@ -4610,6 +4612,13 @@ bool config_load_remap(const char *directory_input_remapping, enum msg_hash_enums msg_remap_loaded = MSG_GAME_REMAP_FILE_LOADED; settings_t *settings = config_st; bool notification_show_remap_load = settings->bools.notification_show_remap_load; + unsigned joypad_port = settings->uints.input_joypad_index[0]; + const char *input_device_name = input_config_get_device_display_name(joypad_port); + const char *input_device_dir = NULL; + char *remap_path = NULL; + bool sort_remaps_by_controller = settings->bools.input_remap_sort_by_controller_enable; + size_t remap_path_total_len = 0; + size_t _len = 0; content_dir_name[0] = '\0'; core_path[0] = '\0'; @@ -4622,6 +4631,36 @@ bool config_load_remap(const char *directory_input_remapping, || string_is_empty(directory_input_remapping)) return false; + if ( sort_remaps_by_controller + && input_device_name != NULL + && !string_is_empty(input_device_name)) + { + /* Ensure directory does not contain special chars */ + input_device_dir = sanitize_path_part(input_device_name, strlen(input_device_name)); + + /* Allocate memory for the new path */ + remap_path_total_len = strlen(core_name) + strlen(input_device_dir) + 2; + remap_path = (char *)malloc(remap_path_total_len); + + /* Build the new path with the controller name */ + _len = strlcpy(remap_path, core_name, remap_path_total_len); + _len += strlcpy(remap_path + _len, PATH_DEFAULT_SLASH(), remap_path_total_len - _len); + _len += strlcpy(remap_path + _len, input_device_dir, remap_path_total_len - _len); + + /* Deallocate as we no longer this */ + free((char*)input_device_dir); + input_device_dir = NULL; + } + else + { + /* Allocate memory for the new path */ + remap_path_total_len = strlen(core_name) + 1; + remap_path = (char *)malloc(remap_path_total_len); + + /* We're not using controller path, just use core name */ + strlcpy(remap_path, core_name, remap_path_total_len); + } + /* Concatenate strings into full paths for core_path, * game_path, content_path */ if (has_content) @@ -4631,24 +4670,26 @@ bool config_load_remap(const char *directory_input_remapping, game_name = path_basename_nocompression(rarch_path_basename); fill_pathname_join_special_ext(game_path, - directory_input_remapping, core_name, + directory_input_remapping, remap_path, game_name, FILE_PATH_REMAP_EXTENSION, sizeof(game_path)); fill_pathname_join_special_ext(content_path, - directory_input_remapping, core_name, + directory_input_remapping, remap_path, content_dir_name, FILE_PATH_REMAP_EXTENSION, sizeof(content_path)); } fill_pathname_join_special_ext(core_path, - directory_input_remapping, core_name, + directory_input_remapping, remap_path, core_name, FILE_PATH_REMAP_EXTENSION, sizeof(core_path)); + free(remap_path); + /* If a game remap file exists, load it. */ if (has_content && (new_conf = config_file_new_from_path_to_string(game_path))) { diff --git a/configuration.h b/configuration.h index 42a89bb8c6..5aff79a60c 100644 --- a/configuration.h +++ b/configuration.h @@ -680,6 +680,7 @@ typedef struct settings /* Input */ bool input_remap_binds_enable; + bool input_remap_sort_by_controller_enable; bool input_autodetect_enable; bool input_sensors_enable; bool input_overlay_enable; diff --git a/intl/msg_hash_lbl.h b/intl/msg_hash_lbl.h index 745d18325b..a597cb1a02 100644 --- a/intl/msg_hash_lbl.h +++ b/intl/msg_hash_lbl.h @@ -2152,6 +2152,10 @@ MSG_HASH( MENU_ENUM_LABEL_INPUT_REMAP_BINDS_ENABLE, "input_remap_binds_enable" ) +MSG_HASH( + MENU_ENUM_LABEL_INPUT_REMAP_SORT_BY_CONTROLLER_ENABLE, + "input_remap_sort_by_controller_enable" + ) MSG_HASH( MENU_ENUM_LABEL_INPUT_SETTINGS, "input_settings" diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index df2c13d45a..b1e91bbd22 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -3349,6 +3349,14 @@ MSG_HASH( MENU_ENUM_SUBLABEL_INPUT_REMAP_BINDS_ENABLE, "Override the input binds with the remapped binds set for the current core." ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_INPUT_REMAP_SORT_BY_CONTROLLER_ENABLE, + "Sort Remaps By Gamepad" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_INPUT_REMAP_SORT_BY_CONTROLLER_ENABLE, + "Remaps will only apply to the active gamepad in which they were saved." + ) MSG_HASH( MENU_ENUM_LABEL_VALUE_INPUT_AUTODETECT_ENABLE, "Autoconfig" diff --git a/libretro-common/file/file_path.c b/libretro-common/file/file_path.c index 180df01c45..6cff2dcdc3 100644 --- a/libretro-common/file/file_path.c +++ b/libretro-common/file/file_path.c @@ -1172,6 +1172,42 @@ size_t fill_pathname_abbreviate_special(char *out_path, return strlcpy(out_path, in_path, size); } +/** + * sanitize_path_part: + * + * @path_part : directory or filename + * + * Takes single part of a path eg. single filename + * or directory, and removes any special chars that are + * unavailable. + * + * @returns new string that has been sanitized + **/ +const char *sanitize_path_part(const char *path_part, size_t size) +{ + int i; + int j = 0; + int len = 0; + char *temp = NULL; + const char *special_chars = "<>:\"/\\|?*"; + + if (string_is_empty(path_part)) + return NULL; + + temp = (char *)malloc((size + 1) * sizeof(char)); + + for (i = 0; path_part[i] != '\0'; i++) + /* Check if the current character is one of the special characters */ + if (strchr(special_chars, path_part[i]) == NULL) + /* If not, copy it to the temporary array */ + temp[j++] = path_part[i]; + + temp[j] = '\0'; + + /* Return the new string */ + return temp; +} + /** * pathname_conform_slashes_to_os: * diff --git a/libretro-common/include/file/file_path.h b/libretro-common/include/file/file_path.h index f1a34a76c5..4648a06afc 100644 --- a/libretro-common/include/file/file_path.h +++ b/libretro-common/include/file/file_path.h @@ -551,6 +551,20 @@ size_t fill_pathname_abbreviate_special(char *out_path, size_t fill_pathname_abbreviated_or_relative(char *out_path, const char *in_refpath, const char *in_path, size_t size); +/** + * sanitize_path_part: + * + * @path_part : directory or filename + * @size : length of path_part + * + * Takes single part of a path eg. single filename + * or directory, and removes any special chars that are + * unavailable. + * + * @returns new string that has been sanitized + **/ +const char *sanitize_path_part(const char *path_part, size_t size); + /** * pathname_conform_slashes_to_os: * diff --git a/menu/cbs/menu_cbs_ok.c b/menu/cbs/menu_cbs_ok.c index c759bd98f6..39a4df1b5b 100644 --- a/menu/cbs/menu_cbs_ok.c +++ b/menu/cbs/menu_cbs_ok.c @@ -3643,13 +3643,20 @@ static int generic_action_ok_remap_file_operation(const char *path, #ifdef HAVE_CONFIGFILE char content_dir_name[DIR_MAX_LENGTH]; char remap_file_path[PATH_MAX_LENGTH]; - struct menu_state *menu_st = menu_state_get_ptr(); - rarch_system_info_t *sys_info = &runloop_state_get_ptr()->system; - const char *core_name = sys_info ? sys_info->info.library_name : NULL; - const char *rarch_path_basename = path_get(RARCH_PATH_BASENAME); - bool has_content = !string_is_empty(rarch_path_basename); - settings_t *settings = config_get_ptr(); - const char *directory_input_remapping = settings->paths.directory_input_remapping; + struct menu_state *menu_st = menu_state_get_ptr(); + rarch_system_info_t *sys_info = &runloop_state_get_ptr()->system; + const char *core_name = sys_info ? sys_info->info.library_name : NULL; + const char *rarch_path_basename = path_get(RARCH_PATH_BASENAME); + bool has_content = !string_is_empty(rarch_path_basename); + settings_t *settings = config_get_ptr(); + const char *directory_input_remapping = settings->paths.directory_input_remapping; + unsigned joypad_port = settings->uints.input_joypad_index[0]; + const char *input_device_name = input_config_get_device_display_name(joypad_port); + const char *input_device_dir = NULL; + char *remap_path = NULL; + bool sort_remaps_by_controller = settings->bools.input_remap_sort_by_controller_enable; + size_t remap_path_total_len = 0; + size_t _len = 0; content_dir_name[0] = '\0'; remap_file_path[0] = '\0'; @@ -3659,11 +3666,41 @@ static int generic_action_ok_remap_file_operation(const char *path, if (string_is_empty(core_name)) return -1; + if ( sort_remaps_by_controller + && input_device_name != NULL + && !string_is_empty(input_device_name)) + { + /* Ensure directory does not contain special chars */ + input_device_dir = sanitize_path_part(input_device_name, strlen(input_device_name)); + + /* Allocate memory for the new path */ + remap_path_total_len = strlen(core_name) + strlen(input_device_dir) + 2; + remap_path = (char *)malloc(remap_path_total_len); + + /* Build the new path with the controller name */ + _len = strlcpy(remap_path, core_name, remap_path_total_len); + _len += strlcpy(remap_path + _len, PATH_DEFAULT_SLASH(), remap_path_total_len - _len); + _len += strlcpy(remap_path + _len, input_device_dir, remap_path_total_len - _len); + + /* Deallocate as we no longer this */ + free((char*)input_device_dir); + input_device_dir = NULL; + } + else + { + /* Allocate memory for the new path */ + remap_path_total_len = strlen(core_name) + 1; + remap_path = (char *)malloc(remap_path_total_len); + + /* We're not using controller path, just use core name */ + strlcpy(remap_path, core_name, remap_path_total_len); + } + switch (action_type) { case ACTION_OK_REMAP_FILE_SAVE_AS: fill_pathname_join_special_ext(remap_file_path, - directory_input_remapping, core_name, + directory_input_remapping, remap_path, path, FILE_PATH_REMAP_EXTENSION, sizeof(remap_file_path)); @@ -3671,7 +3708,7 @@ static int generic_action_ok_remap_file_operation(const char *path, case ACTION_OK_REMAP_FILE_SAVE_CORE: case ACTION_OK_REMAP_FILE_REMOVE_CORE: fill_pathname_join_special_ext(remap_file_path, - directory_input_remapping, core_name, + directory_input_remapping, remap_path, core_name, FILE_PATH_REMAP_EXTENSION, sizeof(remap_file_path)); @@ -3680,7 +3717,7 @@ static int generic_action_ok_remap_file_operation(const char *path, case ACTION_OK_REMAP_FILE_REMOVE_GAME: if (has_content) fill_pathname_join_special_ext(remap_file_path, - directory_input_remapping, core_name, + directory_input_remapping, remap_path, path_basename(rarch_path_basename), FILE_PATH_REMAP_EXTENSION, sizeof(remap_file_path)); @@ -3693,7 +3730,7 @@ static int generic_action_ok_remap_file_operation(const char *path, rarch_path_basename, sizeof(content_dir_name)); fill_pathname_join_special_ext(remap_file_path, - directory_input_remapping, core_name, + directory_input_remapping, remap_path, content_dir_name, FILE_PATH_REMAP_EXTENSION, sizeof(remap_file_path)); @@ -3701,6 +3738,8 @@ static int generic_action_ok_remap_file_operation(const char *path, break; } + free(remap_path); + if (action_type < ACTION_OK_REMAP_FILE_REMOVE_CORE) { if ( !string_is_empty(remap_file_path) diff --git a/menu/cbs/menu_cbs_sublabel.c b/menu/cbs/menu_cbs_sublabel.c index 5e8c1f588a..32c93e8384 100644 --- a/menu/cbs/menu_cbs_sublabel.c +++ b/menu/cbs/menu_cbs_sublabel.c @@ -801,6 +801,7 @@ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_autosave_interval, MENU_ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_replay_max_keep, MENU_ENUM_SUBLABEL_REPLAY_MAX_KEEP) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_replay_checkpoint_interval, MENU_ENUM_SUBLABEL_REPLAY_CHECKPOINT_INTERVAL) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_remap_binds_enable, MENU_ENUM_SUBLABEL_INPUT_REMAP_BINDS_ENABLE) +DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_remap_sort_by_controller_enable, MENU_ENUM_SUBLABEL_INPUT_REMAP_SORT_BY_CONTROLLER_ENABLE) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_autodetect_enable, MENU_ENUM_SUBLABEL_INPUT_AUTODETECT_ENABLE) #if defined(HAVE_DINPUT) || defined(HAVE_WINRAWINPUT) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_nowinkey_enable, MENU_ENUM_SUBLABEL_INPUT_NOWINKEY_ENABLE) @@ -3992,6 +3993,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs, case MENU_ENUM_LABEL_INPUT_REMAP_BINDS_ENABLE: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_input_remap_binds_enable); break; + case MENU_ENUM_LABEL_INPUT_REMAP_SORT_BY_CONTROLLER_ENABLE: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_input_remap_sort_by_controller_enable); + break; case MENU_ENUM_LABEL_AUTOSAVE_INTERVAL: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_autosave_interval); break; diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index 80d5e6cdea..03697abf93 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -7958,6 +7958,7 @@ unsigned menu_displaylist_build_list( {MENU_ENUM_LABEL_INPUT_MAX_USERS, PARSE_ONLY_UINT, true }, {MENU_ENUM_LABEL_INPUT_AUTODETECT_ENABLE, PARSE_ONLY_BOOL, true }, {MENU_ENUM_LABEL_INPUT_REMAP_BINDS_ENABLE, PARSE_ONLY_BOOL, true }, + {MENU_ENUM_LABEL_INPUT_REMAP_SORT_BY_CONTROLLER_ENABLE, PARSE_ONLY_BOOL, true }, {MENU_ENUM_LABEL_INPUT_POLL_TYPE_BEHAVIOR, PARSE_ONLY_UINT, true }, {MENU_ENUM_LABEL_INPUT_ICADE_ENABLE, PARSE_ONLY_BOOL, true }, {MENU_ENUM_LABEL_INPUT_SMALL_KEYBOARD_ENABLE, PARSE_ONLY_BOOL, true }, diff --git a/menu/menu_setting.c b/menu/menu_setting.c index e23cc6533b..11ec78a550 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -15671,6 +15671,22 @@ static bool setting_append_list( SD_FLAG_ADVANCED ); + CONFIG_BOOL( + list, list_info, + &settings->bools.input_remap_sort_by_controller_enable, + MENU_ENUM_LABEL_INPUT_REMAP_SORT_BY_CONTROLLER_ENABLE, + MENU_ENUM_LABEL_VALUE_INPUT_REMAP_SORT_BY_CONTROLLER_ENABLE, + false, + MENU_ENUM_LABEL_VALUE_OFF, + MENU_ENUM_LABEL_VALUE_ON, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler, + SD_FLAG_ADVANCED + ); + CONFIG_BOOL( list, list_info, &settings->bools.input_autodetect_enable, diff --git a/msg_hash.h b/msg_hash.h index 45fc2b0046..6cf193e897 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -1292,6 +1292,7 @@ enum msg_hash_enums MENU_LABEL(INPUT_BIND_TIMEOUT), MENU_LABEL(INPUT_BIND_HOLD), MENU_LABEL(INPUT_REMAP_BINDS_ENABLE), + MENU_LABEL(INPUT_REMAP_SORT_BY_CONTROLLER_ENABLE), MENU_LABEL(MENU_INPUT_SWAP_OK_CANCEL), MENU_LABEL(MENU_INPUT_SWAP_SCROLL), MENU_LABEL(INPUT_OVERLAY_ENABLE),