diff --git a/Source/Core/Core/AchievementManager.cpp b/Source/Core/Core/AchievementManager.cpp index f9ae5fadb5..dd81a6e871 100644 --- a/Source/Core/Core/AchievementManager.cpp +++ b/Source/Core/Core/AchievementManager.cpp @@ -23,11 +23,10 @@ #include "Core/System.h" #include "DiscIO/Blob.h" #include "UICommon/DiscordPresence.h" +#include "VideoCommon/Assets/CustomTextureData.h" #include "VideoCommon/OnScreenDisplay.h" #include "VideoCommon/VideoEvents.h" -static std::unique_ptr DecodeBadgeToOSDIcon(const AchievementManager::Badge& badge); - AchievementManager& AchievementManager::GetInstance() { static AchievementManager s_instance; @@ -306,9 +305,9 @@ const AchievementManager::BadgeStatus& AchievementManager::GetGameBadge() const const AchievementManager::BadgeStatus& AchievementManager::GetAchievementBadge(AchievementId id, bool locked) const { - auto& badge_list = locked ? m_locked_badges : m_locked_badges; - auto itr = badge_list.find(id); - return (itr == badge_list.end()) ? m_default_badge : itr->second; + auto& badge_list = locked ? m_locked_badges : m_unlocked_badges; + // Brief regression - difficult to return a default BadgeStatus, will be fixed in later commit + return badge_list.find(id)->second; } const AchievementManager::LeaderboardStatus* @@ -330,7 +329,7 @@ AchievementManager::RichPresence AchievementManager::GetRichPresence() const return m_rich_presence; } -const AchievementManager::NamedIconMap& AchievementManager::GetChallengeIcons() const +const AchievementManager::NamedBadgeMap& AchievementManager::GetChallengeIcons() const { return m_active_challenges; } @@ -639,8 +638,7 @@ void AchievementManager::DisplayWelcomeMessage() rc_client_get_hardcore_enabled(m_client) ? OSD::Color::YELLOW : OSD::Color::CYAN; if (!m_game_badge.name.empty()) { - OSD::AddMessage("", OSD::Duration::VERY_LONG, OSD::Color::GREEN, - DecodeBadgeToOSDIcon(m_game_badge.badge)); + OSD::AddMessage("", OSD::Duration::VERY_LONG, OSD::Color::GREEN, &m_game_badge.badge); } auto info = rc_client_get_game_info(m_client); if (!info) @@ -671,15 +669,14 @@ void AchievementManager::DisplayWelcomeMessage() void AchievementManager::HandleAchievementTriggeredEvent(const rc_client_event_t* client_event) { - OSD::AddMessage(fmt::format("Unlocked: {} ({})", client_event->achievement->title, - client_event->achievement->points), - OSD::Duration::VERY_LONG, - (rc_client_get_hardcore_enabled(AchievementManager::GetInstance().m_client)) ? - OSD::Color::YELLOW : - OSD::Color::CYAN, - DecodeBadgeToOSDIcon(AchievementManager::GetInstance() - .m_unlocked_badges[client_event->achievement->id] - .badge)); + OSD::AddMessage( + fmt::format("Unlocked: {} ({})", client_event->achievement->title, + client_event->achievement->points), + OSD::Duration::VERY_LONG, + (rc_client_get_hardcore_enabled(AchievementManager::GetInstance().m_client)) ? + OSD::Color::YELLOW : + OSD::Color::CYAN, + &AchievementManager::GetInstance().m_unlocked_badges[client_event->achievement->id].badge); } void AchievementManager::HandleLeaderboardStartedEvent(const rc_client_event_t* client_event) @@ -741,7 +738,7 @@ void AchievementManager::HandleAchievementChallengeIndicatorShowEvent( unlocked_iter != unlocked_badges.end()) { AchievementManager::GetInstance().m_active_challenges[client_event->achievement->badge_name] = - DecodeBadgeToOSDIcon(unlocked_iter->second.badge); + &unlocked_iter->second.badge; } } @@ -755,12 +752,11 @@ void AchievementManager::HandleAchievementChallengeIndicatorHideEvent( void AchievementManager::HandleAchievementProgressIndicatorShowEvent( const rc_client_event_t* client_event) { - OSD::AddMessage(fmt::format("{} {}", client_event->achievement->title, - client_event->achievement->measured_progress), - OSD::Duration::SHORT, OSD::Color::GREEN, - DecodeBadgeToOSDIcon(AchievementManager::GetInstance() - .m_unlocked_badges[client_event->achievement->id] - .badge)); + OSD::AddMessage( + fmt::format("{} {}", client_event->achievement->title, + client_event->achievement->measured_progress), + OSD::Duration::SHORT, OSD::Color::GREEN, + &AchievementManager::GetInstance().m_unlocked_badges[client_event->achievement->id].badge); } void AchievementManager::HandleGameCompletedEvent(const rc_client_event_t* client_event, @@ -777,7 +773,7 @@ void AchievementManager::HandleGameCompletedEvent(const rc_client_event_t* clien OSD::AddMessage(fmt::format("Congratulations! {} has {} {}", user_info->display_name, hardcore ? "mastered" : "completed", game_info->title), OSD::Duration::VERY_LONG, hardcore ? OSD::Color::YELLOW : OSD::Color::CYAN, - DecodeBadgeToOSDIcon(AchievementManager::GetInstance().m_game_badge.badge)); + &AchievementManager::GetInstance().m_game_badge.badge); } void AchievementManager::HandleResetEvent(const rc_client_event_t* client_event) @@ -792,20 +788,6 @@ void AchievementManager::HandleServerErrorEvent(const rc_client_event_t* client_ client_event->server_error->api, client_event->server_error->error_message); } -static std::unique_ptr DecodeBadgeToOSDIcon(const AchievementManager::Badge& badge) -{ - if (badge.empty()) - return nullptr; - - auto icon = std::make_unique(); - if (!Common::LoadPNG(badge, &icon->rgba_data, &icon->width, &icon->height)) - { - ERROR_LOG_FMT(ACHIEVEMENTS, "Error decoding badge."); - return nullptr; - } - return icon; -} - void AchievementManager::Request(const rc_api_request_t* request, rc_client_server_callback_t callback, void* callback_data, rc_client_t* client) @@ -914,7 +896,6 @@ void AchievementManager::FetchBadge(AchievementManager::BadgeStatus* badge, u32 } rc_api_destroy_request(&api_request); - fetched_badge = std::move(*http_response); INFO_LOG_FMT(ACHIEVEMENTS, "Successfully downloaded badge id {}.", name_to_fetch); std::lock_guard lg{m_lock}; @@ -923,7 +904,12 @@ void AchievementManager::FetchBadge(AchievementManager::BadgeStatus* badge, u32 INFO_LOG_FMT(ACHIEVEMENTS, "Requested outdated badge id {}.", name_to_fetch); return; } - badge->badge = std::move(fetched_badge); + + if (!LoadPNGTexture(&badge->badge, *http_response)) + { + ERROR_LOG_FMT(ACHIEVEMENTS, "Default game badge '{}' failed to load", + DEFAULT_GAME_BADGE_FILENAME); + } badge->name = std::move(name_to_fetch); m_update_callback(callback_data); diff --git a/Source/Core/Core/AchievementManager.h b/Source/Core/Core/AchievementManager.h index 5d133e023f..e7aaa672e3 100644 --- a/Source/Core/Core/AchievementManager.h +++ b/Source/Core/Core/AchievementManager.h @@ -22,6 +22,7 @@ #include "Common/HttpRequest.h" #include "Common/WorkQueueThread.h" #include "DiscIO/Volume.h" +#include "VideoCommon/Assets/CustomTextureData.h" namespace Core { @@ -29,11 +30,6 @@ class CPUThreadGuard; class System; } // namespace Core -namespace OSD -{ -struct Icon; -} - class AchievementManager { public: @@ -47,8 +43,8 @@ public: using LeaderboardRank = u32; static constexpr size_t RP_SIZE = 256; using RichPresence = std::array; - using Badge = std::vector; - using NamedIconMap = std::map, std::less<>>; + using Badge = VideoCommon::CustomTextureData::ArraySlice::Level; + using NamedBadgeMap = std::unordered_map; static constexpr size_t MAX_DISPLAYED_LBOARDS = 4; struct BadgeStatus @@ -116,7 +112,7 @@ public: const BadgeStatus& GetAchievementBadge(AchievementId id, bool locked) const; const LeaderboardStatus* GetLeaderboardInfo(AchievementId leaderboard_id); RichPresence GetRichPresence() const; - const NamedIconMap& GetChallengeIcons() const; + const NamedBadgeMap& GetChallengeIcons() const; std::vector GetActiveLeaderboards() const; void DoState(PointerWrap& p); @@ -134,8 +130,6 @@ private: std::unique_ptr volume; }; - const BadgeStatus m_default_badge; - static void* FilereaderOpenByFilepath(const char* path_utf8); static void* FilereaderOpenByVolume(const char* path_utf8); static void FilereaderSeek(void* file_handle, int64_t offset, int origin); @@ -200,7 +194,7 @@ private: std::chrono::steady_clock::time_point m_last_rp_time = std::chrono::steady_clock::now(); std::unordered_map m_leaderboard_map; - NamedIconMap m_active_challenges; + NamedBadgeMap m_active_challenges; std::vector m_active_leaderboards; Common::WorkQueueThread> m_queue; diff --git a/Source/Core/DolphinQt/Achievements/AchievementBox.cpp b/Source/Core/DolphinQt/Achievements/AchievementBox.cpp index f563f9cfc9..8b4bccbf31 100644 --- a/Source/Core/DolphinQt/Achievements/AchievementBox.cpp +++ b/Source/Core/DolphinQt/Achievements/AchievementBox.cpp @@ -63,15 +63,13 @@ void AchievementBox::UpdateData() color = AchievementManager::BLUE; if (badge.name != "") { - QImage i_badge{}; - if (i_badge.loadFromData(&badge.badge.front(), static_cast(badge.badge.size()))) - { - m_badge->setPixmap(QPixmap::fromImage(i_badge).scaled(64, 64, Qt::KeepAspectRatio, - Qt::SmoothTransformation)); - m_badge->adjustSize(); - m_badge->setStyleSheet( - QStringLiteral("border: 4px solid %1").arg(QtUtils::FromStdString(color))); - } + QImage i_badge(&badge.badge.data.front(), badge.badge.width, badge.badge.height, + QImage::Format_RGBA8888); + m_badge->setPixmap( + QPixmap::fromImage(i_badge).scaled(64, 64, Qt::KeepAspectRatio, Qt::SmoothTransformation)); + m_badge->adjustSize(); + m_badge->setStyleSheet( + QStringLiteral("border: 4px solid %1").arg(QtUtils::FromStdString(color))); } else { diff --git a/Source/Core/DolphinQt/Achievements/AchievementHeaderWidget.cpp b/Source/Core/DolphinQt/Achievements/AchievementHeaderWidget.cpp index e2fc6addbc..ffa0bf35e0 100644 --- a/Source/Core/DolphinQt/Achievements/AchievementHeaderWidget.cpp +++ b/Source/Core/DolphinQt/Achievements/AchievementHeaderWidget.cpp @@ -76,15 +76,13 @@ void AchievementHeaderWidget::UpdateData() m_user_icon->setText({}); if (!player_badge.name.empty()) { - QImage i_user_icon{}; - if (i_user_icon.loadFromData(&player_badge.badge.front(), (int)player_badge.badge.size())) - { - m_user_icon->setPixmap(QPixmap::fromImage(i_user_icon) - .scaled(64, 64, Qt::KeepAspectRatio, Qt::SmoothTransformation)); - m_user_icon->adjustSize(); - m_user_icon->setStyleSheet(QStringLiteral("border: 4px solid transparent")); - m_user_icon->setVisible(true); - } + QImage i_user_icon(&player_badge.badge.data.front(), player_badge.badge.width, + player_badge.badge.height, QImage::Format_RGBA8888); + m_user_icon->setPixmap(QPixmap::fromImage(i_user_icon) + .scaled(64, 64, Qt::KeepAspectRatio, Qt::SmoothTransformation)); + m_user_icon->adjustSize(); + m_user_icon->setStyleSheet(QStringLiteral("border: 4px solid transparent")); + m_user_icon->setVisible(true); } m_game_icon->setVisible(false); m_game_icon->clear(); @@ -97,22 +95,20 @@ void AchievementHeaderWidget::UpdateData() if (!game_badge.name.empty()) { - QImage i_game_icon{}; - if (i_game_icon.loadFromData(&game_badge.badge.front(), (int)game_badge.badge.size())) + QImage i_game_icon(&game_badge.badge.data.front(), game_badge.badge.width, + game_badge.badge.height, QImage::Format_RGBA8888); + m_game_icon->setPixmap(QPixmap::fromImage(i_game_icon) + .scaled(64, 64, Qt::KeepAspectRatio, Qt::SmoothTransformation)); + m_game_icon->adjustSize(); + std::string_view color = AchievementManager::GRAY; + if (game_summary.num_core_achievements == game_summary.num_unlocked_achievements) { - m_game_icon->setPixmap(QPixmap::fromImage(i_game_icon) - .scaled(64, 64, Qt::KeepAspectRatio, Qt::SmoothTransformation)); - m_game_icon->adjustSize(); - std::string_view color = AchievementManager::GRAY; - if (game_summary.num_core_achievements == game_summary.num_unlocked_achievements) - { - color = - instance.IsHardcoreModeActive() ? AchievementManager::GOLD : AchievementManager::BLUE; - } - m_game_icon->setStyleSheet( - QStringLiteral("border: 4px solid %1").arg(QtUtils::FromStdString(color))); - m_game_icon->setVisible(true); + color = + instance.IsHardcoreModeActive() ? AchievementManager::GOLD : AchievementManager::BLUE; } + m_game_icon->setStyleSheet( + QStringLiteral("border: 4px solid %1").arg(QtUtils::FromStdString(color))); + m_game_icon->setVisible(true); } m_name->setText(tr("%1 is playing %2").arg(user_name).arg(game_name)); diff --git a/Source/Core/VideoCommon/Assets/CustomTextureData.cpp b/Source/Core/VideoCommon/Assets/CustomTextureData.cpp index 23af429d11..d30532142a 100644 --- a/Source/Core/VideoCommon/Assets/CustomTextureData.cpp +++ b/Source/Core/VideoCommon/Assets/CustomTextureData.cpp @@ -574,6 +574,14 @@ bool LoadPNGTexture(CustomTextureData::ArraySlice::Level* level, const std::stri std::vector buffer(file.GetSize()); file.ReadBytes(buffer.data(), file.GetSize()); + return LoadPNGTexture(level, buffer); +} + +bool LoadPNGTexture(CustomTextureData::ArraySlice::Level* level, const std::vector& buffer) +{ + if (!level) [[unlikely]] + return false; + if (!Common::LoadPNG(buffer, &level->data, &level->width, &level->height)) return false; diff --git a/Source/Core/VideoCommon/Assets/CustomTextureData.h b/Source/Core/VideoCommon/Assets/CustomTextureData.h index fe15c05eaa..32607f4e88 100644 --- a/Source/Core/VideoCommon/Assets/CustomTextureData.h +++ b/Source/Core/VideoCommon/Assets/CustomTextureData.h @@ -33,4 +33,5 @@ bool LoadDDSTexture(CustomTextureData* texture, const std::string& filename); bool LoadDDSTexture(CustomTextureData::ArraySlice::Level* level, const std::string& filename, u32 mip_level); bool LoadPNGTexture(CustomTextureData::ArraySlice::Level* level, const std::string& filename); +bool LoadPNGTexture(CustomTextureData::ArraySlice::Level* level, const std::vector& buffer); } // namespace VideoCommon diff --git a/Source/Core/VideoCommon/OnScreenDisplay.cpp b/Source/Core/VideoCommon/OnScreenDisplay.cpp index bfa8bc5321..c24fee6538 100644 --- a/Source/Core/VideoCommon/OnScreenDisplay.cpp +++ b/Source/Core/VideoCommon/OnScreenDisplay.cpp @@ -20,6 +20,7 @@ #include "VideoCommon/AbstractGfx.h" #include "VideoCommon/AbstractTexture.h" +#include "VideoCommon/Assets/CustomTextureData.h" #include "VideoCommon/TextureConfig.h" namespace OSD @@ -36,8 +37,9 @@ static std::atomic s_obscured_pixels_top = 0; struct Message { Message() = default; - Message(std::string text_, u32 duration_, u32 color_, std::unique_ptr icon_ = nullptr) - : text(std::move(text_)), duration(duration_), color(color_), icon(std::move(icon_)) + Message(std::string text_, u32 duration_, u32 color_, + const VideoCommon::CustomTextureData::ArraySlice::Level* icon_ = nullptr) + : text(std::move(text_)), duration(duration_), color(color_), icon(icon_) { timer.Start(); } @@ -48,7 +50,7 @@ struct Message bool ever_drawn = false; bool should_discard = false; u32 color = 0; - std::unique_ptr icon; + const VideoCommon::CustomTextureData::ArraySlice::Level* icon; std::unique_ptr texture; }; static std::multimap s_messages; @@ -95,13 +97,13 @@ static float DrawMessage(int index, Message& msg, const ImVec2& position, int ti msg.texture = g_gfx->CreateTexture(tex_config); if (msg.texture) { - msg.texture->Load(0, width, height, width, msg.icon->rgba_data.data(), + msg.texture->Load(0, width, height, width, msg.icon->data.data(), sizeof(u32) * width * height); } else { // don't try again next time - msg.icon.reset(); + msg.icon = nullptr; } } @@ -127,7 +129,7 @@ static float DrawMessage(int index, Message& msg, const ImVec2& position, int ti } void AddTypedMessage(MessageType type, std::string message, u32 ms, u32 argb, - std::unique_ptr icon) + const VideoCommon::CustomTextureData::ArraySlice::Level* icon) { std::lock_guard lock{s_messages_mutex}; @@ -141,7 +143,8 @@ void AddTypedMessage(MessageType type, std::string message, u32 ms, u32 argb, s_messages.emplace(type, Message(std::move(message), ms, argb, std::move(icon))); } -void AddMessage(std::string message, u32 ms, u32 argb, std::unique_ptr icon) +void AddMessage(std::string message, u32 ms, u32 argb, + const VideoCommon::CustomTextureData::ArraySlice::Level* icon) { std::lock_guard lock{s_messages_mutex}; s_messages.emplace(MessageType::Typeless, Message(std::move(message), ms, argb, std::move(icon))); diff --git a/Source/Core/VideoCommon/OnScreenDisplay.h b/Source/Core/VideoCommon/OnScreenDisplay.h index f566eb0bf1..33d8662744 100644 --- a/Source/Core/VideoCommon/OnScreenDisplay.h +++ b/Source/Core/VideoCommon/OnScreenDisplay.h @@ -10,6 +10,8 @@ #include "Common/CommonTypes.h" +#include "VideoCommon/Assets/CustomTextureData.h" + namespace OSD { enum class MessageType @@ -37,18 +39,12 @@ constexpr u32 NORMAL = 5000; constexpr u32 VERY_LONG = 10000; }; // namespace Duration -struct Icon -{ - std::vector rgba_data; - u32 width = 0; - u32 height = 0; -}; // struct Icon - // On-screen message display (colored yellow by default) void AddMessage(std::string message, u32 ms = Duration::SHORT, u32 argb = Color::YELLOW, - std::unique_ptr icon = nullptr); + const VideoCommon::CustomTextureData::ArraySlice::Level* icon = nullptr); void AddTypedMessage(MessageType type, std::string message, u32 ms = Duration::SHORT, - u32 argb = Color::YELLOW, std::unique_ptr icon = nullptr); + u32 argb = Color::YELLOW, + const VideoCommon::CustomTextureData::ArraySlice::Level* icon = nullptr); // Draw the current messages on the screen. Only call once per frame. void DrawMessages(); diff --git a/Source/Core/VideoCommon/OnScreenUI.cpp b/Source/Core/VideoCommon/OnScreenUI.cpp index 8f85db85b0..1231851c9a 100644 --- a/Source/Core/VideoCommon/OnScreenUI.cpp +++ b/Source/Core/VideoCommon/OnScreenUI.cpp @@ -358,7 +358,7 @@ void OnScreenUI::DrawChallengesAndLeaderboards() TextureConfig tex_config(width, height, 1, 1, 1, AbstractTextureFormat::RGBA8, 0, AbstractTextureType::Texture_2DArray); auto res = m_challenge_texture_map.insert_or_assign(name, g_gfx->CreateTexture(tex_config)); - res.first->second->Load(0, width, height, width, icon->rgba_data.data(), + res.first->second->Load(0, width, height, width, icon->data.data(), sizeof(u32) * width * height); } for (auto& [name, texture] : m_challenge_texture_map)