diff --git a/CMakeLists.txt b/CMakeLists.txt index 820d46f9d..b05a175bb 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD_REQUIRED True) if(APPLE) - enable_language(OBJC) + list(APPEND ADDITIONAL_LANGUAGES OBJC) set(CMAKE_OSX_DEPLOYMENT_TARGET 14) endif() @@ -16,7 +16,7 @@ if (NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Release) endif() -project(shadPS4 CXX C ASM) +project(shadPS4 CXX C ASM ${ADDITIONAL_LANGUAGES}) # Forcing PIE makes sure that the base address is high enough so that it doesn't clash with the PS4 memory. if(UNIX AND NOT APPLE) @@ -954,12 +954,6 @@ set(QT_GUI src/qt_gui/about_dialog.cpp ) endif() -set(RESOURCEFOLDER Resources/bronze.png - Resources/gold.png - Resources/platinum.png - Resources/silver.png -) - if (ENABLE_QT_GUI) qt_add_executable(shadps4 ${AUDIO_CORE} @@ -971,7 +965,6 @@ if (ENABLE_QT_GUI) ${SHADER_RECOMPILER} ${VIDEO_CORE} ${EMULATOR} - ${RESOURCEFOLDER} src/images/shadPS4.icns ) else() @@ -984,7 +977,6 @@ else() ${SHADER_RECOMPILER} ${VIDEO_CORE} ${EMULATOR} - ${RESOURCEFOLDER} src/main.cpp src/emulator.cpp src/emulator.h @@ -1117,7 +1109,10 @@ include(CMakeRC) cmrc_add_resource_library(embedded-resources ALIAS res::embedded NAMESPACE res - ${RESOURCEFOLDER}) + src/images/bronze.png + src/images/gold.png + src/images/platinum.png + src/images/silver.png) target_link_libraries(shadps4 PRIVATE res::embedded) diff --git a/REUSE.toml b/REUSE.toml index 9c6ada42a..d8f31c2f2 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -19,11 +19,11 @@ path = [ "documents/Screenshots/*", "documents/Screenshots/Linux/*", "externals/MoltenVK/MoltenVK_icd.json", - "Resources/bronze.png", - "Resources/gold.png", - "Resources/platinum.png", - "Resources/silver.png", "scripts/ps4_names.txt", + "src/images/bronze.png", + "src/images/gold.png", + "src/images/platinum.png", + "src/images/silver.png", "src/images/about_icon.png", "src/images/controller_icon.png", "src/images/discord.png", diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 4ce5636d8..bb434677d 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -18,8 +18,6 @@ endif() # Boost if (NOT TARGET Boost::headers) - set(BOOST_ROOT "${CMAKE_SOURCE_DIR}/externals/ext-boost" CACHE STRING "") - set(Boost_NO_SYSTEM_PATHS ON CACHE BOOL "") add_subdirectory(ext-boost) endif() @@ -77,6 +75,7 @@ endif() # SDL3 if (NOT TARGET SDL3::SDL3) + set(SDL_TEST_LIBRARY OFF) set(SDL_PIPEWIRE OFF) add_subdirectory(sdl3) endif() @@ -131,6 +130,14 @@ endif() # Toml11 if (NOT TARGET toml11::toml11) add_subdirectory(toml11) + + if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC") + get_target_property(_toml11_compile_options toml11 INTERFACE_COMPILE_OPTIONS) + list(REMOVE_ITEM _toml11_compile_options "/Zc:preprocessor") + set_target_properties(toml11 PROPERTIES INTERFACE_COMPILE_OPTIONS ${_toml11_compile_options}) + endif() + endif() endif() # xxHash diff --git a/externals/gcn/CMakeLists.txt b/externals/gcn/CMakeLists.txt index 592f28d0d..f5c612be1 100644 --- a/externals/gcn/CMakeLists.txt +++ b/externals/gcn/CMakeLists.txt @@ -3,6 +3,10 @@ project(gcn LANGUAGES CXX) -add_library(gcn dummy.cpp) +add_library(gcn INTERFACE) +target_sources(gcn PRIVATE + "include/gcn/si_ci_vi_merged_offset.h" + "include/gcn/si_ci_vi_merged_pm4_it_opcodes.h" +) target_include_directories(gcn INTERFACE include) diff --git a/externals/gcn/dummy.cpp b/externals/gcn/dummy.cpp deleted file mode 100644 index 4fd1bb62d..000000000 --- a/externals/gcn/dummy.cpp +++ /dev/null @@ -1,2 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/common/config.cpp b/src/common/config.cpp index 0260d8e17..36566a14c 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -53,6 +53,7 @@ static bool isShaderDebug = false; static bool isShowSplash = false; static bool isAutoUpdate = false; static bool isAlwaysShowChangelog = false; +static bool isLeftSideTrophy = false; static bool isNullGpu = false; static bool shouldCopyGPUBuffers = false; static bool shouldDumpShaders = false; @@ -69,6 +70,7 @@ static bool isFpsColor = true; static bool isSeparateLogFilesEnabled = false; static s16 cursorState = HideCursorState::Idle; static int cursorHideTimeout = 5; // 5 seconds (default) +static double trophyNotificationDuration = 6.0; static bool useUnifiedInputConfig = true; static bool overrideControllerColor = false; static int controllerCustomColorRGB[3] = {0, 0, 255}; @@ -196,6 +198,10 @@ int getCursorHideTimeout() { return cursorHideTimeout; } +double getTrophyNotificationDuration() { + return trophyNotificationDuration; +} + u32 getScreenWidth() { return screenWidth; } @@ -264,6 +270,10 @@ bool alwaysShowChangelog() { return isAlwaysShowChangelog; } +bool leftSideTrophy() { + return isLeftSideTrophy; +} + bool nullGpu() { return isNullGpu; } @@ -371,6 +381,9 @@ void setAutoUpdate(bool enable) { void setAlwaysShowChangelog(bool enable) { isAlwaysShowChangelog = enable; } +void setLeftSideTrophy(bool enable) { + isLeftSideTrophy = enable; +} void setNullGpu(bool enable) { isNullGpu = enable; @@ -435,6 +448,9 @@ void setCursorState(s16 newCursorState) { void setCursorHideTimeout(int newcursorHideTimeout) { cursorHideTimeout = newcursorHideTimeout; } +void setTrophyNotificationDuration(double newTrophyNotificationDuration) { + trophyNotificationDuration = newTrophyNotificationDuration; +} void setLanguage(u32 language) { m_language = language; @@ -706,6 +722,8 @@ void load(const std::filesystem::path& path) { isNeo = toml::find_or(general, "isPS4Pro", false); playBGM = toml::find_or(general, "playBGM", false); isTrophyPopupDisabled = toml::find_or(general, "isTrophyPopupDisabled", false); + trophyNotificationDuration = + toml::find_or(general, "trophyNotificationDuration", 5.0); BGMvolume = toml::find_or(general, "BGMvolume", 50); enableDiscordRPC = toml::find_or(general, "enableDiscordRPC", true); logFilter = toml::find_or(general, "logFilter", ""); @@ -719,6 +737,7 @@ void load(const std::filesystem::path& path) { isShowSplash = toml::find_or(general, "showSplash", true); isAutoUpdate = toml::find_or(general, "autoUpdate", false); isAlwaysShowChangelog = toml::find_or(general, "alwaysShowChangelog", false); + isLeftSideTrophy = toml::find_or(general, "leftSideTrophy", false); separateupdatefolder = toml::find_or(general, "separateUpdateEnabled", false); compatibilityData = toml::find_or(general, "compatibilityEnabled", false); checkCompatibilityOnStartup = @@ -834,7 +853,7 @@ void load(const std::filesystem::path& path) { } void save(const std::filesystem::path& path) { - toml::value data; + toml::ordered_value data; std::error_code error; if (std::filesystem::exists(path, error)) { @@ -842,7 +861,8 @@ void save(const std::filesystem::path& 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}); + 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; @@ -856,6 +876,7 @@ void save(const std::filesystem::path& path) { data["General"]["isPS4Pro"] = isNeo; data["General"]["isTrophyPopupDisabled"] = isTrophyPopupDisabled; + data["General"]["trophyNotificationDuration"] = trophyNotificationDuration; data["General"]["playBGM"] = playBGM; data["General"]["BGMvolume"] = BGMvolume; data["General"]["enableDiscordRPC"] = enableDiscordRPC; @@ -867,6 +888,7 @@ void save(const std::filesystem::path& path) { data["General"]["showSplash"] = isShowSplash; data["General"]["autoUpdate"] = isAutoUpdate; data["General"]["alwaysShowChangelog"] = isAlwaysShowChangelog; + data["General"]["leftSideTrophy"] = isLeftSideTrophy; data["General"]["separateUpdateEnabled"] = separateupdatefolder; data["General"]["compatibilityEnabled"] = compatibilityData; data["General"]["checkCompatibilityOnStartup"] = checkCompatibilityOnStartup; @@ -924,7 +946,7 @@ void save(const std::filesystem::path& path) { } void saveMainWindow(const std::filesystem::path& path) { - toml::value data; + toml::ordered_value data; std::error_code error; if (std::filesystem::exists(path, error)) { @@ -932,7 +954,8 @@ void saveMainWindow(const std::filesystem::path& 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}); + 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; @@ -986,6 +1009,7 @@ void setDefaultValues() { chooseHomeTab = "General"; cursorState = HideCursorState::Idle; cursorHideTimeout = 5; + trophyNotificationDuration = 6.0; backButtonBehavior = "left"; useSpecialPad = false; specialPadClass = 1; @@ -994,6 +1018,7 @@ void setDefaultValues() { isShowSplash = false; isAutoUpdate = false; isAlwaysShowChangelog = false; + isLeftSideTrophy = false; isNullGpu = false; shouldDumpShaders = false; vblankDivider = 1; diff --git a/src/common/config.h b/src/common/config.h index abf8da8aa..988734b93 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -41,6 +41,7 @@ std::string getChooseHomeTab(); s16 getCursorState(); int getCursorHideTimeout(); +double getTrophyNotificationDuration(); std::string getBackButtonBehavior(); bool getUseSpecialPad(); int getSpecialPadClass(); @@ -62,6 +63,7 @@ bool collectShadersForDebug(); bool showSplash(); bool autoUpdate(); bool alwaysShowChangelog(); +bool leftSideTrophy(); bool nullGpu(); bool copyGPUCmdBuffers(); bool dumpShaders(); @@ -75,6 +77,7 @@ void setCollectShaderForDebug(bool enable); void setShowSplash(bool enable); void setAutoUpdate(bool enable); void setAlwaysShowChangelog(bool enable); +void setLeftSideTrophy(bool enable); void setNullGpu(bool enable); void setAllowHDR(bool enable); void setCopyGPUCmdBuffers(bool enable); @@ -104,6 +107,7 @@ void setShowBackgroundImage(bool show); void setCursorState(s16 cursorState); void setCursorHideTimeout(int newcursorHideTimeout); +void setTrophyNotificationDuration(double newTrophyNotificationDuration); void setBackButtonBehavior(const std::string& type); void setUseSpecialPad(bool use); void setSpecialPadClass(int type); diff --git a/src/common/path_util.cpp b/src/common/path_util.cpp index a4312fada..d48e8c3fe 100644 --- a/src/common/path_util.cpp +++ b/src/common/path_util.cpp @@ -128,6 +128,7 @@ static auto UserPaths = [] { create_path(PathType::CheatsDir, user_dir / CHEATS_DIR); create_path(PathType::PatchesDir, user_dir / PATCHES_DIR); create_path(PathType::MetaDataDir, user_dir / METADATA_DIR); + create_path(PathType::CustomTrophy, user_dir / CUSTOM_TROPHY); return paths; }(); diff --git a/src/common/path_util.h b/src/common/path_util.h index 7190378d6..2fd9b1588 100644 --- a/src/common/path_util.h +++ b/src/common/path_util.h @@ -27,6 +27,7 @@ enum class PathType { CheatsDir, // Where cheats are stored. PatchesDir, // Where patches are stored. MetaDataDir, // Where game metadata (e.g. trophies and menu backgrounds) is stored. + CustomTrophy, // Where custom files for trophies are stored. }; constexpr auto PORTABLE_DIR = "user"; @@ -44,6 +45,7 @@ constexpr auto CAPTURES_DIR = "captures"; constexpr auto CHEATS_DIR = "cheats"; constexpr auto PATCHES_DIR = "patches"; constexpr auto METADATA_DIR = "game_data"; +constexpr auto CUSTOM_TROPHY = "custom_trophy"; // Filenames constexpr auto LOG_FILE = "shad_log.txt"; diff --git a/src/core/libraries/np_trophy/np_trophy.cpp b/src/core/libraries/np_trophy/np_trophy.cpp index 91dd5b4b4..a951d5655 100644 --- a/src/core/libraries/np_trophy/np_trophy.cpp +++ b/src/core/libraries/np_trophy/np_trophy.cpp @@ -923,15 +923,16 @@ int PS4_SYSV_ABI sceNpTrophyUnlockTrophy(OrbisNpTrophyContext context, OrbisNpTr node.attribute("unlockstate").set_value("true"); } - Rtc::OrbisRtcTick trophyTimestamp; - Rtc::sceRtcGetCurrentTick(&trophyTimestamp); + auto trophyTimestamp = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); if (node.attribute("timestamp").empty()) { node.append_attribute("timestamp") = - std::to_string(trophyTimestamp.tick).c_str(); + std::to_string(trophyTimestamp).c_str(); } else { node.attribute("timestamp") - .set_value(std::to_string(trophyTimestamp.tick).c_str()); + .set_value(std::to_string(trophyTimestamp).c_str()); } std::string trophy_icon_file = "TROP"; @@ -955,15 +956,16 @@ int PS4_SYSV_ABI sceNpTrophyUnlockTrophy(OrbisNpTrophyContext context, OrbisNpTr platinum_node.attribute("unlockstate").set_value("true"); } - Rtc::OrbisRtcTick trophyTimestamp; - Rtc::sceRtcGetCurrentTick(&trophyTimestamp); + auto trophyTimestamp = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); if (platinum_node.attribute("timestamp").empty()) { platinum_node.append_attribute("timestamp") = - std::to_string(trophyTimestamp.tick).c_str(); + std::to_string(trophyTimestamp).c_str(); } else { platinum_node.attribute("timestamp") - .set_value(std::to_string(trophyTimestamp.tick).c_str()); + .set_value(std::to_string(trophyTimestamp).c_str()); } int platinum_trophy_id = diff --git a/src/core/libraries/np_trophy/trophy_ui.cpp b/src/core/libraries/np_trophy/trophy_ui.cpp index 2b909e5cf..2564cbf5d 100644 --- a/src/core/libraries/np_trophy/trophy_ui.cpp +++ b/src/core/libraries/np_trophy/trophy_ui.cpp @@ -2,9 +2,17 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include +#include #include #include +#include #include + +#ifdef ENABLE_QT_GUI +#include +#endif + #include "common/assert.h" #include "common/config.h" #include "common/singleton.h" @@ -12,18 +20,23 @@ #include "trophy_ui.h" CMRC_DECLARE(res); - +namespace fs = std::filesystem; using namespace ImGui; namespace Libraries::NpTrophy { std::optional current_trophy_ui; std::queue trophy_queue; std::mutex queueMtx; +bool isLeftSide; +double trophy_timer; TrophyUI::TrophyUI(const std::filesystem::path& trophyIconPath, const std::string& trophyName, const std::string_view& rarity) : trophy_name(trophyName), trophy_type(rarity) { + isLeftSide = Config::leftSideTrophy(); + trophy_timer = Config::getTrophyNotificationDuration(); + if (std::filesystem::exists(trophyIconPath)) { trophy_icon = RefCountedTexture::DecodePngFile(trophyIconPath); } else { @@ -31,23 +44,57 @@ TrophyUI::TrophyUI(const std::filesystem::path& trophyIconPath, const std::strin fmt::UTF(trophyIconPath.u8string())); } - std::string pathString; + std::string pathString = "src/images/"; + if (trophy_type == "P") { - pathString = "Resources/platinum.png"; + pathString += "platinum.png"; } else if (trophy_type == "G") { - pathString = "Resources/gold.png"; + pathString += "gold.png"; } else if (trophy_type == "S") { - pathString = "Resources/silver.png"; + pathString += "silver.png"; } else if (trophy_type == "B") { - pathString = "Resources/bronze.png"; + pathString += "bronze.png"; + } + + const auto CustomTrophy_Dir = Common::FS::GetUserPath(Common::FS::PathType::CustomTrophy); + std::string customPath; + + if (trophy_type == "P" && fs::exists(CustomTrophy_Dir / "platinum.png")) { + customPath = (CustomTrophy_Dir / "platinum.png").string(); + } else if (trophy_type == "G" && fs::exists(CustomTrophy_Dir / "gold.png")) { + customPath = (CustomTrophy_Dir / "gold.png").string(); + } else if (trophy_type == "S" && fs::exists(CustomTrophy_Dir / "silver.png")) { + customPath = (CustomTrophy_Dir / "silver.png").string(); + } else if (trophy_type == "B" && fs::exists(CustomTrophy_Dir / "bronze.png")) { + customPath = (CustomTrophy_Dir / "bronze.png").string(); + } + + std::vector imgdata; + if (!customPath.empty()) { + std::ifstream file(customPath, std::ios::binary); + if (file) { + imgdata = std::vector(std::istreambuf_iterator(file), + std::istreambuf_iterator()); + } else { + LOG_ERROR(Lib_NpTrophy, "Could not open custom file for trophy in {}", customPath); + } + } else { + auto resource = cmrc::res::get_filesystem(); + auto file = resource.open(pathString); + imgdata = std::vector(file.begin(), file.end()); } - auto resource = cmrc::res::get_filesystem(); - auto file = resource.open(pathString); - std::vector imgdata(file.begin(), file.end()); trophy_type_icon = RefCountedTexture::DecodePngTexture(imgdata); AddLayer(this); + +#ifdef ENABLE_QT_GUI + QString musicPath = QString::fromStdString(CustomTrophy_Dir.string() + "/trophy.mp3"); + if (fs::exists(musicPath.toStdString())) { + BackgroundMusicPlayer::getInstance().setVolume(100); + BackgroundMusicPlayer::getInstance().playMusic(musicPath, false); + } +#endif } TrophyUI::~TrophyUI() { @@ -58,6 +105,13 @@ void TrophyUI::Finish() { RemoveLayer(this); } +float fade_opacity = 0.0f; // Initial opacity (invisible) +ImVec2 start_pos = ImVec2(1280.0f, 50.0f); // Starts off screen, right +ImVec2 target_pos = ImVec2(0.0f, 50.0f); // Final position +float animation_duration = 0.5f; // Animation duration +float elapsed_time = 0.0f; // Animation time +float fade_out_duration = 0.5f; // Final fade duration + void TrophyUI::Draw() { const auto& io = GetIO(); @@ -68,26 +122,60 @@ void TrophyUI::Draw() { std::min(io.DisplaySize.y, (70 * AdjustHeight)), }; + elapsed_time += io.DeltaTime; + float progress = std::min(elapsed_time / animation_duration, 1.0f); + + // left or right position + float final_pos_x; + if (isLeftSide) { + start_pos.x = -window_size.x; + final_pos_x = 20 * AdjustWidth; + } else { + start_pos.x = io.DisplaySize.x; + final_pos_x = io.DisplaySize.x - window_size.x - 20 * AdjustWidth; + } + + ImVec2 current_pos = ImVec2(start_pos.x + (final_pos_x - start_pos.x) * progress, + start_pos.y + (target_pos.y - start_pos.y) * progress); + + trophy_timer -= io.DeltaTime; + + // If the remaining time of the trophy is less than or equal to 1 second, the fade-out begins. + if (trophy_timer <= 1.0f) { + float fade_out_time = 1.0f - (trophy_timer / 1.0f); + fade_opacity = 1.0f - fade_out_time; + } else { + // Fade in , 0 to 1 + fade_opacity = progress; + } + + fade_opacity = std::max(0.0f, std::min(fade_opacity, 1.0f)); + SetNextWindowSize(window_size); + SetNextWindowPos(current_pos); SetNextWindowCollapsed(false); - SetNextWindowPos(ImVec2(io.DisplaySize.x - (370 * AdjustWidth), (50 * AdjustHeight))); KeepNavHighlight(); + PushStyleVar(ImGuiStyleVar_Alpha, fade_opacity); + if (Begin("Trophy Window", nullptr, ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoInputs)) { + + // Displays the trophy icon if (trophy_type_icon) { SetCursorPosY((window_size.y * 0.5f) - (25 * AdjustHeight)); Image(trophy_type_icon.GetTexture().im_id, ImVec2((50 * AdjustWidth), (50 * AdjustHeight))); ImGui::SameLine(); } else { - // placeholder + // Placeholder const auto pos = GetCursorScreenPos(); ImGui::GetWindowDrawList()->AddRectFilled(pos, pos + ImVec2{50.0f * AdjustHeight}, GetColorU32(ImVec4{0.7f})); ImGui::Indent(60); } + // Displays the name of the trophy const std::string combinedString = "Trophy earned!\n%s" + trophy_name; const float wrap_width = CalcWrapWidthForPos(GetCursorScreenPos(), (window_size.x - (60 * AdjustWidth))); @@ -108,11 +196,12 @@ void TrophyUI::Draw() { TextWrapped("Trophy earned!\n%s", trophy_name.c_str()); ImGui::SameLine(window_size.x - (60 * AdjustWidth)); + // Displays the trophy icon if (trophy_icon) { SetCursorPosY((window_size.y * 0.5f) - (25 * AdjustHeight)); Image(trophy_icon.GetTexture().im_id, ImVec2((50 * AdjustWidth), (50 * AdjustHeight))); } else { - // placeholder + // Placeholder const auto pos = GetCursorScreenPos(); ImGui::GetWindowDrawList()->AddRectFilled(pos, pos + ImVec2{50.0f * AdjustHeight}, GetColorU32(ImVec4{0.7f})); @@ -120,7 +209,8 @@ void TrophyUI::Draw() { } End(); - trophy_timer -= io.DeltaTime; + PopStyleVar(); + if (trophy_timer <= 0) { std::lock_guard lock(queueMtx); if (!trophy_queue.empty()) { @@ -141,13 +231,27 @@ void AddTrophyToQueue(const std::filesystem::path& trophyIconPath, const std::st if (Config::getisTrophyPopupDisabled()) { return; } else if (current_trophy_ui.has_value()) { - TrophyInfo new_trophy; - new_trophy.trophy_icon_path = trophyIconPath; - new_trophy.trophy_name = trophyName; - new_trophy.trophy_type = rarity; - trophy_queue.push(new_trophy); - } else { - current_trophy_ui.emplace(trophyIconPath, trophyName, rarity); + current_trophy_ui.reset(); + } + + TrophyInfo new_trophy; + new_trophy.trophy_icon_path = trophyIconPath; + new_trophy.trophy_name = trophyName; + new_trophy.trophy_type = rarity; + trophy_queue.push(new_trophy); + + if (!current_trophy_ui.has_value()) { +#ifdef ENABLE_QT_GUI + BackgroundMusicPlayer::getInstance().stopMusic(); +#endif + // Resetting the animation for the next trophy + elapsed_time = 0.0f; // Resetting animation time + fade_opacity = 0.0f; // Starts invisible + start_pos = ImVec2(1280.0f, 50.0f); // Starts off screen, right + TrophyInfo next_trophy = trophy_queue.front(); + trophy_queue.pop(); + current_trophy_ui.emplace(next_trophy.trophy_icon_path, next_trophy.trophy_name, + next_trophy.trophy_type); } } diff --git a/src/core/libraries/np_trophy/trophy_ui.h b/src/core/libraries/np_trophy/trophy_ui.h index 16e707059..553c99f6f 100644 --- a/src/core/libraries/np_trophy/trophy_ui.h +++ b/src/core/libraries/np_trophy/trophy_ui.h @@ -28,7 +28,6 @@ public: private: std::string trophy_name; std::string_view trophy_type; - float trophy_timer = 5.0f; ImGui::RefCountedTexture trophy_icon; ImGui::RefCountedTexture trophy_type_icon; }; diff --git a/src/core/libraries/pad/pad.cpp b/src/core/libraries/pad/pad.cpp index bcc90c49b..2e5973144 100644 --- a/src/core/libraries/pad/pad.cpp +++ b/src/core/libraries/pad/pad.cpp @@ -315,12 +315,13 @@ int PS4_SYSV_ABI scePadRead(s32 handle, OrbisPadData* pData, s32 num) { pData[i].angularVelocity.x = states[i].angularVelocity.x; pData[i].angularVelocity.y = states[i].angularVelocity.y; pData[i].angularVelocity.z = states[i].angularVelocity.z; + pData[i].orientation = {0.0f, 0.0f, 0.0f, 1.0f}; if (engine) { - const auto accel_poll_rate = engine->GetAccelPollRate(); - if (accel_poll_rate != 0.0f) { + const auto gyro_poll_rate = engine->GetAccelPollRate(); + if (gyro_poll_rate != 0.0f) { GameController::CalculateOrientation(pData[i].acceleration, pData[i].angularVelocity, - 1.0f / accel_poll_rate, pData[i].orientation); + 1.0f / gyro_poll_rate, pData[i].orientation); } } pData[i].touchData.touchNum = @@ -384,11 +385,12 @@ int PS4_SYSV_ABI scePadReadState(s32 handle, OrbisPadData* pData) { pData->angularVelocity.x = state.angularVelocity.x; pData->angularVelocity.y = state.angularVelocity.y; pData->angularVelocity.z = state.angularVelocity.z; + pData->orientation = {0.0f, 0.0f, 0.0f, 1.0f}; if (engine) { - const auto accel_poll_rate = engine->GetAccelPollRate(); - if (accel_poll_rate != 0.0f) { + const auto gyro_poll_rate = engine->GetAccelPollRate(); + if (gyro_poll_rate != 0.0f) { GameController::CalculateOrientation(pData->acceleration, pData->angularVelocity, - 1.0f / accel_poll_rate, pData->orientation); + 1.0f / gyro_poll_rate, pData->orientation); } } pData->touchData.touchNum = diff --git a/src/emulator.cpp b/src/emulator.cpp index 0e282b89f..2b770c612 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -107,6 +107,11 @@ void Emulator::Run(const std::filesystem::path& file, const std::vectorGetHostPath("/app0/sce_sys/param.sfo"); + if (!std::filesystem::exists(param_sfo_path) || !Config::getSeparateLogFilesEnabled()) { + Common::Log::Initialize(); + Common::Log::Start(); + } + if (std::filesystem::exists(param_sfo_path)) { auto* param_sfo = Common::Singleton::Instance(); const bool success = param_sfo->Open(param_sfo_path); @@ -117,10 +122,8 @@ void Emulator::Run(const std::filesystem::path& file, const std::vectorsetAudioOutput(m_audioOutput); - m_mediaPlayer->setLoops(QMediaPlayer::Infinite); } void BackgroundMusicPlayer::setVolume(int volume) { @@ -16,7 +15,7 @@ void BackgroundMusicPlayer::setVolume(int volume) { m_audioOutput->setVolume(linearVolume); } -void BackgroundMusicPlayer::playMusic(const QString& snd0path) { +void BackgroundMusicPlayer::playMusic(const QString& snd0path, bool loops) { if (snd0path.isEmpty()) { stopMusic(); return; @@ -28,6 +27,12 @@ void BackgroundMusicPlayer::playMusic(const QString& snd0path) { return; } + if (loops) { + m_mediaPlayer->setLoops(QMediaPlayer::Infinite); + } else { + m_mediaPlayer->setLoops(1); + } + m_currentMusic = newMusic; m_mediaPlayer->setSource(newMusic); m_mediaPlayer->play(); diff --git a/src/qt_gui/background_music_player.h b/src/qt_gui/background_music_player.h index 6d70fe68c..078710a01 100644 --- a/src/qt_gui/background_music_player.h +++ b/src/qt_gui/background_music_player.h @@ -17,7 +17,7 @@ public: } void setVolume(int volume); - void playMusic(const QString& snd0path); + void playMusic(const QString& snd0path, bool loops = true); void stopMusic(); private: diff --git a/src/qt_gui/cheats_patches.cpp b/src/qt_gui/cheats_patches.cpp index bf7877f18..7239affd5 100644 --- a/src/qt_gui/cheats_patches.cpp +++ b/src/qt_gui/cheats_patches.cpp @@ -1082,7 +1082,11 @@ void CheatsPatches::addCheatsToLayout(const QJsonArray& modsArray, const QJsonAr QLabel* creditsLabel = new QLabel(); QString creditsText = tr("Author: "); if (!creditsArray.isEmpty()) { - creditsText += creditsArray[0].toString(); + QStringList authors; + for (const QJsonValue& credit : creditsArray) { + authors << credit.toString(); + } + creditsText += authors.join(", "); } creditsLabel->setText(creditsText); creditsLabel->setAlignment(Qt::AlignLeft); diff --git a/src/qt_gui/compatibility_info.cpp b/src/qt_gui/compatibility_info.cpp index 443d56a20..da32f24ae 100644 --- a/src/qt_gui/compatibility_info.cpp +++ b/src/qt_gui/compatibility_info.cpp @@ -78,25 +78,38 @@ void CompatibilityInfoClass::UpdateCompatibilityDatabase(QWidget* parent, bool f CompatibilityEntry CompatibilityInfoClass::GetCompatibilityInfo(const std::string& serial) { QString title_id = QString::fromStdString(serial); if (m_compatibility_database.contains(title_id)) { - { - QJsonObject compatibility_obj = m_compatibility_database[title_id].toObject(); - for (int os_int = 0; os_int != static_cast(OSType::Last); os_int++) { - QString os_string = OSTypeToString.at(static_cast(os_int)); - if (compatibility_obj.contains(os_string)) { - QJsonObject compatibility_entry_obj = compatibility_obj[os_string].toObject(); - CompatibilityEntry compatibility_entry{ - LabelToCompatStatus.at(compatibility_entry_obj["status"].toString()), - compatibility_entry_obj["version"].toString(), - QDateTime::fromString(compatibility_entry_obj["last_tested"].toString(), - Qt::ISODate), - compatibility_entry_obj["url"].toString(), - compatibility_entry_obj["issue_number"].toString()}; - return compatibility_entry; - } - } + QJsonObject compatibility_obj = m_compatibility_database[title_id].toObject(); + + // Set current_os automatically + QString current_os; +#ifdef Q_OS_WIN + current_os = "os-windows"; +#elif defined(Q_OS_MAC) + current_os = "os-macOS"; +#elif defined(Q_OS_LINUX) + current_os = "os-linux"; +#else + current_os = "os-unknown"; +#endif + // Check if the game is compatible with the current operating system + if (compatibility_obj.contains(current_os)) { + QJsonObject compatibility_entry_obj = compatibility_obj[current_os].toObject(); + CompatibilityEntry compatibility_entry{ + LabelToCompatStatus.at(compatibility_entry_obj["status"].toString()), + compatibility_entry_obj["version"].toString(), + QDateTime::fromString(compatibility_entry_obj["last_tested"].toString(), + Qt::ISODate), + compatibility_entry_obj["url"].toString(), + compatibility_entry_obj["issue_number"].toString()}; + return compatibility_entry; + } else { + // If there is no entry for the current operating system, return "Unknown" + return CompatibilityEntry{CompatibilityStatus::Unknown, "", + QDateTime::currentDateTime(), "", 0}; } } + // If title not found, return "Unknown" return CompatibilityEntry{CompatibilityStatus::Unknown, "", QDateTime::currentDateTime(), "", 0}; } @@ -200,4 +213,4 @@ const QString CompatibilityInfoClass::GetCompatStatusString(const CompatibilityS default: return tr("Unknown"); } -} \ No newline at end of file +} diff --git a/src/qt_gui/gui_context_menus.h b/src/qt_gui/gui_context_menus.h index 1a059a850..8a6e07847 100644 --- a/src/qt_gui/gui_context_menus.h +++ b/src/qt_gui/gui_context_menus.h @@ -97,11 +97,13 @@ public: QAction* deleteUpdate = new QAction(tr("Delete Update"), widget); QAction* deleteSaveData = new QAction(tr("Delete Save Data"), widget); QAction* deleteDLC = new QAction(tr("Delete DLC"), widget); + QAction* deleteTrophy = new QAction(tr("Delete Trophy"), widget); deleteMenu->addAction(deleteGame); deleteMenu->addAction(deleteUpdate); deleteMenu->addAction(deleteSaveData); deleteMenu->addAction(deleteDLC); + deleteMenu->addAction(deleteTrophy); menu.addMenu(deleteMenu); @@ -155,10 +157,54 @@ public: } if (selected == openLogFolder) { - QString userPath; - Common::FS::PathToQString(userPath, - Common::FS::GetUserPath(Common::FS::PathType::UserDir)); - QDesktopServices::openUrl(QUrl::fromLocalFile(userPath + "/log")); + QString logPath; + Common::FS::PathToQString(logPath, + Common::FS::GetUserPath(Common::FS::PathType::LogDir)); + if (!Config::getSeparateLogFilesEnabled()) { + QDesktopServices::openUrl(QUrl::fromLocalFile(logPath)); + } else { + QString fileName = QString::fromStdString(m_games[itemID].serial) + ".log"; + QString filePath = logPath + "/" + fileName; + QStringList arguments; + if (QFile::exists(filePath)) { +#ifdef Q_OS_WIN + arguments << "/select," << filePath.replace("/", "\\"); + QProcess::startDetached("explorer", arguments); + +#elif defined(Q_OS_MAC) + arguments << "-R" << filePath; + QProcess::startDetached("open", arguments); + +#elif defined(Q_OS_LINUX) + QStringList arguments; + arguments << "--select" << filePath; + if (!QProcess::startDetached("nautilus", arguments)) { + // Failed to open Nautilus to select file + arguments.clear(); + arguments << logPath; + if (!QProcess::startDetached("xdg-open", arguments)) { + // Failed to open directory on Linux + } + } +#else + QDesktopServices::openUrl(QUrl::fromLocalFile(logPath)); +#endif + } else { + QMessageBox msgBox; + msgBox.setIcon(QMessageBox::Information); + msgBox.setText(tr("No log file found for this game!")); + + QPushButton* okButton = msgBox.addButton(QMessageBox::Ok); + QPushButton* openFolderButton = + msgBox.addButton(tr("Open Log Folder"), QMessageBox::ActionRole); + + msgBox.exec(); + + if (msgBox.clickedButton() == openFolderButton) { + QDesktopServices::openUrl(QUrl::fromLocalFile(logPath)); + } + } + } } if (selected == &openSfoViewer) { @@ -380,9 +426,9 @@ public: } if (selected == deleteGame || selected == deleteUpdate || selected == deleteDLC || - selected == deleteSaveData) { + selected == deleteSaveData || selected == deleteTrophy) { bool error = false; - QString folder_path, game_update_path, dlc_path, save_data_path; + QString folder_path, game_update_path, dlc_path, save_data_path, trophy_data_path; Common::FS::PathToQString(folder_path, m_games[itemID].path); game_update_path = folder_path + "-UPDATE"; Common::FS::PathToQString( @@ -391,6 +437,11 @@ public: Common::FS::PathToQString(save_data_path, Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "savedata/1" / m_games[itemID].serial); + + Common::FS::PathToQString(trophy_data_path, + Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) / + m_games[itemID].serial / "TrophyFiles"); + QString message_type = tr("Game"); if (selected == deleteUpdate) { @@ -420,6 +471,16 @@ public: folder_path = save_data_path; message_type = tr("Save Data"); } + } else if (selected == deleteTrophy) { + if (!std::filesystem::exists(Common::FS::PathFromQString(trophy_data_path))) { + QMessageBox::critical( + nullptr, tr("Error"), + QString(tr("This game has no saved trophies to delete!"))); + error = true; + } else { + folder_path = trophy_data_path; + message_type = tr("Trophy"); + } } if (!error) { QString gameName = QString::fromStdString(m_games[itemID].name); diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index 96bd1d9e5..d9fb45fac 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -130,6 +130,7 @@ void MainWindow::CreateActions() { m_theme_act_group->addAction(ui->setThemeViolet); m_theme_act_group->addAction(ui->setThemeGruvbox); m_theme_act_group->addAction(ui->setThemeTokyoNight); + m_theme_act_group->addAction(ui->setThemeOled); } void MainWindow::AddUiWidgets() { @@ -651,6 +652,14 @@ void MainWindow::CreateConnects() { isIconBlack = false; } }); + connect(ui->setThemeOled, &QAction::triggered, &m_window_themes, [this]() { + m_window_themes.SetWindowTheme(Theme::Oled, ui->mw_searchbar); + Config::setMainWindowTheme(static_cast(Theme::Oled)); + if (isIconBlack) { + SetUiIcons(false); + isIconBlack = false; + } + }); } void MainWindow::StartGame() { @@ -1098,6 +1107,11 @@ void MainWindow::SetLastUsedTheme() { isIconBlack = false; SetUiIcons(false); break; + case Theme::Oled: + ui->setThemeOled->setChecked(true); + isIconBlack = false; + SetUiIcons(false); + break; } } diff --git a/src/qt_gui/main_window_themes.cpp b/src/qt_gui/main_window_themes.cpp index 5fffd4c9e..c5574fca9 100644 --- a/src/qt_gui/main_window_themes.cpp +++ b/src/qt_gui/main_window_themes.cpp @@ -6,6 +6,7 @@ void WindowThemes::SetWindowTheme(Theme theme, QLineEdit* mw_searchbar) { QPalette themePalette; + qApp->setStyleSheet(""); switch (theme) { case Theme::Dark: mw_searchbar->setStyleSheet( @@ -165,5 +166,29 @@ void WindowThemes::SetWindowTheme(Theme theme, QLineEdit* mw_searchbar) { themePalette.setColor(QPalette::HighlightedText, Qt::black); qApp->setPalette(themePalette); break; + case Theme::Oled: + mw_searchbar->setStyleSheet("QLineEdit:focus {" + "border: 1px solid #2A82DA; }"); + themePalette.setColor(QPalette::Window, Qt::black); + themePalette.setColor(QPalette::WindowText, Qt::white); + themePalette.setColor(QPalette::Base, Qt::black); + themePalette.setColor(QPalette::AlternateBase, Qt::black); + themePalette.setColor(QPalette::ToolTipBase, Qt::black); + themePalette.setColor(QPalette::ToolTipText, Qt::white); + themePalette.setColor(QPalette::Text, Qt::white); + themePalette.setColor(QPalette::Button, QColor(5, 5, 5)); + themePalette.setColor(QPalette::ButtonText, Qt::white); + themePalette.setColor(QPalette::BrightText, Qt::red); + themePalette.setColor(QPalette::Link, QColor(42, 130, 218)); + themePalette.setColor(QPalette::Highlight, QColor(42, 130, 218)); + themePalette.setColor(QPalette::HighlightedText, Qt::black); + qApp->setPalette(themePalette); + qApp->setStyleSheet("QLineEdit {" + "background-color: #000000; color: #ffffff; border: 1px solid #a0a0a0; " + "border-radius: 4px; padding: 5px; }" + + "QCheckBox::indicator:unchecked {" + "border: 1px solid #808080; border-radius: 4px; }"); + break; } } \ No newline at end of file diff --git a/src/qt_gui/main_window_themes.h b/src/qt_gui/main_window_themes.h index 0ec2cce58..babde0f27 100644 --- a/src/qt_gui/main_window_themes.h +++ b/src/qt_gui/main_window_themes.h @@ -7,7 +7,7 @@ #include #include -enum class Theme : int { Dark, Light, Green, Blue, Violet, Gruvbox, TokyoNight }; +enum class Theme : int { Dark, Light, Green, Blue, Violet, Gruvbox, TokyoNight, Oled }; class WindowThemes : public QObject { Q_OBJECT diff --git a/src/qt_gui/main_window_ui.h b/src/qt_gui/main_window_ui.h index e74ffcacb..3ebfcee9e 100644 --- a/src/qt_gui/main_window_ui.h +++ b/src/qt_gui/main_window_ui.h @@ -39,6 +39,7 @@ public: QAction* setThemeViolet; QAction* setThemeGruvbox; QAction* setThemeTokyoNight; + QAction* setThemeOled; QWidget* centralWidget; QLineEdit* mw_searchbar; QPushButton* playButton; @@ -171,6 +172,9 @@ public: setThemeTokyoNight = new QAction(MainWindow); setThemeTokyoNight->setObjectName("setThemeTokyoNight"); setThemeTokyoNight->setCheckable(true); + setThemeOled = new QAction(MainWindow); + setThemeOled->setObjectName("setThemeOled"); + setThemeOled->setCheckable(true); centralWidget = new QWidget(MainWindow); centralWidget->setObjectName("centralWidget"); sizePolicy.setHeightForWidth(centralWidget->sizePolicy().hasHeightForWidth()); @@ -303,6 +307,7 @@ public: menuThemes->addAction(setThemeViolet); menuThemes->addAction(setThemeGruvbox); menuThemes->addAction(setThemeTokyoNight); + menuThemes->addAction(setThemeOled); menuGame_List_Icons->addAction(setIconSizeTinyAct); menuGame_List_Icons->addAction(setIconSizeSmallAct); menuGame_List_Icons->addAction(setIconSizeMediumAct); @@ -393,6 +398,7 @@ public: setThemeViolet->setText(QCoreApplication::translate("MainWindow", "Violet", nullptr)); setThemeGruvbox->setText("Gruvbox"); setThemeTokyoNight->setText("Tokyo Night"); + setThemeOled->setText("OLED"); toolBar->setWindowTitle(QCoreApplication::translate("MainWindow", "toolBar", nullptr)); } // retranslateUi }; diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp index ce6f59937..bde104828 100644 --- a/src/qt_gui/settings_dialog.cpp +++ b/src/qt_gui/settings_dialog.cpp @@ -225,6 +225,17 @@ SettingsDialog::SettingsDialog(std::span physical_devices, Config::setShowBackgroundImage(state == Qt::Checked); }); } + + // User TAB + { + connect(ui->OpenCustomTrophyLocationButton, &QPushButton::clicked, this, []() { + QString userPath; + Common::FS::PathToQString(userPath, + Common::FS::GetUserPath(Common::FS::PathType::CustomTrophy)); + QDesktopServices::openUrl(QUrl::fromLocalFile(userPath)); + }); + } + // Input TAB { connect(ui->hideCursorComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, @@ -280,8 +291,8 @@ SettingsDialog::SettingsDialog(std::span physical_devices, connect(ui->OpenLogLocationButton, &QPushButton::clicked, this, []() { QString userPath; Common::FS::PathToQString(userPath, - Common::FS::GetUserPath(Common::FS::PathType::UserDir)); - QDesktopServices::openUrl(QUrl::fromLocalFile(userPath + "/log")); + Common::FS::GetUserPath(Common::FS::PathType::LogDir)); + QDesktopServices::openUrl(QUrl::fromLocalFile(userPath)); }); } @@ -308,6 +319,9 @@ SettingsDialog::SettingsDialog(std::span physical_devices, ui->checkCompatibilityOnStartupCheckBox->installEventFilter(this); ui->updateCompatibilityButton->installEventFilter(this); + // User + ui->OpenCustomTrophyLocationButton->installEventFilter(this); + // Input ui->hideCursorGroupBox->installEventFilter(this); ui->idleTimeoutGroupBox->installEventFilter(this); @@ -403,6 +417,9 @@ void SettingsDialog::LoadValuesFromConfig() { ui->playBGMCheckBox->setChecked(toml::find_or(data, "General", "playBGM", false)); ui->disableTrophycheckBox->setChecked( toml::find_or(data, "General", "isTrophyPopupDisabled", false)); + ui->popUpDurationSpinBox->setValue(Config::getTrophyNotificationDuration()); + ui->radioButton_Left->setChecked(Config::leftSideTrophy()); + ui->radioButton_Right->setChecked(!ui->radioButton_Left->isChecked()); ui->BGMVolumeSlider->setValue(toml::find_or(data, "General", "BGMvolume", 50)); ui->discordRPCCheckbox->setChecked( toml::find_or(data, "General", "enableDiscordRPC", true)); @@ -593,6 +610,11 @@ void SettingsDialog::updateNoteTextEdit(const QString& elementName) { text = tr("Update Compatibility Database:\\nImmediately update the compatibility database."); } + //User + if (elementName == "OpenCustomTrophyLocationButton") { + text = tr("Open the custom trophy images/sounds folder:\\nYou can add custom images to the trophies and an audio.\\nAdd the files to custom_trophy with the following names:\\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png"); + } + // Input if (elementName == "hideCursorGroupBox") { text = tr("Hide Cursor:\\nChoose when the cursor will disappear:\\nNever: You will always see the mouse.\\nidle: Set a time for it to disappear after being idle.\\nAlways: you will never see the mouse."); @@ -677,11 +699,14 @@ void SettingsDialog::UpdateSettings() { const QVector TouchPadIndex = {"left", "center", "right", "none"}; Config::setBackButtonBehavior(TouchPadIndex[ui->backButtonBehaviorComboBox->currentIndex()]); - Config::setIsFullscreen(ui->displayModeComboBox->currentText().toStdString() != "Windowed"); + Config::setIsFullscreen(screenModeMap.value(ui->displayModeComboBox->currentText()) != + "Windowed"); Config::setFullscreenMode( screenModeMap.value(ui->displayModeComboBox->currentText()).toStdString()); Config::setIsMotionControlsEnabled(ui->motionControlsCheckBox->isChecked()); Config::setisTrophyPopupDisabled(ui->disableTrophycheckBox->isChecked()); + Config::setTrophyNotificationDuration(ui->popUpDurationSpinBox->value()); + Config::setLeftSideTrophy(ui->radioButton_Left->isChecked()); Config::setPlayBGM(ui->playBGMCheckBox->isChecked()); Config::setAllowHDR(ui->enableHDRCheckBox->isChecked()); Config::setLogType(logTypeMap.value(ui->logTypeComboBox->currentText()).toStdString()); diff --git a/src/qt_gui/settings_dialog.ui b/src/qt_gui/settings_dialog.ui index 2df328fbe..c793aced5 100644 --- a/src/qt_gui/settings_dialog.ui +++ b/src/qt_gui/settings_dialog.ui @@ -59,7 +59,7 @@ - 6 + 0 @@ -73,8 +73,8 @@ 0 0 - 718 - 332 + 946 + 545 @@ -454,8 +454,8 @@ 0 0 - 646 - 395 + 946 + 545 @@ -903,8 +903,8 @@ 0 0 - 545 - 141 + 946 + 545 @@ -1198,8 +1198,8 @@ 0 0 - 234 - 292 + 946 + 545 @@ -1264,30 +1264,121 @@ - Disable Trophy Pop-ups + Disable Trophy Notification - + + + 0 + + + + + + 0 + 0 + + + + Trophy Notification Position + + + + + + + Left + + + + + + + + 0 + 0 + + + + Right + + + + + + + + + 0 + + + + + + 0 + 0 + + + + Notification Duration + + + + + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + + 0 + + + + + Trophy Key + + + + + + + + 0 + 0 + + + + + 10 + false + + + + + + + + - Trophy Key - - - - - - - - 0 - 0 - - - - - 10 - false - + Open the custom trophy images/sounds folder @@ -1342,8 +1433,8 @@ 0 0 - 455 - 252 + 946 + 545 @@ -1626,8 +1717,8 @@ 0 0 - 216 - 254 + 946 + 545 @@ -1717,7 +1808,7 @@ 0 0 946 - 536 + 545 diff --git a/src/qt_gui/translations/en_US.ts b/src/qt_gui/translations/en_US.ts index 263267aba..92fb59a02 100644 --- a/src/qt_gui/translations/en_US.ts +++ b/src/qt_gui/translations/en_US.ts @@ -419,11 +419,11 @@ Left - + Left Right - + Right Down @@ -775,6 +775,10 @@ Delete DLC Delete DLC + + Delete Trophy + Delete Trophy + Compatibility... Compatibility... @@ -851,6 +855,10 @@ This game has no update folder to open! + + No log file found for this game! + + Failed to convert icon. @@ -859,10 +867,18 @@ This game has no save data to delete! + + This game has no saved trophies to delete! + + Save Data + + Trophy + Trophy + SFO Viewer for @@ -1311,6 +1327,10 @@ Trophy Trophy + + Open the custom trophy images/sounds folder + Open the custom trophy images/sounds folder + Logger Logger @@ -1476,8 +1496,8 @@ Title Music - Disable Trophy Pop-ups - Disable Trophy Pop-ups + Disable Trophy Notification + Disable Trophy Notification Background Image @@ -1611,6 +1631,10 @@ Update Compatibility Database:\nImmediately update the compatibility database. Update Compatibility Database:\nImmediately update the compatibility database. + + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png + Never Never @@ -1803,6 +1827,22 @@ Separate Log Files:\nWrites a separate logfile for each game. + + Trophy Notification Position + Trophy Notification Position + + + Left + Left + + + Right + Right + + + Notification Duration + Notification Duration + TrophyViewer @@ -1810,5 +1850,21 @@ Trophy Viewer Trophy Viewer + + Progress + + + + Show Earned Trophies + + + + Show Not Earned Trophies + + + + Show Hidden Trophies + + diff --git a/src/qt_gui/translations/es_ES.ts b/src/qt_gui/translations/es_ES.ts index 932d72d2a..148613422 100644 --- a/src/qt_gui/translations/es_ES.ts +++ b/src/qt_gui/translations/es_ES.ts @@ -411,7 +411,7 @@ D-Pad - D-Pad + Botones de dirección Up @@ -419,27 +419,27 @@ Left - Left + Izquierda Right - Right + Derecha Down - Down + Abajo Left Stick Deadzone (def:2 max:127) - Left Stick Deadzone (def:2 max:127) + Zona muerta del stick izquierdo (defecto: 2, máx.: 127) Left Deadzone - Left Deadzone + Zona muerta del stick izquierdo Left Stick - Left Stick + Stick izquierdo Config Selection @@ -455,31 +455,31 @@ L1 / LB - L1 / LB + L1/LB L2 / LT - L2 / LT + L2/LT Back - Back + Back R1 / RB - R1 / RB + R1/RB R2 / RT - R2 / RT + R2/RT L3 - L3 + L3 Options / Start - Options / Start + Options/Start R3 @@ -487,19 +487,19 @@ Face Buttons - Face Buttons + Botones de acción Triangle / Y - Triangle / Y + Triángulo/Y Square / X - Square / X + Cuadrado/X Circle / B - Circle / B + Círculo/B Cross / A @@ -507,31 +507,31 @@ Right Stick Deadzone (def:2, max:127) - Right Stick Deadzone (def:2, max:127) + Zona muerta del stick derecho (defecto: 2, máx.: 127) Right Deadzone - Right Deadzone + Zona muerta del stick derecho Right Stick - Right Stick + Stick derecho Color Adjustment - Color Adjustment + Calibración de color R: - R: + R: G: - G: + V: B: - B: + A: Override Lightbar Color @@ -580,11 +580,11 @@ Error - Error + Error Directory to install DLC - Directory to install DLC + Carpeta para instalar DLC @@ -603,7 +603,7 @@ Compatibility - Compatibility + Compatibilidad Region @@ -611,7 +611,7 @@ Firmware - Firmware + Firmware Size @@ -635,31 +635,31 @@ h - h + h m - m + m s - s + s Compatibility is untested - Compatibility is untested + Compatibilidad no comprobada Game does not initialize properly / crashes the emulator - Game does not initialize properly / crashes the emulator + El juego no se inicia correctamente o cuelga el emulador Game boots, but only displays a blank screen - Game boots, but only displays a blank screen + El juego arranca, pero se queda en blanco Game displays an image but does not go past the menu - Game displays an image but does not go past the menu + El juego muestra imágenes, pero no va más allá de los menús Game has game-breaking glitches or unplayable performance @@ -667,7 +667,7 @@ Game can be completed with playable performance and no major glitches - Game can be completed with playable performance and no major glitches + El juego puede completarse con un rendimiento jugable y sin errores de importancia Click to see details on github @@ -682,19 +682,19 @@ GameListUtils B - B + B KB - KB + KB MB - MB + MB GB - GB + GB TB @@ -749,7 +749,7 @@ Copy Version - Copy Version + Copiar versión Copy Size @@ -761,11 +761,11 @@ Delete... - Delete... + Eliminar... Delete Game - Delete Game + Eliminar juego Delete Update @@ -773,23 +773,23 @@ Delete DLC - Delete DLC + Eliminar DLC Compatibility... - Compatibility... + Compatibilidad... Update database - Update database + Actualizar base de datos View report - View report + Ver informe Submit a report - Submit a report + Enviar un informe Shortcut creation @@ -801,7 +801,7 @@ Error - Error + Error Error creating shortcut! @@ -813,59 +813,59 @@ Game - Game + Juego This game has no update to delete! - This game has no update to delete! + ¡Este juego no tiene actualizaciones! Update - Update + Actualización This game has no DLC to delete! - This game has no DLC to delete! + ¡Este juego no tiene DLCs! DLC - DLC + DLC Delete %1 - Delete %1 + Eliminar %1 Are you sure you want to delete %1's %2 directory? - Are you sure you want to delete %1's %2 directory? + ¿Seguro que quieres eliminar el directorio %2 de %1? Open Update Folder - Open Update Folder + Abrir carpeta de actualizaciones Delete Save Data - Delete Save Data + Eliminar datos guardados This game has no update folder to open! - This game has no update folder to open! + ¡Este juego no tiene carpeta de actualizaciones! Failed to convert icon. - Failed to convert icon. + Error al convertir el icono. This game has no save data to delete! - This game has no save data to delete! + ¡Este juego no tiene datos guardados! Save Data - Save Data + Datos guardados SFO Viewer for - SFO Viewer for + Visualizador de SFO para @@ -876,15 +876,15 @@ Select which directory you want to install to. - Select which directory you want to install to. + Selecciona el directorio de instalación. Install All Queued to Selected Folder - Install All Queued to Selected Folder + Instalar toda la cola en la carpeta seleccionada Delete PKG File on Install - Delete PKG File on Install + Eliminar archivo PKG tras la instalación @@ -923,7 +923,7 @@ Open shadPS4 Folder - Open shadPS4 Folder + Abrir carpeta de shadPS4 Exit @@ -1163,7 +1163,7 @@ Run Game - Run Game + Ejecutar juego Eboot.bin file not found @@ -1171,19 +1171,19 @@ PKG File (*.PKG *.pkg) - PKG File (*.PKG *.pkg) + Archivo PKG (*.PKG *.pkg) PKG is a patch or DLC, please install the game first! - PKG is a patch or DLC, please install the game first! + El archivo PKG es un parche o un DLC, ¡debes instalar el juego primero! Game is already running! - Game is already running! + ¡El juego ya se está ejecutando! shadPS4 - shadPS4 + shadPS4 @@ -1214,19 +1214,19 @@ Category - Category + Categoría Type - Type + Tipo App Ver - App Ver + Versión de aplicación FW - FW + FW Region @@ -1234,7 +1234,7 @@ Flags - Flags + Etiquetas Path @@ -1250,7 +1250,7 @@ Package - Package + Paquete @@ -1261,7 +1261,7 @@ General - General + General System @@ -1281,7 +1281,7 @@ Enable Separate Update Folder - Enable Separate Update Folder + Habilitar carpeta independiente de actualizaciones Default tab when opening settings @@ -1305,11 +1305,11 @@ Trophy Key - Trophy Key + Clave de trofeos Trophy - Trophy + Trofeo Logger @@ -1333,7 +1333,7 @@ Cursor - Cursor + Cursor Hide Cursor @@ -1345,7 +1345,7 @@ s - s + s Controller @@ -1389,7 +1389,7 @@ Enable HDR - Enable HDR + Habilitar HDR Paths @@ -1429,15 +1429,15 @@ Enable Crash Diagnostics - Enable Crash Diagnostics + Habilitar diagnóstico de fallos Collect Shaders - Collect Shaders + Recopilar shaders Copy GPU Buffers - Copy GPU Buffers + Copiar búferes de GPU Host Debug Markers @@ -1473,11 +1473,11 @@ Title Music - Title Music + Música de título Disable Trophy Pop-ups - Disable Trophy Pop-ups + Deshabilitar mensajes de trofeos Background Image @@ -1497,7 +1497,7 @@ Update Compatibility Database On Startup - Update Compatibility Database On Startup + Actualizar base de datos de compatibilidad al iniciar Game Compatibility @@ -1505,11 +1505,11 @@ Display Compatibility Data - Display Compatibility Data + Mostrar datos de compatibilidad Update Compatibility Database - Update Compatibility Database + Actualizar base de datos de compatibilidad Volume @@ -1721,11 +1721,11 @@ Release - Release + Principal Nightly - Nightly + Nightly Set the volume of the background music. @@ -1733,11 +1733,11 @@ Enable Motion Controls - Enable Motion Controls + Habilitar controles de movimiento Save Data Path - Save Data Path + Ruta de datos guardados Browse @@ -1753,7 +1753,7 @@ Auto Select - Auto Select + Selección automática Directory to install games @@ -1761,39 +1761,39 @@ Directory to save data - Directory to save data + Directorio para guardar datos Video - Video + Vídeo Display Mode - Display Mode + Modo de imagen Windowed - Windowed + Ventana Fullscreen - Fullscreen + Pantalla completa Fullscreen (Borderless) - Fullscreen (Borderless) + Pantalla completa (sin bordes) Window Size - Window Size + Tamaño de ventana W: - W: + Ancho: H: - H: + Alto: Separate Log Files diff --git a/src/qt_gui/translations/fr_FR.ts b/src/qt_gui/translations/fr_FR.ts index fef03d7bb..a812631f5 100644 --- a/src/qt_gui/translations/fr_FR.ts +++ b/src/qt_gui/translations/fr_FR.ts @@ -519,19 +519,19 @@ Color Adjustment - Color Adjustment + Ajustement des couleurs R: - R: + R: G: - G: + G: B: - B: + B: Override Lightbar Color @@ -539,7 +539,7 @@ Override Color - Override Color + Remplacer la couleur @@ -1234,7 +1234,7 @@ Flags - Flags + Les indicateurs Path @@ -1577,7 +1577,7 @@ Background Image:\nControl the opacity of the game background image. - Background Image:\nControl the opacity of the game background image. + Image de fond :\nContrôle l'opacité de l'image de fond du jeu. Play Title Music:\nIf a game supports it, enable playing special music when selecting the game in the GUI. @@ -1661,7 +1661,7 @@ Enable HDR:\nEnables HDR in games that support it.\nYour monitor must have support for the BT2020 PQ color space and the RGB10A2 swapchain format. - Enable HDR:\nEnables HDR in games that support it.\nYour monitor must have support for the BT2020 PQ color space and the RGB10A2 swapchain format. + Activer HDR:\nActive le HDR dans les jeux qui le supportent.\nVotre moniteur doit avoir la prise en charge de l'espace couleur PQ BT2020 et du format swapchain RGB10A2. Game Folders:\nThe list of folders to check for installed games. @@ -1721,11 +1721,11 @@ Release - Release + Sortie Nightly - Nightly + Nocturne Set the volume of the background music. @@ -1737,7 +1737,7 @@ Save Data Path - Save Data Path + Enregistrer le chemin vers les données Browse @@ -1745,15 +1745,15 @@ async - async + asynchrone sync - sync + synchrone Auto Select - Auto Select + Sélection automatique Directory to install games @@ -1761,39 +1761,39 @@ Directory to save data - Directory to save data + Répertoire d'enregistrement des données Video - Video + Vidéo Display Mode - Display Mode + Mode d'affichage Windowed - Windowed + Fenêtré Fullscreen - Fullscreen + Plein écran Fullscreen (Borderless) - Fullscreen (Borderless) + Plein écran (sans bordure) Window Size - Window Size + Taille de fenêtre W: - W: + W: H: - H: + H: Separate Log Files diff --git a/src/qt_gui/translations/pl_PL.ts b/src/qt_gui/translations/pl_PL.ts index 05ce4d9be..407948fae 100644 --- a/src/qt_gui/translations/pl_PL.ts +++ b/src/qt_gui/translations/pl_PL.ts @@ -22,7 +22,7 @@ CheatsPatches Cheats / Patches for - Kody / Łatki dla + Kody / Poprawki dla Cheats/Patches are experimental.\nUse with caution.\n\nDownload cheats individually by selecting the repository and clicking the download button.\nIn the Patches tab, you can download all patches at once, choose which ones you want to use, and save your selection.\n\nSince we do not develop the Cheats/Patches,\nplease report issues to the cheat author.\n\nCreated a new cheat? Visit:\n @@ -58,15 +58,15 @@ Delete File - Delete File + Usuń plik No files selected. - No files selected. + Nie wybrano pliku. You can delete the cheats you don't want after downloading them. - You can delete the cheats you don't want after downloading them. + Możesz usunąć kody, których nie chcesz po ich pobraniu. Do you want to delete the selected file?\n%1 @@ -202,7 +202,7 @@ You may need to update your game. - Możesz potrzebować zaktualizować swoją grę. + Może być konieczne uaktualnienie gry. Incompatibility Notice @@ -230,11 +230,11 @@ Failed to open files.json for reading. - Failed to open files.json for reading. + Nie można otworzyć pliku files.json do odczytu. Name: - Name: + Nazwa: Can't apply cheats before the game is started @@ -249,7 +249,7 @@ CheckUpdate Auto Updater - Automatyczne aktualizacje + Asystent aktualizacji Error @@ -289,7 +289,7 @@ Update Channel - Kanał Aktualizacji + Kanał aktualizacji Current Version @@ -297,7 +297,7 @@ Latest Version - Ostatnia wersja + Najnowsza wersja Do you want to update? @@ -388,7 +388,7 @@ Boots - Buty + Uruchamia się Menus @@ -400,146 +400,146 @@ Playable - Do grania + Grywalne ControlSettings Configure Controls - Configure Controls + Skonfiguruj sterowanie D-Pad - D-Pad + Krzyżak Up - Up + Góra Left - Left + Lewo Right - Right + Prawo Down - Down + Dół Left Stick Deadzone (def:2 max:127) - Left Stick Deadzone (def:2 max:127) + Martwa strefa lewego drążka (def:2 max:127) Left Deadzone - Left Deadzone + Martwa strefa lewego drążka Left Stick - Left Stick + Lewy drążek Config Selection - Config Selection + Wybór konfiguracji Common Config - Common Config + Typowa konfiguracja Use per-game configs - Use per-game configs + Użyj osobnej konfiguracji dla każdej gry L1 / LB - L1 / LB + L1 / LB L2 / LT - L2 / LT + L2 / LT Back - Back + Wstecz R1 / RB - R1 / RB + R1 / RB R2 / RT - R2 / RT + R2 / RT L3 - L3 + L3 Options / Start - Options / Start + Opcje / Start R3 - R3 + R3 Face Buttons - Face Buttons + Przyciski akcji Triangle / Y - Triangle / Y + Trójkąt / Y Square / X - Square / X + Kwadrat / X Circle / B - Circle / B + Kółko / B Cross / A - Cross / A + Krzyżyk / A Right Stick Deadzone (def:2, max:127) - Right Stick Deadzone (def:2, max:127) + Martwa strefa prawego drążka (def:2 max:127) Right Deadzone - Right Deadzone + Martwa strefa prawego drążka Right Stick - Right Stick + Prawy drążek Color Adjustment - Color Adjustment + Dostosowanie koloru R: - R: + Czerwony: G: - G: + Zielony: B: - B: + Niebieski: Override Lightbar Color - Override Lightbar Color + Zastąp kolor paska świetlnego Override Color - Override Color + Zastąp kolor @@ -584,7 +584,7 @@ Directory to install DLC - Directory to install DLC + Katalog do instalacji dodatkowej zawartości (DLC) @@ -603,11 +603,11 @@ Compatibility - Zgodność + Kompatybilność Region - Region + Region Firmware @@ -635,15 +635,15 @@ h - h + godz. m - m + min s - s + s Compatibility is untested @@ -682,23 +682,23 @@ GameListUtils B - B + B KB - KB + KB MB - MB + MB GB - GB + GB TB - TB + TB @@ -729,7 +729,7 @@ Open Save Data Folder - Otwórz Folder Danych Zapisów + Otwórz folder zapisanych danych Open Log Folder @@ -749,11 +749,11 @@ Copy Version - Copy Version + Kopiuj wersję Copy Size - Copy Size + Kopiuj rozmiar Copy All @@ -765,19 +765,19 @@ Delete Game - Usuń Grę + Usuń grę Delete Update - Usuń Aktualizację + Usuń aktualizację Delete DLC - Usuń DLC + Usuń dodatkową zawartość (DLC) Compatibility... - kompatybilność... + Kompatybilność... Update database @@ -825,11 +825,11 @@ This game has no DLC to delete! - Ta gra nie ma DLC do usunięcia! + Ta gra nie ma dodatkowej zawartości (DLC) do usunięcia! DLC - DLC + Dodatkowa zawartość (DLC) Delete %1 @@ -841,31 +841,31 @@ Open Update Folder - Open Update Folder + Otwórz folder aktualizacji Delete Save Data - Delete Save Data + Usuń zapisane dane This game has no update folder to open! - This game has no update folder to open! + Ta gra nie ma folderu aktualizacji do otwarcia! Failed to convert icon. - Failed to convert icon. + Nie udało się przekonwertować ikony. This game has no save data to delete! - This game has no save data to delete! + Ta gra nie ma zapisów do usunięcia! Save Data - Save Data + Zapisane dane SFO Viewer for - SFO Viewer for + Menedżer plików SFO dla @@ -880,11 +880,11 @@ Install All Queued to Selected Folder - Install All Queued to Selected Folder + Zainstaluj wszystkie oczekujące do wybranego folderu Delete PKG File on Install - Delete PKG File on Install + Usuń plik PKG po instalacji @@ -1127,15 +1127,15 @@ DLC Installation - Instalacja DLC + Instalacja dodatkowej zawartości (DLC) Would you like to install DLC: %1? - Czy chcesz zainstalować DLC: %1? + Czy chcesz zainstalować dodatkową zawartość (DLC): %1? DLC already installed: - DLC już zainstalowane: + Dodatkowa zawartość (DLC) już zainstalowana: Game already installed @@ -1163,27 +1163,27 @@ Run Game - Run Game + Uruchom grę Eboot.bin file not found - Eboot.bin file not found + Nie znaleziono pliku EBOOT.BIN PKG File (*.PKG *.pkg) - PKG File (*.PKG *.pkg) + Plik PKG (*.PKG *.pkg) PKG is a patch or DLC, please install the game first! - PKG is a patch or DLC, please install the game first! + PKG jest aktualizacją lub dodatkową zawartością (DLC), najpierw zainstaluj grę! Game is already running! - Game is already running! + Gra jest już uruchomiona! shadPS4 - shadPS4 + shadPS4 @@ -1206,7 +1206,7 @@ Installed - Installed + Zainstalowano Size @@ -1214,27 +1214,27 @@ Category - Category + Kategoria Type - Type + Typ App Ver - App Ver + Wersja aplikacji FW - FW + Oprogramowanie Region - Region + Region Flags - Flags + Flagi Path @@ -1250,7 +1250,7 @@ Package - Package + Paczka @@ -1265,7 +1265,7 @@ System - System + System Console Language @@ -1277,7 +1277,7 @@ Emulator - Emulator + Emulator Enable Separate Update Folder @@ -1329,7 +1329,7 @@ Input - Wejście + Sterowanie Cursor @@ -1345,7 +1345,7 @@ s - s + s Controller @@ -1389,7 +1389,7 @@ Enable HDR - Enable HDR + Włącz HDR Paths @@ -1429,23 +1429,23 @@ Enable Crash Diagnostics - Enable Crash Diagnostics + Włącz diagnostykę awarii Collect Shaders - Collect Shaders + Zbieraj cienie Copy GPU Buffers - Copy GPU Buffers + Kopiuj bufory GPU Host Debug Markers - Host Debug Markers + Znaczniki diagnostyczne gospodarza Guest Debug Markers - Guest Debug Markers + Znaczniki diagnostyczne gościa Update @@ -1473,7 +1473,7 @@ Title Music - Title Music + Muzyka tytułowa Disable Trophy Pop-ups @@ -1481,15 +1481,15 @@ Background Image - Background Image + Obraz tła Show Background Image - Show Background Image + Pokaż obraz tła Opacity - Opacity + Przezroczystość Play title music @@ -1577,7 +1577,7 @@ Background Image:\nControl the opacity of the game background image. - Background Image:\nControl the opacity of the game background image. + Obraz tła:\nKontroluj przezroczystość obrazu tła gry. Play Title Music:\nIf a game supports it, enable playing special music when selecting the game in the GUI. @@ -1661,7 +1661,7 @@ Enable HDR:\nEnables HDR in games that support it.\nYour monitor must have support for the BT2020 PQ color space and the RGB10A2 swapchain format. - Enable HDR:\nEnables HDR in games that support it.\nYour monitor must have support for the BT2020 PQ color space and the RGB10A2 swapchain format. + Włącz HDR:\nWłącza HDR w grach, które go wspierają.\nTwój monitor musi mieć wsparcie dla przestrzeni kolorów BT2020 PQ oraz formatu RGB10A2 swapchain. Game Folders:\nThe list of folders to check for installed games. @@ -1693,51 +1693,51 @@ Collect Shaders:\nYou need this enabled to edit shaders with the debug menu (Ctrl + F10). - Collect Shaders:\nYou need this enabled to edit shaders with the debug menu (Ctrl + F10). + Zbieranie cieni:\nPotrzebujesz tej opcji aby edytować cienie za pomocą menu debugowania (Ctrl + F10). Crash Diagnostics:\nCreates a .yaml file with info about the Vulkan state at the time of crashing.\nUseful for debugging 'Device lost' errors. If you have this enabled, you should enable Host AND Guest Debug Markers.\nDoes not work on Intel GPUs.\nYou need Vulkan Validation Layers enabled and the Vulkan SDK for this to work. - Crash Diagnostics:\nCreates a .yaml file with info about the Vulkan state at the time of crashing.\nUseful for debugging 'Device lost' errors. If you have this enabled, you should enable Host AND Guest Debug Markers.\nDoes not work on Intel GPUs.\nYou need Vulkan Validation Layers enabled and the Vulkan SDK for this to work. + Diagnostyka awarii:\nTworzy plik .yaml z informacjami o stanie Vulkan w momencie awarii.\nPrzydatne do debugowania błędów 'DEVICE LOST' . Jeśli ta opcja jest włączona, powinieneś włączyć "Znaczniki błędów gospodarza" oraz "Znaczniki błędów gościa".\nNie działa na kartach graficznych Intela.\nOpcja "Włącz warstwy walidacji Vulkan" i Vulkan SDK jest wymagana do działania. Copy GPU Buffers:\nGets around race conditions involving GPU submits.\nMay or may not help with PM4 type 0 crashes. - Copy GPU Buffers:\nGets around race conditions involving GPU submits.\nMay or may not help with PM4 type 0 crashes. + Kopiowanie buforów karty graficznej:\nOmija problemy wyścigów związane z przesyłaniem danych do karty graficznej.\nMoże, ale nie musi, pomóc w przypadku awarii typu PM4 0. Host Debug Markers:\nInserts emulator-side information like markers for specific AMDGPU commands around Vulkan commands, as well as giving resources debug names.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. - Host Debug Markers:\nInserts emulator-side information like markers for specific AMDGPU commands around Vulkan commands, as well as giving resources debug names.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + Wskaźniki debugowania gospodarza:\nWstawia informacje emulatora, takie jak znaczniki dla konkretnych poleceń AMDGPU wokół poleceń Vulkan, a także nadaje nazwy debugowania zasobów.\nJeśli ta opcja jest włączona, powinieneś włączyć diagnostykę awarii.\nPrzydatne dla programów takich jak RenderDoc. Guest Debug Markers:\nInserts any debug markers the game itself has added to the command buffer.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. - Guest Debug Markers:\nInserts any debug markers the game itself has added to the command buffer.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + Znaczniki debugowania gościa:\nWstawia wszystkie znaczniki debugowania, które gra dodała do buforu poleceń.\nJeśli ta opcja jest włączona, powinieneś włączyć diagnostykę awarii.\nPrzydatne dla programów takich jak RenderDoc. Save Data Path:\nThe folder where game save data will be saved. - Save Data Path:\nThe folder where game save data will be saved. + Ścieżka zapisu danych:\nFolder, w którym zapisywane będą dane gry. Browse:\nBrowse for a folder to set as the save data path. - Browse:\nBrowse for a folder to set as the save data path. + Przeglądaj:\nPrzeglądaj folder, aby ustawić ścieżkę zapisywania danych. Release - Release + Wersja stablina Nightly - Nightly + Wersja rozwojowa Set the volume of the background music. - Set the volume of the background music. + Wybierz poziom głośności muzyki w tle. Enable Motion Controls - Enable Motion Controls + Włącz sterowanie ruchem Save Data Path - Save Data Path + Ścieżka zapisanych danych Browse @@ -1745,15 +1745,15 @@ async - async + asynchroniczny sync - sync + synchroniczny Auto Select - Auto Select + Wybór automatyczny Directory to install games @@ -1761,47 +1761,47 @@ Directory to save data - Directory to save data + Katalog do zapisywania danych Video - Video + Wyświetlanie Display Mode - Display Mode + Tryb wyświetlania Windowed - Windowed + Tryb okna Fullscreen - Fullscreen + Tryb pełnoekranowy Fullscreen (Borderless) - Fullscreen (Borderless) + Tryb pełnoekranowy (bez obramowania) Window Size - Window Size + Rozmiar okna W: - W: + Szerokość: H: - H: + Wysokość: Separate Log Files - Separate Log Files + Oddzielne pliki dziennika Separate Log Files:\nWrites a separate logfile for each game. - Separate Log Files:\nWrites a separate logfile for each game. + Oddzielne pliki dziennika:\nZapisuje oddzielny plik dziennika dla każdej gry. diff --git a/src/qt_gui/translations/pt_BR.ts b/src/qt_gui/translations/pt_BR.ts index 3ff4e106a..406109150 100644 --- a/src/qt_gui/translations/pt_BR.ts +++ b/src/qt_gui/translations/pt_BR.ts @@ -11,26 +11,26 @@ shadPS4 is an experimental open-source emulator for the PlayStation 4. - shadPS4 é um emulador experimental de código-fonte aberto para o PlayStation 4. + O shadPS4 é um emulador experimental de código-fonte aberto para o PlayStation 4. This software should not be used to play games you have not legally obtained. - Este programa não deve ser usado para jogar jogos que tenham sido obtidos ilegalmente. + Este programa não deve ser usado para executar jogos que tenham sido obtidos ilegalmente. CheatsPatches Cheats / Patches for - Cheats / Patches para + Trapaças / Patches para Cheats/Patches are experimental.\nUse with caution.\n\nDownload cheats individually by selecting the repository and clicking the download button.\nIn the Patches tab, you can download all patches at once, choose which ones you want to use, and save your selection.\n\nSince we do not develop the Cheats/Patches,\nplease report issues to the cheat author.\n\nCreated a new cheat? Visit:\n - Cheats/Patches são experimentais.\nUse com cautela.\n\nBaixe os cheats individualmente selecionando o repositório e clicando no botão de download.\nNa aba Patches, você pode baixar todos os Patches de uma vez, escolha qual deseja usar e salve a opção.\n\nComo não desenvolvemos os Cheats/Patches,\npor favor, reporte os problemas relacionados ao autor do cheat.\n\nCriou um novo cheat? Visite:\n + As Trapaças/Patches são experimentais.\nUse com cautela.\n\nBaixe as trapaças individualmente selecionando o repositório e clicando no botão de baixar.\nNa aba Patches, você pode baixar todos os patches de uma vez, escolha qual deseja usar e salve a opção.\n\nComo não desenvolvemos as Trapaças/Patches,\npor favor, reporte os problemas relacionados ao autor da trapaça.\n\nCriou uma nova trapaça? Visite:\n No Image Available - Imagem Não Disponível + Nenhuma Imagem Disponível Serial: @@ -46,7 +46,7 @@ Select Cheat File: - Selecione o Arquivo de Cheat: + Selecione o Arquivo de Trapaça: Repository: @@ -54,7 +54,7 @@ Download Cheats - Baixar Cheats + Baixar Trapaças Delete File @@ -66,7 +66,7 @@ You can delete the cheats you don't want after downloading them. - Você pode excluir os cheats que não deseja após baixá-los. + Você pode excluir as trapaças que não deseja após baixá-las. Do you want to delete the selected file?\n%1 @@ -86,7 +86,7 @@ Cheats - Cheats + Trapaças Patches @@ -118,7 +118,7 @@ Failed to parse XML: - Falha ao analisar XML: + Falha ao analisar o XML: Success @@ -154,19 +154,19 @@ Cheats Not Found - Cheats Não Encontrados + Trapaças Não Encontradas No Cheats found for this game in this version of the selected repository,try another repository or a different version of the game. - Nenhum cheat encontrado para este jogo nesta versão do repositório selecionado, tente outro repositório ou uma versão diferente do jogo. + Nenhuma trapaça encontrada para este jogo nesta versão do repositório selecionado, tente outro repositório ou uma versão diferente do jogo. Cheats Downloaded Successfully - Cheats Baixados com Sucesso + Trapaças Baixadas com Sucesso You have successfully downloaded the cheats for this version of the game from the selected repository. You can try downloading from another repository, if it is available it will also be possible to use it by selecting the file from the list. - Você baixou os cheats para esta versão do jogo do repositório selecionado com sucesso. É possível tentar baixar de outro repositório, se estiver disponível, também será possível utilizá-lo selecionando o arquivo da lista. + Você baixou as trapaças para esta versão do jogo do repositório selecionado com sucesso. É possível tentar baixar de outro repositório, se estiver disponível, também será possível utilizá-lo selecionando o arquivo da lista. Failed to save: @@ -182,11 +182,11 @@ Patches Downloaded Successfully! All Patches available for all games have been downloaded, there is no need to download them individually for each game as happens in Cheats. If the patch does not appear, it may be that it does not exist for the specific serial and version of the game. - Patches Baixados com Sucesso! Todos os patches disponíveis para todos os jogos foram baixados, não é necessário baixá-los individualmente para cada jogo como acontece em Cheats. Se o patch não aparecer, pode ser que ele não exista para o serial e versão específicas do jogo. + Patches Baixados com Sucesso! Todos os patches disponíveis para todos os jogos foram baixados, não é necessário baixá-los individualmente para cada jogo como acontece em Trapaças. Se o patch não aparecer, pode ser que ele não exista para o serial e versão específicas do jogo. Failed to parse JSON data from HTML. - Falha ao analisar dados JSON do HTML. + Falha ao analisar os dados JSON do HTML. Failed to retrieve HTML page. @@ -206,7 +206,7 @@ Incompatibility Notice - Aviso de incompatibilidade + Aviso de Incompatibilidade Failed to open file: @@ -214,7 +214,7 @@ XML ERROR: - ERRO de XML: + ERRO DE XML: Failed to open files.json for writing @@ -238,7 +238,7 @@ Can't apply cheats before the game is started - Não é possível aplicar cheats antes que o jogo comece. + Não é possível aplicar trapaças antes de começar o jogo. Close @@ -285,7 +285,7 @@ Update Available - Atualização disponível + Atualização Disponível Update Channel @@ -309,7 +309,7 @@ Check for Updates at Startup - Verificar Atualizações ao Iniciar + Verificar por Atualizações ao Iniciar Update @@ -376,7 +376,7 @@ Unable to open compatibility_data.json for writing. - Não foi possível abrir o compatibility_data.json para escrita. + Não foi possível abrir o compatibility_data.json para gravação. Unknown @@ -388,7 +388,7 @@ Boots - Boot + Inicia Menus @@ -487,7 +487,7 @@ Face Buttons - Botões de Face + Botões de Ação Triangle / Y @@ -535,7 +535,7 @@ Override Lightbar Color - Substituir cor da Lightbar + Substituir Cor da Barra de Luz Override Color @@ -631,7 +631,7 @@ Never Played - Nunca jogado + Nunca Jogado h @@ -663,11 +663,11 @@ Game has game-breaking glitches or unplayable performance - O jogo tem falhas que interrompem o jogo ou desempenho injogável + O jogo tem defeitos que interrompem o jogo ou desempenho injogável Game can be completed with playable performance and no major glitches - O jogo pode ser concluído com desempenho jogável e sem grandes falhas + O jogo pode ser concluído com desempenho jogável e sem grandes defeitos Click to see details on github @@ -709,7 +709,7 @@ Cheats / Patches - Cheats / Patches + Trapaças / Patches SFO Viewer @@ -761,19 +761,19 @@ Delete... - Deletar... + Excluir... Delete Game - Deletar Jogo + Excluir Jogo Delete Update - Deletar Atualização + Excluir Atualização Delete DLC - Deletar DLC + Excluir DLC Compatibility... @@ -825,7 +825,7 @@ This game has no DLC to delete! - Este jogo não tem DLC para deletar! + Este jogo não tem DLC para excluir! DLC @@ -833,11 +833,11 @@ Delete %1 - Deletar %1 + Excluir %1 Are you sure you want to delete %1's %2 directory? - Tem certeza de que deseja excluir o diretório %2 de %1 ? + Tem certeza de que deseja excluir o diretório %2 de %1? Open Update Folder @@ -849,7 +849,7 @@ This game has no update folder to open! - Este jogo não tem atualização para deletar! + Este jogo não possui pasta de atualização para abrir! Failed to convert icon. @@ -857,7 +857,7 @@ This game has no save data to delete! - Este jogo não tem dados salvos para deletar! + Este jogo não tem dados salvos para excluir! Save Data @@ -880,11 +880,11 @@ Install All Queued to Selected Folder - Instalar Todas da Fila para a Pasta Selecionada + Instalar Tudo da Fila para a Pasta Selecionada Delete PKG File on Install - Deletar PKG após instalação + Excluir o PKG após a Instalação @@ -923,7 +923,7 @@ Open shadPS4 Folder - Abrir pasta shadPS4 + Abrir Pasta do shadPS4 Exit @@ -979,11 +979,11 @@ Download Cheats/Patches - Baixar Cheats/Patches + Baixar Trapaças/Patches Dump Game List - Dumpar Lista de Jogos + Exportar Lista de Jogos PKG Viewer @@ -1059,7 +1059,7 @@ Download Cheats For All Installed Games - Baixar Cheats para Todos os Jogos Instalados + Baixar Trapaças para Todos os Jogos Instalados Download Patches For All Games @@ -1071,7 +1071,7 @@ You have downloaded cheats for all the games you have installed. - Você baixou cheats para todos os jogos que instalou. + Você baixou trapaças para todos os jogos que instalou. Patches Downloaded Successfully! @@ -1107,15 +1107,15 @@ PKG and Game versions match: - As versões do PKG e do Jogo são igual: + As versões do PKG e do Jogo são iguais: Would you like to overwrite? - Gostaria de substituir? + Você gostaria de sobrescrever? PKG Version %1 is older than installed version: - Versão do PKG %1 é mais antiga do que a versão instalada: + A Versão do PKG %1 é mais antiga do que a versão instalada: Game is installed: @@ -1143,7 +1143,7 @@ PKG ERROR - ERRO de PKG + ERRO DE PKG Extracting PKG %1/%2 @@ -1194,7 +1194,7 @@ PKG ERROR - ERRO de PKG + ERRO DE PKG Name @@ -1222,7 +1222,7 @@ App Ver - App Ver + Versão do App FW @@ -1238,7 +1238,7 @@ Path - Diretório + Caminho File @@ -1381,7 +1381,7 @@ Enable Shaders Dumping - Ativar Dumping de Shaders + Ativar Exportação de Shaders Enable NULL GPU @@ -1413,7 +1413,7 @@ Enable Debug Dumping - Ativar Depuração de Dumping + Ativar Exportação de Depuração Enable Vulkan Validation Layers @@ -1453,7 +1453,7 @@ Check for Updates at Startup - Verificar Atualizações ao Iniciar + Verificar por Atualizações ao Iniciar Always Show Changelog @@ -1497,7 +1497,7 @@ Update Compatibility Database On Startup - Atualizar Compatibilidade ao Inicializar + Atualizar Base de Dados de Compatibilidade ao Inicializar Game Compatibility @@ -1537,15 +1537,15 @@ Console Language:\nSets the language that the PS4 game uses.\nIt's recommended to set this to a language the game supports, which will vary by region. - Idioma do console:\nDefine o idioma usado pelo jogo do PS4.\nRecomenda-se configurá-lo para um idioma que o jogo suporte, o que pode variar conforme a região. + Idioma do Console:\nDefine o idioma usado pelo jogo do PS4.\nRecomenda-se configurá-lo para um idioma que o jogo suporte, o que pode variar conforme a região. Emulator Language:\nSets the language of the emulator's user interface. - Idioma do emulador:\nDefine o idioma da interface do emulador. + Idioma do Emulador:\nDefine o idioma da interface do emulador. Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management.\nThis can be manually created by adding the extracted update to the game folder with the name "CUSA00000-UPDATE" where the CUSA ID matches the game's ID. - Ativar pasta de atualização separada:\nPermite instalar atualizações de jogos em uma pasta separada para fácil gerenciamento.\nIsso pode ser manualmente criado adicionando a atualização extraída à pasta do jogo com o nome "CUSA00000-UPDATE" onde o ID do CUSA corresponde ao ID do jogo. + Ativar Pasta de Atualização Separada:\nPermite instalar atualizações de jogos em uma pasta separada para fácil gerenciamento.\nIsso pode ser manualmente criado adicionando a atualização extraída à pasta do jogo com o nome "CUSA00000-UPDATE" onde o ID do CUSA corresponde ao ID do jogo. Show Splash Screen:\nShows the game's splash screen (a special image) while the game is starting. @@ -1557,7 +1557,7 @@ Username:\nSets the PS4's account username, which may be displayed by some games. - Nome de usuário:\nDefine o nome de usuário da conta PS4 que pode ser exibido por alguns jogos. + Nome de usuário:\nDefine o nome de usuário da conta do PS4, que pode ser exibido por alguns jogos. Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. @@ -1565,7 +1565,7 @@ Log Type:\nSets whether to synchronize the output of the log window for performance. May have adverse effects on emulation. - Tipo de Registro:\nDetermina se a saída da janela de log deve ser sincronizada por motivos de desempenho. Pode impactar negativamente a emulação. + Tipo de Registro:\nDetermina se a saída da janela de log deve ser sincronizada por motivos de desempenho. Pode impactar negativamente na emulação. Log Filter:\nFilters the log to only print specific information.\nExamples: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical"\nLevels: Trace, Debug, Info, Warning, Error, Critical - in this order, a specific level silences all levels preceding it in the list and logs every level after it. @@ -1601,7 +1601,7 @@ Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. - Exibir Dados de Compatibilidade:\nExibe informações de compatibilidade dos jogos na visualização de tabela.\nAtivar "Atualizar Compatibilidade ao Inicializar" para obter informações atualizadas. + Exibir Dados de Compatibilidade:\nExibe informações de compatibilidade dos jogos na visualização de tabela.\nAtive "Atualizar Compatibilidade ao Inicializar" para obter informações atualizadas. Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. @@ -1609,7 +1609,7 @@ Update Compatibility Database:\nImmediately update the compatibility database. - Atualizar Lista de Compatibilidade:\nAtualizar imediatamente o banco de dados de compatibilidade. + Atualizar Lista de Compatibilidade:\nAtualiza imediatamente o banco de dados de compatibilidade. Never @@ -1633,7 +1633,7 @@ Touchpad Center - Touchpad Centro + Centro do Touchpad None @@ -1653,7 +1653,7 @@ Enable Shaders Dumping:\nFor the sake of technical debugging, saves the games shaders to a folder as they render. - Ativar Dumping de Shaders:\nArmazena os shaders do jogo em uma pasta durante a renderização para fins de depuração técnica. + Ativar Exportação de Shaders:\nArmazena os shaders do jogo em uma pasta durante a renderização para fins de depuração técnica. Enable Null GPU:\nFor the sake of technical debugging, disables game rendering as if there were no graphics card. @@ -1677,7 +1677,7 @@ Enable Debug Dumping:\nSaves the import and export symbols and file header information of the currently running PS4 program to a directory. - Ativar Depuração de Dumping:\nArmazena os símbolos de importação, exportação e informações do cabeçalho do arquivo do programa PS4 atual em um diretório. + Ativar Exportação de Depuração:\nArmazena os símbolos de importação, exportação e informações do cabeçalho do arquivo do programa PS4 atual em um diretório. Enable Vulkan Validation Layers:\nEnables a system that validates the state of the Vulkan renderer and logs information about its internal state.\nThis will reduce performance and likely change the behavior of emulation. diff --git a/src/qt_gui/translations/ro_RO.ts b/src/qt_gui/translations/ro_RO.ts index 4d9052261..7083e6d49 100644 --- a/src/qt_gui/translations/ro_RO.ts +++ b/src/qt_gui/translations/ro_RO.ts @@ -7,7 +7,7 @@ AboutDialog About shadPS4 - About shadPS4 + Despre shadPS4 shadPS4 is an experimental open-source emulator for the PlayStation 4. @@ -584,7 +584,7 @@ Directory to install DLC - Directory to install DLC + Director pentru a instala DLC @@ -845,11 +845,11 @@ Delete Save Data - Delete Save Data + Șterge Salvare Date This game has no update folder to open! - This game has no update folder to open! + Acest joc nu are folderul de actualizări pentru a fi deschis! Failed to convert icon. @@ -1190,7 +1190,7 @@ PKGViewer Open Folder - Open Folder + Deschide Folder PKG ERROR diff --git a/src/qt_gui/translations/sq_AL.ts b/src/qt_gui/translations/sq_AL.ts index ec07db041..0a90bcd10 100644 --- a/src/qt_gui/translations/sq_AL.ts +++ b/src/qt_gui/translations/sq_AL.ts @@ -1765,43 +1765,43 @@ Video - Video + Video Display Mode - Display Mode + Mënyra e Shfaqjes Windowed - Windowed + Dritare Fullscreen - Fullscreen + Ekran të plotë Fullscreen (Borderless) - Fullscreen (Borderless) + Ekran të plotë (Pa kufij) Window Size - Window Size + Masa e Dritares W: - W: + Gjer: H: - H: + Lart: Separate Log Files - Separate Log Files + Skedarë të Ditarit të Ndarë Separate Log Files:\nWrites a separate logfile for each game. - Separate Log Files:\nWrites a separate logfile for each game. + Skedarë të Ditarit të Ndarë:\nShkruan një skedar të ditarit të veçuar për secilën lojë. diff --git a/src/qt_gui/translations/sv_SE.ts b/src/qt_gui/translations/sv_SE.ts index bdd2d2aa0..e9ea2f20a 100644 --- a/src/qt_gui/translations/sv_SE.ts +++ b/src/qt_gui/translations/sv_SE.ts @@ -1765,43 +1765,43 @@ Video - Video + Video Display Mode - Display Mode + Visningsläge Windowed - Windowed + Fönster Fullscreen - Fullscreen + Helskärm Fullscreen (Borderless) - Fullscreen (Borderless) + Helskärm (kantlöst) Window Size - Window Size + Fönsterstorlek W: - W: + B: H: - H: + H: Separate Log Files - Separate Log Files + Separata loggfiler Separate Log Files:\nWrites a separate logfile for each game. - Separate Log Files:\nWrites a separate logfile for each game. + Separata loggfiler:\nSkriver en separat loggfil för varje spel. diff --git a/src/qt_gui/trophy_viewer.cpp b/src/qt_gui/trophy_viewer.cpp index 9a0f33eed..148cbee06 100644 --- a/src/qt_gui/trophy_viewer.cpp +++ b/src/qt_gui/trophy_viewer.cpp @@ -1,27 +1,178 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include +#include +#include #include #include +#include #include "common/path_util.h" +#include "main_window_themes.h" #include "trophy_viewer.h" +namespace fs = std::filesystem; + CMRC_DECLARE(res); +// true: European format; false: American format +bool useEuropeanDateFormat = true; + +void TrophyViewer::updateTrophyInfo() { + int total = 0; + int unlocked = 0; + + // Cycles through each tab (table) of the QTabWidget + for (int i = 0; i < tabWidget->count(); i++) { + QTableWidget* table = qobject_cast(tabWidget->widget(i)); + if (table) { + total += table->rowCount(); + for (int row = 0; row < table->rowCount(); ++row) { + QString cellText; + // The "Unlocked" column can be a widget or a simple item + QWidget* widget = table->cellWidget(row, 0); + if (widget) { + // Looks for the QLabel inside the widget (as defined in SetTableItem) + QLabel* label = widget->findChild(); + if (label) { + cellText = label->text(); + } + } else { + QTableWidgetItem* item = table->item(row, 0); + if (item) { + cellText = item->text(); + } + } + if (cellText == "unlocked") + unlocked++; + } + } + } + int progress = (total > 0) ? (unlocked * 100 / total) : 0; + trophyInfoLabel->setText( + QString(tr("Progress") + ": %1% (%2/%3)").arg(progress).arg(unlocked).arg(total)); +} + +void TrophyViewer::updateTableFilters() { + bool showEarned = showEarnedCheck->isChecked(); + bool showNotEarned = showNotEarnedCheck->isChecked(); + bool showHidden = showHiddenCheck->isChecked(); + + // Cycles through each tab of the QTabWidget + for (int i = 0; i < tabWidget->count(); ++i) { + QTableWidget* table = qobject_cast(tabWidget->widget(i)); + if (!table) + continue; + for (int row = 0; row < table->rowCount(); ++row) { + QString unlockedText; + // Gets the text of the "Unlocked" column (index 0) + QWidget* widget = table->cellWidget(row, 0); + if (widget) { + QLabel* label = widget->findChild(); + if (label) + unlockedText = label->text(); + } else { + QTableWidgetItem* item = table->item(row, 0); + if (item) + unlockedText = item->text(); + } + + QString hiddenText; + // Gets the text of the "Hidden" column (index 7) + QWidget* hiddenWidget = table->cellWidget(row, 7); + if (hiddenWidget) { + QLabel* label = hiddenWidget->findChild(); + if (label) + hiddenText = label->text(); + } else { + QTableWidgetItem* item = table->item(row, 7); + if (item) + hiddenText = item->text(); + } + + bool visible = true; + if (unlockedText == "unlocked" && !showEarned) + visible = false; + if (unlockedText == "locked" && !showNotEarned) + visible = false; + if (hiddenText.toLower() == "yes" && !showHidden) + visible = false; + + table->setRowHidden(row, !visible); + } + } +} + TrophyViewer::TrophyViewer(QString trophyPath, QString gameTrpPath) : QMainWindow() { this->setWindowTitle(tr("Trophy Viewer")); this->setAttribute(Qt::WA_DeleteOnClose); tabWidget = new QTabWidget(this); + + auto lan = Config::getEmulatorLanguage(); + if (lan == "en_US" || lan == "zh_CN" || lan == "zh_TW" || lan == "ja_JP" || lan == "ko_KR" || + lan == "lt_LT" || lan == "nb_NO" || lan == "nl_NL") { + useEuropeanDateFormat = false; + } + gameTrpPath_ = gameTrpPath; headers << "Unlocked" << "Trophy" << "Name" << "Description" + << "Time Unlocked" + << "Type" << "ID" << "Hidden" - << "Type" << "PID"; PopulateTrophyWidget(trophyPath); + + QDockWidget* trophyInfoDock = new QDockWidget("", this); + QWidget* dockWidget = new QWidget(trophyInfoDock); + QVBoxLayout* dockLayout = new QVBoxLayout(dockWidget); + dockLayout->setAlignment(Qt::AlignTop); + + trophyInfoLabel = new QLabel(tr("Progress") + ": 0% (0/0)", dockWidget); + trophyInfoLabel->setStyleSheet( + "font-weight: bold; font-size: 16px; color: white; background: #333; padding: 5px;"); + dockLayout->addWidget(trophyInfoLabel); + + // Creates QCheckBox to filter trophies + showEarnedCheck = new QCheckBox(tr("Show Earned Trophies"), dockWidget); + showNotEarnedCheck = new QCheckBox(tr("Show Not Earned Trophies"), dockWidget); + showHiddenCheck = new QCheckBox(tr("Show Hidden Trophies"), dockWidget); + + // Defines the initial states (all checked) + showEarnedCheck->setChecked(true); + showNotEarnedCheck->setChecked(true); + showHiddenCheck->setChecked(false); + + // Adds checkboxes to the layout + dockLayout->addWidget(showEarnedCheck); + dockLayout->addWidget(showNotEarnedCheck); + dockLayout->addWidget(showHiddenCheck); + + dockWidget->setLayout(dockLayout); + trophyInfoDock->setWidget(dockWidget); + + // Adds the dock to the left area + this->addDockWidget(Qt::LeftDockWidgetArea, trophyInfoDock); + + // Connects checkbox signals to update trophy display +#if (QT_VERSION < QT_VERSION_CHECK(6, 7, 0)) + connect(showEarnedCheck, &QCheckBox::stateChanged, this, &TrophyViewer::updateTableFilters); + connect(showNotEarnedCheck, &QCheckBox::stateChanged, this, &TrophyViewer::updateTableFilters); + connect(showHiddenCheck, &QCheckBox::stateChanged, this, &TrophyViewer::updateTableFilters); +#else + connect(showEarnedCheck, &QCheckBox::checkStateChanged, this, + &TrophyViewer::updateTableFilters); + connect(showNotEarnedCheck, &QCheckBox::checkStateChanged, this, + &TrophyViewer::updateTableFilters); + connect(showHiddenCheck, &QCheckBox::checkStateChanged, this, + &TrophyViewer::updateTableFilters); +#endif + + updateTrophyInfo(); + updateTableFilters(); } void TrophyViewer::PopulateTrophyWidget(QString title) { @@ -68,6 +219,7 @@ void TrophyViewer::PopulateTrophyWidget(QString title) { QStringList trpPid; QStringList trophyNames; QStringList trophyDetails; + QStringList trpTimeUnlocked; QString xmlPath = trpDir + "/Xml/TROP.XML"; QFile file(xmlPath); @@ -84,14 +236,35 @@ void TrophyViewer::PopulateTrophyWidget(QString title) { trpHidden.append(reader.attributes().value("hidden").toString()); trpType.append(reader.attributes().value("ttype").toString()); trpPid.append(reader.attributes().value("pid").toString()); + if (reader.attributes().hasAttribute("unlockstate")) { if (reader.attributes().value("unlockstate").toString() == "true") { trpUnlocked.append("unlocked"); } else { trpUnlocked.append("locked"); } + if (reader.attributes().hasAttribute("timestamp")) { + QString ts = reader.attributes().value("timestamp").toString(); + if (ts.length() > 10) + trpTimeUnlocked.append("unknown"); + else { + bool ok; + qint64 timestampInt = ts.toLongLong(&ok); + if (ok) { + QDateTime dt = QDateTime::fromSecsSinceEpoch(timestampInt); + QString format = useEuropeanDateFormat ? "dd/MM/yyyy HH:mm:ss" + : "MM/dd/yyyy HH:mm:ss"; + trpTimeUnlocked.append(dt.toString(format)); + } else { + trpTimeUnlocked.append("unknown"); + } + } + } else { + trpTimeUnlocked.append(""); + } } else { trpUnlocked.append("locked"); + trpTimeUnlocked.append(""); } } @@ -105,7 +278,7 @@ void TrophyViewer::PopulateTrophyWidget(QString title) { } QTableWidget* tableWidget = new QTableWidget(this); tableWidget->setShowGrid(false); - tableWidget->setColumnCount(8); + tableWidget->setColumnCount(9); tableWidget->setHorizontalHeaderLabels(headers); tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows); tableWidget->setSelectionMode(QAbstractItemView::SingleSelection); @@ -113,6 +286,8 @@ void TrophyViewer::PopulateTrophyWidget(QString title) { tableWidget->horizontalHeader()->setStretchLastSection(true); tableWidget->verticalHeader()->setVisible(false); tableWidget->setRowCount(icons.size()); + tableWidget->setSortingEnabled(true); + for (int row = 0; auto& icon : icons) { QTableWidgetItem* item = new QTableWidgetItem(); item->setData(Qt::DecorationRole, icon); @@ -122,15 +297,34 @@ void TrophyViewer::PopulateTrophyWidget(QString title) { const std::string filename = GetTrpType(trpType[row].at(0)); QTableWidgetItem* typeitem = new QTableWidgetItem(); - auto resource = cmrc::res::get_filesystem(); - std::string resourceString = "Resources/" + filename; - auto file = resource.open(resourceString); - std::vector imgdata(file.begin(), file.end()); - QImage type_icon = QImage::fromData(imgdata).scaled(QSize(64, 64), Qt::KeepAspectRatio, - Qt::SmoothTransformation); + const auto CustomTrophy_Dir = + Common::FS::GetUserPath(Common::FS::PathType::CustomTrophy); + std::string customPath; + + if (fs::exists(CustomTrophy_Dir / filename)) { + customPath = (CustomTrophy_Dir / filename).string(); + } + + std::vector imgdata; + + if (!customPath.empty()) { + std::ifstream file(customPath, std::ios::binary); + if (file) { + imgdata = std::vector(std::istreambuf_iterator(file), + std::istreambuf_iterator()); + } + } else { + auto resource = cmrc::res::get_filesystem(); + std::string resourceString = "src/images/" + filename; + auto file = resource.open(resourceString); + imgdata = std::vector(file.begin(), file.end()); + } + + QImage type_icon = QImage::fromData(imgdata).scaled( + QSize(100, 100), Qt::KeepAspectRatio, Qt::SmoothTransformation); typeitem->setData(Qt::DecorationRole, type_icon); typeitem->setFlags(typeitem->flags() & ~Qt::ItemIsEditable); - tableWidget->setItem(row, 6, typeitem); + tableWidget->setItem(row, 5, typeitem); std::string detailString = trophyDetails[row].toStdString(); std::size_t newline_pos = 0; @@ -143,46 +337,45 @@ void TrophyViewer::PopulateTrophyWidget(QString title) { SetTableItem(tableWidget, row, 0, trpUnlocked[row]); SetTableItem(tableWidget, row, 2, trophyNames[row]); SetTableItem(tableWidget, row, 3, QString::fromStdString(detailString)); - SetTableItem(tableWidget, row, 4, trpId[row]); - SetTableItem(tableWidget, row, 5, trpHidden[row]); - SetTableItem(tableWidget, row, 7, trpPid[row]); + SetTableItem(tableWidget, row, 4, trpTimeUnlocked[row]); + SetTableItem(tableWidget, row, 6, trpId[row]); + SetTableItem(tableWidget, row, 7, trpHidden[row]); + SetTableItem(tableWidget, row, 8, trpPid[row]); } tableWidget->verticalHeader()->resizeSection(row, icon.height()); row++; } tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); int width = 16; - for (int i = 0; i < 8; i++) { + for (int i = 0; i < 9; i++) { width += tableWidget->horizontalHeader()->sectionSize(i); } tableWidget->resize(width, 720); tabWidget->addTab(tableWidget, tabName.insert(6, " ").replace(0, 1, tabName.at(0).toUpper())); - this->resize(width + 20, 720); + + this->showMaximized(); + + tableWidget->horizontalHeader()->setSectionResizeMode(3, QHeaderView::Fixed); + tableWidget->setColumnWidth(3, 650); } this->setCentralWidget(tabWidget); } void TrophyViewer::SetTableItem(QTableWidget* parent, int row, int column, QString str) { - QWidget* widget = new QWidget(); - QVBoxLayout* layout = new QVBoxLayout(); - QLabel* label = new QLabel(str); - QTableWidgetItem* item = new QTableWidgetItem(); - label->setWordWrap(true); - label->setStyleSheet("color: white; font-size: 15px; font-weight: bold;"); + QTableWidgetItem* item = new QTableWidgetItem(str); - // Create shadow effect - QGraphicsDropShadowEffect* shadowEffect = new QGraphicsDropShadowEffect(); - shadowEffect->setBlurRadius(5); // Set the blur radius of the shadow - shadowEffect->setColor(QColor(0, 0, 0, 160)); // Set the color and opacity of the shadow - shadowEffect->setOffset(2, 2); // Set the offset of the shadow - - label->setGraphicsEffect(shadowEffect); // Apply shadow effect to the QLabel - - layout->addWidget(label); if (column != 1 && column != 2 && column != 3) - layout->setAlignment(Qt::AlignCenter); - widget->setLayout(layout); + item->setTextAlignment(Qt::AlignCenter); + item->setFont(QFont("Arial", 12, QFont::Bold)); + + Theme theme = static_cast(Config::getMainWindowTheme()); + + if (theme == Theme::Light) { + item->setForeground(QBrush(Qt::black)); + } else { + item->setForeground(QBrush(Qt::white)); + } + parent->setItem(row, column, item); - parent->setCellWidget(row, column, widget); } diff --git a/src/qt_gui/trophy_viewer.h b/src/qt_gui/trophy_viewer.h index 089de433e..bd99e1a8c 100644 --- a/src/qt_gui/trophy_viewer.h +++ b/src/qt_gui/trophy_viewer.h @@ -23,6 +23,10 @@ class TrophyViewer : public QMainWindow { public: explicit TrophyViewer(QString trophyPath, QString gameTrpPath); + void updateTrophyInfo(); + + void updateTableFilters(); + private: void PopulateTrophyWidget(QString title); void SetTableItem(QTableWidget* parent, int row, int column, QString str); @@ -31,6 +35,10 @@ private: QStringList headers; QString gameTrpPath_; TRP trp; + QLabel* trophyInfoLabel; + QCheckBox* showEarnedCheck; + QCheckBox* showNotEarnedCheck; + QCheckBox* showHiddenCheck; std::string GetTrpType(const QChar trp_) { switch (trp_.toLatin1()) { diff --git a/src/sdl_window.cpp b/src/sdl_window.cpp index 943746e3f..80d196147 100644 --- a/src/sdl_window.cpp +++ b/src/sdl_window.cpp @@ -134,13 +134,17 @@ void SDLInputEngine::Init() { m_gyro_poll_rate = SDL_GetGamepadSensorDataRate(m_gamepad, SDL_SENSOR_GYRO); LOG_INFO(Input, "Gyro initialized, poll rate: {}", m_gyro_poll_rate); } else { - LOG_ERROR(Input, "Failed to initialize gyro controls for gamepad"); + LOG_ERROR(Input, "Failed to initialize gyro controls for gamepad, error: {}", + SDL_GetError()); + SDL_SetGamepadSensorEnabled(m_gamepad, SDL_SENSOR_GYRO, false); } if (SDL_SetGamepadSensorEnabled(m_gamepad, SDL_SENSOR_ACCEL, true)) { m_accel_poll_rate = SDL_GetGamepadSensorDataRate(m_gamepad, SDL_SENSOR_ACCEL); LOG_INFO(Input, "Accel initialized, poll rate: {}", m_accel_poll_rate); } else { - LOG_ERROR(Input, "Failed to initialize accel controls for gamepad"); + LOG_ERROR(Input, "Failed to initialize accel controls for gamepad, error: {}", + SDL_GetError()); + SDL_SetGamepadSensorEnabled(m_gamepad, SDL_SENSOR_ACCEL, false); } } diff --git a/src/sdl_window.h b/src/sdl_window.h index 9acd2b16b..03ba0797b 100644 --- a/src/sdl_window.h +++ b/src/sdl_window.h @@ -27,8 +27,8 @@ public: private: SDL_Gamepad* m_gamepad = nullptr; - float m_gyro_poll_rate{}; - float m_accel_poll_rate{}; + float m_gyro_poll_rate = 0.0f; + float m_accel_poll_rate = 0.0f; }; } // namespace Input diff --git a/src/shader_recompiler/frontend/translate/scalar_alu.cpp b/src/shader_recompiler/frontend/translate/scalar_alu.cpp index b5c7c98ae..39f972848 100644 --- a/src/shader_recompiler/frontend/translate/scalar_alu.cpp +++ b/src/shader_recompiler/frontend/translate/scalar_alu.cpp @@ -27,7 +27,7 @@ void Translator::EmitScalarAlu(const GcnInst& inst) { case Opcode::S_ADD_I32: return S_ADD_I32(inst); case Opcode::S_SUB_I32: - return S_SUB_U32(inst); + return S_SUB_I32(inst); case Opcode::S_ADDC_U32: return S_ADDC_U32(inst); case Opcode::S_MIN_I32: @@ -216,24 +216,52 @@ void Translator::EmitSOPK(const GcnInst& inst) { void Translator::S_ADD_U32(const GcnInst& inst) { const IR::U32 src0{GetSrc(inst.src[0])}; const IR::U32 src1{GetSrc(inst.src[1])}; - SetDst(inst.dst[0], ir.IAdd(src0, src1)); - // TODO: Carry out - ir.SetScc(ir.Imm1(false)); + const IR::U32 result{ir.IAdd(src0, src1)}; + SetDst(inst.dst[0], result); + + // SCC = tmp >= 0x100000000ULL ? 1'1U : 1'0U; + // The above assumes tmp is a 64-bit value. + // It should be enough however to test that the truncated result is less than at least one + // of the operands. In unsigned addition the result is always bigger than both the operands, + // except in the case of overflow where the truncated result is less than both. + ir.SetScc(ir.ILessThan(result, src0, false)); } void Translator::S_SUB_U32(const GcnInst& inst) { const IR::U32 src0{GetSrc(inst.src[0])}; const IR::U32 src1{GetSrc(inst.src[1])}; SetDst(inst.dst[0], ir.ISub(src0, src1)); - // TODO: Carry out - ir.SetScc(ir.Imm1(false)); + + // SCC = S1.u > S0.u ? 1'1U : 1'0U; + ir.SetScc(ir.IGreaterThan(src1, src0, false)); } void Translator::S_ADD_I32(const GcnInst& inst) { const IR::U32 src0{GetSrc(inst.src[0])}; const IR::U32 src1{GetSrc(inst.src[1])}; - SetDst(inst.dst[0], ir.IAdd(src0, src1)); - // TODO: Overflow flag + const IR::U32 result{ir.IAdd(src0, src1)}; + SetDst(inst.dst[0], result); + + // SCC = ((S0.u[31] == S1.u[31]) && (S0.u[31] != Result.u[31])); + const IR::U32 shift{ir.Imm32(31)}; + const IR::U32 sign0{ir.ShiftRightLogical(src0, shift)}; + const IR::U32 sign1{ir.ShiftRightLogical(src1, shift)}; + const IR::U32 signr{ir.ShiftRightLogical(result, shift)}; + ir.SetScc(ir.LogicalAnd(ir.IEqual(sign0, sign1), ir.INotEqual(sign0, signr))); +} + +void Translator::S_SUB_I32(const GcnInst& inst) { + const IR::U32 src0{GetSrc(inst.src[0])}; + const IR::U32 src1{GetSrc(inst.src[1])}; + const IR::U32 result{ir.ISub(src0, src1)}; + SetDst(inst.dst[0], result); + + // SCC = ((S0.u[31] != S1.u[31]) && (S0.u[31] != tmp.u[31])); + const IR::U32 shift{ir.Imm32(31)}; + const IR::U32 sign0{ir.ShiftRightLogical(src0, shift)}; + const IR::U32 sign1{ir.ShiftRightLogical(src1, shift)}; + const IR::U32 signr{ir.ShiftRightLogical(result, shift)}; + ir.SetScc(ir.LogicalAnd(ir.INotEqual(sign0, sign1), ir.INotEqual(sign0, signr))); } void Translator::S_ADDC_U32(const GcnInst& inst) { diff --git a/src/shader_recompiler/frontend/translate/translate.h b/src/shader_recompiler/frontend/translate/translate.h index 190129e5f..2fd48a051 100644 --- a/src/shader_recompiler/frontend/translate/translate.h +++ b/src/shader_recompiler/frontend/translate/translate.h @@ -81,6 +81,7 @@ public: void S_ADD_U32(const GcnInst& inst); void S_SUB_U32(const GcnInst& inst); void S_ADD_I32(const GcnInst& inst); + void S_SUB_I32(const GcnInst& inst); void S_ADDC_U32(const GcnInst& inst); void S_MIN_U32(bool is_signed, const GcnInst& inst); void S_MAX_U32(bool is_signed, const GcnInst& inst); diff --git a/src/video_core/renderer_vulkan/liverpool_to_vk.h b/src/video_core/renderer_vulkan/liverpool_to_vk.h index a9fcd03a9..42da7aa06 100644 --- a/src/video_core/renderer_vulkan/liverpool_to_vk.h +++ b/src/video_core/renderer_vulkan/liverpool_to_vk.h @@ -104,7 +104,7 @@ static inline vk::Format PromoteFormatToDepth(vk::Format fmt) { } else if (fmt == vk::Format::eR16Unorm) { return vk::Format::eD16Unorm; } - UNREACHABLE(); + UNREACHABLE_MSG("Unexpected depth format {}", vk::to_string(fmt)); } } // namespace Vulkan::LiverpoolToVK