From f3df4f91a280dbd9e04d6b6d016ecbf22634bbc2 Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Thu, 23 Jul 2020 02:36:23 +1000 Subject: [PATCH] libretro: Implement disk control interface --- src/common/string_util.cpp | 20 +- src/common/string_util.h | 4 + .../libretro_host_interface.cpp | 179 ++++++++++++++++++ .../libretro_host_interface.h | 16 ++ src/duckstation-libretro/main.cpp | 1 + 5 files changed, 218 insertions(+), 2 deletions(-) diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp index 6f23382d5..5787d2b64 100644 --- a/src/common/string_util.cpp +++ b/src/common/string_util.cpp @@ -1,6 +1,6 @@ #include "string_util.h" -#include #include +#include namespace StringUtil { @@ -137,7 +137,23 @@ std::size_t Strlcpy(char* dst, const char* src, std::size_t size) else { std::memcpy(dst, src, size - 1); - dst[size] = '\0'; + dst[size - 1] = '\0'; + } + return len; +} + +std::size_t Strlcpy(char* dst, const std::string_view& src, std::size_t size) +{ + std::size_t len = src.length(); + if (len < size) + { + std::memcpy(dst, src.data(), len); + dst[len] = '\0'; + } + else + { + std::memcpy(dst, src.data(), size - 1); + dst[size - 1] = '\0'; } return len; } diff --git a/src/common/string_util.h b/src/common/string_util.h index 73ad584c5..ea69931d4 100644 --- a/src/common/string_util.h +++ b/src/common/string_util.h @@ -5,6 +5,7 @@ #include #include #include +#include #if defined(__has_include) && __has_include() #include @@ -27,6 +28,9 @@ bool WildcardMatch(const char* subject, const char* mask, bool case_sensitive = /// Safe version of strlcpy. std::size_t Strlcpy(char* dst, const char* src, std::size_t size); +/// Strlcpy from string_view. +std::size_t Strlcpy(char* dst, const std::string_view& src, std::size_t size); + /// Platform-independent strcasecmp static inline int Strcasecmp(const char* s1, const char* s2) { diff --git a/src/duckstation-libretro/libretro_host_interface.cpp b/src/duckstation-libretro/libretro_host_interface.cpp index d20297204..758c5a4f5 100644 --- a/src/duckstation-libretro/libretro_host_interface.cpp +++ b/src/duckstation-libretro/libretro_host_interface.cpp @@ -27,6 +27,7 @@ Log_SetChannel(LibretroHostInterface); #endif LibretroHostInterface g_libretro_host_interface; +#define P_THIS (&g_libretro_host_interface) retro_environment_t g_retro_environment_callback; retro_video_refresh_t g_retro_video_refresh_callback; @@ -232,6 +233,7 @@ bool LibretroHostInterface::retro_load_game(const struct retro_game_info* game) { SystemBootParameters bp; bp.filename = game->path; + bp.media_playlist_index = m_next_disc_index.value_or(0); bp.force_software_renderer = !m_hw_render_callback_valid; if (!BootSystem(bp)) @@ -907,3 +909,180 @@ void LibretroHostInterface::SwitchToSoftwareRenderer() m_display = std::make_unique(); m_system->RecreateGPU(GPURenderer::Software); } + +bool LibretroHostInterface::DiskControlSetEjectState(bool ejected) +{ + System* system = P_THIS->GetSystem(); + if (!system) + { + Log_ErrorPrintf("DiskControlSetEjectState() - no system"); + return false; + } + + Log_DevPrintf("DiskControlSetEjectState(%u)", static_cast(ejected)); + + if (ejected) + { + if (!system->HasMedia()) + return false; + + system->RemoveMedia(); + return true; + } + else + { + const u32 image_to_insert = P_THIS->m_next_disc_index.value_or(0); + Log_DevPrintf("Inserting image %u", image_to_insert); + return system->SwitchMediaFromPlaylist(image_to_insert); + } +} + +bool LibretroHostInterface::DiskControlGetEjectState() +{ + System* system = P_THIS->GetSystem(); + if (!system) + { + Log_ErrorPrintf("DiskControlGetEjectState() - no system"); + return false; + } + + Log_DevPrintf("DiskControlGetEjectState() -> %u", static_cast(system->HasMedia())); + return system->HasMedia(); +} + +unsigned LibretroHostInterface::DiskControlGetImageIndex() +{ + System* system = P_THIS->GetSystem(); + if (!system) + { + Log_ErrorPrintf("DiskControlGetImageIndex() - no system"); + return false; + } + + const u32 index = P_THIS->m_next_disc_index.value_or(system->GetMediaPlaylistIndex()); + Log_DevPrintf("DiskControlGetImageIndex() -> %u", index); + return index; +} + +bool LibretroHostInterface::DiskControlSetImageIndex(unsigned index) +{ + System* system = P_THIS->GetSystem(); + if (!system) + { + Log_ErrorPrintf("DiskControlSetImageIndex() - no system"); + return false; + } + + Log_DevPrintf("DiskControlSetImageIndex(%u)", index); + + if (index >= system->GetMediaPlaylistCount()) + return false; + + P_THIS->m_next_disc_index = index; + return true; +} + +unsigned LibretroHostInterface::DiskControlGetNumImages() +{ + System* system = P_THIS->GetSystem(); + if (!system) + { + Log_ErrorPrintf("DiskControlGetNumImages() - no system"); + return false; + } + + Log_DevPrintf("DiskControlGetNumImages() -> %u", system->GetMediaPlaylistCount()); + return static_cast(system->GetMediaPlaylistCount()); +} + +bool LibretroHostInterface::DiskControlReplaceImageIndex(unsigned index, const retro_game_info* info) +{ + System* system = P_THIS->GetSystem(); + if (!system) + { + Log_ErrorPrintf("DiskControlReplaceImageIndex() - no system"); + return false; + } + + Log_DevPrintf("DiskControlReplaceImageIndex(%u, %s)", index, info ? info->path : "null"); + if (info && info->path) + return system->ReplaceMediaPathFromPlaylist(index, info->path); + else + return system->RemoveMediaPathFromPlaylist(index); +} + +bool LibretroHostInterface::DiskControlAddImageIndex() +{ + System* system = P_THIS->GetSystem(); + if (!system) + { + Log_ErrorPrintf("DiskControlAddImageIndex() - no system"); + return false; + } + + Log_DevPrintf("DiskControlAddImageIndex() -> %zu", system->GetMediaPlaylistCount()); + system->AddMediaPathToPlaylist({}); + return true; +} + +bool LibretroHostInterface::DiskControlSetInitialImage(unsigned index, const char* path) +{ + Log_DevPrintf("DiskControlSetInitialImage(%u, %s)", index, path); + P_THIS->m_next_disc_index = index; + return true; +} + +bool LibretroHostInterface::DiskControlGetImagePath(unsigned index, char* path, size_t len) +{ + System* system = P_THIS->GetSystem(); + if (!system || index >= system->GetMediaPlaylistCount()) + return false; + + const std::string& image_path = system->GetMediaPlaylistPath(index); + Log_DevPrintf("DiskControlGetImagePath(%u) -> %s", index, image_path.c_str()); + if (image_path.empty()) + return false; + + StringUtil::Strlcpy(path, image_path.c_str(), len); + return true; +} + +bool LibretroHostInterface::DiskControlGetImageLabel(unsigned index, char* label, size_t len) +{ + System* system = P_THIS->GetSystem(); + if (!system || index >= system->GetMediaPlaylistCount()) + return false; + + const std::string& image_path = system->GetMediaPlaylistPath(index); + if (image_path.empty()) + return false; + + const std::string_view title = GameList::GetTitleForPath(label); + StringUtil::Strlcpy(label, title, len); + Log_DevPrintf("DiskControlGetImagePath(%u) -> %s", index, label); + return true; +} + +void LibretroHostInterface::InitDiskControlInterface() +{ + unsigned version = 0; + if (g_retro_environment_callback(RETRO_ENVIRONMENT_GET_DISK_CONTROL_INTERFACE_VERSION, &version) && version >= 1) + { + retro_disk_control_ext_callback ext_cb = { + &LibretroHostInterface::DiskControlSetEjectState, &LibretroHostInterface::DiskControlGetEjectState, + &LibretroHostInterface::DiskControlGetImageIndex, &LibretroHostInterface::DiskControlSetImageIndex, + &LibretroHostInterface::DiskControlGetNumImages, &LibretroHostInterface::DiskControlReplaceImageIndex, + &LibretroHostInterface::DiskControlAddImageIndex, &LibretroHostInterface::DiskControlSetInitialImage, + &LibretroHostInterface::DiskControlGetImagePath, &LibretroHostInterface::DiskControlGetImageLabel}; + if (g_retro_environment_callback(RETRO_ENVIRONMENT_SET_DISK_CONTROL_EXT_INTERFACE, &ext_cb)) + return; + } + + retro_disk_control_callback cb = { + &LibretroHostInterface::DiskControlSetEjectState, &LibretroHostInterface::DiskControlGetEjectState, + &LibretroHostInterface::DiskControlGetImageIndex, &LibretroHostInterface::DiskControlSetImageIndex, + &LibretroHostInterface::DiskControlGetNumImages, &LibretroHostInterface::DiskControlReplaceImageIndex, + &LibretroHostInterface::DiskControlAddImageIndex}; + if (!g_retro_environment_callback(RETRO_ENVIRONMENT_SET_DISK_CONTROL_INTERFACE, &cb)) + Log_WarningPrint("Failed to set disk control interface"); +} diff --git a/src/duckstation-libretro/libretro_host_interface.h b/src/duckstation-libretro/libretro_host_interface.h index 239aa341a..27a5cd8e0 100644 --- a/src/duckstation-libretro/libretro_host_interface.h +++ b/src/duckstation-libretro/libretro_host_interface.h @@ -2,6 +2,8 @@ #include "core/host_interface.h" #include "core/system.h" #include "libretro.h" +#include +#include class LibretroHostInterface : public HostInterface { @@ -12,6 +14,7 @@ public: static void InitLogging(); static bool SetCoreOptions(); static bool HasCoreVariablesChanged(); + static void InitDiskControlInterface(); ALWAYS_INLINE u32 GetResolutionScale() const { return m_settings.gpu_resolution_scale; } @@ -66,10 +69,23 @@ private: static void HardwareRendererContextReset(); static void HardwareRendererContextDestroy(); + // Disk control callbacks + static bool RETRO_CALLCONV DiskControlSetEjectState(bool ejected); + static bool RETRO_CALLCONV DiskControlGetEjectState(); + static unsigned RETRO_CALLCONV DiskControlGetImageIndex(); + static bool RETRO_CALLCONV DiskControlSetImageIndex(unsigned index); + static unsigned RETRO_CALLCONV DiskControlGetNumImages(); + static bool RETRO_CALLCONV DiskControlReplaceImageIndex(unsigned index, const retro_game_info* info); + static bool RETRO_CALLCONV DiskControlAddImageIndex(); + static bool RETRO_CALLCONV DiskControlSetInitialImage(unsigned index, const char* path); + static bool RETRO_CALLCONV DiskControlGetImagePath(unsigned index, char* path, size_t len); + static bool RETRO_CALLCONV DiskControlGetImageLabel(unsigned index, char* label, size_t len); + retro_hw_render_callback m_hw_render_callback = {}; std::unique_ptr m_hw_render_display; bool m_hw_render_callback_valid = false; bool m_using_hardware_renderer = false; + std::optional m_next_disc_index; }; extern LibretroHostInterface g_libretro_host_interface; diff --git a/src/duckstation-libretro/main.cpp b/src/duckstation-libretro/main.cpp index 14bbc10d1..c5e5ebcef 100644 --- a/src/duckstation-libretro/main.cpp +++ b/src/duckstation-libretro/main.cpp @@ -127,6 +127,7 @@ RETRO_API void retro_set_environment(retro_environment_t f) Log_WarningPrintf("Failed to set core options, settings will not be changeable."); g_libretro_host_interface.InitLogging(); + g_libretro_host_interface.InitDiskControlInterface(); } RETRO_API void retro_set_video_refresh(retro_video_refresh_t f)