Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/release-binaries.yml
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ jobs:
-DYUZU_USE_BUNDLED_QT=OFF
-DYUZU_USE_BUNDLED_SDL3=ON
-DENABLE_UPDATE_CHECKER=OFF
-DUSE_DISCORD_PRESENCE=OFF
-DUSE_DISCORD_PRESENCE=ON
-DCMAKE_C_COMPILER_LAUNCHER=sccache
-DCMAKE_CXX_COMPILER_LAUNCHER=sccache

Expand Down
5 changes: 5 additions & 0 deletions src/core/loader/deconstructed_rom_directory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "common/logging.h"
#include "common/random.h"
#include "common/settings.h"
#include "common/string_util.h"
#include "core/core.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/control_metadata.h"
Expand Down Expand Up @@ -264,6 +265,9 @@ AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys
if (nacp_file != nullptr) {
control.emplace(nacp_file);
name = control->GetApplicationName();
} else if (sidecar_dirs.size() > 1 && Common::ToLower(file_dir->GetName()) == "exefs") {
// Without control data, name the game after its folder like the game list does
name = sidecar_dirs.back()->GetName();
}

romfs_exefs_dir = file_dir;
Expand Down Expand Up @@ -522,3 +526,4 @@ ResultStatus AppLoader_DeconstructedRomDirectory::ReadNSOModules(Modules& out_mo
}

} // namespace Loader

65 changes: 60 additions & 5 deletions src/qt_common/discord/discord_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later

#include <chrono>
#include <optional>
#include <string>

#include <QEventLoop>
Expand All @@ -17,6 +18,8 @@
#include "common/common_types.h"
#include "common/string_util.h"
#include "core/core.h"
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/patch_manager.h"
#include "core/loader/loader.h"

#include "discord_impl.h"
Expand All @@ -27,11 +30,37 @@

namespace DiscordRPC {

namespace {

// Community-maintained names and covers, keyed by title ID: <TITLEID>.txt and <TITLEID>.png
constexpr char COVERS_HOST[] = "raw.githubusercontent.com";
constexpr char COVERS_PATH[] = "/mushymeow/eglantine-covers/main/";

std::optional<std::string> FetchFromCovers(const std::string& file_name, bool want_body) {
httplib::SSLClient client(COVERS_HOST);
client.set_connection_timeout(3);
client.set_read_timeout(3);
client.set_follow_location(true);

#ifdef YUZU_BUNDLED_OPENSSL
client.load_ca_cert_store(kCert, sizeof(kCert));
#endif

const std::string path = std::string{COVERS_PATH} + file_name;
const auto res = want_body ? client.Get(path) : client.Head(path);
if (!res || res->status != 200) {
return std::nullopt;
}
return res->body;
}

} // namespace

DiscordImpl::DiscordImpl(Core::System& system_) : system{system_} {
DiscordEventHandlers handlers{};
// The number is the client ID for Eden, it's used for images and the
// The number is the client ID for Eglantine, it's used for images and the
// application name
Discord_Initialize("1397286652128264252", &handlers, 1, nullptr);
Discord_Initialize("1566479696022081606", &handlers, 1, nullptr);
}

DiscordImpl::~DiscordImpl() {
Expand Down Expand Up @@ -71,8 +100,7 @@ std::string DiscordImpl::GetGameString(const std::string& title) {

static constexpr char DEFAULT_DISCORD_TEXT[] = "Eglantine is an emulator for the Nintendo Switch";
static constexpr char DEFAULT_DISCORD_IMAGE[] =
"https://git.eden-emu.dev/eden-emu/eden/raw/branch/master/dist/qt_themes/default/icons/256x256/"
"eden.png";
"https://raw.githubusercontent.com/Djihads80/Eglantine/refs/heads/nx2/dist/Eglantine.png";

void DiscordImpl::UpdateGameStatus(std::string_view game_url, bool has_boxart) {
const std::string url = std::string{has_boxart ? game_url : DEFAULT_DISCORD_IMAGE};
Expand All @@ -87,15 +115,41 @@ void DiscordImpl::UpdateGameStatus(std::string_view game_url, bool has_boxart) {
presence.smallImageText = DEFAULT_DISCORD_TEXT;
presence.state = game_title.c_str();
presence.status_display_type = DiscordStatusDisplayType_State;
presence.details = "Currently in game";
const std::string details =
game_version.empty() ? "Currently in game" : fmt::format("Version {}", game_version);
presence.details = details.c_str();
presence.startTimestamp = start_time;
Discord_UpdatePresence(&presence);
}

void DiscordImpl::Update() {
if (system.IsPoweredOn()) {
game_title.clear();
system.GetAppLoader().ReadTitle(game_title);

// Read the version from the patched metadata so installed updates are reflected
const FileSys::PatchManager pm(system.GetApplicationProcessProgramID(),
system.GetFileSystemController(),
system.GetContentProvider());
const auto metadata = pm.GetControlMetadata();
game_version = metadata.first != nullptr ? metadata.first->GetVersionString() : "";
if (game_version.empty()) {
// Loose dumps without control data can still report a version from their RomFS
system.GetAppLoader().ReadDisplayVersion(game_version);
}

const auto title_id = fmt::format("{:016X}", system.GetApplicationProcessProgramID());
if (const auto name = FetchFromCovers(title_id + ".txt", true)) {
if (auto stripped = Common::StripSpaces(*name); !stripped.empty()) {
game_title = std::move(stripped);
}
}
if (FetchFromCovers(title_id + ".png", false)) {
UpdateGameStatus(fmt::format("https://{}{}{}.png", COVERS_HOST, COVERS_PATH, title_id),
true);
return;
}

// Used to format Icon URL for yuzu website game compatibility page
std::string icon_name = GetGameString(game_title);
auto const game_url = fmt::format(
Expand Down Expand Up @@ -134,3 +188,4 @@ void DiscordImpl::Update() {
Discord_UpdatePresence(&presence);
}
} // namespace DiscordRPC

2 changes: 2 additions & 0 deletions src/qt_common/discord/discord_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ class DiscordImpl : public DiscordInterface {
void UpdateGameStatus(std::string_view game_url, bool use_default);

std::string game_title{};
std::string game_version{};
Core::System& system;
};

} // namespace DiscordRPC