diff --git a/include/dusk/main.h b/include/dusk/main.h index 787c2541e2..c5c4fb27d1 100644 --- a/include/dusk/main.h +++ b/include/dusk/main.h @@ -7,6 +7,14 @@ #include +#if defined(_WIN32) || \ + (defined(__APPLE__) && !TARGET_OS_IOS && !TARGET_OS_TV && !TARGET_OS_MACCATALYST) || \ + (defined(__linux__) && !defined(__ANDROID__)) +#define DUSK_CAN_OPEN_DATA_FOLDER 1 +#else +#define DUSK_CAN_OPEN_DATA_FOLDER 0 +#endif + namespace dusk { extern bool IsRunning; extern bool IsShuttingDown; @@ -22,6 +30,7 @@ namespace dusk { #endif void RequestRestart() noexcept; + bool OpenDataFolder(); } #endif // DUSK_MAIN_H diff --git a/src/dusk/imgui/ImGuiConsole.hpp b/src/dusk/imgui/ImGuiConsole.hpp index 6715846e7b..6362a146b6 100644 --- a/src/dusk/imgui/ImGuiConsole.hpp +++ b/src/dusk/imgui/ImGuiConsole.hpp @@ -2,11 +2,9 @@ #define DUSK_IMGUI_HPP #include -#include #include #include -#include #include #include "ImGuiMenuGame.hpp" @@ -73,24 +71,4 @@ bool ImGuiButtonCenter(std::string_view text); float ImGuiScale(); } // namespace dusk -#if defined(_WIN32) || \ - (defined(__APPLE__) && !TARGET_OS_IOS && !TARGET_OS_TV && !TARGET_OS_MACCATALYST) || \ - (defined(__linux__) && !defined(__ANDROID__)) -#define DUSK_CAN_OPEN_DATA_FOLDER 1 - -namespace fs = std::filesystem; - -static void OpenDataFolder() { - const std::string path = fs::absolute(dusk::ConfigPath).generic_string(); -#if defined(_WIN32) - const std::string url = std::string("file:///") + path; -#else - const std::string url = std::string("file://") + path; -#endif - (void)SDL_OpenURL(url.c_str()); -} -#else -#define DUSK_CAN_OPEN_DATA_FOLDER 0 -#endif - #endif // DUSK_IMGUI_HPP diff --git a/src/dusk/ui/settings.cpp b/src/dusk/ui/settings.cpp index edb136e5e0..d904c823d7 100644 --- a/src/dusk/ui/settings.cpp +++ b/src/dusk/ui/settings.cpp @@ -9,6 +9,7 @@ #include "dusk/file_select.hpp" #include "dusk/imgui/ImGuiEngine.hpp" #include "dusk/livesplit.h" +#include "dusk/main.h" #include "graphics_tuner.hpp" #include "m_Do/m_Do_main.h" #include "menu_bar.hpp" @@ -946,6 +947,18 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) { auto& rightPane = add_child(content, Pane::Type::Uncontrolled); leftPane.add_section("Dusk"); +#if DUSK_CAN_OPEN_DATA_FOLDER + leftPane.register_control( + leftPane.add_button("Open Data Folder").on_pressed([] { + mDoAud_seStartMenu(kSoundClick); + dusk::OpenDataFolder(); + }), + rightPane, [](Pane& pane) { + pane.add_text( + "Open the folder where Dusk stores settings, saves, logs, texture " + "replacements, and other app data."); + }); +#endif leftPane.register_control( leftPane.add_select_button({ .key = "Notifications", diff --git a/src/m_Do/m_Do_main.cpp b/src/m_Do/m_Do_main.cpp index cf51f5fb86..937c9b81aa 100644 --- a/src/m_Do/m_Do_main.cpp +++ b/src/m_Do/m_Do_main.cpp @@ -71,6 +71,7 @@ #include #include "SDL3/SDL_filesystem.h" +#include "SDL3/SDL_misc.h" #include "cxxopts.hpp" #include "d/actor/d_a_movie_player.h" #include "dusk/audio/DuskAudioSystem.h" @@ -114,6 +115,31 @@ void dusk::RequestRestart() noexcept { IsRunning = false; } +bool dusk::OpenDataFolder() { +#if DUSK_CAN_OPEN_DATA_FOLDER + std::error_code ec; + std::filesystem::path path = std::filesystem::absolute(ConfigPath, ec); + if (ec) { + DuskLog.warn("Failed to resolve absolute data folder path '{}': {}", + ConfigPath.string(), ec.message()); + path = ConfigPath; + } + +#if defined(_WIN32) + const std::string url = "file:///" + path.generic_string(); +#else + const std::string url = "file://" + path.generic_string(); +#endif + if (!SDL_OpenURL(url.c_str())) { + DuskLog.warn("Failed to open data folder '{}': {}", path.string(), SDL_GetError()); + return false; + } + return true; +#else + return false; +#endif +} + s32 LOAD_COPYDATE(void*) { char buffer[32]; memset(buffer, 0, sizeof(buffer));