diff --git a/src/duckstation-qt/achievementsettingswidget.cpp b/src/duckstation-qt/achievementsettingswidget.cpp
index c2a62bb2b..9fa4dcf1f 100644
--- a/src/duckstation-qt/achievementsettingswidget.cpp
+++ b/src/duckstation-qt/achievementsettingswidget.cpp
@@ -18,6 +18,8 @@ AchievementSettingsWidget::AchievementSettingsWidget(QtHostInterface* host_inter
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.richPresence, "Cheevos", "RichPresence", true);
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.testMode, "Cheevos", "TestMode", false);
+ SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.unofficialTestMode, "Cheevos",
+ "UnofficialTestMode", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.useFirstDiscFromPlaylist, "Cheevos",
"UseFirstDiscFromPlaylist", true);
m_ui.enable->setChecked(m_host_interface->GetBoolSettingValue("Cheevos", "Enabled", false));
@@ -28,6 +30,10 @@ AchievementSettingsWidget::AchievementSettingsWidget(QtHostInterface* host_inter
dialog->registerWidgetHelp(m_ui.testMode, tr("Enable Test Mode"), tr("Unchecked"),
tr("When enabled, DuckStation will assume all achievements are locked and not send any "
"unlock notifications to the server."));
+ dialog->registerWidgetHelp(
+ m_ui.unofficialTestMode, tr("Test Unofficial Achievements"), tr("Unchecked"),
+ tr("When enabled, DuckStation will list achievements from unofficial sets. Please note that these achievements are "
+ "not tracked by RetroAchievements, so they unlock every time."));
dialog->registerWidgetHelp(
m_ui.richPresence, tr("Enable Rich Presence"), tr("Unchecked"),
tr("When enabled, rich presence information will be collected and sent to the server where supported."));
diff --git a/src/duckstation-qt/achievementsettingswidget.ui b/src/duckstation-qt/achievementsettingswidget.ui
index a965c662a..83c646427 100644
--- a/src/duckstation-qt/achievementsettingswidget.ui
+++ b/src/duckstation-qt/achievementsettingswidget.ui
@@ -67,6 +67,13 @@
+ -
+
+
+ Test Unofficial Achievements
+
+
+
diff --git a/src/frontend-common/cheevos.cpp b/src/frontend-common/cheevos.cpp
index b0a2ae85b..571c87abf 100644
--- a/src/frontend-common/cheevos.cpp
+++ b/src/frontend-common/cheevos.cpp
@@ -55,6 +55,7 @@ u32 g_game_id = 0;
static bool s_logged_in = false;
static bool s_test_mode = false;
+static bool s_unofficial_test_mode = false;
static bool s_use_first_disc_from_playlist = true;
static bool s_rich_presence_enabled = false;
@@ -212,7 +213,8 @@ static std::string GetUserAgent()
return StringUtil::StdStringFromFormat("DuckStation for %s (%s) %s", SYSTEM_STR, CPU_ARCH_STR, g_scm_tag_str);
}
-bool Initialize(bool test_mode, bool use_first_disc_from_playlist, bool enable_rich_presence, bool challenge_mode)
+bool Initialize(bool test_mode, bool use_first_disc_from_playlist, bool enable_rich_presence, bool challenge_mode,
+ bool include_unofficial)
{
s_http_downloader = FrontendCommon::HTTPDownloader::Create(GetUserAgent().c_str());
if (!s_http_downloader)
@@ -224,6 +226,7 @@ bool Initialize(bool test_mode, bool use_first_disc_from_playlist, bool enable_r
g_active = true;
g_challenge_mode = challenge_mode;
s_test_mode = test_mode;
+ s_unofficial_test_mode = include_unofficial;
s_use_first_disc_from_playlist = use_first_disc_from_playlist;
s_rich_presence_enabled = enable_rich_presence;
rc_runtime_init(&s_rcheevos_runtime);
@@ -298,6 +301,11 @@ bool IsTestModeActive()
return s_test_mode;
}
+bool IsUnofficialTestModeActive()
+{
+ return s_unofficial_test_mode;
+}
+
bool IsUsingFirstDiscFromPlaylist()
{
return s_use_first_disc_from_playlist;
@@ -672,16 +680,16 @@ static void GetPatchesCallback(s32 status_code, const FrontendCommon::HTTPDownlo
}
const u32 id = achievement["ID"].GetUint();
- const u32 category = achievement["Flags"].GetUint();
+ const AchievementCategory category = static_cast(achievement["Flags"].GetUint());
const char* memaddr = achievement["MemAddr"].GetString();
std::string title = achievement["Title"].GetString();
std::string description = GetOptionalString(achievement, "Description");
std::string badge_name = GetOptionalString(achievement, "BadgeName");
const u32 points = GetOptionalUInt(achievement, "Points");
- // Skip local and unofficial achievements for now.
- if (static_cast(category) == AchievementCategory::Local ||
- static_cast(category) == AchievementCategory::Unofficial)
+ // Skip local and unofficial achievements for now, unless "Test Unofficial Achievements" is enabled
+ if (!s_unofficial_test_mode &&
+ (category == AchievementCategory::Local || category == AchievementCategory::Unofficial))
{
Log_WarningPrintf("Skipping unofficial achievement %u (%s)", id, title.c_str());
continue;
@@ -701,6 +709,7 @@ static void GetPatchesCallback(s32 status_code, const FrontendCommon::HTTPDownlo
cheevo.locked = true;
cheevo.active = false;
cheevo.points = points;
+ cheevo.category = category;
if (!badge_name.empty())
{
@@ -1101,6 +1110,13 @@ void UnlockAchievement(u32 achievement_id, bool add_notification /* = true*/)
return;
}
+ if (achievement->category != AchievementCategory::Core)
+ {
+ Log_WarningPrintf("Skipping sending achievement %u unlock to server because it's not from the core set.",
+ achievement_id);
+ return;
+ }
+
char url[512];
rc_url_award_cheevo(url, sizeof(url), s_username.c_str(), s_login_token.c_str(), achievement_id,
static_cast(g_challenge_mode), s_game_hash.c_str());
diff --git a/src/frontend-common/cheevos.h b/src/frontend-common/cheevos.h
index d6c108f24..39231a8cb 100644
--- a/src/frontend-common/cheevos.h
+++ b/src/frontend-common/cheevos.h
@@ -23,6 +23,7 @@ struct Achievement
std::string locked_badge_path;
std::string unlocked_badge_path;
u32 points;
+ AchievementCategory category;
bool locked;
bool active;
};
@@ -56,13 +57,15 @@ ALWAYS_INLINE u32 GetGameID()
return g_game_id;
}
-bool Initialize(bool test_mode, bool use_first_disc_from_playlist, bool enable_rich_presence, bool challenge_mode);
+bool Initialize(bool test_mode, bool use_first_disc_from_playlist, bool enable_rich_presence, bool challenge_mode,
+ bool include_unofficial);
void Reset();
void Shutdown();
void Update();
bool IsLoggedIn();
bool IsTestModeActive();
+bool IsUnofficialTestModeActive();
bool IsUsingFirstDiscFromPlaylist();
bool IsRichPresenceEnabled();
const std::string& GetUsername();
diff --git a/src/frontend-common/common_host_interface.cpp b/src/frontend-common/common_host_interface.cpp
index a6c47140f..f0e178346 100644
--- a/src/frontend-common/common_host_interface.cpp
+++ b/src/frontend-common/common_host_interface.cpp
@@ -2807,6 +2807,7 @@ void CommonHostInterface::SetDefaultSettings(SettingsInterface& si)
#ifdef WITH_CHEEVOS
si.SetBoolValue("Cheevos", "Enabled", false);
si.SetBoolValue("Cheevos", "TestMode", false);
+ si.SetBoolValue("Cheevos", "UnofficialTestMode", false);
si.SetBoolValue("Cheevos", "UseFirstDiscFromPlaylist", true);
si.DeleteValue("Cheevos", "Username");
si.DeleteValue("Cheevos", "Token");
@@ -3818,11 +3819,13 @@ void CommonHostInterface::UpdateCheevosActive()
{
const bool cheevos_enabled = GetBoolSettingValue("Cheevos", "Enabled", false);
const bool cheevos_test_mode = GetBoolSettingValue("Cheevos", "TestMode", false);
+ const bool cheevos_unofficial_test_mode = GetBoolSettingValue("Cheevos", "UnofficialTestMode", false);
const bool cheevos_use_first_disc_from_playlist = GetBoolSettingValue("Cheevos", "UseFirstDiscFromPlaylist", true);
const bool cheevos_rich_presence = GetBoolSettingValue("Cheevos", "RichPresence", true);
const bool cheevos_hardcore = GetBoolSettingValue("Cheevos", "ChallengeMode", false);
if (cheevos_enabled != Cheevos::IsActive() || cheevos_test_mode != Cheevos::IsTestModeActive() ||
+ cheevos_unofficial_test_mode != Cheevos::IsUnofficialTestModeActive() ||
cheevos_use_first_disc_from_playlist != Cheevos::IsUsingFirstDiscFromPlaylist() ||
cheevos_rich_presence != Cheevos::IsRichPresenceEnabled() ||
cheevos_hardcore != Cheevos::IsChallengeModeEnabled())
@@ -3831,7 +3834,7 @@ void CommonHostInterface::UpdateCheevosActive()
if (cheevos_enabled)
{
if (!Cheevos::Initialize(cheevos_test_mode, cheevos_use_first_disc_from_playlist, cheevos_rich_presence,
- cheevos_hardcore))
+ cheevos_hardcore, cheevos_unofficial_test_mode))
ReportError("Failed to initialize cheevos after settings change.");
}
}
diff --git a/src/frontend-common/fullscreen_ui.cpp b/src/frontend-common/fullscreen_ui.cpp
index 0a3ce1211..538be5d28 100644
--- a/src/frontend-common/fullscreen_ui.cpp
+++ b/src/frontend-common/fullscreen_ui.cpp
@@ -2281,6 +2281,11 @@ void DrawSettingsWindow()
"When enabled, DuckStation will assume all achievements are locked and not "
"send any unlock notifications to the server.",
"Cheevos", "TestMode", false);
+ settings_changed |=
+ ToggleButtonForNonSetting(ICON_FA_MEDAL " Test Unofficial Achievements",
+ "When enabled, DuckStation will list achievements from unofficial sets. These "
+ "achievements are not tracked by RetroAchievements.",
+ "Cheevos", "UnofficialTestMode", false);
settings_changed |= ToggleButtonForNonSetting(ICON_FA_COMPACT_DISC " Use First Disc From Playlist",
"When enabled, the first disc in a playlist will be used for "
"achievements, regardless of which disc is active.",