CodeWidgets: Show code approval in Hardcore mode

When Hardcore mode is enabled, show an icon for each code in
ARCodeWidget and GeckoCodeWidget indicating whether it's an approved
code or not.
This commit is contained in:
Dentomologist 2025-08-24 11:12:12 -07:00
commit 44f6743a5b
6 changed files with 123 additions and 41 deletions

View file

@ -460,15 +460,18 @@ void AchievementManager::FilterApprovedIni(std::vector<T>& codes, const std::str
for (auto& code : codes) for (auto& code : codes)
{ {
if (code.enabled && !CheckApprovedCode(code, game_id, revision)) if (code.enabled && !IsApprovedCode(code, game_id, revision))
code.enabled = false; code.enabled = false;
} }
} }
template <typename T> template <typename T>
bool AchievementManager::CheckApprovedCode(const T& code, const std::string& game_id, bool AchievementManager::ShouldCodeBeActivated(const T& code, const std::string& game_id,
u16 revision) const u16 revision) const
{ {
if (!code.enabled)
return false;
if (!IsHardcoreModeActive()) if (!IsHardcoreModeActive())
return true; return true;
@ -478,33 +481,42 @@ bool AchievementManager::CheckApprovedCode(const T& code, const std::string& gam
INFO_LOG_FMT(ACHIEVEMENTS, "Verifying code {}", code.name); INFO_LOG_FMT(ACHIEVEMENTS, "Verifying code {}", code.name);
bool verified = false; if (IsApprovedCode(code, game_id, revision))
return true;
auto hash = Common::SHA1::DigestToString(GetCodeHash(code)); OSD::AddMessage(fmt::format("Failed to verify code {} for game ID {}.", code.name, game_id),
OSD::Duration::VERY_LONG, OSD::Color::RED);
OSD::AddMessage("Disable hardcore mode to enable this code.", OSD::Duration::VERY_LONG,
OSD::Color::RED);
return false;
}
template <typename T>
bool AchievementManager::IsApprovedCode(const T& code, const std::string& game_id,
u16 revision) const
{
// Approved codes list failed to hash
if (!m_ini_root->is<picojson::value::object>())
return false;
const auto hash = Common::SHA1::DigestToString(GetCodeHash(code));
for (const std::string& filename : ConfigLoaders::GetGameIniFilenames(game_id, revision)) for (const std::string& filename : ConfigLoaders::GetGameIniFilenames(game_id, revision))
{ {
auto config = filename.substr(0, filename.length() - 4); const auto config = filename.substr(0, filename.length() - 4);
if (m_ini_root->contains(config)) if (m_ini_root->contains(config))
{ {
auto ini_config = m_ini_root->get(config); const auto ini_config = m_ini_root->get(config);
if (ini_config.is<picojson::object>() && ini_config.contains(code.name)) if (ini_config.is<picojson::object>() && ini_config.contains(code.name))
{ {
auto ini_code = ini_config.get(code.name); const auto ini_code = ini_config.get(code.name);
if (ini_code.template is<std::string>()) if (ini_code.template is<std::string>() && ini_code.template get<std::string>() == hash)
verified = (ini_code.template get<std::string>() == hash); return true;
} }
} }
} }
return false;
if (!verified)
{
OSD::AddMessage(fmt::format("Failed to verify code {} for game ID {}.", code.name, game_id),
OSD::Duration::VERY_LONG, OSD::Color::RED);
OSD::AddMessage("Disable hardcore mode to enable this code.", OSD::Duration::VERY_LONG,
OSD::Color::RED);
}
return verified;
} }
Common::SHA1::Digest AchievementManager::GetCodeHash(const PatchEngine::Patch& patch) const Common::SHA1::Digest AchievementManager::GetCodeHash(const PatchEngine::Patch& patch) const
@ -564,16 +576,27 @@ void AchievementManager::FilterApprovedARCodes(std::vector<ActionReplay::ARCode>
FilterApprovedIni(codes, game_id, revision); FilterApprovedIni(codes, game_id, revision);
} }
bool AchievementManager::CheckApprovedGeckoCode(const Gecko::GeckoCode& code, bool AchievementManager::ShouldGeckoCodeBeActivated(const Gecko::GeckoCode& code,
const std::string& game_id, u16 revision) const const std::string& game_id, u16 revision) const
{ {
return CheckApprovedCode(code, game_id, revision); return ShouldCodeBeActivated(code, game_id, revision);
} }
bool AchievementManager::CheckApprovedARCode(const ActionReplay::ARCode& code, bool AchievementManager::ShouldARCodeBeActivated(const ActionReplay::ARCode& code,
const std::string& game_id, u16 revision) const
{
return ShouldCodeBeActivated(code, game_id, revision);
}
bool AchievementManager::IsApprovedGeckoCode(const Gecko::GeckoCode& code,
const std::string& game_id, u16 revision) const const std::string& game_id, u16 revision) const
{ {
return CheckApprovedCode(code, game_id, revision); return IsApprovedCode(code, game_id, revision);
}
bool AchievementManager::IsApprovedARCode(const ActionReplay::ARCode& code,
const std::string& game_id, u16 revision) const
{
return IsApprovedCode(code, game_id, revision);
} }
void AchievementManager::SetSpectatorMode() void AchievementManager::SetSpectatorMode()

View file

@ -149,10 +149,14 @@ public:
u16 revision) const; u16 revision) const;
void FilterApprovedARCodes(std::vector<ActionReplay::ARCode>& codes, const std::string& game_id, void FilterApprovedARCodes(std::vector<ActionReplay::ARCode>& codes, const std::string& game_id,
u16 revision) const; u16 revision) const;
bool CheckApprovedGeckoCode(const Gecko::GeckoCode& code, const std::string& game_id, bool ShouldGeckoCodeBeActivated(const Gecko::GeckoCode& code, const std::string& game_id,
u16 revision) const; u16 revision) const;
bool CheckApprovedARCode(const ActionReplay::ARCode& code, const std::string& game_id, bool ShouldARCodeBeActivated(const ActionReplay::ARCode& code, const std::string& game_id,
u16 revision) const;
bool IsApprovedGeckoCode(const Gecko::GeckoCode& code, const std::string& game_id,
u16 revision) const; u16 revision) const;
bool IsApprovedARCode(const ActionReplay::ARCode& code, const std::string& game_id,
u16 revision) const;
void SetSpectatorMode(); void SetSpectatorMode();
std::string_view GetPlayerDisplayName() const; std::string_view GetPlayerDisplayName() const;
@ -223,7 +227,9 @@ private:
template <typename T> template <typename T>
void FilterApprovedIni(std::vector<T>& codes, const std::string& game_id, u16 revision) const; void FilterApprovedIni(std::vector<T>& codes, const std::string& game_id, u16 revision) const;
template <typename T> template <typename T>
bool CheckApprovedCode(const T& code, const std::string& game_id, u16 revision) const; bool ShouldCodeBeActivated(const T& code, const std::string& game_id, u16 revision) const;
template <typename T>
bool IsApprovedCode(const T& code, const std::string& game_id, u16 revision) const;
Common::SHA1::Digest GetCodeHash(const PatchEngine::Patch& patch) const; Common::SHA1::Digest GetCodeHash(const PatchEngine::Patch& patch) const;
Common::SHA1::Digest GetCodeHash(const Gecko::GeckoCode& code) const; Common::SHA1::Digest GetCodeHash(const Gecko::GeckoCode& code) const;
Common::SHA1::Digest GetCodeHash(const ActionReplay::ARCode& code) const; Common::SHA1::Digest GetCodeHash(const ActionReplay::ARCode& code) const;
@ -338,14 +344,14 @@ public:
constexpr bool IsHardcoreModeActive() { return false; } constexpr bool IsHardcoreModeActive() { return false; }
constexpr bool CheckApprovedGeckoCode(const Gecko::GeckoCode& code, const std::string& game_id, constexpr bool ShouldGeckoCodeBeActivated(const Gecko::GeckoCode& code,
u16 revision) const std::string& game_id, u16 revision)
{ {
return true; return true;
} }
constexpr bool CheckApprovedARCode(const ActionReplay::ARCode& code, const std::string& game_id, constexpr bool ShouldARCodeBeActivated(const ActionReplay::ARCode& code,
u16 revision) const std::string& game_id, u16 revision)
{ {
return true; return true;
} }

View file

@ -122,11 +122,11 @@ void ApplyCodes(std::span<const ARCode> codes, const std::string& game_id, u16 r
std::lock_guard guard(s_lock); std::lock_guard guard(s_lock);
s_disable_logging = false; s_disable_logging = false;
s_active_codes.clear(); s_active_codes.clear();
std::copy_if(codes.begin(), codes.end(), std::back_inserter(s_active_codes),
[&game_id, &revision](const ARCode& code) { const auto should_be_activated = [&game_id, &revision](const ARCode& code) {
return code.enabled && AchievementManager::GetInstance().CheckApprovedARCode( return AchievementManager::GetInstance().ShouldARCodeBeActivated(code, game_id, revision);
code, game_id, revision); };
}); std::copy_if(codes.begin(), codes.end(), std::back_inserter(s_active_codes), should_be_activated);
s_active_codes.shrink_to_fit(); s_active_codes.shrink_to_fit();
} }

View file

@ -59,11 +59,11 @@ void SetActiveCodes(std::span<const GeckoCode> gcodes, const std::string& game_i
{ {
s_active_codes.reserve(gcodes.size()); s_active_codes.reserve(gcodes.size());
const auto should_be_activated = [&game_id, &revision](const GeckoCode& code) {
return AchievementManager::GetInstance().ShouldGeckoCodeBeActivated(code, game_id, revision);
};
std::copy_if(gcodes.begin(), gcodes.end(), std::back_inserter(s_active_codes), std::copy_if(gcodes.begin(), gcodes.end(), std::back_inserter(s_active_codes),
[&game_id, &revision](const GeckoCode& code) { should_be_activated);
return code.enabled && AchievementManager::GetInstance().CheckApprovedGeckoCode(
code, game_id, revision);
});
} }
s_active_codes.shrink_to_fit(); s_active_codes.shrink_to_fit();

View file

@ -8,14 +8,21 @@
#include <QCursor> #include <QCursor>
#include <QHBoxLayout> #include <QHBoxLayout>
#ifdef USE_RETRO_ACHIEVEMENTS
#include <QIcon>
#endif // USE_RETRO_ACHIEVEMENTS
#include <QListWidget> #include <QListWidget>
#include <QMenu> #include <QMenu>
#include <QPushButton> #include <QPushButton>
#ifdef USE_RETRO_ACHIEVEMENTS
#include <QStyle>
#endif // USE_RETRO_ACHIEVEMENTS
#include <QVBoxLayout> #include <QVBoxLayout>
#include "Common/FileUtil.h" #include "Common/FileUtil.h"
#include "Common/IniFile.h" #include "Common/IniFile.h"
#include "Core/AchievementManager.h"
#include "Core/ActionReplay.h" #include "Core/ActionReplay.h"
#include "Core/ConfigManager.h" #include "Core/ConfigManager.h"
@ -23,6 +30,9 @@
#include "DolphinQt/Config/CheatWarningWidget.h" #include "DolphinQt/Config/CheatWarningWidget.h"
#include "DolphinQt/Config/HardcoreWarningWidget.h" #include "DolphinQt/Config/HardcoreWarningWidget.h"
#include "DolphinQt/QtUtils/NonDefaultQPushButton.h" #include "DolphinQt/QtUtils/NonDefaultQPushButton.h"
#ifdef USE_RETRO_ACHIEVEMENTS
#include "DolphinQt/Settings.h"
#endif // USE_RETRO_ACHIEVEMENTS
ARCodeWidget::ARCodeWidget(std::string game_id, u16 game_revision, bool restart_required) ARCodeWidget::ARCodeWidget(std::string game_id, u16 game_revision, bool restart_required)
: m_game_id(std::move(game_id)), m_game_revision(game_revision), : m_game_id(std::move(game_id)), m_game_revision(game_revision),
@ -90,6 +100,7 @@ void ARCodeWidget::ConnectWidgets()
#ifdef USE_RETRO_ACHIEVEMENTS #ifdef USE_RETRO_ACHIEVEMENTS
connect(m_hc_warning, &HardcoreWarningWidget::OpenAchievementSettings, this, connect(m_hc_warning, &HardcoreWarningWidget::OpenAchievementSettings, this,
&ARCodeWidget::OpenAchievementSettings); &ARCodeWidget::OpenAchievementSettings);
connect(&Settings::Instance(), &Settings::EmulationStateChanged, this, &ARCodeWidget::UpdateList);
#endif // USE_RETRO_ACHIEVEMENTS #endif // USE_RETRO_ACHIEVEMENTS
connect(m_code_list, &QListWidget::itemChanged, this, &ARCodeWidget::OnItemChanged); connect(m_code_list, &QListWidget::itemChanged, this, &ARCodeWidget::OnItemChanged);
@ -199,6 +210,21 @@ void ARCodeWidget::UpdateList()
item->setCheckState(ar.enabled ? Qt::Checked : Qt::Unchecked); item->setCheckState(ar.enabled ? Qt::Checked : Qt::Unchecked);
item->setData(Qt::UserRole, static_cast<int>(i)); item->setData(Qt::UserRole, static_cast<int>(i));
#ifdef USE_RETRO_ACHIEVEMENTS
const AchievementManager& achievement_manager = AchievementManager::GetInstance();
if (achievement_manager.IsHardcoreModeActive())
{
const QIcon approved_icon = style()->standardIcon(QStyle::SP_DialogYesButton);
const QIcon warning_icon = style()->standardIcon(QStyle::SP_MessageBoxWarning);
if (achievement_manager.IsApprovedARCode(ar, m_game_id, m_game_revision))
item->setIcon(approved_icon);
else
item->setIcon(warning_icon);
}
#endif // USE_RETRO_ACHIEVEMENTS
m_code_list->addItem(item); m_code_list->addItem(item);
} }

View file

@ -10,16 +10,23 @@
#include <QFontDatabase> #include <QFontDatabase>
#include <QFormLayout> #include <QFormLayout>
#include <QHBoxLayout> #include <QHBoxLayout>
#ifdef USE_RETRO_ACHIEVEMENTS
#include <QIcon>
#endif // USE_RETRO_ACHIEVEMENTS
#include <QLabel> #include <QLabel>
#include <QListWidget> #include <QListWidget>
#include <QMenu> #include <QMenu>
#include <QPushButton> #include <QPushButton>
#ifdef USE_RETRO_ACHIEVEMENTS
#include <QStyle>
#endif // USE_RETRO_ACHIEVEMENTS
#include <QTextEdit> #include <QTextEdit>
#include <QVBoxLayout> #include <QVBoxLayout>
#include "Common/FileUtil.h" #include "Common/FileUtil.h"
#include "Common/IniFile.h" #include "Common/IniFile.h"
#include "Core/AchievementManager.h"
#include "Core/ConfigManager.h" #include "Core/ConfigManager.h"
#include "Core/GeckoCode.h" #include "Core/GeckoCode.h"
#include "Core/GeckoCodeConfig.h" #include "Core/GeckoCodeConfig.h"
@ -31,6 +38,9 @@
#include "DolphinQt/QtUtils/NonDefaultQPushButton.h" #include "DolphinQt/QtUtils/NonDefaultQPushButton.h"
#include "DolphinQt/QtUtils/QtUtils.h" #include "DolphinQt/QtUtils/QtUtils.h"
#include "DolphinQt/QtUtils/WrapInScrollArea.h" #include "DolphinQt/QtUtils/WrapInScrollArea.h"
#ifdef USE_RETRO_ACHIEVEMENTS
#include "DolphinQt/Settings.h"
#endif // USE_RETRO_ACHIEVEMENTS
GeckoCodeWidget::GeckoCodeWidget(std::string game_id, std::string gametdb_id, u16 game_revision, GeckoCodeWidget::GeckoCodeWidget(std::string game_id, std::string gametdb_id, u16 game_revision,
bool restart_required) bool restart_required)
@ -158,6 +168,8 @@ void GeckoCodeWidget::ConnectWidgets()
#ifdef USE_RETRO_ACHIEVEMENTS #ifdef USE_RETRO_ACHIEVEMENTS
connect(m_hc_warning, &HardcoreWarningWidget::OpenAchievementSettings, this, connect(m_hc_warning, &HardcoreWarningWidget::OpenAchievementSettings, this,
&GeckoCodeWidget::OpenAchievementSettings); &GeckoCodeWidget::OpenAchievementSettings);
connect(&Settings::Instance(), &Settings::EmulationStateChanged, this,
&GeckoCodeWidget::UpdateList);
#endif // USE_RETRO_ACHIEVEMENTS #endif // USE_RETRO_ACHIEVEMENTS
} }
@ -356,6 +368,21 @@ void GeckoCodeWidget::UpdateList()
item->setCheckState(code.enabled ? Qt::Checked : Qt::Unchecked); item->setCheckState(code.enabled ? Qt::Checked : Qt::Unchecked);
item->setData(Qt::UserRole, static_cast<int>(i)); item->setData(Qt::UserRole, static_cast<int>(i));
#ifdef USE_RETRO_ACHIEVEMENTS
const AchievementManager& achievement_manager = AchievementManager::GetInstance();
if (achievement_manager.IsHardcoreModeActive())
{
const QIcon approved_icon = style()->standardIcon(QStyle::SP_DialogYesButton);
const QIcon warning_icon = style()->standardIcon(QStyle::SP_MessageBoxWarning);
if (achievement_manager.IsApprovedGeckoCode(code, m_game_id, m_game_revision))
item->setIcon(approved_icon);
else
item->setIcon(warning_icon);
}
#endif // USE_RETRO_ACHIEVEMENTS
m_code_list->addItem(item); m_code_list->addItem(item);
} }