diff --git a/docs/retroarch.1 b/docs/retroarch.1 index 0993ed674e..d535b4cfaa 100644 --- a/docs/retroarch.1 +++ b/docs/retroarch.1 @@ -21,7 +21,7 @@ libretro provides emulation of a game system, and can be implemented by any fron .TP \fB[rom file]\fR If no rom file path is defined on the command line, \fBretroarch\fR will try to load a rom from \fBstdin\fR. -\fBretroarch\fR only supports loading of \fBuncompressed\fR roms, such as *.sfc or *.smc. +\fBretroarch\fR does not uncompress roms. ROM types supported depends on the libretro implementation used. .TP \fB--help, -h\fR @@ -37,6 +37,10 @@ Path to a libretro implementation which is to be used. This option will override any setting in a config file. This option is only available if RetroArch is compiled with dynamic libretro loading. +If PATH is a directory, RetroArch will try to find the first implementation that claims to support +the extension of the ROM used, and use that libretro implementation. +If two or more different implementations claim to support a certain ROM extension, there might be collisions. + .TP \fB--save PATH, -s PATH\fR Overrides the path used for save ram (*.srm). diff --git a/dynamic.c b/dynamic.c index c86638e07a..9137e6c0e1 100644 --- a/dynamic.c +++ b/dynamic.c @@ -17,6 +17,7 @@ #include "general.h" #include "compat/strl.h" #include "compat/posix_string.h" +#include "file.h" #include #ifdef RARCH_CONSOLE @@ -90,14 +91,98 @@ size_t (*pretro_get_memory_size)(unsigned); static void set_environment(void); static void set_environment_defaults(void); +#if defined(__APPLE__) +#define DYNAMIC_EXT "dylib" +#elif defined(_WIN32) +#define DYNAMIC_EXT "dll" +#else +#define DYNAMIC_EXT "so" +#endif + +static bool find_first_libretro(char *path, size_t size, + const char *dir, const char *rom_path) +{ + bool ret = false; + const char *ext = path_get_extension(rom_path); + if (!ext || !*ext) + { + RARCH_ERR("Path has no extension. Cannot infer libretro implementation.\n"); + return false; + } + + RARCH_LOG("Searching for valid libretro implementation in: \"%s\".\n", dir); + + struct string_list *list = dir_list_new(dir, DYNAMIC_EXT, false); + if (!list) + { + RARCH_ERR("Couldn't open directory: \"%s\".\n", dir); + return false; + } + + for (size_t i = 0; i < list->size && !ret; i++) + { + RARCH_LOG("Checking library: \"%s\".\n", list->elems[i].data); + dylib_t lib = dylib_load(list->elems[i].data); + if (!lib) + continue; + + void (*proc)(struct retro_system_info*) = + (void (*)(struct retro_system_info*))dylib_proc(lib, "retro_get_system_info"); + + if (!proc) + { + dylib_close(lib); + continue; + } + + struct retro_system_info info = {0}; + proc(&info); + + if (!info.valid_extensions) + { + dylib_close(lib); + continue; + } + + struct string_list *supported_ext = string_split(info.valid_extensions, "|"); + + if (string_list_find_elem(supported_ext, ext)) + { + strlcpy(path, list->elems[i].data, size); + ret = true; + } + + string_list_free(supported_ext); + dylib_close(lib); + } + + dir_list_free(list); + return ret; +} + static void load_symbols(void) { + const char *libretro_path = g_settings.libretro; + char libretro_core_buffer[PATH_MAX]; + + if (path_is_directory(g_settings.libretro)) + { + if (!find_first_libretro(libretro_core_buffer, sizeof(libretro_core_buffer), + g_settings.libretro, g_extern.fullpath)) + { + RARCH_ERR("libretro_path is a directory, but no valid libretro implementation was found.\n"); + rarch_fail(1, "load_dynamic()"); + } + + libretro_path = libretro_core_buffer; + } + #ifdef HAVE_DYNAMIC - RARCH_LOG("Loading dynamic libretro from: \"%s\"\n", g_settings.libretro); - lib_handle = dylib_load(g_settings.libretro); + RARCH_LOG("Loading dynamic libretro from: \"%s\"\n", libretro_path); + lib_handle = dylib_load(libretro_path); if (!lib_handle) { - RARCH_ERR("Failed to open dynamic library: \"%s\"\n", g_settings.libretro); + RARCH_ERR("Failed to open dynamic library: \"%s\"\n", libretro_path); rarch_fail(1, "load_dynamic()"); } #endif diff --git a/file.h b/file.h index 308f74a127..2379d83440 100644 --- a/file.h +++ b/file.h @@ -63,6 +63,7 @@ void dir_list_free(struct string_list *list); bool string_list_find_elem(const struct string_list *list, const char *elem); bool string_list_find_elem_prefix(const struct string_list *list, const char *prefix, const char *elem); struct string_list *string_split(const char *str, const char *delim); +void string_list_free(struct string_list *list); bool path_is_directory(const char *path); bool path_file_exists(const char *path); diff --git a/file_path.c b/file_path.c index 8fe2d99628..684dfc0c9c 100644 --- a/file_path.c +++ b/file_path.c @@ -47,7 +47,7 @@ #include #endif -static void string_list_free(struct string_list *list) +void string_list_free(struct string_list *list) { if (!list) return; diff --git a/retroarch.cfg b/retroarch.cfg index ef36e7ef96..7cfa4e7e16 100644 --- a/retroarch.cfg +++ b/retroarch.cfg @@ -15,6 +15,10 @@ # Load libretro from a dynamic location for dynamically built RetroArch. # This option is mandatory. + +# If a directory, RetroArch will look through the directory until it finds an implementation +# that appears to support the extension of the ROM loaded. +# This could fail if ROM extensions overlap. # libretro_path = "/path/to/libretro.so" # Environment variables internally in RetroArch.