diff --git a/rpcs3/Emu/Cell/Modules/sceNpTrophy.cpp b/rpcs3/Emu/Cell/Modules/sceNpTrophy.cpp index 10af888b73..e7330bc545 100644 --- a/rpcs3/Emu/Cell/Modules/sceNpTrophy.cpp +++ b/rpcs3/Emu/Cell/Modules/sceNpTrophy.cpp @@ -16,6 +16,10 @@ logs::channel sceNpTrophy("sceNpTrophy"); +TrophyNotificationBase::~TrophyNotificationBase() +{ +} + struct trophy_context_t { static const u32 id_base = 1; @@ -490,6 +494,40 @@ error_code sceNpTrophyUnlockTrophy(u32 context, u32 handle, s32 trophyId, vm::pt *platinumId = SCE_NP_TROPHY_INVALID_TROPHY_ID; // TODO } + if (g_cfg.misc.show_trophy_popups) + { + // Figure out how many zeros are needed for padding. (either 0, 1, or 2) + std::string padding = ""; + if (trophyId < 10) + { + padding = "00"; + } + else if (trophyId < 100) + { + padding = "0"; + } + + // Get icon for the notification. + std::string trophyIconPath = "/dev_hdd0/home/00000001/trophy/" + ctxt->trp_name + "/TROP" + padding + std::to_string(trophyId) + ".PNG"; + fs::file trophyIconFile = fs::file(vfs::get(trophyIconPath)); + u32 iconSize = trophyIconFile.size(); + std::vector trophyIconData; + trophyIconFile.read(trophyIconData, iconSize); + + vm::ptr details = vm::make_var(SceNpTrophyDetails()); + vm::ptr _ = vm::make_var(SceNpTrophyData()); + + s32 ret = sceNpTrophyGetTrophyInfo(context, handle, trophyId, details, _); + if (ret != CELL_OK) + { + sceNpTrophy.error("Failed to get info for trophy dialog. Error code %x", ret); + *details = SceNpTrophyDetails(); + } + Emu.CallAfter([det = *details, trophyIconData]() { + Emu.GetCallbacks().get_trophy_notification_dialog()->ShowTrophyNotification(det, trophyIconData); + }); + } + return CELL_OK; } diff --git a/rpcs3/Emu/Cell/Modules/sceNpTrophy.h b/rpcs3/Emu/Cell/Modules/sceNpTrophy.h index 67b5cc5257..526778c9b6 100644 --- a/rpcs3/Emu/Cell/Modules/sceNpTrophy.h +++ b/rpcs3/Emu/Cell/Modules/sceNpTrophy.h @@ -137,3 +137,14 @@ enum }; using SceNpTrophyStatusCallback = s32(u32 context, u32 status, s32 completed, s32 total, vm::ptr arg); + +// Forward declare this function since trophyunlock needs it +error_code sceNpTrophyGetTrophyInfo(u32 context, u32 handle, s32 trophyId, vm::ptr details, vm::ptr data); + +class TrophyNotificationBase +{ +public: + virtual ~TrophyNotificationBase(); + + virtual s32 ShowTrophyNotification(const SceNpTrophyDetails& trophy, const std::vector& trophyIconBfr) = 0; +}; diff --git a/rpcs3/Emu/System.h b/rpcs3/Emu/System.h index bc1adb4c0e..f827ceeef5 100644 --- a/rpcs3/Emu/System.h +++ b/rpcs3/Emu/System.h @@ -165,6 +165,7 @@ struct EmuCallbacks std::function()> get_audio; std::function()> get_msg_dialog; std::function()> get_save_dialog; + std::function()> get_trophy_notification_dialog; }; class Emulator final @@ -402,6 +403,7 @@ struct cfg_root : cfg::node cfg::_bool autoexit{this, "Exit RPCS3 when process finishes"}; cfg::_bool start_fullscreen{ this, "Start games in fullscreen mode" }; cfg::_bool show_fps_in_title{ this, "Show FPS counter in window title", true}; + cfg::_bool show_trophy_popups{ this, "Show trophy popups", true}; cfg::_int<1, 65535> gdb_server_port{this, "Port", 2345}; } misc{this}; diff --git a/rpcs3/Json/tooltips.json b/rpcs3/Json/tooltips.json index b0076dd0d0..98e78b819e 100644 --- a/rpcs3/Json/tooltips.json +++ b/rpcs3/Json/tooltips.json @@ -62,6 +62,7 @@ "startGameFullscreen": "Automatically puts the game window in fullscreen.\nDouble click on the game window or press alt+enter to toggle fullscreen and windowed mode.", "showFPSInTitle": "Shows the framerate in the game window title. May cause buggy or outdated recording software to not notice RPCS3.", "gs_resizeOnBoot": "Automatically resizes the game window on boot.\nThis does not change the internal game resolution.", + "showTrophyPopups": "Show trophy popups when a trophy is unlocked.", "gs_disableMouse": "Disables the activation of fullscreen mode per doubleclick while the game screen is active.\nCheck this if you want to play with mouse and keyboard (for example with UCR)." } }, diff --git a/rpcs3/Loader/TROPUSR.cpp b/rpcs3/Loader/TROPUSR.cpp index 143a0c6f69..0b2d87bc43 100644 --- a/rpcs3/Loader/TROPUSR.cpp +++ b/rpcs3/Loader/TROPUSR.cpp @@ -9,14 +9,12 @@ bool TROPUSRLoader::Load(const std::string& filepath, const std::string& configp { const std::string& path = vfs::get(filepath); - if (!Generate(filepath, configpath)) - { - return false; - } - if (!m_file.open(path, fs::read)) { - return false; + if (!Generate(filepath, configpath)) + { + return false; + } } if (!LoadHeader() || !LoadTableHeaders() || !LoadTables()) diff --git a/rpcs3/rpcs3.vcxproj b/rpcs3/rpcs3.vcxproj index 5cec667b85..0acad15c18 100644 --- a/rpcs3/rpcs3.vcxproj +++ b/rpcs3/rpcs3.vcxproj @@ -897,6 +897,8 @@ + + @@ -1431,6 +1433,8 @@ .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DQT_OPENGL_LIB -DQT_WIDGETS_LIB -DQT_QUICK_LIB -DQT_GUI_LIB -DQT_QML_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -DQT_WINEXTRAS_LIB -DLLVM_AVAILABLE -D_SCL_SECURE_NO_WARNINGS -D_UNICODE "-I.\..\Vulkan\Vulkan-LoaderAndValidationLayers\include" "-I.\.." "-I.\..\3rdparty\minidx12\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtOpenGL" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtQuick" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtQml" "-I$(QTDIR)\include\QtNetwork" "-I$(QTDIR)\include\QtCore" "-I.\debug" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)\." "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(NOINHERIT)\." + + diff --git a/rpcs3/rpcs3.vcxproj.filters b/rpcs3/rpcs3.vcxproj.filters index 226ac19247..f46b65b03e 100644 --- a/rpcs3/rpcs3.vcxproj.filters +++ b/rpcs3/rpcs3.vcxproj.filters @@ -60,7 +60,10 @@ ui true - + + {d49ebf7f-ea65-4006-943a-2c47f25c9aa7} + + {31799965-1274-4c87-83a3-3bff31e8715d} @@ -426,16 +429,19 @@ Generated Files\Debug - LLVM - Gui\saves + Gui\save - Gui\saves + Gui\save - Gui\saves + Gui\save - - Gui\saves + + Gui\trophy + + + Gui\trophy Generated Files\Release - LLVM @@ -548,6 +554,9 @@ Generated Files\Debug + + Gui\save + @@ -581,7 +590,13 @@ Generated Files - Gui\saves + Gui\save + + + Gui\trophy + + + Gui\trophy Io @@ -649,13 +664,10 @@ Form Files - Gui\saves + Gui\save - Gui\saves - - - Gui\saves + Gui\save Gui\syntax highlighter @@ -723,6 +735,9 @@ Generated Files + + Gui\save + diff --git a/rpcs3/rpcs3_app.cpp b/rpcs3/rpcs3_app.cpp index 64afbe9684..1c58fb3cbf 100644 --- a/rpcs3/rpcs3_app.cpp +++ b/rpcs3/rpcs3_app.cpp @@ -6,6 +6,9 @@ #include "rpcs3qt/gs_frame.h" #include "rpcs3qt/gl_gs_frame.h" +#include "Emu/Cell/Modules/sceNpTrophy.h" +#include "rpcs3qt/trophy_notification_helper.h" + #include "Emu/Cell/Modules/cellSaveData.h" #include "rpcs3qt/save_data_dialog.h" @@ -241,6 +244,11 @@ void rpcs3_app::InitializeCallbacks() return std::make_unique(); }; + callbacks.get_trophy_notification_dialog = [=]() -> std::unique_ptr + { + return std::make_unique(gameWindow); + }; + callbacks.on_run = [=]() { OnEmulatorRun(); }; callbacks.on_pause = [=]() { OnEmulatorPause(); }; callbacks.on_resume = [=]() { OnEmulatorResume(); }; diff --git a/rpcs3/rpcs3qt/emu_settings.h b/rpcs3/rpcs3qt/emu_settings.h index 8f3fd176a7..000edd4578 100644 --- a/rpcs3/rpcs3qt/emu_settings.h +++ b/rpcs3/rpcs3qt/emu_settings.h @@ -82,6 +82,7 @@ public: StartOnBoot, StartGameFullscreen, ShowFPSInTitle, + ShowTrophyPopups, ShowWelcomeScreen, // Network @@ -236,6 +237,7 @@ private: {StartOnBoot, { "Miscellaneous", "Automatically start games after boot" }}, {StartGameFullscreen, { "Miscellaneous", "Start games in fullscreen mode"}}, {ShowFPSInTitle, { "Miscellaneous", "Show FPS counter in window title"}}, + {ShowTrophyPopups, { "Miscellaneous", "Show trophy popups"}}, {ShowWelcomeScreen, { "Miscellaneous", "Show Welcome Screen"}}, // Networking diff --git a/rpcs3/rpcs3qt/save_data_dialog.cpp b/rpcs3/rpcs3qt/save_data_dialog.cpp index a265cd7219..21ea7882ba 100644 --- a/rpcs3/rpcs3qt/save_data_dialog.cpp +++ b/rpcs3/rpcs3qt/save_data_dialog.cpp @@ -1,6 +1,3 @@ -#include "stdafx.h" -#include "Emu/Memory/Memory.h" - #include "save_data_dialog.h" #include "save_data_list_dialog.h" diff --git a/rpcs3/rpcs3qt/save_data_dialog.h b/rpcs3/rpcs3qt/save_data_dialog.h index 9aaad0d937..af59883a40 100644 --- a/rpcs3/rpcs3qt/save_data_dialog.h +++ b/rpcs3/rpcs3qt/save_data_dialog.h @@ -1,5 +1,7 @@ #pragma once +#include "stdafx.h" +#include "Emu/Memory/Memory.h" #include "Emu/Cell/Modules/cellSaveData.h" class save_data_dialog : public SaveDialogBase diff --git a/rpcs3/rpcs3qt/settings_dialog.cpp b/rpcs3/rpcs3qt/settings_dialog.cpp index ae3af41d5c..db2fdbd7a6 100644 --- a/rpcs3/rpcs3qt/settings_dialog.cpp +++ b/rpcs3/rpcs3qt/settings_dialog.cpp @@ -715,6 +715,9 @@ settings_dialog::settings_dialog(std::shared_ptr guiSettings, std: xemu_settings->EnhanceCheckBox(ui->showFPSInTitle, emu_settings::ShowFPSInTitle); SubscribeTooltip(ui->showFPSInTitle, json_emu_misc["showFPSInTitle"].toString()); + xemu_settings->EnhanceCheckBox(ui->showTrophyPopups, emu_settings::ShowTrophyPopups); + SubscribeTooltip(ui->showTrophyPopups, json_emu_misc["showTrophyPopups"].toString()); + if (game) { ui->gb_stylesheets->setEnabled(false); diff --git a/rpcs3/rpcs3qt/settings_dialog.ui b/rpcs3/rpcs3qt/settings_dialog.ui index add69c4473..69e2f67826 100644 --- a/rpcs3/rpcs3qt/settings_dialog.ui +++ b/rpcs3/rpcs3qt/settings_dialog.ui @@ -1161,6 +1161,13 @@ + + + + Show trophy popups + + + diff --git a/rpcs3/rpcs3qt/trophy_notification_frame.cpp b/rpcs3/rpcs3qt/trophy_notification_frame.cpp new file mode 100644 index 0000000000..5afaf5742f --- /dev/null +++ b/rpcs3/rpcs3qt/trophy_notification_frame.cpp @@ -0,0 +1,65 @@ +#include "trophy_notification_frame.h" + +#include +#include +#include + +static const int TROPHY_TIMEOUT_MS = 7500; + +inline QString qstr(const std::string& _in) { return QString::fromUtf8(_in.data(), _in.size()); } + +trophy_notification_frame::trophy_notification_frame(const std::vector& imgBuffer, const SceNpTrophyDetails& trophy, int height) : QWidget() +{ + setWindowFlags(Qt::FramelessWindowHint); + + // Fill the background with black + QPalette black_background; + black_background.setColor(QPalette::Window, Qt::black); + black_background.setColor(QPalette::WindowText, Qt::white); + + // Make the label + QLabel* trophyImgLabel = new QLabel; + trophyImgLabel->setAutoFillBackground(true); + trophyImgLabel->setPalette(black_background); + + QImage trophyImg; + if (imgBuffer.size() > 0 && trophyImg.loadFromData((uchar*)&imgBuffer[0], imgBuffer.size(), "PNG")) + { + trophyImg = trophyImg.scaledToHeight(height); // I might consider adding ability to change size since on hidpi this will be rather small. + trophyImgLabel->setPixmap(QPixmap::fromImage(trophyImg)); + } + else + { + // This looks hideous, but it's a good placeholder. + trophyImgLabel->setPixmap(QPixmap::fromImage(QImage(":/rpcs3.ico"))); + } + + QLabel* trophyName = new QLabel; + trophyName->setWordWrap(true); + trophyName->setAlignment(Qt::AlignCenter); + + QString trophyType = ""; + switch (trophy.trophyGrade) + { + case SCE_NP_TROPHY_GRADE_BRONZE: trophyType = "bronze"; break; + case SCE_NP_TROPHY_GRADE_SILVER: trophyType = "silver"; break; + case SCE_NP_TROPHY_GRADE_GOLD: trophyType = "gold"; break; + case SCE_NP_TROPHY_GRADE_PLATINUM: trophyType = "platinum"; break; + default: break; + } + + trophyName->setText("You have earned the " + trophyType + " trophy\n" + qstr(trophy.name)); + trophyName->setAutoFillBackground(true); + trophyName->setPalette(black_background); + + QHBoxLayout* globalLayout = new QHBoxLayout; + globalLayout->addWidget(trophyImgLabel); + globalLayout->addWidget(trophyName); + setLayout(globalLayout); + setPalette(black_background); + + // I may consider moving this code later to be done at a better location. + QTimer::singleShot(TROPHY_TIMEOUT_MS, [this]() { + deleteLater(); + }); +} diff --git a/rpcs3/rpcs3qt/trophy_notification_frame.h b/rpcs3/rpcs3qt/trophy_notification_frame.h new file mode 100644 index 0000000000..135729d816 --- /dev/null +++ b/rpcs3/rpcs3qt/trophy_notification_frame.h @@ -0,0 +1,13 @@ +#pragma once + +#include "stdafx.h" +#include "Emu/Memory/Memory.h" +#include "Emu/Cell/Modules/sceNpTrophy.h" + +#include + +class trophy_notification_frame : public QWidget +{ +public: + trophy_notification_frame(const std::vector& imgBuffer, const SceNpTrophyDetails& trophy, int height); +}; \ No newline at end of file diff --git a/rpcs3/rpcs3qt/trophy_notification_helper.cpp b/rpcs3/rpcs3qt/trophy_notification_helper.cpp new file mode 100644 index 0000000000..c64a62fc29 --- /dev/null +++ b/rpcs3/rpcs3qt/trophy_notification_helper.cpp @@ -0,0 +1,14 @@ +#include "trophy_notification_helper.h" + +#include "trophy_notification_frame.h" + +s32 trophy_notification_helper::ShowTrophyNotification(const SceNpTrophyDetails& trophy, const std::vector& trophy_icon_buffer) +{ + trophy_notification_frame* trophy_notification = new trophy_notification_frame(trophy_icon_buffer, trophy, m_game_window->frameGeometry().height()/10); + + // Move notification to upper lefthand corner + trophy_notification->move(m_game_window->mapToGlobal(QPoint(0, 0))); + trophy_notification->show(); + + return 0; +} diff --git a/rpcs3/rpcs3qt/trophy_notification_helper.h b/rpcs3/rpcs3qt/trophy_notification_helper.h new file mode 100644 index 0000000000..4f3849e98c --- /dev/null +++ b/rpcs3/rpcs3qt/trophy_notification_helper.h @@ -0,0 +1,16 @@ +#pragma once + +#include "stdafx.h" +#include "Emu/Memory/Memory.h" +#include "Emu/Cell/Modules/sceNpTrophy.h" + +#include + +class trophy_notification_helper : public TrophyNotificationBase +{ +public: + trophy_notification_helper(QWindow* game_window) : m_game_window(game_window) { }; + s32 ShowTrophyNotification(const SceNpTrophyDetails& trophy, const std::vector& trophy_icon_buffer) override; +private: + QWindow* m_game_window; +};