diff --git a/bin/resources/icons/ra-icon.svg b/bin/resources/icons/ra-icon.svg
new file mode 100644
index 0000000000..a423507a83
--- /dev/null
+++ b/bin/resources/icons/ra-icon.svg
@@ -0,0 +1,106 @@
+
+
diff --git a/pcsx2/ImGui/FullscreenUI.cpp b/pcsx2/ImGui/FullscreenUI.cpp
index 88e10e1011..57551b3b45 100644
--- a/pcsx2/ImGui/FullscreenUI.cpp
+++ b/pcsx2/ImGui/FullscreenUI.cpp
@@ -255,6 +255,13 @@ namespace FullscreenUI
static bool s_was_paused_on_quick_menu_open = false;
static bool s_about_window_open = false;
+ // achievements login dialog state
+ static bool s_achievements_login_open = false;
+ static bool s_achievements_login_logging_in = false;
+ static char s_achievements_login_username[256] = {};
+ static char s_achievements_login_password[256] = {};
+ static Achievements::LoginRequestReason s_achievements_login_reason = Achievements::LoginRequestReason::UserInitiated;
+
// local copies of the currently-running game
static std::string s_current_game_title;
static std::string s_current_game_subtitle;
@@ -493,6 +500,7 @@ namespace FullscreenUI
//////////////////////////////////////////////////////////////////////////
static void SwitchToAchievementsWindow();
static void SwitchToLeaderboardsWindow();
+ static void DrawAchievementsLoginWindow();
} // namespace FullscreenUI
//////////////////////////////////////////////////////////////////////////
@@ -1034,6 +1042,9 @@ void FullscreenUI::Render()
if (s_about_window_open)
DrawAboutWindow();
+ if (s_achievements_login_open)
+ DrawAchievementsLoginWindow();
+
if (s_input_binding_type != InputBindingInfo::Type::Unknown)
DrawInputBindingWindow();
@@ -7207,6 +7218,229 @@ bool FullscreenUI::OpenAchievementsWindow()
return true;
}
+void FullscreenUI::DrawAchievementsLoginWindow()
+{
+ ImGui::SetNextWindowSize(LayoutScale(400.0f, 330.0f));
+ ImGui::SetNextWindowPos(ImGui::GetIO().DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
+
+ ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, LayoutScale(12.0f));
+ ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, LayoutScale(24.0f, 24.0f));
+ ImGui::PushStyleColor(ImGuiCol_PopupBg, ImVec4(0.13f, 0.13f, 0.13f, 0.95f));
+
+ if (ImGui::BeginPopupModal("RetroAchievements", &s_achievements_login_open, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar))
+ {
+ const float content_width = ImGui::GetContentRegionAvail().x;
+
+ ImGui::PushFont(g_large_font);
+
+ const float icon_height = LayoutScale(24.0f);
+ const float icon_width = icon_height * (500.0f / 275.0f);
+ GSTexture* ra_icon = GetCachedSvgTextureAsync("icons/ra-icon.svg", ImVec2(icon_width, icon_height));
+ const float title_width = ImGui::CalcTextSize("RetroAchievements").x;
+ const float header_width = (ra_icon ? icon_width + LayoutScale(10.0f) : 0.0f) + title_width;
+ const float header_start = (content_width - header_width) * 0.5f;
+
+ ImGui::SetCursorPosX(ImGui::GetCursorPosX() + header_start);
+
+ if (ra_icon)
+ {
+ ImGui::Image(reinterpret_cast(ra_icon->GetNativeHandle()),
+ ImVec2(icon_width, icon_height));
+ ImGui::SameLine();
+ }
+
+ ImGui::SetCursorPosY(ImGui::GetCursorPosY() + LayoutScale(1.0f));
+ ImGui::TextUnformatted(FSUI_CSTR("RetroAchievements"));
+ ImGui::PopFont();
+
+ ImGui::Spacing();
+ ImGui::Spacing();
+
+ ImGui::PushTextWrapPos(content_width);
+ ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.8f, 0.8f, 0.8f, 1.0f));
+ ImGui::TextWrapped(FSUI_CSTR("Please enter your user name and password for retroachievements.org below. Your password will not be saved in PCSX2, an access token will be generated and used instead."));
+ ImGui::PopStyleColor();
+ ImGui::PopTextWrapPos();
+
+ ImGui::Spacing();
+ ImGui::Spacing();
+
+ ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, LayoutScale(6.0f));
+ ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, LayoutScale(12.0f, 10.0f));
+ ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.2f, 0.2f, 0.2f, 1.0f));
+ ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ImVec4(0.25f, 0.25f, 0.25f, 1.0f));
+ ImGui::PushStyleColor(ImGuiCol_FrameBgActive, ImVec4(0.3f, 0.3f, 0.3f, 1.0f));
+
+ if (s_achievements_login_logging_in)
+ ImGui::BeginDisabled();
+
+ ImGui::SetNextItemWidth(content_width);
+ ImGui::InputTextWithHint("##username", FSUI_CSTR("Username"), s_achievements_login_username, sizeof(s_achievements_login_username));
+
+ ImGui::Spacing();
+
+ ImGui::SetNextItemWidth(content_width);
+ ImGui::InputTextWithHint("##password", FSUI_CSTR("Password"), s_achievements_login_password, sizeof(s_achievements_login_password), ImGuiInputTextFlags_Password);
+
+ ImGui::PopStyleColor(3);
+ ImGui::PopStyleVar(2);
+
+ if (s_achievements_login_logging_in)
+ ImGui::EndDisabled();
+
+ ImGui::Spacing();
+ ImGui::Spacing();
+
+ if (s_achievements_login_logging_in)
+ {
+ ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.7f, 0.7f, 0.7f, 1.0f));
+ const float status_width = ImGui::CalcTextSize("Logging in...").x;
+ ImGui::SetCursorPosX(ImGui::GetCursorPosX() + (content_width - status_width) * 0.5f);
+ ImGui::TextUnformatted(FSUI_CSTR("Logging in..."));
+ ImGui::PopStyleColor();
+ ImGui::Spacing();
+ }
+
+ const float button_height = LayoutScale(36.0f);
+ const float button_width = LayoutScale(100.0f);
+ const float button_spacing = LayoutScale(12.0f);
+ const float total_width = (button_width * 2) + button_spacing;
+ const float start_x = (content_width - total_width) * 0.5f;
+
+ ImGui::SetCursorPosX(ImGui::GetCursorPosX() + start_x);
+
+ ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, LayoutScale(6.0f));
+
+ const bool can_login = !s_achievements_login_logging_in &&
+ strlen(s_achievements_login_username) > 0 &&
+ strlen(s_achievements_login_password) > 0;
+
+ if (can_login)
+ {
+ ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.2f, 0.5f, 0.9f, 1.0f));
+ ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.3f, 0.6f, 1.0f, 1.0f));
+ ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.1f, 0.4f, 0.8f, 1.0f));
+ }
+ else
+ {
+ ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.3f, 0.3f, 0.3f, 1.0f));
+ ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.3f, 0.3f, 0.3f, 1.0f));
+ ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.3f, 0.3f, 0.3f, 1.0f));
+ }
+
+ if (ImGui::Button(FSUI_CSTR("Login"), ImVec2(button_width, button_height)) && can_login)
+ {
+ s_achievements_login_logging_in = true;
+
+ Host::RunOnCPUThread([username = std::string(s_achievements_login_username),
+ password = std::string(s_achievements_login_password)]() {
+ Error error;
+ const bool result = Achievements::Login(username.c_str(), password.c_str(), &error);
+
+ s_achievements_login_logging_in = false;
+
+ if (!result)
+ {
+ ShowToast(std::string(), fmt::format(FSUI_FSTR("Login failed.\nError: {}\n\nPlease check your username and password, and try again."),
+ error.GetDescription()));
+ return;
+ }
+
+ ImGui::CloseCurrentPopup();
+ s_achievements_login_open = false;
+
+ s_achievements_login_username[0] = '\0';
+ s_achievements_login_password[0] = '\0';
+
+ if (s_achievements_login_reason == Achievements::LoginRequestReason::UserInitiated)
+ {
+ if (!Host::GetBaseBoolSettingValue("Achievements", "Enabled", false))
+ {
+ OpenConfirmMessageDialog(FSUI_STR("Enable Achievements"),
+ FSUI_STR("Achievement tracking is not currently enabled. Your login will have no effect until "
+ "after tracking is enabled.\n\nDo you want to enable tracking now?"),
+ [](bool result) {
+ if (result)
+ {
+ Host::SetBaseBoolSettingValue("Achievements", "Enabled", true);
+ Host::CommitBaseSettingChanges();
+ VMManager::ApplySettings();
+ }
+ });
+ }
+
+ if (!Host::GetBaseBoolSettingValue("Achievements", "ChallengeMode", false))
+ {
+ OpenConfirmMessageDialog(FSUI_STR("Enable Hardcore Mode"),
+ FSUI_STR("Hardcore mode is not currently enabled. Enabling hardcore mode allows you to set times, scores, and "
+ "participate in game-specific leaderboards.\n\nHowever, hardcore mode also prevents the usage of save "
+ "states, cheats and slowdown functionality.\n\nDo you want to enable hardcore mode?"),
+ [](bool result) {
+ if (result)
+ {
+ Host::SetBaseBoolSettingValue("Achievements", "ChallengeMode", true);
+ Host::CommitBaseSettingChanges();
+ VMManager::ApplySettings();
+
+ bool has_active_game;
+ {
+ auto lock = Achievements::GetLock();
+ has_active_game = Achievements::HasActiveGame();
+ }
+
+ if (has_active_game)
+ {
+ OpenConfirmMessageDialog(FSUI_STR("Reset System"),
+ FSUI_STR("Hardcore mode will not be enabled until the system is reset. Do you want to reset the system now?"),
+ [](bool reset) {
+ if (reset && VMManager::HasValidVM())
+ RequestReset();
+ });
+ }
+ }
+ });
+ }
+ }
+ });
+ }
+
+ ImGui::PopStyleColor(3);
+
+ ImGui::SameLine(0, button_spacing);
+
+ ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.4f, 0.4f, 0.4f, 1.0f));
+ ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.5f, 0.5f, 0.5f, 1.0f));
+ ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.3f, 0.3f, 0.3f, 1.0f));
+
+ if (ImGui::Button(FSUI_CSTR("Cancel"), ImVec2(button_width, button_height)) && !s_achievements_login_logging_in)
+ {
+ if (s_achievements_login_reason == Achievements::LoginRequestReason::TokenInvalid)
+ {
+ if (VMManager::HasValidVM() && !Achievements::HasActiveGame())
+ Achievements::DisableHardcoreMode();
+ }
+
+ ImGui::CloseCurrentPopup();
+ s_achievements_login_open = false;
+ s_achievements_login_logging_in = false;
+
+ s_achievements_login_username[0] = '\0';
+ s_achievements_login_password[0] = '\0';
+ }
+
+ ImGui::PopStyleColor(3);
+ ImGui::PopStyleVar();
+
+ ImGui::EndPopup();
+ }
+
+ ImGui::PopStyleColor();
+ ImGui::PopStyleVar(2);
+
+ if (!ImGui::IsPopupOpen("RetroAchievements"))
+ ImGui::OpenPopup("RetroAchievements");
+}
+
bool FullscreenUI::IsAchievementsWindowOpen()
{
return (s_current_main_window == MainWindowType::Achievements);
@@ -7414,7 +7648,10 @@ void FullscreenUI::DrawAchievementsSettingsPage(std::unique_lock& se
ActiveButton(FSUI_ICONSTR(ICON_FA_USER, "Not Logged In"), false, false, ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY);
if (MenuButton(FSUI_ICONSTR(ICON_FA_KEY, "Login"), FSUI_CSTR("Logs in to RetroAchievements.")))
- Host::OnAchievementsLoginRequested(Achievements::LoginRequestReason::UserInitiated);
+ {
+ s_achievements_login_reason = Achievements::LoginRequestReason::UserInitiated;
+ s_achievements_login_open = true;
+ }
}
MenuHeading(FSUI_CSTR("Current Game"));
@@ -8363,5 +8600,13 @@ TRANSLATE_NOOP("FullscreenUI", "Game not loaded or no RetroAchievements availabl
TRANSLATE_NOOP("FullscreenUI", "Card Enabled");
TRANSLATE_NOOP("FullscreenUI", "Card Name");
TRANSLATE_NOOP("FullscreenUI", "Eject Card");
+TRANSLATE_NOOP("FullscreenUI", "Username");
+TRANSLATE_NOOP("FullscreenUI", "Password");
+TRANSLATE_NOOP("FullscreenUI", "Please enter your user name and password for retroachievements.org below. Your password will not be saved in PCSX2, an access token will be generated and used instead.");
+TRANSLATE_NOOP("FullscreenUI", "Login failed.\nError: {}\n\nPlease check your username and password, and try again.");
+TRANSLATE_NOOP("FullscreenUI", "Achievement tracking is not currently enabled. Your login will have no effect until after tracking is enabled.\n\nDo you want to enable tracking now?");
+TRANSLATE_NOOP("FullscreenUI", "Hardcore mode is not currently enabled. Enabling hardcore mode allows you to set times, scores, and participate in game-specific leaderboards.\n\nHowever, hardcore mode also prevents the usage of save states, cheats and slowdown functionality.\n\nDo you want to enable hardcore mode?");
+TRANSLATE_NOOP("FullscreenUI", "Logging in...");
// TRANSLATION-STRING-AREA-END
#endif
+