diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000000..263762bd621 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +ko_fi: shadps4 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 53af3296b64..b0c145934ee 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -113,6 +113,7 @@ jobs: target: desktop arch: win64_msvc2019_64 archives: qtbase qttools + modules: qtmultimedia - name: Cache CMake Configuration uses: actions/cache@v4 @@ -237,6 +238,7 @@ jobs: target: desktop arch: clang_64 archives: qtbase qttools + modules: qtmultimedia - name: Cache CMake Configuration uses: actions/cache@v4 @@ -341,7 +343,7 @@ jobs: submodules: recursive - name: Install dependencies - run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libfuse2 clang build-essential qt6-base-dev qt6-tools-dev + run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libfuse2 clang build-essential qt6-base-dev qt6-tools-dev qt6-multimedia-dev - name: Cache CMake Configuration uses: actions/cache@v4 diff --git a/.reuse/dep5 b/.reuse/dep5 deleted file mode 100644 index 0609248137b..00000000000 --- a/.reuse/dep5 +++ /dev/null @@ -1,64 +0,0 @@ -Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ -Comment: It is best to use this file to record copyright information about - generated, binary and third party files - -Files: CMakeSettings.json - .github/shadps4.desktop - .github/shadps4.png - .gitmodules - documents/changelog.txt - documents/Quickstart/2.png - documents/Screenshots/Bloodborne.png - documents/Screenshots/Sonic Mania.png - documents/Screenshots/Undertale.png - documents/Screenshots/We are DOOMED.png - scripts/ps4_names.txt - src/images/about_icon.png - src/images/controller_icon.png - src/images/dump_icon.png - src/images/exit_icon.png - src/images/file_icon.png - src/images/flag_china.png - src/images/flag_eu.png - src/images/flag_jp.png - src/images/flag_unk.png - src/images/flag_us.png - src/images/flag_world.png - src/images/folder_icon.png - src/images/grid_icon.png - src/images/iconsize_icon.png - src/images/list_icon.png - src/images/list_mode_icon.png - src/images/pause_icon.png - src/images/play_icon.png - src/images/refresh_icon.png - src/images/settings_icon.png - src/images/stop_icon.png - src/images/shadPS4.icns - src/images/shadps4.ico - src/images/themes_icon.png - src/images/update_icon.png - src/shadps4.qrc - src/shadps4.rc -Copyright: shadPS4 Emulator Project -License: GPL-2.0-or-later - -Files: externals/cmake-modules/* -Copyright: 2009-2010 Iowa State University -License: BSL-1.0 - -Files: externals/renderdoc/* -Copyright: 2019-2024 Baldur Karlsson -License: MIT - -Files: externals/stb_image.h -Copyright: 2017 Sean Barrett -License: MIT - -Files: externals/tracy/* -Copyright: 2017-2024 Bartosz Taudul -License: BSD-3-Clause - -Files: src/imgui/renderer/fonts/NotoSansJP-Regular.ttf -Copyright: 2012 Google Inc. All Rights Reserved. -License: OFL-1.1 diff --git a/CMakeLists.txt b/CMakeLists.txt index c03cc3bccac..c9ea1591840 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -144,7 +144,7 @@ add_subdirectory(externals) include_directories(src) if(ENABLE_QT_GUI) - find_package(Qt6 REQUIRED COMPONENTS Widgets Concurrent LinguistTools Network) + find_package(Qt6 REQUIRED COMPONENTS Widgets Concurrent LinguistTools Network Multimedia) qt_standard_project_setup() set(CMAKE_AUTORCC ON) set(CMAKE_AUTOMOC ON) @@ -653,6 +653,8 @@ qt_add_resources(RESOURCE_FILES src/shadps4.qrc) set(QT_GUI src/qt_gui/about_dialog.cpp src/qt_gui/about_dialog.h src/qt_gui/about_dialog.ui + src/qt_gui/background_music_player.cpp + src/qt_gui/background_music_player.h src/qt_gui/cheats_patches.cpp src/qt_gui/cheats_patches.h src/qt_gui/check_update.cpp @@ -760,7 +762,7 @@ else() endif() if (ENABLE_QT_GUI) - target_link_libraries(shadps4 PRIVATE Qt6::Widgets Qt6::Concurrent Qt6::Network) + target_link_libraries(shadps4 PRIVATE Qt6::Widgets Qt6::Concurrent Qt6::Network Qt6::Multimedia) add_definitions(-DENABLE_QT_GUI) endif() diff --git a/REUSE.toml b/REUSE.toml new file mode 100644 index 00000000000..866f94a073d --- /dev/null +++ b/REUSE.toml @@ -0,0 +1,78 @@ +version = 1 + +[[annotations]] +path = [ + "REUSE.toml", + "CMakeSettings.json", + ".github/FUNDING.yml", + ".github/shadps4.desktop", + ".github/shadps4.png", + ".gitmodules", + "documents/changelog.txt", + "documents/Quickstart/2.png", + "documents/Screenshots/Bloodborne.png", + "documents/Screenshots/Sonic Mania.png", + "documents/Screenshots/Undertale.png", + "documents/Screenshots/We are DOOMED.png", + "scripts/ps4_names.txt", + "src/images/about_icon.png", + "src/images/controller_icon.png", + "src/images/dump_icon.png", + "src/images/exit_icon.png", + "src/images/file_icon.png", + "src/images/flag_china.png", + "src/images/flag_eu.png", + "src/images/flag_jp.png", + "src/images/flag_unk.png", + "src/images/flag_us.png", + "src/images/flag_world.png", + "src/images/folder_icon.png", + "src/images/grid_icon.png", + "src/images/iconsize_icon.png", + "src/images/list_icon.png", + "src/images/list_mode_icon.png", + "src/images/pause_icon.png", + "src/images/play_icon.png", + "src/images/refresh_icon.png", + "src/images/settings_icon.png", + "src/images/stop_icon.png", + "src/images/shadPS4.icns", + "src/images/shadps4.ico", + "src/images/themes_icon.png", + "src/images/update_icon.png", + "src/shadps4.qrc", + "src/shadps4.rc", +] +precedence = "aggregate" +SPDX-FileCopyrightText = "shadPS4 Emulator Project" +SPDX-License-Identifier = "GPL-2.0-or-later" + +[[annotations]] +path = "externals/cmake-modules/**" +precedence = "aggregate" +SPDX-FileCopyrightText = "2009-2010 Iowa State University" +SPDX-License-Identifier = "BSL-1.0" + +[[annotations]] +path = "externals/renderdoc/**" +precedence = "aggregate" +SPDX-FileCopyrightText = "2019-2024 Baldur Karlsson" +SPDX-License-Identifier = "MIT" + +[[annotations]] +path = "externals/stb_image.h" +precedence = "aggregate" +SPDX-FileCopyrightText = "2017 Sean Barrett" +SPDX-License-Identifier = "MIT" + +[[annotations]] +path = "externals/tracy/**" +precedence = "aggregate" +SPDX-FileCopyrightText = "2017-2024 Bartosz Taudul " +SPDX-License-Identifier = "BSD-3-Clause" + +[[annotations]] +path = "src/imgui/renderer/fonts/NotoSansJP-Regular.ttf" +precedence = "aggregate" +SPDX-FileCopyrightText = "2012 Google Inc. All Rights Reserved." +SPDX-License-Identifier = "OFL-1.1" diff --git a/documents/building-windows.md b/documents/building-windows.md index fb1bb93caa3..48fd09c41ea 100644 --- a/documents/building-windows.md +++ b/documents/building-windows.md @@ -80,7 +80,7 @@ Normal x86-based computers, follow: 1. Open "MSYS2 MINGW64" from your new applications 2. Run `pacman -Syu`, let it complete; 3. Run `pacman -S --needed git mingw-w64-x86_64-binutils mingw-w64-x86_64-clang mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-ffmpeg` - 1. Optional (Qt only): run `pacman -S --needed mingw-w64-x86_64-qt6-base mingw-w64-x86_64-qt6-tools` + 1. Optional (Qt only): run `pacman -S --needed mingw-w64-x86_64-qt6-base mingw-w64-x86_64-qt6-tools mingw-w64-x86_64-qt6-multimedia` 4. Run `git clone --depth 1 --recursive https://github.com/shadps4-emu/shadPS4` 5. Run `cd shadPS4` 6. Run `cmake -S . -B build -DCMAKE_C_COMPILER="clang.exe" -DCMAKE_CXX_COMPILER="clang++.exe" -DCMAKE_CXX_FLAGS="-O2 -march=native"` @@ -94,7 +94,7 @@ ARM64-based computers, follow: 1. Open "MSYS2 CLANGARM64" from your new applications 2. Run `pacman -Syu`, let it complete; 3. Run `pacman -S --needed git mingw-w64-clang-aarch64-binutils mingw-w64-clang-aarch64-clang mingw-w64-clang-aarch64-cmake mingw-w64-clang-aarch64-ninja mingw-w64-clang-aarch64-ffmpeg` - 1. Optional (Qt only): run `pacman -S --needed mingw-w64-clang-aarch64-qt6-base mingw-w64-clang-aarch64-qt6-tools` + 1. Optional (Qt only): run `pacman -S --needed mingw-w64-clang-aarch64-qt6-base mingw-w64-clang-aarch64-qt6-tools mingw-w64-clang-aarch64-qt6-multimedia` 4. Run `git clone --depth 1 --recursive https://github.com/shadps4-emu/shadPS4` 5. Run `cd shadPS4` 6. Run `cmake -S . -B build -DCMAKE_C_COMPILER="clang.exe" -DCMAKE_CXX_COMPILER="clang++.exe" -DCMAKE_CXX_FLAGS="-O2 -march=native"` diff --git a/src/common/config.cpp b/src/common/config.cpp index b5248c89a02..4fb2818e43b 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -4,13 +4,30 @@ #include #include #include +#include // for wstring support #include +#include "common/logging/formatter.h" #include "config.h" +namespace toml { +template +std::filesystem::path find_fs_path_or(const basic_value& v, const K& ky, + std::filesystem::path opt) { + try { + auto str = find(v, ky); + std::u8string u8str{(char8_t*)&str.front(), (char8_t*)&str.back() + 1}; + return std::filesystem::path{u8str}; + } catch (...) { + return opt; + } +} +} // namespace toml + namespace Config { static bool isNeo = false; static bool isFullscreen = false; +static bool playBGM = false; static u32 screenWidth = 1280; static u32 screenHeight = 720; static s32 gpuId = -1; // Vulkan physical device index. Set to negative for auto select @@ -36,7 +53,7 @@ static bool vkMarkers = false; static bool vkCrashDiagnostic = false; // Gui -std::string settings_install_dir = ""; +std::filesystem::path settings_install_dir = {}; u32 main_window_geometry_x = 400; u32 main_window_geometry_y = 400; u32 main_window_geometry_w = 1280; @@ -64,6 +81,10 @@ bool isFullscreenMode() { return isFullscreen; } +bool getPlayBGM() { + return playBGM; +} + u32 getScreenWidth() { return screenWidth; } @@ -220,6 +241,10 @@ void setFullscreenMode(bool enable) { isFullscreen = enable; } +void setPlayBGM(bool enable) { + playBGM = enable; +} + void setLanguage(u32 language) { m_language = language; } @@ -258,7 +283,7 @@ void setMainWindowGeometry(u32 x, u32 y, u32 w, u32 h) { main_window_geometry_w = w; main_window_geometry_h = h; } -void setGameInstallDir(const std::string& dir) { +void setGameInstallDir(const std::filesystem::path& dir) { settings_install_dir = dir; } void setMainWindowTheme(u32 theme) { @@ -314,7 +339,7 @@ u32 getMainWindowGeometryW() { u32 getMainWindowGeometryH() { return main_window_geometry_h; } -std::string getGameInstallDir() { +std::filesystem::path getGameInstallDir() { return settings_install_dir; } u32 getMainWindowTheme() { @@ -369,7 +394,10 @@ void load(const std::filesystem::path& path) { toml::value data; try { - data = toml::parse(path); + std::ifstream ifs; + ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit); + ifs.open(path, std::ios_base::binary); + data = toml::parse(ifs, std::string{fmt::UTF(path.filename().u8string()).data}); } catch (std::exception& ex) { fmt::print("Got exception trying to load config file. Exception: {}\n", ex.what()); return; @@ -379,6 +407,7 @@ void load(const std::filesystem::path& path) { isNeo = toml::find_or(general, "isPS4Pro", false); isFullscreen = toml::find_or(general, "Fullscreen", false); + playBGM = toml::find_or(general, "playBGM", false); logFilter = toml::find_or(general, "logFilter", ""); logType = toml::find_or(general, "logType", "sync"); userName = toml::find_or(general, "userName", "shadPS4"); @@ -434,7 +463,7 @@ void load(const std::filesystem::path& path) { mw_themes = toml::find_or(gui, "theme", 0); m_window_size_W = toml::find_or(gui, "mw_width", 0); m_window_size_H = toml::find_or(gui, "mw_height", 0); - settings_install_dir = toml::find_or(gui, "installDir", ""); + settings_install_dir = toml::find_fs_path_or(gui, "installDir", {}); main_window_geometry_x = toml::find_or(gui, "geometry_x", 0); main_window_geometry_y = toml::find_or(gui, "geometry_y", 0); main_window_geometry_w = toml::find_or(gui, "geometry_w", 0); @@ -458,21 +487,24 @@ void save(const std::filesystem::path& path) { std::error_code error; if (std::filesystem::exists(path, error)) { try { - data = toml::parse(path); + std::ifstream ifs; + ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit); + ifs.open(path, std::ios_base::binary); + data = toml::parse(ifs, std::string{fmt::UTF(path.filename().u8string()).data}); } catch (const std::exception& ex) { fmt::print("Exception trying to parse config file. Exception: {}\n", ex.what()); return; } } else { if (error) { - fmt::print("Filesystem error accessing {} (error: {})\n", path.string(), - error.message().c_str()); + fmt::print("Filesystem error: {}\n", error.message()); } - fmt::print("Saving new configuration file {}\n", path.string()); + fmt::print("Saving new configuration file {}\n", fmt::UTF(path.u8string())); } data["General"]["isPS4Pro"] = isNeo; data["General"]["Fullscreen"] = isFullscreen; + data["General"]["playBGM"] = playBGM; data["General"]["logFilter"] = logFilter; data["General"]["logType"] = logType; data["General"]["userName"] = userName; @@ -504,7 +536,7 @@ void save(const std::filesystem::path& path) { data["GUI"]["gameTableMode"] = m_table_mode; data["GUI"]["mw_width"] = m_window_size_W; data["GUI"]["mw_height"] = m_window_size_H; - data["GUI"]["installDir"] = settings_install_dir; + data["GUI"]["installDir"] = std::string{fmt::UTF(settings_install_dir.u8string()).data}; data["GUI"]["geometry_x"] = main_window_geometry_x; data["GUI"]["geometry_y"] = main_window_geometry_y; data["GUI"]["geometry_w"] = main_window_geometry_w; @@ -524,6 +556,7 @@ void save(const std::filesystem::path& path) { void setDefaultValues() { isNeo = false; isFullscreen = false; + playBGM = false; screenWidth = 1280; screenHeight = 720; logFilter = ""; @@ -550,4 +583,4 @@ void setDefaultValues() { gpuId = -1; } -} // namespace Config \ No newline at end of file +} // namespace Config diff --git a/src/common/config.h b/src/common/config.h index 63dca08d743..f2b5187f8d9 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -13,6 +13,7 @@ void save(const std::filesystem::path& path); bool isNeoMode(); bool isFullscreenMode(); +bool getPlayBGM(); std::string getLogFilter(); std::string getLogType(); std::string getUserName(); @@ -47,6 +48,7 @@ void setGpuId(s32 selectedGpuId); void setScreenWidth(u32 width); void setScreenHeight(u32 height); void setFullscreenMode(bool enable); +void setPlayBGM(bool enable); void setLanguage(u32 language); void setNeoMode(bool enable); void setUserName(const std::string& type); @@ -70,7 +72,7 @@ bool vkCrashDiagnosticEnabled(); // Gui void setMainWindowGeometry(u32 x, u32 y, u32 w, u32 h); -void setGameInstallDir(const std::string& dir); +void setGameInstallDir(const std::filesystem::path& dir); void setMainWindowTheme(u32 theme); void setIconSize(u32 size); void setIconSizeGrid(u32 size); @@ -88,7 +90,7 @@ u32 getMainWindowGeometryX(); u32 getMainWindowGeometryY(); u32 getMainWindowGeometryW(); u32 getMainWindowGeometryH(); -std::string getGameInstallDir(); +std::filesystem::path getGameInstallDir(); u32 getMainWindowTheme(); u32 getIconSize(); u32 getIconSizeGrid(); diff --git a/src/common/logging/formatter.h b/src/common/logging/formatter.h index f80905cc35d..79df33e4a1f 100644 --- a/src/common/logging/formatter.h +++ b/src/common/logging/formatter.h @@ -19,3 +19,24 @@ struct fmt::formatter, char>> } }; #endif + +namespace fmt { +template +struct UTF { + T data; + + explicit UTF(const std::u8string_view view) { + data = T{(const char*)&view.front(), (const char*)&view.back() + 1}; + } + + explicit UTF(const std::u8string& str) : UTF(std::u8string_view{str}) {} +}; +} // namespace fmt + +template <> +struct fmt::formatter, char> : formatter { + template + auto format(const UTF& wrapper, FormatContext& ctx) const { + return formatter::format(wrapper.data, ctx); + } +}; \ No newline at end of file diff --git a/src/common/memory_patcher.cpp b/src/common/memory_patcher.cpp index 85a25167a30..d2930cf5e24 100644 --- a/src/common/memory_patcher.cpp +++ b/src/common/memory_patcher.cpp @@ -119,9 +119,9 @@ std::string convertValueToHex(const std::string type, const std::string valueStr void OnGameLoaded() { if (!patchFile.empty()) { - std::string patchDir = Common::FS::GetUserPath(Common::FS::PathType::PatchesDir).string(); + std::filesystem::path patchDir = Common::FS::GetUserPath(Common::FS::PathType::PatchesDir); - std::string filePath = patchDir + "/" + patchFile; + auto filePath = (patchDir / patchFile).native(); pugi::xml_document doc; pugi::xml_parse_result result = doc.load_file(filePath.c_str()); @@ -187,8 +187,8 @@ void OnGameLoaded() { #ifdef ENABLE_QT_GUI // We use the QT headers for the xml and json parsing, this define is only true on QT builds - QString patchDir = - QString::fromStdString(Common::FS::GetUserPath(Common::FS::PathType::PatchesDir).string()); + QString patchDir; + Common::FS::PathToQString(patchDir, Common::FS::GetUserPath(Common::FS::PathType::PatchesDir)); QString repositories[] = {"GoldHEN", "shadPS4"}; for (const QString& repository : repositories) { diff --git a/src/common/path_util.cpp b/src/common/path_util.cpp index cce12ebcf42..d7274fc7455 100644 --- a/src/common/path_util.cpp +++ b/src/common/path_util.cpp @@ -22,6 +22,10 @@ #endif #endif +#ifdef ENABLE_QT_GUI +#include +#endif + namespace Common::FS { namespace fs = std::filesystem; @@ -165,4 +169,22 @@ void SetUserPath(PathType shad_path, const fs::path& new_path) { UserPaths.insert_or_assign(shad_path, new_path); } +#ifdef ENABLE_QT_GUI +void PathToQString(QString& result, const std::filesystem::path& path) { +#ifdef _WIN32 + result = QString::fromStdWString(path.wstring()); +#else + result = QString::fromStdString(path.string()); +#endif +} + +std::filesystem::path PathFromQString(const QString& path) { +#ifdef _WIN32 + return std::filesystem::path(path.toStdWString()); +#else + return std::filesystem::path(path.toStdString()); +#endif +} +#endif + } // namespace Common::FS \ No newline at end of file diff --git a/src/common/path_util.h b/src/common/path_util.h index 623b285ed2a..d40f4aab49c 100644 --- a/src/common/path_util.h +++ b/src/common/path_util.h @@ -6,6 +6,10 @@ #include #include +#ifdef ENABLE_QT_GUI +class QString; // to avoid including in this header +#endif + namespace Common::FS { enum class PathType { @@ -96,4 +100,23 @@ constexpr auto LOG_FILE = "shad_log.txt"; */ void SetUserPath(PathType user_path, const std::filesystem::path& new_path); +#ifdef ENABLE_QT_GUI +/** + * Converts an std::filesystem::path to a QString. + * The native underlying string of a path is wstring on Windows and string on POSIX. + * + * @param result The resulting QString + * @param path The path to convert + */ +void PathToQString(QString& result, const std::filesystem::path& path); + +/** + * Converts a QString to an std::filesystem::path. + * The native underlying string of a path is wstring on Windows and string on POSIX. + * + * @param path The path to convert + */ +[[nodiscard]] std::filesystem::path PathFromQString(const QString& path); +#endif + } // namespace Common::FS diff --git a/src/core/file_format/pkg.cpp b/src/core/file_format/pkg.cpp index d86f3b28d0e..f329e81a629 100644 --- a/src/core/file_format/pkg.cpp +++ b/src/core/file_format/pkg.cpp @@ -371,8 +371,7 @@ bool PKG::Extract(const std::filesystem::path& filepath, const std::filesystem:: if (table.type == PFS_CURRENT_DIR) { current_dir = extractPaths[table.inode]; } - extractPaths[table.inode] = - current_dir.string() / std::filesystem::path(table.name); + extractPaths[table.inode] = current_dir / std::filesystem::path(table.name); if (table.type == PFS_FILE || table.type == PFS_DIR) { if (table.type == PFS_DIR) { // Create dirs. @@ -402,7 +401,7 @@ void PKG::ExtractFiles(const int index) { int bsize = iNodeBuf[inode_number].Size; Common::FS::IOFile inflated; - inflated.Open(extractPaths[inode_number].string(), Common::FS::FileAccessMode::Write); + inflated.Open(extractPaths[inode_number], Common::FS::FileAccessMode::Write); Common::FS::IOFile pkgFile; // Open the file for each iteration to avoid conflict. pkgFile.Open(pkgpath, Common::FS::FileAccessMode::Read); diff --git a/src/core/file_format/splash.cpp b/src/core/file_format/splash.cpp index f41789a8543..5e06c912d90 100644 --- a/src/core/file_format/splash.cpp +++ b/src/core/file_format/splash.cpp @@ -12,8 +12,8 @@ #define STBI_NO_STDIO #include "externals/stb_image.h" -bool Splash::Open(const std::string& filepath) { - ASSERT_MSG(filepath.ends_with(".png"), "Unexpected file format passed"); +bool Splash::Open(const std::filesystem::path& filepath) { + ASSERT_MSG(filepath.stem().string() != "png", "Unexpected file format passed"); Common::FS::IOFile file(filepath, Common::FS::FileAccessMode::Read); if (!file.IsOpen()) { diff --git a/src/core/file_format/splash.h b/src/core/file_format/splash.h index 68cc33c47f2..7c563f317af 100644 --- a/src/core/file_format/splash.h +++ b/src/core/file_format/splash.h @@ -3,6 +3,7 @@ #pragma once +#include #include #include #include "common/types.h" @@ -22,7 +23,7 @@ class Splash { Splash() = default; ~Splash() = default; - bool Open(const std::string& filepath); + bool Open(const std::filesystem::path& filepath); [[nodiscard]] bool IsLoaded() const { return img_data.size(); } diff --git a/src/core/file_format/trp.cpp b/src/core/file_format/trp.cpp index 86865fe63b2..724f9078297 100644 --- a/src/core/file_format/trp.cpp +++ b/src/core/file_format/trp.cpp @@ -29,7 +29,7 @@ static void removePadding(std::vector& vec) { } bool TRP::Extract(const std::filesystem::path& trophyPath) { - std::string title = trophyPath.filename().string(); + std::filesystem::path title = trophyPath.filename(); std::filesystem::path gameSysDir = trophyPath / "sce_sys/trophy/"; if (!std::filesystem::exists(gameSysDir)) { return false; diff --git a/src/core/libraries/np_trophy/np_trophy.cpp b/src/core/libraries/np_trophy/np_trophy.cpp index c28e49dacf9..91fdeb9910f 100644 --- a/src/core/libraries/np_trophy/np_trophy.cpp +++ b/src/core/libraries/np_trophy/np_trophy.cpp @@ -557,10 +557,10 @@ s32 PS4_SYSV_ABI sceNpTrophyGetTrophyUnlockState(OrbisNpTrophyContext context, const auto trophyDir = Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) / game_serial / "TrophyFiles"; + auto trophy_file = trophyDir / "trophy00" / "Xml" / "TROP.XML"; pugi::xml_document doc; - pugi::xml_parse_result result = - doc.load_file((trophyDir.string() + "/trophy00/Xml/TROP.XML").c_str()); + pugi::xml_parse_result result = doc.load_file(trophy_file.native().c_str()); int numTrophies = 0; diff --git a/src/core/libraries/save_data/dialog/savedatadialog_ui.cpp b/src/core/libraries/save_data/dialog/savedatadialog_ui.cpp index 793b4dd38f1..c4bf8425877 100644 --- a/src/core/libraries/save_data/dialog/savedatadialog_ui.cpp +++ b/src/core/libraries/save_data/dialog/savedatadialog_ui.cpp @@ -79,55 +79,58 @@ SaveDialogState::SaveDialogState(const OrbisSaveDataDialogParam& param) { this->title_id = item->titleId->data.to_string(); } - for (u32 i = 0; i < item->dirNameNum; i++) { - const auto dir_name = item->dirName[i].data.to_view(); + if (item->dirName != nullptr) { + for (u32 i = 0; i < item->dirNameNum; i++) { + const auto dir_name = item->dirName[i].data.to_view(); - if (dir_name.empty()) { - continue; - } + if (dir_name.empty()) { + continue; + } - auto dir_path = SaveInstance::MakeDirSavePath(user_id, title_id, dir_name); + auto dir_path = SaveInstance::MakeDirSavePath(user_id, title_id, dir_name); - auto param_sfo_path = dir_path / "sce_sys" / "param.sfo"; - if (!std::filesystem::exists(param_sfo_path)) { - continue; - } + auto param_sfo_path = dir_path / "sce_sys" / "param.sfo"; + if (!std::filesystem::exists(param_sfo_path)) { + continue; + } - PSF param_sfo; - param_sfo.Open(param_sfo_path); + PSF param_sfo; + param_sfo.Open(param_sfo_path); - auto last_write = param_sfo.GetLastWrite(); + auto last_write = param_sfo.GetLastWrite(); #ifdef _WIN32 - auto utc_time = std::chrono::file_clock::to_utc(last_write); + auto utc_time = std::chrono::file_clock::to_utc(last_write); #else - auto utc_time = std::chrono::file_clock::to_sys(last_write); + auto utc_time = std::chrono::file_clock::to_sys(last_write); #endif - std::string date_str = fmt::format("{:%d %b, %Y %R}", utc_time); + std::string date_str = fmt::format("{:%d %b, %Y %R}", utc_time); - size_t size = Common::FS::GetDirectorySize(dir_path); - std::string size_str = SpaceSizeToString(size); + size_t size = Common::FS::GetDirectorySize(dir_path); + std::string size_str = SpaceSizeToString(size); - auto icon_path = dir_path / "sce_sys" / "icon0.png"; - RefCountedTexture icon; - if (std::filesystem::exists(icon_path)) { - icon = RefCountedTexture::DecodePngFile(icon_path); - } - - bool is_corrupted = std::filesystem::exists(dir_path / "sce_sys" / "corrupted"); - - this->save_list.emplace_back(Item{ - .dir_name = std::string{dir_name}, - .icon = icon, + auto icon_path = dir_path / "sce_sys" / "icon0.png"; + RefCountedTexture icon; + if (std::filesystem::exists(icon_path)) { + icon = RefCountedTexture::DecodePngFile(icon_path); + } - .title = std::string{param_sfo.GetString(SaveParams::MAINTITLE).value_or("Unknown")}, - .subtitle = std::string{param_sfo.GetString(SaveParams::SUBTITLE).value_or("")}, - .details = std::string{param_sfo.GetString(SaveParams::DETAIL).value_or("")}, - .date = date_str, - .size = size_str, - .last_write = param_sfo.GetLastWrite(), - .pfo = param_sfo, - .is_corrupted = is_corrupted, - }); + bool is_corrupted = std::filesystem::exists(dir_path / "sce_sys" / "corrupted"); + + this->save_list.emplace_back(Item{ + .dir_name = std::string{dir_name}, + .icon = icon, + + .title = + std::string{param_sfo.GetString(SaveParams::MAINTITLE).value_or("Unknown")}, + .subtitle = std::string{param_sfo.GetString(SaveParams::SUBTITLE).value_or("")}, + .details = std::string{param_sfo.GetString(SaveParams::DETAIL).value_or("")}, + .date = date_str, + .size = size_str, + .last_write = param_sfo.GetLastWrite(), + .pfo = param_sfo, + .is_corrupted = is_corrupted, + }); + } } if (type == DialogType::SAVE && item->newItem != nullptr) { @@ -299,7 +302,7 @@ SaveDialogState::ProgressBarState::ProgressBarState(const SaveDialogState& state } else { switch (bar.sysMsgType) { case ProgressSystemMessageType::INVALID: - this->msg = "INVALID"; + this->msg = ""; break; case ProgressSystemMessageType::PROGRESS: switch (state.type) { diff --git a/src/core/libraries/save_data/save_backup.cpp b/src/core/libraries/save_data/save_backup.cpp index d8281fab96d..1d935aee1e6 100644 --- a/src/core/libraries/save_data/save_backup.cpp +++ b/src/core/libraries/save_data/save_backup.cpp @@ -107,14 +107,16 @@ static void BackupThreadBody() { } g_backup_status = WorkerStatus::Running; - LOG_INFO(Lib_SaveData, "Backing up the following directory: {}", req.save_path.string()); + LOG_INFO(Lib_SaveData, "Backing up the following directory: {}", + fmt::UTF(req.save_path.u8string())); try { backup(req.save_path); } catch (const std::filesystem::filesystem_error& err) { - LOG_ERROR(Lib_SaveData, "Failed to backup {}: {}", req.save_path.string(), err.what()); + LOG_ERROR(Lib_SaveData, "Failed to backup {}: {}", fmt::UTF(req.save_path.u8string()), + err.what()); } LOG_DEBUG(Lib_SaveData, "Backing up the following directory: {} finished", - req.save_path.string()); + fmt::UTF(req.save_path.u8string())); { std::scoped_lock lk{g_backup_queue_mutex}; g_backup_queue.front().done = true; @@ -160,7 +162,7 @@ bool NewRequest(OrbisUserServiceUserId user_id, std::string_view title_id, if (g_backup_status != WorkerStatus::Waiting && g_backup_status != WorkerStatus::Running) { LOG_ERROR(Lib_SaveData, "Called backup while status is {}. Backup request to {} ignored", - magic_enum::enum_name(g_backup_status.load()), save_path.string()); + magic_enum::enum_name(g_backup_status.load()), fmt::UTF(save_path.u8string())); return false; } { @@ -184,7 +186,7 @@ bool NewRequest(OrbisUserServiceUserId user_id, std::string_view title_id, } bool Restore(const std::filesystem::path& save_path) { - LOG_INFO(Lib_SaveData, "Restoring backup for {}", save_path.string()); + LOG_INFO(Lib_SaveData, "Restoring backup for {}", fmt::UTF(save_path.u8string())); std::unique_lock lk{g_backup_running_mutex}; if (!fs::exists(save_path) || !fs::exists(save_path / backup_dir)) { return false; diff --git a/src/core/libraries/save_data/save_memory.cpp b/src/core/libraries/save_data/save_memory.cpp index 44f5311e1e1..0a714a26f2f 100644 --- a/src/core/libraries/save_data/save_memory.cpp +++ b/src/core/libraries/save_data/save_memory.cpp @@ -77,7 +77,7 @@ static void SaveFileSafe(void* buf, size_t count, const std::filesystem::path& p g_saving_memory = true; std::scoped_lock lk{g_saving_memory_mutex}; try { - LOG_DEBUG(Lib_SaveData, "Saving save data memory {}", g_save_path.string()); + LOG_DEBUG(Lib_SaveData, "Saving save data memory {}", fmt::UTF(g_save_path.u8string())); if (g_memory_dirty) { g_memory_dirty = false; @@ -163,7 +163,8 @@ size_t CreateSaveMemory(size_t memory_size) { bool ok = g_param_sfo.Open(g_param_sfo_path); if (!ok) { - LOG_ERROR(Lib_SaveData, "Failed to open SFO at {}", g_param_sfo_path.string()); + LOG_ERROR(Lib_SaveData, "Failed to open SFO at {}", + fmt::UTF(g_param_sfo_path.u8string())); throw std::filesystem::filesystem_error( "failed to open SFO", g_param_sfo_path, std::make_error_code(std::errc::illegal_byte_sequence)); @@ -268,9 +269,6 @@ bool TriggerSave() { void ReadMemory(void* buf, size_t buf_size, int64_t offset) { std::scoped_lock lk{g_saving_memory_mutex}; - if (offset > g_save_memory.size()) { - UNREACHABLE_MSG("ReadMemory out of bounds"); - } if (offset + buf_size > g_save_memory.size()) { UNREACHABLE_MSG("ReadMemory out of bounds"); } @@ -279,11 +277,8 @@ void ReadMemory(void* buf, size_t buf_size, int64_t offset) { void WriteMemory(void* buf, size_t buf_size, int64_t offset) { std::scoped_lock lk{g_saving_memory_mutex}; - if (offset > g_save_memory.size()) { - UNREACHABLE_MSG("WriteMemory out of bounds"); - } if (offset + buf_size > g_save_memory.size()) { - UNREACHABLE_MSG("WriteMemory out of bounds"); + g_save_memory.resize(offset + buf_size); } std::memcpy(g_save_memory.data() + offset, buf, buf_size); g_memory_dirty = true; diff --git a/src/core/libraries/save_data/savedata.cpp b/src/core/libraries/save_data/savedata.cpp index 9599c1ffd05..da885d97706 100644 --- a/src/core/libraries/save_data/savedata.cpp +++ b/src/core/libraries/save_data/savedata.cpp @@ -607,7 +607,7 @@ Error PS4_SYSV_ABI sceSaveDataCheckBackupData(const OrbisSaveDataCheckBackupData if (check->param != nullptr) { PSF sfo; if (!sfo.Open(backup_path / "sce_sys" / "param.sfo")) { - LOG_ERROR(Lib_SaveData, "Failed to read SFO at {}", backup_path.string()); + LOG_ERROR(Lib_SaveData, "Failed to read SFO at {}", fmt::UTF(backup_path.u8string())); return Error::INTERNAL; } check->param->FromSFO(sfo); @@ -818,7 +818,7 @@ Error PS4_SYSV_ABI sceSaveDataDirNameSearch(const OrbisSaveDataDirNameSearchCond const auto sfo_path = SaveInstance::GetParamSFOPath(dir_path); PSF sfo; if (!sfo.Open(sfo_path)) { - LOG_ERROR(Lib_SaveData, "Failed to read SFO: {}", sfo_path.string()); + LOG_ERROR(Lib_SaveData, "Failed to read SFO: {}", fmt::UTF(sfo_path.u8string())); ASSERT_MSG(false, "Failed to read SFO"); } map_dir_sfo.emplace(dir_name, std::move(sfo)); diff --git a/src/emulator.cpp b/src/emulator.cpp index 4a2e38ff8a9..b27c73867a6 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -139,7 +139,7 @@ void Emulator::Run(const std::filesystem::path& file) { if (splash->IsLoaded()) { continue; } - if (!splash->Open(entry.path().string())) { + if (!splash->Open(entry.path())) { LOG_ERROR(Loader, "Game splash: unable to open file"); } } @@ -189,7 +189,7 @@ void Emulator::Run(const std::filesystem::path& file) { if (!std::filesystem::exists(mount_captures_dir)) { std::filesystem::create_directory(mount_captures_dir); } - VideoCore::SetOutputDir(mount_captures_dir.generic_string(), id); + VideoCore::SetOutputDir(mount_captures_dir, id); // Initialize kernel and library facilities. Libraries::Kernel::init_pthreads(); @@ -205,7 +205,7 @@ void Emulator::Run(const std::filesystem::path& file) { std::filesystem::path sce_module_folder = file.parent_path() / "sce_module"; if (std::filesystem::is_directory(sce_module_folder)) { for (const auto& entry : std::filesystem::directory_iterator(sce_module_folder)) { - LOG_INFO(Loader, "Loading {}", entry.path().string().c_str()); + LOG_INFO(Loader, "Loading {}", fmt::UTF(entry.path().u8string())); linker->LoadModule(entry.path()); } } diff --git a/src/imgui/renderer/imgui_core.cpp b/src/imgui/renderer/imgui_core.cpp index a3f4c507fe1..8c2b31c9e86 100644 --- a/src/imgui/renderer/imgui_core.cpp +++ b/src/imgui/renderer/imgui_core.cpp @@ -51,8 +51,16 @@ void Initialize(const ::Vulkan::Instance& instance, const Frontend::WindowSDL& w io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; io.DisplaySize = ImVec2((float)window.getWidth(), (float)window.getHeight()); - io.IniFilename = SDL_strdup(config_path.string().c_str()); - io.LogFilename = SDL_strdup(log_path.string().c_str()); + + auto path = config_path.u8string(); + char* config_file_buf = new char[path.size() + 1](); + std::memcpy(config_file_buf, path.c_str(), path.size()); + io.IniFilename = config_file_buf; + + path = log_path.u8string(); + char* log_file_buf = new char[path.size() + 1](); + std::memcpy(log_file_buf, path.c_str(), path.size()); + io.LogFilename = log_file_buf; ImFontGlyphRangesBuilder rb{}; rb.AddRanges(io.Fonts->GetGlyphRangesDefault()); @@ -114,8 +122,8 @@ void Shutdown(const vk::Device& device) { Sdl::Shutdown(); DestroyContext(); - SDL_free(ini_filename); - SDL_free(log_filename); + delete[] (char*)ini_filename; + delete[] (char*)log_filename; } bool ProcessEvent(SDL_Event* event) { diff --git a/src/main.cpp b/src/main.cpp index cea92be07b5..de1d92326b2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,7 +5,15 @@ #include "common/memory_patcher.h" #include "emulator.h" +#ifdef _WIN32 +#include +#endif + int main(int argc, char* argv[]) { +#ifdef _WIN32 + SetConsoleOutputCP(CP_UTF8); +#endif + if (argc == 1) { fmt::print("Usage: {} \n", argv[0]); return -1; diff --git a/src/qt_gui/background_music_player.cpp b/src/qt_gui/background_music_player.cpp new file mode 100644 index 00000000000..37d87709835 --- /dev/null +++ b/src/qt_gui/background_music_player.cpp @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "background_music_player.h" + +BackgroundMusicPlayer::BackgroundMusicPlayer(QObject* parent) : QObject(parent) { + m_mediaPlayer = new QMediaPlayer(this); + m_audioOutput = new QAudioOutput(this); + m_mediaPlayer->setAudioOutput(m_audioOutput); + m_mediaPlayer->setLoops(QMediaPlayer::Infinite); +} + +void BackgroundMusicPlayer::playMusic(const QString& snd0path) { + if (snd0path.isEmpty()) { + stopMusic(); + return; + } + const auto newMusic = QUrl::fromLocalFile(snd0path); + if (m_mediaPlayer->playbackState() == QMediaPlayer::PlayingState && + m_currentMusic == newMusic) { + // already playing the correct music + return; + } + + m_currentMusic = newMusic; + m_mediaPlayer->setSource(newMusic); + m_mediaPlayer->play(); +} + +void BackgroundMusicPlayer::stopMusic() { + m_mediaPlayer->stop(); +} diff --git a/src/qt_gui/background_music_player.h b/src/qt_gui/background_music_player.h new file mode 100644 index 00000000000..52f44f431b0 --- /dev/null +++ b/src/qt_gui/background_music_player.h @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include + +class BackgroundMusicPlayer : public QObject { + Q_OBJECT + +public: + static BackgroundMusicPlayer& getInstance() { + static BackgroundMusicPlayer instance; + return instance; + } + + void playMusic(const QString& snd0path); + void stopMusic(); + +private: + BackgroundMusicPlayer(QObject* parent = nullptr); + + QMediaPlayer* m_mediaPlayer; + QAudioOutput* m_audioOutput; + QUrl m_currentMusic; +}; \ No newline at end of file diff --git a/src/qt_gui/cheats_patches.cpp b/src/qt_gui/cheats_patches.cpp index aaa6b4f01a1..655478d2b48 100644 --- a/src/qt_gui/cheats_patches.cpp +++ b/src/qt_gui/cheats_patches.cpp @@ -50,8 +50,9 @@ void CheatsPatches::setupUI() { defaultTextEdit = tr("defaultTextEdit_MSG"); defaultTextEdit.replace("\\n", "\n"); - QString CHEATS_DIR_QString = - QString::fromStdString(Common::FS::GetUserPath(Common::FS::PathType::CheatsDir).string()); + QString CHEATS_DIR_QString; + Common::FS::PathToQString(CHEATS_DIR_QString, + Common::FS::GetUserPath(Common::FS::PathType::CheatsDir)); QString NameCheatJson = m_gameSerial + "_" + m_gameVersion + ".json"; m_cheatFilePath = CHEATS_DIR_QString + "/" + NameCheatJson; @@ -275,9 +276,9 @@ void CheatsPatches::onSaveButtonClicked() { int separatorIndex = selectedPatchName.indexOf(" | "); selectedPatchName = selectedPatchName.mid(separatorIndex + 3); - QString patchDir = - QString::fromStdString(Common::FS::GetUserPath(Common::FS::PathType::PatchesDir).string()) + - "/" + selectedPatchName; + QString patchDir; + Common::FS::PathToQString(patchDir, Common::FS::GetUserPath(Common::FS::PathType::PatchesDir)); + patchDir += "/" + selectedPatchName; QString filesJsonPath = patchDir + "/files.json"; QFile jsonFile(filesJsonPath); @@ -555,10 +556,10 @@ void CheatsPatches::downloadCheats(const QString& source, const QString& gameSer if (dotIndex != -1) { baseFileName.insert(dotIndex, "_wolf2022"); } - QString filePath = - QString::fromStdString( - Common::FS::GetUserPath(Common::FS::PathType::CheatsDir).string()) + - "/" + baseFileName; + QString filePath; + Common::FS::PathToQString(filePath, + Common::FS::GetUserPath(Common::FS::PathType::CheatsDir)); + filePath += "/" + baseFileName; if (QFile::exists(filePath) && showMessageBox) { QMessageBox::StandardButton reply2; reply2 = @@ -612,8 +613,9 @@ void CheatsPatches::populateFileListPatches() { } m_patchInfos.clear(); - QString patchesDir = - QString::fromStdString(Common::FS::GetUserPath(Common::FS::PathType::PatchesDir).string()); + QString patchesDir; + Common::FS::PathToQString(patchesDir, + Common::FS::GetUserPath(Common::FS::PathType::PatchesDir)); QDir dir(patchesDir); QStringList folders = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); @@ -906,8 +908,8 @@ void CheatsPatches::addCheatsToLayout(const QJsonArray& modsArray, const QJsonAr } void CheatsPatches::populateFileListCheats() { - QString cheatsDir = - QString::fromStdString(Common::FS::GetUserPath(Common::FS::PathType::CheatsDir).string()); + QString cheatsDir; + Common::FS::PathToQString(cheatsDir, Common::FS::GetUserPath(Common::FS::PathType::CheatsDir)); QString pattern = m_gameSerial + "_" + m_gameVersion + "*.json"; QDir dir(cheatsDir); @@ -932,8 +934,9 @@ void CheatsPatches::populateFileListCheats() { if (!selectedIndexes.isEmpty()) { QString selectedFileName = selectedIndexes.first().data().toString(); - QString cheatsDir = QString::fromStdString( - Common::FS::GetUserPath(Common::FS::PathType::CheatsDir).string()); + QString cheatsDir; + Common::FS::PathToQString( + cheatsDir, Common::FS::GetUserPath(Common::FS::PathType::CheatsDir)); QFile file(cheatsDir + "/" + selectedFileName); if (file.open(QIODevice::ReadOnly)) { diff --git a/src/qt_gui/game_grid_frame.cpp b/src/qt_gui/game_grid_frame.cpp index 9fba0c47cbe..3113aecc0d3 100644 --- a/src/qt_gui/game_grid_frame.cpp +++ b/src/qt_gui/game_grid_frame.cpp @@ -39,6 +39,16 @@ GameGridFrame::GameGridFrame(std::shared_ptr game_info_get, QWidg }); } +void GameGridFrame::PlayBackgroundMusic(QTableWidgetItem* item) { + if (!item) { + BackgroundMusicPlayer::getInstance().stopMusic(); + return; + } + QString snd0path; + Common::FS::PathToQString(snd0path, m_game_info->m_games[item->row()].snd0_path); + BackgroundMusicPlayer::getInstance().playMusic(snd0path); +} + void GameGridFrame::PopulateGameGrid(QVector m_games_search, bool fromSearch) { QVector m_games_; this->clearContents(); @@ -113,14 +123,12 @@ void GameGridFrame::SetGridBackgroundImage(int row, int column) { int itemID = (row * this->columnCount()) + column; QWidget* item = this->cellWidget(row, column); if (item) { - QString pic1Path = QString::fromStdString((*m_games_shared)[itemID].pic_path); + QString pic1Path; + Common::FS::PathToQString(pic1Path, (*m_games_shared)[itemID].pic_path); const auto blurredPic1Path = Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) / (*m_games_shared)[itemID].serial / "pic1.png"; -#ifdef _WIN32 - const auto blurredPic1PathQt = QString::fromStdWString(blurredPic1Path.wstring()); -#else - const auto blurredPic1PathQt = QString::fromStdString(blurredPic1Path.string()); -#endif + QString blurredPic1PathQt; + Common::FS::PathToQString(blurredPic1PathQt, blurredPic1Path); backgroundImage = QImage(blurredPic1PathQt); if (backgroundImage.isNull()) { diff --git a/src/qt_gui/game_grid_frame.h b/src/qt_gui/game_grid_frame.h index 50b53a581b5..0083fd688ea 100644 --- a/src/qt_gui/game_grid_frame.h +++ b/src/qt_gui/game_grid_frame.h @@ -5,6 +5,7 @@ #include +#include "background_music_player.h" #include "common/config.h" #include "game_info.h" #include "game_list_utils.h" @@ -19,6 +20,7 @@ class GameGridFrame : public QTableWidget { public Q_SLOTS: void SetGridBackgroundImage(int row, int column); void RefreshGridBackgroundImage(); + void PlayBackgroundMusic(QTableWidgetItem* item); private: QImage backgroundImage; diff --git a/src/qt_gui/game_info.cpp b/src/qt_gui/game_info.cpp index 2821a0322a2..6e8d89713f2 100644 --- a/src/qt_gui/game_info.cpp +++ b/src/qt_gui/game_info.cpp @@ -3,13 +3,15 @@ #include +#include "common/path_util.h" #include "game_info.h" GameInfoClass::GameInfoClass() = default; GameInfoClass::~GameInfoClass() = default; void GameInfoClass::GetGameInfo(QWidget* parent) { - QString installDir = QString::fromStdString(Config::getGameInstallDir()); + QString installDir; + Common::FS::PathToQString(installDir, Config::getGameInstallDir()); QStringList filePaths; QDir parentFolder(installDir); QFileInfoList fileList = parentFolder.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); @@ -19,7 +21,7 @@ void GameInfoClass::GetGameInfo(QWidget* parent) { } } m_games = QtConcurrent::mapped(filePaths, [&](const QString& path) { - return readGameInfo(path.toStdString()); + return readGameInfo(Common::FS::PathFromQString(path)); }).results(); // Progress bar, please be patient :) diff --git a/src/qt_gui/game_info.h b/src/qt_gui/game_info.h index a4bcd20ee27..9db25482ada 100644 --- a/src/qt_gui/game_info.h +++ b/src/qt_gui/game_info.h @@ -22,16 +22,19 @@ class GameInfoClass : public QObject { return a.name < b.name; } - static GameInfo readGameInfo(const std::string& filePath) { + static GameInfo readGameInfo(const std::filesystem::path& filePath) { GameInfo game; game.path = filePath; PSF psf; - if (psf.Open(std::filesystem::path(game.path) / "sce_sys" / "param.sfo")) { - game.icon_path = game.path + "/sce_sys/icon0.png"; - QString iconpath = QString::fromStdString(game.icon_path); + if (psf.Open(game.path / "sce_sys" / "param.sfo")) { + game.icon_path = game.path / "sce_sys" / "icon0.png"; + QString iconpath; + Common::FS::PathToQString(iconpath, game.icon_path); game.icon = QImage(iconpath); - game.pic_path = game.path + "/sce_sys/pic1.png"; + game.pic_path = game.path / "sce_sys" / "pic1.png"; + game.snd0_path = game.path / "sce_sys" / "snd0.at9"; + if (const auto title = psf.GetString("TITLE"); title.has_value()) { game.name = *title; } diff --git a/src/qt_gui/game_install_dialog.cpp b/src/qt_gui/game_install_dialog.cpp index 5f031bdec2d..d8cc7a837ef 100644 --- a/src/qt_gui/game_install_dialog.cpp +++ b/src/qt_gui/game_install_dialog.cpp @@ -41,7 +41,9 @@ QWidget* GameInstallDialog::SetupGamesDirectory() { // Input. m_gamesDirectory = new QLineEdit(); - m_gamesDirectory->setText(QString::fromStdString(Config::getGameInstallDir())); + QString install_dir; + Common::FS::PathToQString(install_dir, Config::getGameInstallDir()); + m_gamesDirectory->setText(install_dir); m_gamesDirectory->setMinimumWidth(400); layout->addWidget(m_gamesDirectory); @@ -76,7 +78,7 @@ void GameInstallDialog::Save() { return; } - Config::setGameInstallDir(gamesDirectory.toStdString()); + Config::setGameInstallDir(Common::FS::PathFromQString(gamesDirectory)); const auto config_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir); Config::save(config_dir / "config.toml"); accept(); diff --git a/src/qt_gui/game_list_frame.cpp b/src/qt_gui/game_list_frame.cpp index b17da127e82..45e52f37d09 100644 --- a/src/qt_gui/game_list_frame.cpp +++ b/src/qt_gui/game_list_frame.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "common/path_util.h" +#include "common/string_util.h" #include "game_list_frame.h" GameListFrame::GameListFrame(std::shared_ptr game_info_get, QWidget* parent) @@ -68,6 +69,16 @@ GameListFrame::GameListFrame(std::shared_ptr game_info_get, QWidg }); } +void GameListFrame::PlayBackgroundMusic(QTableWidgetItem* item) { + if (!item) { + BackgroundMusicPlayer::getInstance().stopMusic(); + return; + } + QString snd0path; + Common::FS::PathToQString(snd0path, m_game_info->m_games[item->row()].snd0_path); + BackgroundMusicPlayer::getInstance().playMusic(snd0path); +} + void GameListFrame::PopulateGameList() { this->setRowCount(m_game_info->m_games.size()); ResizeIcons(icon_size); @@ -79,7 +90,9 @@ void GameListFrame::PopulateGameList() { SetTableItem(i, 4, QString::fromStdString(m_game_info->m_games[i].fw)); SetTableItem(i, 5, QString::fromStdString(m_game_info->m_games[i].size)); SetTableItem(i, 6, QString::fromStdString(m_game_info->m_games[i].version)); - SetTableItem(i, 7, QString::fromStdString(m_game_info->m_games[i].path)); + QString path; + Common::FS::PathToQString(path, m_game_info->m_games[i].path); + SetTableItem(i, 7, path); } } @@ -89,14 +102,12 @@ void GameListFrame::SetListBackgroundImage(QTableWidgetItem* item) { return; } - QString pic1Path = QString::fromStdString(m_game_info->m_games[item->row()].pic_path); + QString pic1Path; + Common::FS::PathToQString(pic1Path, m_game_info->m_games[item->row()].pic_path); const auto blurredPic1Path = Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) / m_game_info->m_games[item->row()].serial / "pic1.png"; -#ifdef _WIN32 - const auto blurredPic1PathQt = QString::fromStdWString(blurredPic1Path.wstring()); -#else - const auto blurredPic1PathQt = QString::fromStdString(blurredPic1Path.string()); -#endif + QString blurredPic1PathQt; + Common::FS::PathToQString(blurredPic1PathQt, blurredPic1Path); backgroundImage = QImage(blurredPic1PathQt); if (backgroundImage.isNull()) { diff --git a/src/qt_gui/game_list_frame.h b/src/qt_gui/game_list_frame.h index ab70e6f6bda..a1ec5c56313 100644 --- a/src/qt_gui/game_list_frame.h +++ b/src/qt_gui/game_list_frame.h @@ -5,6 +5,7 @@ #include +#include "background_music_player.h" #include "game_info.h" #include "game_list_utils.h" #include "gui_context_menus.h" @@ -21,6 +22,7 @@ public Q_SLOTS: void RefreshListBackgroundImage(); void SortNameAscending(int columnIndex); void SortNameDescending(int columnIndex); + void PlayBackgroundMusic(QTableWidgetItem* item); private: void SetTableItem(int row, int column, QString itemStr); diff --git a/src/qt_gui/game_list_utils.h b/src/qt_gui/game_list_utils.h index 476c900359d..f62275eff8d 100644 --- a/src/qt_gui/game_list_utils.h +++ b/src/qt_gui/game_list_utils.h @@ -3,10 +3,14 @@ #pragma once +#include "common/path_util.h" + struct GameInfo { - std::string path; // root path of game directory (normally directory that contains eboot.bin) - std::string icon_path; // path of icon0.png - std::string pic_path; // path of pic1.png + std::filesystem::path path; // root path of game directory + // (normally directory that contains eboot.bin) + std::filesystem::path icon_path; // path of icon0.png + std::filesystem::path pic_path; // path of pic1.png + std::filesystem::path snd0_path; // path of snd0.at9 QImage icon; std::string size; // variables extracted from param.sfo @@ -43,7 +47,9 @@ class GameListUtils { } static void GetFolderSize(GameInfo& game) { - QDir dir(QString::fromStdString(game.path)); + QString dirPath; + Common::FS::PathToQString(dirPath, game.path); + QDir dir(dirPath); QDirIterator it(dir.absolutePath(), QDirIterator::Subdirectories); qint64 total = 0; while (it.hasNext()) { diff --git a/src/qt_gui/gui_context_menus.h b/src/qt_gui/gui_context_menus.h index 9300ca6e574..3218884d5b0 100644 --- a/src/qt_gui/gui_context_menus.h +++ b/src/qt_gui/gui_context_menus.h @@ -75,7 +75,8 @@ class GuiContextMenus : public QObject { } if (selected == &openFolder) { - QString folderPath = QString::fromStdString(m_games[itemID].path); + QString folderPath; + Common::FS::PathToQString(folderPath, m_games[itemID].path); QDesktopServices::openUrl(QUrl::fromLocalFile(folderPath)); } @@ -158,7 +159,9 @@ class GuiContextMenus : public QObject { QString gameSerial = QString::fromStdString(m_games[itemID].serial); QString gameVersion = QString::fromStdString(m_games[itemID].version); QString gameSize = QString::fromStdString(m_games[itemID].size); - QPixmap gameImage(QString::fromStdString(m_games[itemID].icon_path)); + QString iconPath; + Common::FS::PathToQString(iconPath, m_games[itemID].icon_path); + QPixmap gameImage(iconPath); CheatsPatches* cheatsPatches = new CheatsPatches(gameName, gameSerial, gameVersion, gameSize, gameImage); cheatsPatches->show(); @@ -167,8 +170,9 @@ class GuiContextMenus : public QObject { } if (selected == &openTrophyViewer) { - QString trophyPath = QString::fromStdString(m_games[itemID].serial); - QString gameTrpPath = QString::fromStdString(m_games[itemID].path); + QString trophyPath, gameTrpPath; + Common::FS::PathToQString(trophyPath, m_games[itemID].serial); + Common::FS::PathToQString(gameTrpPath, m_games[itemID].path); TrophyViewer* trophyViewer = new TrophyViewer(trophyPath, gameTrpPath); trophyViewer->show(); connect(widget->parent(), &QWidget::destroyed, trophyViewer, @@ -176,11 +180,13 @@ class GuiContextMenus : public QObject { } if (selected == &createShortcut) { - QString targetPath = QString::fromStdString(m_games[itemID].path); + QString targetPath; + Common::FS::PathToQString(targetPath, m_games[itemID].path); QString ebootPath = targetPath + "/eboot.bin"; // Get the full path to the icon - QString iconPath = QString::fromStdString(m_games[itemID].icon_path); + QString iconPath; + Common::FS::PathToQString(iconPath, m_games[itemID].icon_path); QFileInfo iconFileInfo(iconPath); QString icoPath = iconFileInfo.absolutePath() + "/" + iconFileInfo.baseName() + ".ico"; diff --git a/src/qt_gui/main.cpp b/src/qt_gui/main.cpp index 68a674f5e7d..8c565a19bee 100644 --- a/src/qt_gui/main.cpp +++ b/src/qt_gui/main.cpp @@ -8,10 +8,18 @@ #include "game_install_dialog.h" #include "main_window.h" +#ifdef _WIN32 +#include +#endif + // Custom message handler to ignore Qt logs void customMessageHandler(QtMsgType, const QMessageLogContext&, const QString&) {} int main(int argc, char* argv[]) { +#ifdef _WIN32 + SetConsoleOutputCP(CP_UTF8); +#endif + QApplication a(argc, argv); // Load configurations and initialize Qt application @@ -22,7 +30,7 @@ int main(int argc, char* argv[]) { bool has_command_line_argument = argc > 1; // Check if the game install directory is set - if (Config::getGameInstallDir() == "" && !has_command_line_argument) { + if (Config::getGameInstallDir().empty() && !has_command_line_argument) { GameInstallDialog dlg; dlg.exec(); } diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index 535e470f85c..759c6992e19 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -4,10 +4,12 @@ #include #include +#include #include "about_dialog.h" #include "cheats_patches.h" #include "check_update.h" #include "common/io_file.h" +#include "common/path_util.h" #include "common/string_util.h" #include "common/version.h" #include "core/file_format/pkg.h" @@ -43,7 +45,14 @@ bool MainWindow::Init() { GetPhysicalDevices(); // show ui setMinimumSize(350, minimumSizeHint().height()); - setWindowTitle(QString::fromStdString("shadPS4 v" + std::string(Common::VERSION))); + std::string window_title = ""; + if (Common::isRelease) { + window_title = fmt::format("shadPS4 v{}", Common::VERSION); + } else { + window_title = fmt::format("shadPS4 v{} {} {}", Common::VERSION, Common::g_scm_branch, + Common::g_scm_desc); + } + setWindowTitle(QString::fromStdString(window_title)); this->show(); // load game list LoadGameLists(); @@ -433,12 +442,14 @@ void MainWindow::CreateConnects() { .arg(" APP VERSION", -11) .arg(" Path"); for (const GameInfo& game : m_game_info->m_games) { + QString game_path; + Common::FS::PathToQString(game_path, game.path); out << QString("%1 %2 %3 %4 %5\n") .arg(QString::fromStdString(game.name), -50) .arg(QString::fromStdString(game.serial), -10) .arg(QString::fromStdString(game.fw), -4) .arg(QString::fromStdString(game.version), -11) - .arg(QString::fromStdString(game.path)); + .arg(game_path); } }); @@ -501,36 +512,58 @@ void MainWindow::CreateConnects() { isIconBlack = false; } }); + + connect(m_game_grid_frame.get(), &QTableWidget::cellClicked, this, + &MainWindow::PlayBackgroundMusic); + connect(m_game_list_frame.get(), &QTableWidget::cellClicked, this, + &MainWindow::PlayBackgroundMusic); +} + +void MainWindow::PlayBackgroundMusic() { + if (isGameRunning || !Config::getPlayBGM()) { + BackgroundMusicPlayer::getInstance().stopMusic(); + return; + } + int itemID = isTableList ? m_game_list_frame->currentItem()->row() + : m_game_grid_frame->crtRow * m_game_grid_frame->columnCnt + + m_game_grid_frame->crtColumn; + + QString snd0path; + Common::FS::PathToQString(snd0path, m_game_info->m_games[itemID].snd0_path); + BackgroundMusicPlayer::getInstance().playMusic(snd0path); } void MainWindow::StartGame() { + isGameRunning = true; + BackgroundMusicPlayer::getInstance().stopMusic(); QString gamePath = ""; int table_mode = Config::getTableMode(); if (table_mode == 0) { if (m_game_list_frame->currentItem()) { int itemID = m_game_list_frame->currentItem()->row(); - gamePath = QString::fromStdString(m_game_info->m_games[itemID].path + "/eboot.bin"); + Common::FS::PathToQString(gamePath, m_game_info->m_games[itemID].path / "eboot.bin"); } } else if (table_mode == 1) { if (m_game_grid_frame->cellClicked) { int itemID = (m_game_grid_frame->crtRow * m_game_grid_frame->columnCnt) + m_game_grid_frame->crtColumn; - gamePath = QString::fromStdString(m_game_info->m_games[itemID].path + "/eboot.bin"); + Common::FS::PathToQString(gamePath, m_game_info->m_games[itemID].path / "eboot.bin"); } } else { if (m_elf_viewer->currentItem()) { int itemID = m_elf_viewer->currentItem()->row(); - gamePath = QString::fromStdString(m_elf_viewer->m_elf_list[itemID].toStdString()); + gamePath = m_elf_viewer->m_elf_list[itemID]; } } if (gamePath != "") { AddRecentFiles(gamePath); Core::Emulator emulator; - if (!std::filesystem::exists(gamePath.toUtf8().constData())) { + const auto path = Common::FS::PathFromQString(gamePath); + if (!std::filesystem::exists(path)) { QMessageBox::critical(nullptr, tr("Run Game"), QString(tr("Eboot.bin file not found"))); return; } - emulator.Run(gamePath.toUtf8().constData()); + emulator.Run(path); } } @@ -646,9 +679,11 @@ void MainWindow::InstallDragDropPkg(std::filesystem::path file, int pkgNum, int pkg = PKG(); pkg.Open(file); std::string failreason; - auto extract_path = std::filesystem::path(Config::getGameInstallDir()) / pkg.GetTitleID(); + auto extract_path = Config::getGameInstallDir() / pkg.GetTitleID(); QString pkgType = QString::fromStdString(pkg.GetPkgFlags()); - QDir game_dir(QString::fromStdString(extract_path.string())); + QString gameDirPath; + Common::FS::PathToQString(gameDirPath, extract_path); + QDir game_dir(gameDirPath); if (game_dir.exists()) { QMessageBox msgBox; msgBox.setWindowTitle(tr("PKG Extraction")); @@ -670,7 +705,9 @@ void MainWindow::InstallDragDropPkg(std::filesystem::path file, int pkgNum, int auto addon_extract_path = Common::FS::GetUserPath(Common::FS::PathType::AddonsDir) / pkg.GetTitleID() / entitlement_label; - QDir addon_dir(QString::fromStdString(addon_extract_path.string())); + QString addonDirPath; + Common::FS::PathToQString(addonDirPath, addon_extract_path); + QDir addon_dir(addonDirPath); auto category = psf.GetString("CATEGORY"); if (pkgType.contains("PATCH")) { @@ -735,8 +772,7 @@ void MainWindow::InstallDragDropPkg(std::filesystem::path file, int pkgNum, int return; } } else { - msgBox.setText(QString(tr("DLC already installed:") + "\n" + - QString::fromStdString(addon_extract_path.string()) + + msgBox.setText(QString(tr("DLC already installed:") + "\n" + addonDirPath + "\n\n" + tr("Would you like to overwrite?"))); msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); msgBox.setDefaultButton(QMessageBox::No); @@ -748,8 +784,7 @@ void MainWindow::InstallDragDropPkg(std::filesystem::path file, int pkgNum, int } } } else { - msgBox.setText(QString(tr("Game already installed") + "\n" + - QString::fromStdString(extract_path.string()) + "\n" + + msgBox.setText(QString(tr("Game already installed") + "\n" + addonDirPath + "\n" + tr("Would you like to overwrite?"))); msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); msgBox.setDefaultButton(QMessageBox::No); @@ -792,7 +827,8 @@ void MainWindow::InstallDragDropPkg(std::filesystem::path file, int pkgNum, int QFutureWatcher futureWatcher; connect(&futureWatcher, &QFutureWatcher::finished, this, [=, this]() { if (pkgNum == nPkg) { - QString path = QString::fromStdString(Config::getGameInstallDir()); + QString path; + Common::FS::PathToQString(path, Config::getGameInstallDir()); QMessageBox extractMsgBox(this); extractMsgBox.setWindowTitle(tr("Extraction Finished")); extractMsgBox.setText( @@ -964,14 +1000,14 @@ void MainWindow::CreateRecentGameActions() { } connect(m_recent_files_group, &QActionGroup::triggered, this, [this](QAction* action) { - QString gamePath = action->text(); - AddRecentFiles(gamePath); // Update the list. + auto gamePath = Common::FS::PathFromQString(action->text()); + AddRecentFiles(action->text()); // Update the list. Core::Emulator emulator; - if (!std::filesystem::exists(gamePath.toUtf8().constData())) { + if (!std::filesystem::exists(gamePath)) { QMessageBox::critical(nullptr, tr("Run Game"), QString(tr("Eboot.bin file not found"))); return; } - emulator.Run(gamePath.toUtf8().constData()); + emulator.Run(gamePath); }); } @@ -1005,4 +1041,4 @@ void MainWindow::OnLanguageChanged(const std::string& locale) { Config::setEmulatorLanguage(locale); LoadTranslation(); -} \ No newline at end of file +} diff --git a/src/qt_gui/main_window.h b/src/qt_gui/main_window.h index 6da94de12b7..9294ef8c923 100644 --- a/src/qt_gui/main_window.h +++ b/src/qt_gui/main_window.h @@ -7,6 +7,7 @@ #include #include +#include "background_music_player.h" #include "common/config.h" #include "common/path_util.h" #include "core/file_format/psf.h" @@ -63,9 +64,11 @@ private Q_SLOTS: void BootGame(); void AddRecentFiles(QString filePath); void LoadTranslation(); + void PlayBackgroundMusic(); QIcon RecolorIcon(const QIcon& icon, bool isWhite); bool isIconBlack = false; bool isTableList = true; + bool isGameRunning = false; QActionGroup* m_icon_size_act_group = nullptr; QActionGroup* m_list_mode_act_group = nullptr; QActionGroup* m_theme_act_group = nullptr; diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp index 1986be5b7d1..f05b0f92508 100644 --- a/src/qt_gui/settings_dialog.cpp +++ b/src/qt_gui/settings_dialog.cpp @@ -134,6 +134,9 @@ SettingsDialog::SettingsDialog(std::span physical_devices, QWidge auto checkUpdate = new CheckUpdate(true); checkUpdate->exec(); }); + + connect(ui->playBGMCheckBox, &QCheckBox::stateChanged, this, + [](int val) { Config::setPlayBGM(val); }); } // GPU TAB @@ -192,7 +195,7 @@ void SettingsDialog::LoadValuesFromConfig() { ui->dumpShadersCheckBox->setChecked(Config::dumpShaders()); ui->nullGpuCheckBox->setChecked(Config::nullGpu()); ui->dumpPM4CheckBox->setChecked(Config::dumpPM4()); - + ui->playBGMCheckBox->setChecked(Config::getPlayBGM()); ui->fullscreenCheckBox->setChecked(Config::isFullscreenMode()); ui->showSplashCheckBox->setChecked(Config::showSplash()); ui->ps4proCheckBox->setChecked(Config::isNeoMode()); diff --git a/src/qt_gui/settings_dialog.ui b/src/qt_gui/settings_dialog.ui index 5184aa0a7da..0601ac53b76 100644 --- a/src/qt_gui/settings_dialog.ui +++ b/src/qt_gui/settings_dialog.ui @@ -52,7 +52,7 @@ 0 0 836 - 428 + 432 @@ -70,7 +70,7 @@ - + @@ -340,19 +340,55 @@ + + + + + + + 0 + 0 + + + + GUI Settings + + + + + 10 + 30 + 241 + 41 + + + + + + + + 0 + 0 + + + + Play title music + + + + + + + + + - Qt::Horizontal + Qt::Orientation::Horizontal - QSizePolicy::Expanding - - - - 500 - 20 - + QSizePolicy::Policy::Expanding diff --git a/src/qt_gui/translations/sq.ts b/src/qt_gui/translations/sq.ts index 3ffe20ff88c..9ba34bf7d49 100644 --- a/src/qt_gui/translations/sq.ts +++ b/src/qt_gui/translations/sq.ts @@ -171,7 +171,7 @@ Check for Updates - Kontrolloni për përditësime + Kontrollo për përditësime @@ -510,7 +510,7 @@ Check for Updates at Startup - Kontrollo për përditësime në fillim + Kontrollo për përditësime në nisje @@ -1025,37 +1025,37 @@ Failed to parse update information. - Deshtoi të analizojë informacionin e përditësimit. + Analizimi i informacionit të përditësimit deshtoi. No pre-releases found. - Nuk u gjetën publikime paraprake. + Nuk u gjetën botime paraprake. Invalid release data. - të dhënat e lëshimit janë të pavlefshme. + Të dhënat e lëshimit janë të pavlefshme. No download URL found for the specified asset. - Nuk u gjet URL për shkarkim për burimin e specifikuar. + Nuk u gjet URL-ja e shkarkimit për burimin e specifikuar. Your version is already up to date! - Versioni juaj është tashmë i përditësuar! + Versioni jotë është i përditësuar tashmë! Update Available - Përditësim i disponueshëm + Ofrohet një përditësim Current Version - Versioni aktual + Versioni i tanishëm @@ -1065,7 +1065,7 @@ Do you want to update? - Donëi të përditësoni? + Do të përditësosh? @@ -1075,7 +1075,7 @@ Check for Updates at Startup - Kontrollo për përditësime në fillim + Kontrollo për përditësime në nisje @@ -1100,27 +1100,27 @@ Network error occurred while trying to access the URL - Ka ndodhur një gabim rrjeti gjatë përpjekjes për të aksesuar URL-në + Ka ndodhur një gabim rrjeti gjatë përpjekjes për të qasur në URL-në Download Complete - Shkarkimi i përfunduar + Shkarkimi përfundoi The update has been downloaded, press OK to install. - Përditësimi është shkarkuar, shtypni OK për ta instaluar. + Përditësimi është shkarkuar, shtyp OK për ta instaluar. Failed to save the update file at - Deshtoi të ruajë skedarin e përditësimit në + Dështoi ruajtja e skedarit të përditësimit në Failed to open the ZIP file - Deshtoi të hapë skedarin ZIP + Hapja e skedarit ZIP dështoi @@ -1130,32 +1130,32 @@ Failed to create directory - Deshtoi të krijojë direktorinë + Krijimi i dosjes dështoi Error decompressing file - Gabim gjatë dekompresimit të skedarit + Gabim gjatë shpaketimit të skedarit Failed to open output file - Deshtoi të hapë skedarin e daljes + Hapja e skedarit dalës dështoi Unsupported compression method for file: - Metodë kompresimi e mbështetur për skedarin: + Mënyra e ngjeshjes nuk mbështetet për skedarin: Failed to create TAR extraction directory - Deshtoi të krijojë direktorinë e ekstraktimit TAR + Krijimi i dosjes për shpaketimin TAR dështoi Failed to extract the TAR file - Deshtoi të nxjerrë skedarin TAR + Shpaketimi i skedarit TAR dështoi @@ -1165,7 +1165,7 @@ Failed to create the update script file - Deshtoi të krijojë skedarin e skriptit të përditësimit + Krijimi i skedarit skript të përditësimit dështoi - \ No newline at end of file + diff --git a/src/qt_gui/trophy_viewer.cpp b/src/qt_gui/trophy_viewer.cpp index 8b96948cbdc..528beee0d52 100644 --- a/src/qt_gui/trophy_viewer.cpp +++ b/src/qt_gui/trophy_viewer.cpp @@ -21,15 +21,10 @@ TrophyViewer::TrophyViewer(QString trophyPath, QString gameTrpPath) : QMainWindo } void TrophyViewer::PopulateTrophyWidget(QString title) { -#ifdef _WIN32 const auto trophyDir = Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) / - title.toStdWString() / "TrophyFiles"; - const auto trophyDirQt = QString::fromStdWString(trophyDir.wstring()); -#else - const auto trophyDir = Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) / - title.toStdString() / "TrophyFiles"; - const auto trophyDirQt = QString::fromStdString(trophyDir.string()); -#endif + Common::FS::PathFromQString(title) / "TrophyFiles"; + QString trophyDirQt; + Common::FS::PathToQString(trophyDirQt, trophyDir); QDir dir(trophyDirQt); if (!dir.exists()) { diff --git a/src/video_core/renderdoc.cpp b/src/video_core/renderdoc.cpp index 7f88e126424..7e0994992df 100644 --- a/src/video_core/renderdoc.cpp +++ b/src/video_core/renderdoc.cpp @@ -110,11 +110,11 @@ void TriggerCapture() { } } -void SetOutputDir(const std::string& path, const std::string& prefix) { +void SetOutputDir(const std::filesystem::path& path, const std::string& prefix) { if (!rdoc_api) { return; } - rdoc_api->SetCaptureFilePathTemplate((path + '\\' + prefix).c_str()); + rdoc_api->SetCaptureFilePathTemplate(fmt::UTF((path / prefix).u8string()).data.data()); } } // namespace VideoCore diff --git a/src/video_core/renderdoc.h b/src/video_core/renderdoc.h index febf6fbc13a..91e242d043b 100644 --- a/src/video_core/renderdoc.h +++ b/src/video_core/renderdoc.h @@ -20,6 +20,6 @@ void EndCapture(); void TriggerCapture(); /// Sets output directory for captures -void SetOutputDir(const std::string& path, const std::string& prefix); +void SetOutputDir(const std::filesystem::path& path, const std::string& prefix); } // namespace VideoCore