diff --git a/src/citron/discord_impl.cpp b/src/citron/discord_impl.cpp index 6391fdae4..f1607de44 100644 --- a/src/citron/discord_impl.cpp +++ b/src/citron/discord_impl.cpp @@ -2,116 +2,120 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include + #include #include + #include + #include #include + #include -#include "common/common_types.h" -#include "common/string_util.h" -#include "core/core.h" -#include "core/loader/loader.h" #include "citron/discord_impl.h" + #include "citron/uisettings.h" +#include "common/common_types.h" + +#include "common/string_util.h" + +#include "core/core.h" + +#include "core/loader/loader.h" + namespace DiscordRPC { -DiscordImpl::DiscordImpl(Core::System& system_) : system{system_} { - DiscordEventHandlers handlers{}; - // The number is the client ID for citron, it's used for images and the - // application name - Discord_Initialize("712465656758665259", &handlers, 1, nullptr); +DiscordImpl::DiscordImpl(Core::System & system_): system { +system_ +} { +DiscordEventHandlers handlers {}; +// The number is the client ID for citron, it's used for images and the +// application name +Discord_Initialize("1322413013248118888", & handlers, 1, nullptr); } DiscordImpl::~DiscordImpl() { - Discord_ClearPresence(); - Discord_Shutdown(); +Discord_ClearPresence(); +Discord_Shutdown(); } void DiscordImpl::Pause() { - Discord_ClearPresence(); -} - -std::string DiscordImpl::GetGameString(const std::string& title) { - // Convert to lowercase - std::string icon_name = Common::ToLower(title); - - // Replace spaces with dashes - std::replace(icon_name.begin(), icon_name.end(), ' ', '-'); - - // Remove non-alphanumeric characters but keep dashes - std::erase_if(icon_name, [](char c) { return !std::isalnum(c) && c != '-'; }); - - // Remove dashes from the start and end of the string - icon_name.erase(icon_name.begin(), std::find_if(icon_name.begin(), icon_name.end(), - [](int ch) { return ch != '-'; })); - icon_name.erase( - std::find_if(icon_name.rbegin(), icon_name.rend(), [](int ch) { return ch != '-'; }).base(), - icon_name.end()); - - // Remove double dashes - icon_name.erase(std::unique(icon_name.begin(), icon_name.end(), - [](char a, char b) { return a == '-' && b == '-'; }), - icon_name.end()); - - return icon_name; +Discord_ClearPresence(); } void DiscordImpl::UpdateGameStatus(bool use_default) { - const std::string default_text = "citron is an emulator for the Nintendo Switch"; - const std::string default_image = "citron_logo"; - const std::string url = use_default ? default_image : game_url; - s64 start_time = std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()) - .count(); - DiscordRichPresence presence{}; +const std::string default_text = "Citron Is A Homebrew Emulator For The Nintendo Switch"; +const std::string default_image = "citron_logo"; +const std::string tinfoil_base_url = "https://tinfoil.media/ti/"; +s64 start_time = std::chrono::duration_cast < std::chrono::seconds > ( +std::chrono::system_clock::now().time_since_epoch()) +.count(); +DiscordRichPresence presence {}; - presence.largeImageKey = url.c_str(); - presence.largeImageText = game_title.c_str(); - presence.smallImageKey = default_image.c_str(); - presence.smallImageText = default_text.c_str(); - presence.state = game_title.c_str(); - presence.details = "Currently in game"; - presence.startTimestamp = start_time; - Discord_UpdatePresence(&presence); +// Store the URL string to prevent it from being destroyed +if (!game_title_id.empty()) { +game_url = fmt::format("{}{}/128/128", tinfoil_base_url, game_title_id); +// Make sure the string stays alive for the duration of the presence +cached_url = game_url; +presence.largeImageKey = cached_url.c_str(); +} else { +presence.largeImageKey = cached_url.c_str(); +} + +presence.largeImageText = game_title.c_str(); +presence.smallImageKey = default_image.c_str(); +presence.smallImageText = default_text.c_str(); +// Remove title ID from display, only show game title +presence.state = game_title.c_str(); +presence.details = "Currently in game"; +presence.startTimestamp = start_time; +Discord_UpdatePresence( & presence); } void DiscordImpl::Update() { - const std::string default_text = "citron is an emulator for the Nintendo Switch"; - const std::string default_image = "citron_logo"; +const std::string default_text = "Citron Is A Homebrew Emulator For The Nintendo Switch"; +const std::string default_image = "citron_logo"; - if (system.IsPoweredOn()) { - system.GetAppLoader().ReadTitle(game_title); +if (system.IsPoweredOn()) { +system.GetAppLoader().ReadTitle(game_title); +system.GetAppLoader().ReadProgramId(program_id); +game_title_id = fmt::format("{:016X}", program_id); - // Used to format Icon URL for citron website game compatibility page - std::string icon_name = GetGameString(game_title); - game_url = fmt::format("https://citron-emu.org/images/game/boxart/{}.png", icon_name); +fmt::print("Title ID: {}\n", game_title_id); - QNetworkAccessManager manager; - QNetworkRequest request; - request.setUrl(QUrl(QString::fromStdString(game_url))); - request.setTransferTimeout(3000); - QNetworkReply* reply = manager.head(request); - QEventLoop request_event_loop; - QObject::connect(reply, &QNetworkReply::finished, &request_event_loop, &QEventLoop::quit); - request_event_loop.exec(); - UpdateGameStatus(reply->error()); - return; - } +QNetworkAccessManager manager; +QNetworkRequest request; +request.setUrl(QUrl(QString::fromStdString( +fmt::format("https://tinfoil.media/ti/{}/128/128", game_title_id)))); +request.setTransferTimeout(10000); +QNetworkReply * reply = manager.head(request); +QEventLoop request_event_loop; +QObject::connect(reply, & QNetworkReply::finished, & request_event_loop, & QEventLoop::quit); +request_event_loop.exec(); - s64 start_time = std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()) - .count(); +if (reply -> error()) { +fmt::print("Failed to fetch game image: {} ({})\n", reply -> errorString().toStdString(), +program_id); +} - DiscordRichPresence presence{}; - presence.largeImageKey = default_image.c_str(); - presence.largeImageText = default_text.c_str(); - presence.details = "Currently not in game"; - presence.startTimestamp = start_time; - Discord_UpdatePresence(&presence); +UpdateGameStatus(reply -> error()); +reply -> deleteLater(); +return; +} + +s64 start_time = std::chrono::duration_cast < std::chrono::seconds > ( +std::chrono::system_clock::now().time_since_epoch()) +.count(); + +DiscordRichPresence presence {}; +presence.largeImageKey = default_image.c_str(); +presence.largeImageText = default_text.c_str(); +presence.details = "Currently not in game"; +presence.startTimestamp = start_time; +Discord_UpdatePresence( & presence); } } // namespace DiscordRPC diff --git a/src/citron/discord_impl.h b/src/citron/discord_impl.h index 04edbc5a1..738cb1f61 100644 --- a/src/citron/discord_impl.h +++ b/src/citron/discord_impl.h @@ -11,22 +11,22 @@ class System; namespace DiscordRPC { -class DiscordImpl : public DiscordInterface { -public: - DiscordImpl(Core::System& system_); - ~DiscordImpl() override; +class DiscordImpl: public DiscordInterface { +public: DiscordImpl(Core::System & system_); +~DiscordImpl() override; - void Pause() override; - void Update() override; +void Pause() override; +void Update() override; -private: - std::string GetGameString(const std::string& title); - void UpdateGameStatus(bool use_default); +private: void UpdateGameStatus(bool use_default); - std::string game_url{}; - std::string game_title{}; +std::string game_url {}; +std::string game_title {}; +std::string game_title_id {}; +std::string cached_url; - Core::System& system; +Core::System & system; +u64 program_id = 0; }; } // namespace DiscordRPC