mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-05-14 08:12:16 +00:00
Custom Trophy images / sound | and improvements (#2539)
* Custom Trophy images * text and button - settings * Description * + * plural * translation for 'Trophy earned!' * Revert: translation for 'Trophy earned!' * play audio * fixes crash due to having too many trophies The game 'My Name is Mayo' has so many trophies in sequence that when overlapping them, the emulator ended up crashing, so if there is something on the screen and a new trophies are achieved, it will clear and show the new one. * Animations, config: position, duration * - * TR * fix sdl/qt * clang \O/ * Side menu with filter options. Sorting * +TR * fix showHiddenCheck * Time Unlocked * Fixes ghost text, larger image, black text in light theme * Button - Delete Trophy * limits the width of Description - showMaximized * changing column positions * useEuropeanDateFormat en_US, zh_CN, zh_TW, ja_JP, ko_KR, lt_LT, nb_NO, nl_NL useEuropeanDateFormat = false
This commit is contained in:
parent
63b50ff92c
commit
5e5ca2138e
15 changed files with 620 additions and 104 deletions
|
@ -53,6 +53,7 @@ static bool isShaderDebug = false;
|
||||||
static bool isShowSplash = false;
|
static bool isShowSplash = false;
|
||||||
static bool isAutoUpdate = false;
|
static bool isAutoUpdate = false;
|
||||||
static bool isAlwaysShowChangelog = false;
|
static bool isAlwaysShowChangelog = false;
|
||||||
|
static bool isLeftSideTrophy = false;
|
||||||
static bool isNullGpu = false;
|
static bool isNullGpu = false;
|
||||||
static bool shouldCopyGPUBuffers = false;
|
static bool shouldCopyGPUBuffers = false;
|
||||||
static bool shouldDumpShaders = false;
|
static bool shouldDumpShaders = false;
|
||||||
|
@ -69,6 +70,7 @@ static bool isFpsColor = true;
|
||||||
static bool isSeparateLogFilesEnabled = false;
|
static bool isSeparateLogFilesEnabled = false;
|
||||||
static s16 cursorState = HideCursorState::Idle;
|
static s16 cursorState = HideCursorState::Idle;
|
||||||
static int cursorHideTimeout = 5; // 5 seconds (default)
|
static int cursorHideTimeout = 5; // 5 seconds (default)
|
||||||
|
static double trophyNotificationDuration = 6.0;
|
||||||
static bool useUnifiedInputConfig = true;
|
static bool useUnifiedInputConfig = true;
|
||||||
static bool overrideControllerColor = false;
|
static bool overrideControllerColor = false;
|
||||||
static int controllerCustomColorRGB[3] = {0, 0, 255};
|
static int controllerCustomColorRGB[3] = {0, 0, 255};
|
||||||
|
@ -196,6 +198,10 @@ int getCursorHideTimeout() {
|
||||||
return cursorHideTimeout;
|
return cursorHideTimeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double getTrophyNotificationDuration() {
|
||||||
|
return trophyNotificationDuration;
|
||||||
|
}
|
||||||
|
|
||||||
u32 getScreenWidth() {
|
u32 getScreenWidth() {
|
||||||
return screenWidth;
|
return screenWidth;
|
||||||
}
|
}
|
||||||
|
@ -264,6 +270,10 @@ bool alwaysShowChangelog() {
|
||||||
return isAlwaysShowChangelog;
|
return isAlwaysShowChangelog;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool leftSideTrophy() {
|
||||||
|
return isLeftSideTrophy;
|
||||||
|
}
|
||||||
|
|
||||||
bool nullGpu() {
|
bool nullGpu() {
|
||||||
return isNullGpu;
|
return isNullGpu;
|
||||||
}
|
}
|
||||||
|
@ -371,6 +381,9 @@ void setAutoUpdate(bool enable) {
|
||||||
void setAlwaysShowChangelog(bool enable) {
|
void setAlwaysShowChangelog(bool enable) {
|
||||||
isAlwaysShowChangelog = enable;
|
isAlwaysShowChangelog = enable;
|
||||||
}
|
}
|
||||||
|
void setLeftSideTrophy(bool enable) {
|
||||||
|
isLeftSideTrophy = enable;
|
||||||
|
}
|
||||||
|
|
||||||
void setNullGpu(bool enable) {
|
void setNullGpu(bool enable) {
|
||||||
isNullGpu = enable;
|
isNullGpu = enable;
|
||||||
|
@ -435,6 +448,9 @@ void setCursorState(s16 newCursorState) {
|
||||||
void setCursorHideTimeout(int newcursorHideTimeout) {
|
void setCursorHideTimeout(int newcursorHideTimeout) {
|
||||||
cursorHideTimeout = newcursorHideTimeout;
|
cursorHideTimeout = newcursorHideTimeout;
|
||||||
}
|
}
|
||||||
|
void setTrophyNotificationDuration(double newTrophyNotificationDuration) {
|
||||||
|
trophyNotificationDuration = newTrophyNotificationDuration;
|
||||||
|
}
|
||||||
|
|
||||||
void setLanguage(u32 language) {
|
void setLanguage(u32 language) {
|
||||||
m_language = language;
|
m_language = language;
|
||||||
|
@ -706,6 +722,8 @@ void load(const std::filesystem::path& path) {
|
||||||
isNeo = toml::find_or<bool>(general, "isPS4Pro", false);
|
isNeo = toml::find_or<bool>(general, "isPS4Pro", false);
|
||||||
playBGM = toml::find_or<bool>(general, "playBGM", false);
|
playBGM = toml::find_or<bool>(general, "playBGM", false);
|
||||||
isTrophyPopupDisabled = toml::find_or<bool>(general, "isTrophyPopupDisabled", false);
|
isTrophyPopupDisabled = toml::find_or<bool>(general, "isTrophyPopupDisabled", false);
|
||||||
|
trophyNotificationDuration =
|
||||||
|
toml::find_or<double>(general, "trophyNotificationDuration", 5.0);
|
||||||
BGMvolume = toml::find_or<int>(general, "BGMvolume", 50);
|
BGMvolume = toml::find_or<int>(general, "BGMvolume", 50);
|
||||||
enableDiscordRPC = toml::find_or<bool>(general, "enableDiscordRPC", true);
|
enableDiscordRPC = toml::find_or<bool>(general, "enableDiscordRPC", true);
|
||||||
logFilter = toml::find_or<std::string>(general, "logFilter", "");
|
logFilter = toml::find_or<std::string>(general, "logFilter", "");
|
||||||
|
@ -719,6 +737,7 @@ void load(const std::filesystem::path& path) {
|
||||||
isShowSplash = toml::find_or<bool>(general, "showSplash", true);
|
isShowSplash = toml::find_or<bool>(general, "showSplash", true);
|
||||||
isAutoUpdate = toml::find_or<bool>(general, "autoUpdate", false);
|
isAutoUpdate = toml::find_or<bool>(general, "autoUpdate", false);
|
||||||
isAlwaysShowChangelog = toml::find_or<bool>(general, "alwaysShowChangelog", false);
|
isAlwaysShowChangelog = toml::find_or<bool>(general, "alwaysShowChangelog", false);
|
||||||
|
isLeftSideTrophy = toml::find_or<bool>(general, "leftSideTrophy", false);
|
||||||
separateupdatefolder = toml::find_or<bool>(general, "separateUpdateEnabled", false);
|
separateupdatefolder = toml::find_or<bool>(general, "separateUpdateEnabled", false);
|
||||||
compatibilityData = toml::find_or<bool>(general, "compatibilityEnabled", false);
|
compatibilityData = toml::find_or<bool>(general, "compatibilityEnabled", false);
|
||||||
checkCompatibilityOnStartup =
|
checkCompatibilityOnStartup =
|
||||||
|
@ -857,6 +876,7 @@ void save(const std::filesystem::path& path) {
|
||||||
|
|
||||||
data["General"]["isPS4Pro"] = isNeo;
|
data["General"]["isPS4Pro"] = isNeo;
|
||||||
data["General"]["isTrophyPopupDisabled"] = isTrophyPopupDisabled;
|
data["General"]["isTrophyPopupDisabled"] = isTrophyPopupDisabled;
|
||||||
|
data["General"]["trophyNotificationDuration"] = trophyNotificationDuration;
|
||||||
data["General"]["playBGM"] = playBGM;
|
data["General"]["playBGM"] = playBGM;
|
||||||
data["General"]["BGMvolume"] = BGMvolume;
|
data["General"]["BGMvolume"] = BGMvolume;
|
||||||
data["General"]["enableDiscordRPC"] = enableDiscordRPC;
|
data["General"]["enableDiscordRPC"] = enableDiscordRPC;
|
||||||
|
@ -868,6 +888,7 @@ void save(const std::filesystem::path& path) {
|
||||||
data["General"]["showSplash"] = isShowSplash;
|
data["General"]["showSplash"] = isShowSplash;
|
||||||
data["General"]["autoUpdate"] = isAutoUpdate;
|
data["General"]["autoUpdate"] = isAutoUpdate;
|
||||||
data["General"]["alwaysShowChangelog"] = isAlwaysShowChangelog;
|
data["General"]["alwaysShowChangelog"] = isAlwaysShowChangelog;
|
||||||
|
data["General"]["leftSideTrophy"] = isLeftSideTrophy;
|
||||||
data["General"]["separateUpdateEnabled"] = separateupdatefolder;
|
data["General"]["separateUpdateEnabled"] = separateupdatefolder;
|
||||||
data["General"]["compatibilityEnabled"] = compatibilityData;
|
data["General"]["compatibilityEnabled"] = compatibilityData;
|
||||||
data["General"]["checkCompatibilityOnStartup"] = checkCompatibilityOnStartup;
|
data["General"]["checkCompatibilityOnStartup"] = checkCompatibilityOnStartup;
|
||||||
|
@ -988,6 +1009,7 @@ void setDefaultValues() {
|
||||||
chooseHomeTab = "General";
|
chooseHomeTab = "General";
|
||||||
cursorState = HideCursorState::Idle;
|
cursorState = HideCursorState::Idle;
|
||||||
cursorHideTimeout = 5;
|
cursorHideTimeout = 5;
|
||||||
|
trophyNotificationDuration = 6.0;
|
||||||
backButtonBehavior = "left";
|
backButtonBehavior = "left";
|
||||||
useSpecialPad = false;
|
useSpecialPad = false;
|
||||||
specialPadClass = 1;
|
specialPadClass = 1;
|
||||||
|
@ -996,6 +1018,7 @@ void setDefaultValues() {
|
||||||
isShowSplash = false;
|
isShowSplash = false;
|
||||||
isAutoUpdate = false;
|
isAutoUpdate = false;
|
||||||
isAlwaysShowChangelog = false;
|
isAlwaysShowChangelog = false;
|
||||||
|
isLeftSideTrophy = false;
|
||||||
isNullGpu = false;
|
isNullGpu = false;
|
||||||
shouldDumpShaders = false;
|
shouldDumpShaders = false;
|
||||||
vblankDivider = 1;
|
vblankDivider = 1;
|
||||||
|
|
|
@ -41,6 +41,7 @@ std::string getChooseHomeTab();
|
||||||
|
|
||||||
s16 getCursorState();
|
s16 getCursorState();
|
||||||
int getCursorHideTimeout();
|
int getCursorHideTimeout();
|
||||||
|
double getTrophyNotificationDuration();
|
||||||
std::string getBackButtonBehavior();
|
std::string getBackButtonBehavior();
|
||||||
bool getUseSpecialPad();
|
bool getUseSpecialPad();
|
||||||
int getSpecialPadClass();
|
int getSpecialPadClass();
|
||||||
|
@ -62,6 +63,7 @@ bool collectShadersForDebug();
|
||||||
bool showSplash();
|
bool showSplash();
|
||||||
bool autoUpdate();
|
bool autoUpdate();
|
||||||
bool alwaysShowChangelog();
|
bool alwaysShowChangelog();
|
||||||
|
bool leftSideTrophy();
|
||||||
bool nullGpu();
|
bool nullGpu();
|
||||||
bool copyGPUCmdBuffers();
|
bool copyGPUCmdBuffers();
|
||||||
bool dumpShaders();
|
bool dumpShaders();
|
||||||
|
@ -75,6 +77,7 @@ void setCollectShaderForDebug(bool enable);
|
||||||
void setShowSplash(bool enable);
|
void setShowSplash(bool enable);
|
||||||
void setAutoUpdate(bool enable);
|
void setAutoUpdate(bool enable);
|
||||||
void setAlwaysShowChangelog(bool enable);
|
void setAlwaysShowChangelog(bool enable);
|
||||||
|
void setLeftSideTrophy(bool enable);
|
||||||
void setNullGpu(bool enable);
|
void setNullGpu(bool enable);
|
||||||
void setAllowHDR(bool enable);
|
void setAllowHDR(bool enable);
|
||||||
void setCopyGPUCmdBuffers(bool enable);
|
void setCopyGPUCmdBuffers(bool enable);
|
||||||
|
@ -104,6 +107,7 @@ void setShowBackgroundImage(bool show);
|
||||||
|
|
||||||
void setCursorState(s16 cursorState);
|
void setCursorState(s16 cursorState);
|
||||||
void setCursorHideTimeout(int newcursorHideTimeout);
|
void setCursorHideTimeout(int newcursorHideTimeout);
|
||||||
|
void setTrophyNotificationDuration(double newTrophyNotificationDuration);
|
||||||
void setBackButtonBehavior(const std::string& type);
|
void setBackButtonBehavior(const std::string& type);
|
||||||
void setUseSpecialPad(bool use);
|
void setUseSpecialPad(bool use);
|
||||||
void setSpecialPadClass(int type);
|
void setSpecialPadClass(int type);
|
||||||
|
|
|
@ -128,6 +128,7 @@ static auto UserPaths = [] {
|
||||||
create_path(PathType::CheatsDir, user_dir / CHEATS_DIR);
|
create_path(PathType::CheatsDir, user_dir / CHEATS_DIR);
|
||||||
create_path(PathType::PatchesDir, user_dir / PATCHES_DIR);
|
create_path(PathType::PatchesDir, user_dir / PATCHES_DIR);
|
||||||
create_path(PathType::MetaDataDir, user_dir / METADATA_DIR);
|
create_path(PathType::MetaDataDir, user_dir / METADATA_DIR);
|
||||||
|
create_path(PathType::CustomTrophy, user_dir / CUSTOM_TROPHY);
|
||||||
|
|
||||||
return paths;
|
return paths;
|
||||||
}();
|
}();
|
||||||
|
|
|
@ -27,6 +27,7 @@ enum class PathType {
|
||||||
CheatsDir, // Where cheats are stored.
|
CheatsDir, // Where cheats are stored.
|
||||||
PatchesDir, // Where patches are stored.
|
PatchesDir, // Where patches are stored.
|
||||||
MetaDataDir, // Where game metadata (e.g. trophies and menu backgrounds) is 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";
|
constexpr auto PORTABLE_DIR = "user";
|
||||||
|
@ -44,6 +45,7 @@ constexpr auto CAPTURES_DIR = "captures";
|
||||||
constexpr auto CHEATS_DIR = "cheats";
|
constexpr auto CHEATS_DIR = "cheats";
|
||||||
constexpr auto PATCHES_DIR = "patches";
|
constexpr auto PATCHES_DIR = "patches";
|
||||||
constexpr auto METADATA_DIR = "game_data";
|
constexpr auto METADATA_DIR = "game_data";
|
||||||
|
constexpr auto CUSTOM_TROPHY = "custom_trophy";
|
||||||
|
|
||||||
// Filenames
|
// Filenames
|
||||||
constexpr auto LOG_FILE = "shad_log.txt";
|
constexpr auto LOG_FILE = "shad_log.txt";
|
||||||
|
|
|
@ -923,15 +923,16 @@ int PS4_SYSV_ABI sceNpTrophyUnlockTrophy(OrbisNpTrophyContext context, OrbisNpTr
|
||||||
node.attribute("unlockstate").set_value("true");
|
node.attribute("unlockstate").set_value("true");
|
||||||
}
|
}
|
||||||
|
|
||||||
Rtc::OrbisRtcTick trophyTimestamp;
|
auto trophyTimestamp = std::chrono::duration_cast<std::chrono::seconds>(
|
||||||
Rtc::sceRtcGetCurrentTick(&trophyTimestamp);
|
std::chrono::system_clock::now().time_since_epoch())
|
||||||
|
.count();
|
||||||
|
|
||||||
if (node.attribute("timestamp").empty()) {
|
if (node.attribute("timestamp").empty()) {
|
||||||
node.append_attribute("timestamp") =
|
node.append_attribute("timestamp") =
|
||||||
std::to_string(trophyTimestamp.tick).c_str();
|
std::to_string(trophyTimestamp).c_str();
|
||||||
} else {
|
} else {
|
||||||
node.attribute("timestamp")
|
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";
|
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");
|
platinum_node.attribute("unlockstate").set_value("true");
|
||||||
}
|
}
|
||||||
|
|
||||||
Rtc::OrbisRtcTick trophyTimestamp;
|
auto trophyTimestamp = std::chrono::duration_cast<std::chrono::seconds>(
|
||||||
Rtc::sceRtcGetCurrentTick(&trophyTimestamp);
|
std::chrono::system_clock::now().time_since_epoch())
|
||||||
|
.count();
|
||||||
|
|
||||||
if (platinum_node.attribute("timestamp").empty()) {
|
if (platinum_node.attribute("timestamp").empty()) {
|
||||||
platinum_node.append_attribute("timestamp") =
|
platinum_node.append_attribute("timestamp") =
|
||||||
std::to_string(trophyTimestamp.tick).c_str();
|
std::to_string(trophyTimestamp).c_str();
|
||||||
} else {
|
} else {
|
||||||
platinum_node.attribute("timestamp")
|
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 =
|
int platinum_trophy_id =
|
||||||
|
|
|
@ -2,9 +2,17 @@
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <cmrc/cmrc.hpp>
|
#include <cmrc/cmrc.hpp>
|
||||||
|
#include <common/path_util.h>
|
||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
|
|
||||||
|
#ifdef ENABLE_QT_GUI
|
||||||
|
#include <qt_gui/background_music_player.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/config.h"
|
#include "common/config.h"
|
||||||
#include "common/singleton.h"
|
#include "common/singleton.h"
|
||||||
|
@ -12,18 +20,23 @@
|
||||||
#include "trophy_ui.h"
|
#include "trophy_ui.h"
|
||||||
|
|
||||||
CMRC_DECLARE(res);
|
CMRC_DECLARE(res);
|
||||||
|
namespace fs = std::filesystem;
|
||||||
using namespace ImGui;
|
using namespace ImGui;
|
||||||
namespace Libraries::NpTrophy {
|
namespace Libraries::NpTrophy {
|
||||||
|
|
||||||
std::optional<TrophyUI> current_trophy_ui;
|
std::optional<TrophyUI> current_trophy_ui;
|
||||||
std::queue<TrophyInfo> trophy_queue;
|
std::queue<TrophyInfo> trophy_queue;
|
||||||
std::mutex queueMtx;
|
std::mutex queueMtx;
|
||||||
|
bool isLeftSide;
|
||||||
|
double trophy_timer;
|
||||||
|
|
||||||
TrophyUI::TrophyUI(const std::filesystem::path& trophyIconPath, const std::string& trophyName,
|
TrophyUI::TrophyUI(const std::filesystem::path& trophyIconPath, const std::string& trophyName,
|
||||||
const std::string_view& rarity)
|
const std::string_view& rarity)
|
||||||
: trophy_name(trophyName), trophy_type(rarity) {
|
: trophy_name(trophyName), trophy_type(rarity) {
|
||||||
|
|
||||||
|
isLeftSide = Config::leftSideTrophy();
|
||||||
|
trophy_timer = Config::getTrophyNotificationDuration();
|
||||||
|
|
||||||
if (std::filesystem::exists(trophyIconPath)) {
|
if (std::filesystem::exists(trophyIconPath)) {
|
||||||
trophy_icon = RefCountedTexture::DecodePngFile(trophyIconPath);
|
trophy_icon = RefCountedTexture::DecodePngFile(trophyIconPath);
|
||||||
} else {
|
} else {
|
||||||
|
@ -31,23 +44,57 @@ TrophyUI::TrophyUI(const std::filesystem::path& trophyIconPath, const std::strin
|
||||||
fmt::UTF(trophyIconPath.u8string()));
|
fmt::UTF(trophyIconPath.u8string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string pathString;
|
std::string pathString = "src/images/";
|
||||||
|
|
||||||
if (trophy_type == "P") {
|
if (trophy_type == "P") {
|
||||||
pathString = "src/images/platinum.png";
|
pathString += "platinum.png";
|
||||||
} else if (trophy_type == "G") {
|
} else if (trophy_type == "G") {
|
||||||
pathString = "src/images/gold.png";
|
pathString += "gold.png";
|
||||||
} else if (trophy_type == "S") {
|
} else if (trophy_type == "S") {
|
||||||
pathString = "src/images/silver.png";
|
pathString += "silver.png";
|
||||||
} else if (trophy_type == "B") {
|
} else if (trophy_type == "B") {
|
||||||
pathString = "src/images/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<u8> imgdata;
|
||||||
|
if (!customPath.empty()) {
|
||||||
|
std::ifstream file(customPath, std::ios::binary);
|
||||||
|
if (file) {
|
||||||
|
imgdata = std::vector<u8>(std::istreambuf_iterator<char>(file),
|
||||||
|
std::istreambuf_iterator<char>());
|
||||||
|
} 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<u8>(file.begin(), file.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
auto resource = cmrc::res::get_filesystem();
|
|
||||||
auto file = resource.open(pathString);
|
|
||||||
std::vector<u8> imgdata(file.begin(), file.end());
|
|
||||||
trophy_type_icon = RefCountedTexture::DecodePngTexture(imgdata);
|
trophy_type_icon = RefCountedTexture::DecodePngTexture(imgdata);
|
||||||
|
|
||||||
AddLayer(this);
|
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() {
|
TrophyUI::~TrophyUI() {
|
||||||
|
@ -58,6 +105,13 @@ void TrophyUI::Finish() {
|
||||||
RemoveLayer(this);
|
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() {
|
void TrophyUI::Draw() {
|
||||||
const auto& io = GetIO();
|
const auto& io = GetIO();
|
||||||
|
|
||||||
|
@ -68,26 +122,60 @@ void TrophyUI::Draw() {
|
||||||
std::min(io.DisplaySize.y, (70 * AdjustHeight)),
|
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);
|
SetNextWindowSize(window_size);
|
||||||
|
SetNextWindowPos(current_pos);
|
||||||
SetNextWindowCollapsed(false);
|
SetNextWindowCollapsed(false);
|
||||||
SetNextWindowPos(ImVec2(io.DisplaySize.x - (370 * AdjustWidth), (50 * AdjustHeight)));
|
|
||||||
KeepNavHighlight();
|
KeepNavHighlight();
|
||||||
|
PushStyleVar(ImGuiStyleVar_Alpha, fade_opacity);
|
||||||
|
|
||||||
if (Begin("Trophy Window", nullptr,
|
if (Begin("Trophy Window", nullptr,
|
||||||
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoSavedSettings |
|
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoSavedSettings |
|
||||||
ImGuiWindowFlags_NoInputs)) {
|
ImGuiWindowFlags_NoInputs)) {
|
||||||
|
|
||||||
|
// Displays the trophy icon
|
||||||
if (trophy_type_icon) {
|
if (trophy_type_icon) {
|
||||||
SetCursorPosY((window_size.y * 0.5f) - (25 * AdjustHeight));
|
SetCursorPosY((window_size.y * 0.5f) - (25 * AdjustHeight));
|
||||||
Image(trophy_type_icon.GetTexture().im_id,
|
Image(trophy_type_icon.GetTexture().im_id,
|
||||||
ImVec2((50 * AdjustWidth), (50 * AdjustHeight)));
|
ImVec2((50 * AdjustWidth), (50 * AdjustHeight)));
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
} else {
|
} else {
|
||||||
// placeholder
|
// Placeholder
|
||||||
const auto pos = GetCursorScreenPos();
|
const auto pos = GetCursorScreenPos();
|
||||||
ImGui::GetWindowDrawList()->AddRectFilled(pos, pos + ImVec2{50.0f * AdjustHeight},
|
ImGui::GetWindowDrawList()->AddRectFilled(pos, pos + ImVec2{50.0f * AdjustHeight},
|
||||||
GetColorU32(ImVec4{0.7f}));
|
GetColorU32(ImVec4{0.7f}));
|
||||||
ImGui::Indent(60);
|
ImGui::Indent(60);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Displays the name of the trophy
|
||||||
const std::string combinedString = "Trophy earned!\n%s" + trophy_name;
|
const std::string combinedString = "Trophy earned!\n%s" + trophy_name;
|
||||||
const float wrap_width =
|
const float wrap_width =
|
||||||
CalcWrapWidthForPos(GetCursorScreenPos(), (window_size.x - (60 * AdjustWidth)));
|
CalcWrapWidthForPos(GetCursorScreenPos(), (window_size.x - (60 * AdjustWidth)));
|
||||||
|
@ -108,11 +196,12 @@ void TrophyUI::Draw() {
|
||||||
TextWrapped("Trophy earned!\n%s", trophy_name.c_str());
|
TextWrapped("Trophy earned!\n%s", trophy_name.c_str());
|
||||||
ImGui::SameLine(window_size.x - (60 * AdjustWidth));
|
ImGui::SameLine(window_size.x - (60 * AdjustWidth));
|
||||||
|
|
||||||
|
// Displays the trophy icon
|
||||||
if (trophy_icon) {
|
if (trophy_icon) {
|
||||||
SetCursorPosY((window_size.y * 0.5f) - (25 * AdjustHeight));
|
SetCursorPosY((window_size.y * 0.5f) - (25 * AdjustHeight));
|
||||||
Image(trophy_icon.GetTexture().im_id, ImVec2((50 * AdjustWidth), (50 * AdjustHeight)));
|
Image(trophy_icon.GetTexture().im_id, ImVec2((50 * AdjustWidth), (50 * AdjustHeight)));
|
||||||
} else {
|
} else {
|
||||||
// placeholder
|
// Placeholder
|
||||||
const auto pos = GetCursorScreenPos();
|
const auto pos = GetCursorScreenPos();
|
||||||
ImGui::GetWindowDrawList()->AddRectFilled(pos, pos + ImVec2{50.0f * AdjustHeight},
|
ImGui::GetWindowDrawList()->AddRectFilled(pos, pos + ImVec2{50.0f * AdjustHeight},
|
||||||
GetColorU32(ImVec4{0.7f}));
|
GetColorU32(ImVec4{0.7f}));
|
||||||
|
@ -120,7 +209,8 @@ void TrophyUI::Draw() {
|
||||||
}
|
}
|
||||||
End();
|
End();
|
||||||
|
|
||||||
trophy_timer -= io.DeltaTime;
|
PopStyleVar();
|
||||||
|
|
||||||
if (trophy_timer <= 0) {
|
if (trophy_timer <= 0) {
|
||||||
std::lock_guard<std::mutex> lock(queueMtx);
|
std::lock_guard<std::mutex> lock(queueMtx);
|
||||||
if (!trophy_queue.empty()) {
|
if (!trophy_queue.empty()) {
|
||||||
|
@ -141,13 +231,27 @@ void AddTrophyToQueue(const std::filesystem::path& trophyIconPath, const std::st
|
||||||
if (Config::getisTrophyPopupDisabled()) {
|
if (Config::getisTrophyPopupDisabled()) {
|
||||||
return;
|
return;
|
||||||
} else if (current_trophy_ui.has_value()) {
|
} else if (current_trophy_ui.has_value()) {
|
||||||
TrophyInfo new_trophy;
|
current_trophy_ui.reset();
|
||||||
new_trophy.trophy_icon_path = trophyIconPath;
|
}
|
||||||
new_trophy.trophy_name = trophyName;
|
|
||||||
new_trophy.trophy_type = rarity;
|
TrophyInfo new_trophy;
|
||||||
trophy_queue.push(new_trophy);
|
new_trophy.trophy_icon_path = trophyIconPath;
|
||||||
} else {
|
new_trophy.trophy_name = trophyName;
|
||||||
current_trophy_ui.emplace(trophyIconPath, trophyName, rarity);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,6 @@ public:
|
||||||
private:
|
private:
|
||||||
std::string trophy_name;
|
std::string trophy_name;
|
||||||
std::string_view trophy_type;
|
std::string_view trophy_type;
|
||||||
float trophy_timer = 5.0f;
|
|
||||||
ImGui::RefCountedTexture trophy_icon;
|
ImGui::RefCountedTexture trophy_icon;
|
||||||
ImGui::RefCountedTexture trophy_type_icon;
|
ImGui::RefCountedTexture trophy_type_icon;
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,7 +7,6 @@ BackgroundMusicPlayer::BackgroundMusicPlayer(QObject* parent) : QObject(parent)
|
||||||
m_mediaPlayer = new QMediaPlayer(this);
|
m_mediaPlayer = new QMediaPlayer(this);
|
||||||
m_audioOutput = new QAudioOutput(this);
|
m_audioOutput = new QAudioOutput(this);
|
||||||
m_mediaPlayer->setAudioOutput(m_audioOutput);
|
m_mediaPlayer->setAudioOutput(m_audioOutput);
|
||||||
m_mediaPlayer->setLoops(QMediaPlayer::Infinite);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BackgroundMusicPlayer::setVolume(int volume) {
|
void BackgroundMusicPlayer::setVolume(int volume) {
|
||||||
|
@ -16,7 +15,7 @@ void BackgroundMusicPlayer::setVolume(int volume) {
|
||||||
m_audioOutput->setVolume(linearVolume);
|
m_audioOutput->setVolume(linearVolume);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BackgroundMusicPlayer::playMusic(const QString& snd0path) {
|
void BackgroundMusicPlayer::playMusic(const QString& snd0path, bool loops) {
|
||||||
if (snd0path.isEmpty()) {
|
if (snd0path.isEmpty()) {
|
||||||
stopMusic();
|
stopMusic();
|
||||||
return;
|
return;
|
||||||
|
@ -28,6 +27,12 @@ void BackgroundMusicPlayer::playMusic(const QString& snd0path) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (loops) {
|
||||||
|
m_mediaPlayer->setLoops(QMediaPlayer::Infinite);
|
||||||
|
} else {
|
||||||
|
m_mediaPlayer->setLoops(1);
|
||||||
|
}
|
||||||
|
|
||||||
m_currentMusic = newMusic;
|
m_currentMusic = newMusic;
|
||||||
m_mediaPlayer->setSource(newMusic);
|
m_mediaPlayer->setSource(newMusic);
|
||||||
m_mediaPlayer->play();
|
m_mediaPlayer->play();
|
||||||
|
|
|
@ -17,7 +17,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void setVolume(int volume);
|
void setVolume(int volume);
|
||||||
void playMusic(const QString& snd0path);
|
void playMusic(const QString& snd0path, bool loops = true);
|
||||||
void stopMusic();
|
void stopMusic();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -97,11 +97,13 @@ public:
|
||||||
QAction* deleteUpdate = new QAction(tr("Delete Update"), widget);
|
QAction* deleteUpdate = new QAction(tr("Delete Update"), widget);
|
||||||
QAction* deleteSaveData = new QAction(tr("Delete Save Data"), widget);
|
QAction* deleteSaveData = new QAction(tr("Delete Save Data"), widget);
|
||||||
QAction* deleteDLC = new QAction(tr("Delete DLC"), widget);
|
QAction* deleteDLC = new QAction(tr("Delete DLC"), widget);
|
||||||
|
QAction* deleteTrophy = new QAction(tr("Delete Trophy"), widget);
|
||||||
|
|
||||||
deleteMenu->addAction(deleteGame);
|
deleteMenu->addAction(deleteGame);
|
||||||
deleteMenu->addAction(deleteUpdate);
|
deleteMenu->addAction(deleteUpdate);
|
||||||
deleteMenu->addAction(deleteSaveData);
|
deleteMenu->addAction(deleteSaveData);
|
||||||
deleteMenu->addAction(deleteDLC);
|
deleteMenu->addAction(deleteDLC);
|
||||||
|
deleteMenu->addAction(deleteTrophy);
|
||||||
|
|
||||||
menu.addMenu(deleteMenu);
|
menu.addMenu(deleteMenu);
|
||||||
|
|
||||||
|
@ -380,9 +382,9 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selected == deleteGame || selected == deleteUpdate || selected == deleteDLC ||
|
if (selected == deleteGame || selected == deleteUpdate || selected == deleteDLC ||
|
||||||
selected == deleteSaveData) {
|
selected == deleteSaveData || selected == deleteTrophy) {
|
||||||
bool error = false;
|
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);
|
Common::FS::PathToQString(folder_path, m_games[itemID].path);
|
||||||
game_update_path = folder_path + "-UPDATE";
|
game_update_path = folder_path + "-UPDATE";
|
||||||
Common::FS::PathToQString(
|
Common::FS::PathToQString(
|
||||||
|
@ -391,6 +393,11 @@ public:
|
||||||
Common::FS::PathToQString(save_data_path,
|
Common::FS::PathToQString(save_data_path,
|
||||||
Common::FS::GetUserPath(Common::FS::PathType::UserDir) /
|
Common::FS::GetUserPath(Common::FS::PathType::UserDir) /
|
||||||
"savedata/1" / m_games[itemID].serial);
|
"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");
|
QString message_type = tr("Game");
|
||||||
|
|
||||||
if (selected == deleteUpdate) {
|
if (selected == deleteUpdate) {
|
||||||
|
@ -420,6 +427,16 @@ public:
|
||||||
folder_path = save_data_path;
|
folder_path = save_data_path;
|
||||||
message_type = tr("Save Data");
|
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) {
|
if (!error) {
|
||||||
QString gameName = QString::fromStdString(m_games[itemID].name);
|
QString gameName = QString::fromStdString(m_games[itemID].name);
|
||||||
|
|
|
@ -225,6 +225,17 @@ SettingsDialog::SettingsDialog(std::span<const QString> physical_devices,
|
||||||
Config::setShowBackgroundImage(state == Qt::Checked);
|
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
|
// Input TAB
|
||||||
{
|
{
|
||||||
connect(ui->hideCursorComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
|
connect(ui->hideCursorComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
|
||||||
|
@ -280,8 +291,8 @@ SettingsDialog::SettingsDialog(std::span<const QString> physical_devices,
|
||||||
connect(ui->OpenLogLocationButton, &QPushButton::clicked, this, []() {
|
connect(ui->OpenLogLocationButton, &QPushButton::clicked, this, []() {
|
||||||
QString userPath;
|
QString userPath;
|
||||||
Common::FS::PathToQString(userPath,
|
Common::FS::PathToQString(userPath,
|
||||||
Common::FS::GetUserPath(Common::FS::PathType::UserDir));
|
Common::FS::GetUserPath(Common::FS::PathType::LogDir));
|
||||||
QDesktopServices::openUrl(QUrl::fromLocalFile(userPath + "/log"));
|
QDesktopServices::openUrl(QUrl::fromLocalFile(userPath));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -308,6 +319,9 @@ SettingsDialog::SettingsDialog(std::span<const QString> physical_devices,
|
||||||
ui->checkCompatibilityOnStartupCheckBox->installEventFilter(this);
|
ui->checkCompatibilityOnStartupCheckBox->installEventFilter(this);
|
||||||
ui->updateCompatibilityButton->installEventFilter(this);
|
ui->updateCompatibilityButton->installEventFilter(this);
|
||||||
|
|
||||||
|
// User
|
||||||
|
ui->OpenCustomTrophyLocationButton->installEventFilter(this);
|
||||||
|
|
||||||
// Input
|
// Input
|
||||||
ui->hideCursorGroupBox->installEventFilter(this);
|
ui->hideCursorGroupBox->installEventFilter(this);
|
||||||
ui->idleTimeoutGroupBox->installEventFilter(this);
|
ui->idleTimeoutGroupBox->installEventFilter(this);
|
||||||
|
@ -403,6 +417,9 @@ void SettingsDialog::LoadValuesFromConfig() {
|
||||||
ui->playBGMCheckBox->setChecked(toml::find_or<bool>(data, "General", "playBGM", false));
|
ui->playBGMCheckBox->setChecked(toml::find_or<bool>(data, "General", "playBGM", false));
|
||||||
ui->disableTrophycheckBox->setChecked(
|
ui->disableTrophycheckBox->setChecked(
|
||||||
toml::find_or<bool>(data, "General", "isTrophyPopupDisabled", false));
|
toml::find_or<bool>(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<int>(data, "General", "BGMvolume", 50));
|
ui->BGMVolumeSlider->setValue(toml::find_or<int>(data, "General", "BGMvolume", 50));
|
||||||
ui->discordRPCCheckbox->setChecked(
|
ui->discordRPCCheckbox->setChecked(
|
||||||
toml::find_or<bool>(data, "General", "enableDiscordRPC", true));
|
toml::find_or<bool>(data, "General", "enableDiscordRPC", true));
|
||||||
|
@ -593,6 +610,11 @@ void SettingsDialog::updateNoteTextEdit(const QString& elementName) {
|
||||||
text = tr("Update Compatibility Database:\\nImmediately update the compatibility database.");
|
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
|
// Input
|
||||||
if (elementName == "hideCursorGroupBox") {
|
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.");
|
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.");
|
||||||
|
@ -683,6 +705,8 @@ void SettingsDialog::UpdateSettings() {
|
||||||
screenModeMap.value(ui->displayModeComboBox->currentText()).toStdString());
|
screenModeMap.value(ui->displayModeComboBox->currentText()).toStdString());
|
||||||
Config::setIsMotionControlsEnabled(ui->motionControlsCheckBox->isChecked());
|
Config::setIsMotionControlsEnabled(ui->motionControlsCheckBox->isChecked());
|
||||||
Config::setisTrophyPopupDisabled(ui->disableTrophycheckBox->isChecked());
|
Config::setisTrophyPopupDisabled(ui->disableTrophycheckBox->isChecked());
|
||||||
|
Config::setTrophyNotificationDuration(ui->popUpDurationSpinBox->value());
|
||||||
|
Config::setLeftSideTrophy(ui->radioButton_Left->isChecked());
|
||||||
Config::setPlayBGM(ui->playBGMCheckBox->isChecked());
|
Config::setPlayBGM(ui->playBGMCheckBox->isChecked());
|
||||||
Config::setAllowHDR(ui->enableHDRCheckBox->isChecked());
|
Config::setAllowHDR(ui->enableHDRCheckBox->isChecked());
|
||||||
Config::setLogType(logTypeMap.value(ui->logTypeComboBox->currentText()).toStdString());
|
Config::setLogType(logTypeMap.value(ui->logTypeComboBox->currentText()).toStdString());
|
||||||
|
|
|
@ -59,7 +59,7 @@
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="currentIndex">
|
<property name="currentIndex">
|
||||||
<number>6</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QScrollArea" name="generalTab">
|
<widget class="QScrollArea" name="generalTab">
|
||||||
<property name="widgetResizable">
|
<property name="widgetResizable">
|
||||||
|
@ -73,8 +73,8 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>718</width>
|
<width>946</width>
|
||||||
<height>332</height>
|
<height>545</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="generalTabVLayout" stretch="0">
|
<layout class="QVBoxLayout" name="generalTabVLayout" stretch="0">
|
||||||
|
@ -454,8 +454,8 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>646</width>
|
<width>946</width>
|
||||||
<height>395</height>
|
<height>545</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="guiTabVLayout" stretch="0">
|
<layout class="QVBoxLayout" name="guiTabVLayout" stretch="0">
|
||||||
|
@ -903,8 +903,8 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>545</width>
|
<width>946</width>
|
||||||
<height>141</height>
|
<height>545</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="graphicsTabVLayout" stretch="0,0">
|
<layout class="QVBoxLayout" name="graphicsTabVLayout" stretch="0,0">
|
||||||
|
@ -1198,8 +1198,8 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>234</width>
|
<width>946</width>
|
||||||
<height>292</height>
|
<height>545</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="userTabVLayout" stretch="0,0,1">
|
<layout class="QVBoxLayout" name="userTabVLayout" stretch="0,0,1">
|
||||||
|
@ -1264,30 +1264,121 @@
|
||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="disableTrophycheckBox">
|
<widget class="QCheckBox" name="disableTrophycheckBox">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Disable Trophy Pop-ups</string>
|
<string>Disable Trophy Notification</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="label_Trophy">
|
<layout class="QHBoxLayout" name="horizontalLayout_SidePopUps">
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_SidePopUps">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Trophy Notification Position</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="radioButton_Left">
|
||||||
|
<property name="text">
|
||||||
|
<string>Left</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="radioButton_Right">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Right</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_PopUpsDuration">
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_PopUpDuration">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Notification Duration</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QDoubleSpinBox" name="popUpDurationSpinBox">
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer_4">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Orientation::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_trophyKey">
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_Trophy">
|
||||||
|
<property name="text">
|
||||||
|
<string>Trophy Key</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="trophyKeyLineEdit">
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>0</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>10</pointsize>
|
||||||
|
<bold>false</bold>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="OpenCustomTrophyLocationButton">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Trophy Key</string>
|
<string>Open the custom trophy images/sounds folder</string>
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLineEdit" name="trophyKeyLineEdit">
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>0</width>
|
|
||||||
<height>0</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="font">
|
|
||||||
<font>
|
|
||||||
<pointsize>10</pointsize>
|
|
||||||
<bold>false</bold>
|
|
||||||
</font>
|
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -1342,8 +1433,8 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>455</width>
|
<width>946</width>
|
||||||
<height>252</height>
|
<height>545</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="inputTabVLayout" stretch="0,0">
|
<layout class="QVBoxLayout" name="inputTabVLayout" stretch="0,0">
|
||||||
|
@ -1626,8 +1717,8 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>216</width>
|
<width>946</width>
|
||||||
<height>254</height>
|
<height>545</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="pathsTabLayout">
|
<layout class="QVBoxLayout" name="pathsTabLayout">
|
||||||
|
@ -1717,7 +1808,7 @@
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>946</width>
|
<width>946</width>
|
||||||
<height>536</height>
|
<height>545</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="debugTabVLayout" stretch="0,0">
|
<layout class="QVBoxLayout" name="debugTabVLayout" stretch="0,0">
|
||||||
|
|
|
@ -775,6 +775,10 @@
|
||||||
<source>Delete DLC</source>
|
<source>Delete DLC</source>
|
||||||
<translation>Delete DLC</translation>
|
<translation>Delete DLC</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Delete Trophy</source>
|
||||||
|
<translation>Delete Trophy</translation>
|
||||||
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Compatibility...</source>
|
<source>Compatibility...</source>
|
||||||
<translation>Compatibility...</translation>
|
<translation>Compatibility...</translation>
|
||||||
|
@ -859,10 +863,18 @@
|
||||||
<source>This game has no save data to delete!</source>
|
<source>This game has no save data to delete!</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>This game has no saved trophies to delete!</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Save Data</source>
|
<source>Save Data</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Trophy</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>SFO Viewer for </source>
|
<source>SFO Viewer for </source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
|
@ -1311,6 +1323,10 @@
|
||||||
<source>Trophy</source>
|
<source>Trophy</source>
|
||||||
<translation>Trophy</translation>
|
<translation>Trophy</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Open the custom trophy images/sounds folder</source>
|
||||||
|
<translation>Open the custom trophy images/sounds folder</translation>
|
||||||
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Logger</source>
|
<source>Logger</source>
|
||||||
<translation>Logger</translation>
|
<translation>Logger</translation>
|
||||||
|
@ -1476,8 +1492,8 @@
|
||||||
<translation>Title Music</translation>
|
<translation>Title Music</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Disable Trophy Pop-ups</source>
|
<source>Disable Trophy Notification</source>
|
||||||
<translation>Disable Trophy Pop-ups</translation>
|
<translation>Disable Trophy Notification</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Background Image</source>
|
<source>Background Image</source>
|
||||||
|
@ -1611,6 +1627,10 @@
|
||||||
<source>Update Compatibility Database:\nImmediately update the compatibility database.</source>
|
<source>Update Compatibility Database:\nImmediately update the compatibility database.</source>
|
||||||
<translation>Update Compatibility Database:\nImmediately update the compatibility database.</translation>
|
<translation>Update Compatibility Database:\nImmediately update the compatibility database.</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>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</source>
|
||||||
|
<translation>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</translation>
|
||||||
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Never</source>
|
<source>Never</source>
|
||||||
<translation>Never</translation>
|
<translation>Never</translation>
|
||||||
|
@ -1803,6 +1823,22 @@
|
||||||
<source>Separate Log Files:\nWrites a separate logfile for each game.</source>
|
<source>Separate Log Files:\nWrites a separate logfile for each game.</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Trophy Notification Position</source>
|
||||||
|
<translation>Trophy Notification Position</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Left</source>
|
||||||
|
<translation>Left</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Right</source>
|
||||||
|
<translation>Right</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Notification Duration</source>
|
||||||
|
<translation>Notification Duration</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>TrophyViewer</name>
|
<name>TrophyViewer</name>
|
||||||
|
@ -1810,5 +1846,21 @@
|
||||||
<source>Trophy Viewer</source>
|
<source>Trophy Viewer</source>
|
||||||
<translation>Trophy Viewer</translation>
|
<translation>Trophy Viewer</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Progress</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Show Earned Trophies</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Show Not Earned Trophies</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Show Hidden Trophies</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
</TS>
|
</TS>
|
|
@ -1,27 +1,169 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include <QCheckBox>
|
||||||
|
#include <QDockWidget>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <cmrc/cmrc.hpp>
|
#include <cmrc/cmrc.hpp>
|
||||||
|
#include <common/config.h>
|
||||||
#include "common/path_util.h"
|
#include "common/path_util.h"
|
||||||
|
#include "main_window_themes.h"
|
||||||
#include "trophy_viewer.h"
|
#include "trophy_viewer.h"
|
||||||
|
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
CMRC_DECLARE(res);
|
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<QTableWidget*>(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<QLabel*>();
|
||||||
|
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<QTableWidget*>(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<QLabel*>();
|
||||||
|
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<QLabel*>();
|
||||||
|
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() {
|
TrophyViewer::TrophyViewer(QString trophyPath, QString gameTrpPath) : QMainWindow() {
|
||||||
this->setWindowTitle(tr("Trophy Viewer"));
|
this->setWindowTitle(tr("Trophy Viewer"));
|
||||||
this->setAttribute(Qt::WA_DeleteOnClose);
|
this->setAttribute(Qt::WA_DeleteOnClose);
|
||||||
tabWidget = new QTabWidget(this);
|
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;
|
gameTrpPath_ = gameTrpPath;
|
||||||
headers << "Unlocked"
|
headers << "Unlocked"
|
||||||
<< "Trophy"
|
<< "Trophy"
|
||||||
<< "Name"
|
<< "Name"
|
||||||
<< "Description"
|
<< "Description"
|
||||||
|
<< "Time Unlocked"
|
||||||
|
<< "Type"
|
||||||
<< "ID"
|
<< "ID"
|
||||||
<< "Hidden"
|
<< "Hidden"
|
||||||
<< "Type"
|
|
||||||
<< "PID";
|
<< "PID";
|
||||||
PopulateTrophyWidget(trophyPath);
|
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
|
||||||
|
connect(showEarnedCheck, &QCheckBox::stateChanged, this, &TrophyViewer::updateTableFilters);
|
||||||
|
connect(showNotEarnedCheck, &QCheckBox::stateChanged, this, &TrophyViewer::updateTableFilters);
|
||||||
|
connect(showHiddenCheck, &QCheckBox::stateChanged, this, &TrophyViewer::updateTableFilters);
|
||||||
|
|
||||||
|
updateTrophyInfo();
|
||||||
|
updateTableFilters();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TrophyViewer::PopulateTrophyWidget(QString title) {
|
void TrophyViewer::PopulateTrophyWidget(QString title) {
|
||||||
|
@ -68,6 +210,7 @@ void TrophyViewer::PopulateTrophyWidget(QString title) {
|
||||||
QStringList trpPid;
|
QStringList trpPid;
|
||||||
QStringList trophyNames;
|
QStringList trophyNames;
|
||||||
QStringList trophyDetails;
|
QStringList trophyDetails;
|
||||||
|
QStringList trpTimeUnlocked;
|
||||||
|
|
||||||
QString xmlPath = trpDir + "/Xml/TROP.XML";
|
QString xmlPath = trpDir + "/Xml/TROP.XML";
|
||||||
QFile file(xmlPath);
|
QFile file(xmlPath);
|
||||||
|
@ -84,14 +227,35 @@ void TrophyViewer::PopulateTrophyWidget(QString title) {
|
||||||
trpHidden.append(reader.attributes().value("hidden").toString());
|
trpHidden.append(reader.attributes().value("hidden").toString());
|
||||||
trpType.append(reader.attributes().value("ttype").toString());
|
trpType.append(reader.attributes().value("ttype").toString());
|
||||||
trpPid.append(reader.attributes().value("pid").toString());
|
trpPid.append(reader.attributes().value("pid").toString());
|
||||||
|
|
||||||
if (reader.attributes().hasAttribute("unlockstate")) {
|
if (reader.attributes().hasAttribute("unlockstate")) {
|
||||||
if (reader.attributes().value("unlockstate").toString() == "true") {
|
if (reader.attributes().value("unlockstate").toString() == "true") {
|
||||||
trpUnlocked.append("unlocked");
|
trpUnlocked.append("unlocked");
|
||||||
} else {
|
} else {
|
||||||
trpUnlocked.append("locked");
|
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 {
|
} else {
|
||||||
trpUnlocked.append("locked");
|
trpUnlocked.append("locked");
|
||||||
|
trpTimeUnlocked.append("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,7 +269,7 @@ void TrophyViewer::PopulateTrophyWidget(QString title) {
|
||||||
}
|
}
|
||||||
QTableWidget* tableWidget = new QTableWidget(this);
|
QTableWidget* tableWidget = new QTableWidget(this);
|
||||||
tableWidget->setShowGrid(false);
|
tableWidget->setShowGrid(false);
|
||||||
tableWidget->setColumnCount(8);
|
tableWidget->setColumnCount(9);
|
||||||
tableWidget->setHorizontalHeaderLabels(headers);
|
tableWidget->setHorizontalHeaderLabels(headers);
|
||||||
tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
|
tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||||
tableWidget->setSelectionMode(QAbstractItemView::SingleSelection);
|
tableWidget->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||||
|
@ -113,6 +277,8 @@ void TrophyViewer::PopulateTrophyWidget(QString title) {
|
||||||
tableWidget->horizontalHeader()->setStretchLastSection(true);
|
tableWidget->horizontalHeader()->setStretchLastSection(true);
|
||||||
tableWidget->verticalHeader()->setVisible(false);
|
tableWidget->verticalHeader()->setVisible(false);
|
||||||
tableWidget->setRowCount(icons.size());
|
tableWidget->setRowCount(icons.size());
|
||||||
|
tableWidget->setSortingEnabled(true);
|
||||||
|
|
||||||
for (int row = 0; auto& icon : icons) {
|
for (int row = 0; auto& icon : icons) {
|
||||||
QTableWidgetItem* item = new QTableWidgetItem();
|
QTableWidgetItem* item = new QTableWidgetItem();
|
||||||
item->setData(Qt::DecorationRole, icon);
|
item->setData(Qt::DecorationRole, icon);
|
||||||
|
@ -122,15 +288,34 @@ void TrophyViewer::PopulateTrophyWidget(QString title) {
|
||||||
const std::string filename = GetTrpType(trpType[row].at(0));
|
const std::string filename = GetTrpType(trpType[row].at(0));
|
||||||
QTableWidgetItem* typeitem = new QTableWidgetItem();
|
QTableWidgetItem* typeitem = new QTableWidgetItem();
|
||||||
|
|
||||||
auto resource = cmrc::res::get_filesystem();
|
const auto CustomTrophy_Dir =
|
||||||
std::string resourceString = "src/images/" + filename;
|
Common::FS::GetUserPath(Common::FS::PathType::CustomTrophy);
|
||||||
auto file = resource.open(resourceString);
|
std::string customPath;
|
||||||
std::vector<char> imgdata(file.begin(), file.end());
|
|
||||||
QImage type_icon = QImage::fromData(imgdata).scaled(QSize(64, 64), Qt::KeepAspectRatio,
|
if (fs::exists(CustomTrophy_Dir / filename)) {
|
||||||
Qt::SmoothTransformation);
|
customPath = (CustomTrophy_Dir / filename).string();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<char> imgdata;
|
||||||
|
|
||||||
|
if (!customPath.empty()) {
|
||||||
|
std::ifstream file(customPath, std::ios::binary);
|
||||||
|
if (file) {
|
||||||
|
imgdata = std::vector<char>(std::istreambuf_iterator<char>(file),
|
||||||
|
std::istreambuf_iterator<char>());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
auto resource = cmrc::res::get_filesystem();
|
||||||
|
std::string resourceString = "src/images/" + filename;
|
||||||
|
auto file = resource.open(resourceString);
|
||||||
|
imgdata = std::vector<char>(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->setData(Qt::DecorationRole, type_icon);
|
||||||
typeitem->setFlags(typeitem->flags() & ~Qt::ItemIsEditable);
|
typeitem->setFlags(typeitem->flags() & ~Qt::ItemIsEditable);
|
||||||
tableWidget->setItem(row, 6, typeitem);
|
tableWidget->setItem(row, 5, typeitem);
|
||||||
|
|
||||||
std::string detailString = trophyDetails[row].toStdString();
|
std::string detailString = trophyDetails[row].toStdString();
|
||||||
std::size_t newline_pos = 0;
|
std::size_t newline_pos = 0;
|
||||||
|
@ -143,46 +328,45 @@ void TrophyViewer::PopulateTrophyWidget(QString title) {
|
||||||
SetTableItem(tableWidget, row, 0, trpUnlocked[row]);
|
SetTableItem(tableWidget, row, 0, trpUnlocked[row]);
|
||||||
SetTableItem(tableWidget, row, 2, trophyNames[row]);
|
SetTableItem(tableWidget, row, 2, trophyNames[row]);
|
||||||
SetTableItem(tableWidget, row, 3, QString::fromStdString(detailString));
|
SetTableItem(tableWidget, row, 3, QString::fromStdString(detailString));
|
||||||
SetTableItem(tableWidget, row, 4, trpId[row]);
|
SetTableItem(tableWidget, row, 4, trpTimeUnlocked[row]);
|
||||||
SetTableItem(tableWidget, row, 5, trpHidden[row]);
|
SetTableItem(tableWidget, row, 6, trpId[row]);
|
||||||
SetTableItem(tableWidget, row, 7, trpPid[row]);
|
SetTableItem(tableWidget, row, 7, trpHidden[row]);
|
||||||
|
SetTableItem(tableWidget, row, 8, trpPid[row]);
|
||||||
}
|
}
|
||||||
tableWidget->verticalHeader()->resizeSection(row, icon.height());
|
tableWidget->verticalHeader()->resizeSection(row, icon.height());
|
||||||
row++;
|
row++;
|
||||||
}
|
}
|
||||||
tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
||||||
int width = 16;
|
int width = 16;
|
||||||
for (int i = 0; i < 8; i++) {
|
for (int i = 0; i < 9; i++) {
|
||||||
width += tableWidget->horizontalHeader()->sectionSize(i);
|
width += tableWidget->horizontalHeader()->sectionSize(i);
|
||||||
}
|
}
|
||||||
tableWidget->resize(width, 720);
|
tableWidget->resize(width, 720);
|
||||||
tabWidget->addTab(tableWidget,
|
tabWidget->addTab(tableWidget,
|
||||||
tabName.insert(6, " ").replace(0, 1, tabName.at(0).toUpper()));
|
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);
|
this->setCentralWidget(tabWidget);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TrophyViewer::SetTableItem(QTableWidget* parent, int row, int column, QString str) {
|
void TrophyViewer::SetTableItem(QTableWidget* parent, int row, int column, QString str) {
|
||||||
QWidget* widget = new QWidget();
|
QTableWidgetItem* item = new QTableWidgetItem(str);
|
||||||
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;");
|
|
||||||
|
|
||||||
// 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)
|
if (column != 1 && column != 2 && column != 3)
|
||||||
layout->setAlignment(Qt::AlignCenter);
|
item->setTextAlignment(Qt::AlignCenter);
|
||||||
widget->setLayout(layout);
|
item->setFont(QFont("Arial", 12, QFont::Bold));
|
||||||
|
|
||||||
|
Theme theme = static_cast<Theme>(Config::getMainWindowTheme());
|
||||||
|
|
||||||
|
if (theme == Theme::Light) {
|
||||||
|
item->setForeground(QBrush(Qt::black));
|
||||||
|
} else {
|
||||||
|
item->setForeground(QBrush(Qt::white));
|
||||||
|
}
|
||||||
|
|
||||||
parent->setItem(row, column, item);
|
parent->setItem(row, column, item);
|
||||||
parent->setCellWidget(row, column, widget);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,10 @@ class TrophyViewer : public QMainWindow {
|
||||||
public:
|
public:
|
||||||
explicit TrophyViewer(QString trophyPath, QString gameTrpPath);
|
explicit TrophyViewer(QString trophyPath, QString gameTrpPath);
|
||||||
|
|
||||||
|
void updateTrophyInfo();
|
||||||
|
|
||||||
|
void updateTableFilters();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void PopulateTrophyWidget(QString title);
|
void PopulateTrophyWidget(QString title);
|
||||||
void SetTableItem(QTableWidget* parent, int row, int column, QString str);
|
void SetTableItem(QTableWidget* parent, int row, int column, QString str);
|
||||||
|
@ -31,6 +35,10 @@ private:
|
||||||
QStringList headers;
|
QStringList headers;
|
||||||
QString gameTrpPath_;
|
QString gameTrpPath_;
|
||||||
TRP trp;
|
TRP trp;
|
||||||
|
QLabel* trophyInfoLabel;
|
||||||
|
QCheckBox* showEarnedCheck;
|
||||||
|
QCheckBox* showNotEarnedCheck;
|
||||||
|
QCheckBox* showHiddenCheck;
|
||||||
|
|
||||||
std::string GetTrpType(const QChar trp_) {
|
std::string GetTrpType(const QChar trp_) {
|
||||||
switch (trp_.toLatin1()) {
|
switch (trp_.toLatin1()) {
|
||||||
|
|
Loading…
Add table
Reference in a new issue