Added UI dialog for viewing achievements

This commit is contained in:
NicknineTheEagle 2024-06-19 19:07:46 +03:00
parent 65b326fba3
commit f61152c0b4
7 changed files with 182 additions and 0 deletions

View File

@ -669,6 +669,15 @@ bool EmulatorWindow::Initialize() {
} }
main_menu->AddChild(std::move(hid_menu)); main_menu->AddChild(std::move(hid_menu));
// Game menu.
auto game_menu = MenuItem::Create(MenuItem::Type::kPopup, "&Game");
{
game_menu->AddChild(
MenuItem::Create(MenuItem::Type::kString, "&Show achievements", "",
std::bind(&EmulatorWindow::ShowAchievements, this)));
}
main_menu->AddChild(std::move(game_menu));
// Help menu. // Help menu.
auto help_menu = MenuItem::Create(MenuItem::Type::kPopup, "&Help"); auto help_menu = MenuItem::Create(MenuItem::Type::kPopup, "&Help");
{ {
@ -1755,6 +1764,12 @@ void EmulatorWindow::DisplayHotKeysConfig() {
msg); msg);
} }
void EmulatorWindow::ShowAchievements() {
if (!emulator_->is_title_open()) return;
emulator_->kernel_state()->achievement_manager()->ShowAchievementsUI(0);
}
std::string EmulatorWindow::CanonicalizeFileExtension( std::string EmulatorWindow::CanonicalizeFileExtension(
const std::filesystem::path& path) { const std::filesystem::path& path) {
return xe::utf8::lower_ascii(xe::path_to_utf8(path.extension())); return xe::utf8::lower_ascii(xe::path_to_utf8(path.extension()));

View File

@ -235,6 +235,8 @@ class EmulatorWindow {
bool IsUseNexusForGameBarEnabled(); bool IsUseNexusForGameBarEnabled();
void DisplayHotKeysConfig(); void DisplayHotKeysConfig();
void ShowAchievements();
static std::string CanonicalizeFileExtension( static std::string CanonicalizeFileExtension(
const std::filesystem::path& path); const std::filesystem::path& path);

View File

@ -12,6 +12,7 @@
#include "xenia/gpu/graphics_system.h" #include "xenia/gpu/graphics_system.h"
#include "xenia/kernel/kernel_state.h" #include "xenia/kernel/kernel_state.h"
#include "xenia/kernel/util/shim_utils.h" #include "xenia/kernel/util/shim_utils.h"
#include "xenia/ui/imgui_dialog.h"
#include "xenia/ui/imgui_notification.h" #include "xenia/ui/imgui_notification.h"
DEFINE_bool(show_achievement_notification, false, DEFINE_bool(show_achievement_notification, false,
@ -181,5 +182,53 @@ void AchievementManager::Load(uint32_t user_index) {
fclose(file); fclose(file);
} }
std::string AchievementManager::GetAchievementsUIText(uint32_t user_index) {
if (user_index >= 4) {
return "";
}
std::string ui_desc = "";
const util::XdbfGameData db = kernel_state()->title_xdbf();
if (db.is_valid()) {
const XLanguage language =
db.GetExistingLanguage(static_cast<XLanguage>(cvars::user_language));
const std::vector<util::XdbfAchievementTableEntry> achievement_list =
db.GetAchievements();
for (const util::XdbfAchievementTableEntry& entry : achievement_list) {
bool is_unlocked = IsAchievementUnlocked(user_index, entry.id);
std::string mark = is_unlocked ? "[X]" : "[ ]";
std::string label = db.GetStringTableEntry(language, entry.label_id);
uint16_t score = entry.gamerscore;
std::string description;
if (is_unlocked) {
description = db.GetStringTableEntry(language, entry.description_id);
} else {
description = db.GetStringTableEntry(language, entry.unachieved_id);
}
ui_desc += fmt::format("{} {}\n {}\n {}G\n\n", mark, label,
description, score);
}
}
return ui_desc;
}
void AchievementManager::ShowAchievementsUI(uint32_t user_index) {
if (user_index >= 4) {
return;
}
std::string ui_desc = GetAchievementsUIText(user_index);
const Emulator* emulator = kernel_state()->emulator();
ui::ImGuiDrawer* imgui_drawer = emulator->imgui_drawer();
new ui::AchievementsDialog(imgui_drawer, ui_desc);
}
} // namespace kernel } // namespace kernel
} // namespace xe } // namespace xe

View File

@ -50,6 +50,8 @@ class AchievementManager {
uint32_t achievement_id); uint32_t achievement_id);
void Save(uint32_t user_index); void Save(uint32_t user_index);
void Load(uint32_t user_index); void Load(uint32_t user_index);
std::string GetAchievementsUIText(uint32_t user_index);
void ShowAchievementsUI(uint32_t user_index);
private: private:
std::map<uint32_t, uint64_t> unlocked_achievements[4]; std::map<uint32_t, uint64_t> unlocked_achievements[4];

View File

@ -208,6 +208,49 @@ X_RESULT xeXamDispatchHeadlessEx(
} }
} }
template <typename T>
X_RESULT xeXamDispatchDialogAsync(T* dialog,
std::function<void(T*)> close_callback) {
// Broadcast XN_SYS_UI = true
kernel_state()->BroadcastNotification(0x9, true);
++xam_dialogs_shown_;
// Important to pass captured vars by value here since we return from this
// without waiting for the dialog to close so the original local vars will be
// destroyed.
// FIXME: Probably not the best idea to call Sleep in UI thread.
dialog->set_close_callback([dialog, close_callback]() {
close_callback(dialog);
--xam_dialogs_shown_;
xe::threading::Sleep(std::chrono::milliseconds(100));
// Broadcast XN_SYS_UI = false
kernel_state()->BroadcastNotification(0x9, false);
});
return X_ERROR_SUCCESS;
}
X_RESULT xeXamDispatchHeadlessAsync(std::function<void()> run_callback) {
// Broadcast XN_SYS_UI = true
kernel_state()->BroadcastNotification(0x9, true);
++xam_dialogs_shown_;
auto display_window = kernel_state()->emulator()->display_window();
display_window->app_context().CallInUIThread([run_callback]() {
run_callback();
--xam_dialogs_shown_;
xe::threading::Sleep(std::chrono::milliseconds(100));
// Broadcast XN_SYS_UI = false
kernel_state()->BroadcastNotification(0x9, false);
});
return X_ERROR_SUCCESS;
}
dword_result_t XamIsUIActive_entry() { return xeXamIsUIActive(); } dword_result_t XamIsUIActive_entry() { return xeXamIsUIActive(); }
DECLARE_XAM_EXPORT2(XamIsUIActive, kUI, kImplemented, kHighFrequency); DECLARE_XAM_EXPORT2(XamIsUIActive, kUI, kImplemented, kHighFrequency);
@ -620,6 +663,31 @@ dword_result_t XamGetDashContext_entry(const ppc_context_t& ctx) {
DECLARE_XAM_EXPORT1(XamGetDashContext, kNone, kImplemented); DECLARE_XAM_EXPORT1(XamGetDashContext, kNone, kImplemented);
dword_result_t XamShowAchievementsUI_entry(dword_t user_index) {
if (user_index >= 4) {
return X_ERROR_INVALID_PARAMETER;
}
if (!kernel_state()->IsUserSignedIn(user_index)) {
return X_ERROR_NO_SUCH_USER;
}
if (cvars::headless) {
return xeXamDispatchHeadlessAsync([]() {});
}
std::string ui_desc =
kernel_state()->achievement_manager()->GetAchievementsUIText(user_index);
const Emulator* emulator = kernel_state()->emulator();
ui::ImGuiDrawer* imgui_drawer = emulator->imgui_drawer();
return xeXamDispatchDialogAsync<ui::AchievementsDialog>(
new ui::AchievementsDialog(imgui_drawer, ui_desc),
[](ui::AchievementsDialog*) {});
}
DECLARE_XAM_EXPORT1(XamShowAchievementsUI, kUI, kSketchy);
} // namespace xam } // namespace xam
} // namespace kernel } // namespace kernel
} // namespace xe } // namespace xe

View File

@ -87,5 +87,32 @@ ImGuiDialog* ImGuiDialog::ShowMessageBox(ImGuiDrawer* imgui_drawer,
return new MessageBoxDialog(imgui_drawer, std::move(title), std::move(body)); return new MessageBoxDialog(imgui_drawer, std::move(title), std::move(body));
} }
void AchievementsDialog::OnDraw(ImGuiIO& io) {
if (!has_opened_) {
ImGui::OpenPopup("Achievements");
has_opened_ = true;
}
if (ImGui::BeginPopupModal("Achievements", nullptr,
ImGuiWindowFlags_AlwaysAutoResize)) {
char* text = const_cast<char*>(body_.c_str());
ImGui::InputTextMultiline(
"##body", text, body_.size(), ImVec2(600, 600),
ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_ReadOnly);
if (ImGui::Button("OK")) {
ImGui::CloseCurrentPopup();
Close();
}
ImGui::EndPopup();
} else {
Close();
}
}
void AchievementsDialog::OnClose() {
if (close_callback_) {
close_callback_();
}
}
} // namespace ui } // namespace ui
} // namespace xe } // namespace xe

View File

@ -53,6 +53,25 @@ class ImGuiDialog {
std::vector<xe::threading::Fence*> waiting_fences_; std::vector<xe::threading::Fence*> waiting_fences_;
}; };
class AchievementsDialog : public xe::ui::ImGuiDialog {
public:
AchievementsDialog(xe::ui::ImGuiDrawer* imgui_drawer, std::string& body)
: xe::ui::ImGuiDialog(imgui_drawer), body_(body) {}
virtual ~AchievementsDialog() {}
void set_close_callback(std::function<void()> close_callback) {
close_callback_ = close_callback;
}
private:
void OnDraw(ImGuiIO& io) override;
void OnClose() override;
bool has_opened_ = false;
std::string body_;
std::function<void()> close_callback_ = nullptr;
};
} // namespace ui } // namespace ui
} // namespace xe } // namespace xe