/** ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** * Copyright 2024 Xenia Canary. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ #include "xenia/app/profile_dialogs.h" #include "xenia/app/emulator_window.h" #include "xenia/base/png_utils.h" #include "xenia/base/system.h" #include "xenia/kernel/util/shim_utils.h" #include "xenia/kernel/xam/xam_ui.h" #include "xenia/ui/file_picker.h" #include "xenia/kernel/xam/ui/create_profile_ui.h" #include "xenia/kernel/xam/ui/gamercard_ui.h" #include "xenia/kernel/xam/ui/signin_ui.h" #include "xenia/kernel/xam/ui/title_info_ui.h" namespace xe { namespace app { void NoProfileDialog::OnDraw(ImGuiIO& io) { auto profile_manager = emulator_window_->emulator() ->kernel_state() ->xam_state() ->profile_manager(); if (profile_manager->GetAccountCount()) { Close(); return; } const auto window_position = ImVec2(GetIO().DisplaySize.x * 0.35f, GetIO().DisplaySize.y * 0.4f); ImGui::SetNextWindowPos(window_position, ImGuiCond_FirstUseEver); ImGui::SetNextWindowBgAlpha(1.0f); bool dialog_open = true; if (!ImGui::Begin("No Profiles Found", &dialog_open, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_HorizontalScrollbar)) { ImGui::End(); Close(); return; } const std::string message = "There is no profile available! You will not be able to save without " "one.\n\nWould you like to create one?"; ImGui::TextUnformatted(message.c_str()); ImGui::Separator(); ImGui::NewLine(); const auto content_files = xe::filesystem::ListDirectories( emulator_window_->emulator()->content_root()); if (ImGui::IsWindowAppearing()) { ImGui::SetKeyboardFocusHere(); } if (content_files.empty()) { if (ImGui::Button("Create Profile")) { new kernel::xam::ui::CreateProfileUI(emulator_window_->imgui_drawer(), emulator_window_->emulator()); } } else { if (ImGui::Button("Create profile & migrate data")) { new kernel::xam::ui::CreateProfileUI(emulator_window_->imgui_drawer(), emulator_window_->emulator(), true); } } ImGui::SameLine(); if (ImGui::Button("Open profile menu")) { emulator_window_->ToggleProfilesConfigDialog(); } ImGui::SameLine(); if (ImGui::Button("Close") || !dialog_open) { emulator_window_->SetHotkeysState(true); ImGui::End(); Close(); return; } ImGui::End(); } void ProfileConfigDialog::LoadProfileIcon() { if (!emulator_window_) { return; } for (uint8_t user_index = 0; user_index < XUserMaxUserCount; user_index++) { const auto profile = emulator_window_->emulator() ->kernel_state() ->xam_state() ->profile_manager() ->GetProfile(user_index); if (!profile) { continue; } LoadProfileIcon(profile->xuid()); } } void ProfileConfigDialog::LoadProfileIcon(const uint64_t xuid) { if (!emulator_window_) { return; } const auto profile_manager = emulator_window_->emulator() ->kernel_state() ->xam_state() ->profile_manager(); if (!profile_manager) { return; } const auto profile = profile_manager->GetProfile(xuid); if (!profile) { if (profile_icon_.contains(xuid)) { profile_icon_[xuid].release(); } return; } const auto profile_icon = profile->GetProfileIcon(kernel::xam::XTileType::kGamerTile); if (profile_icon.empty()) { return; } profile_icon_[xuid].release(); profile_icon_[xuid] = imgui_drawer()->LoadImGuiIcon(profile_icon); } void ProfileConfigDialog::OnDraw(ImGuiIO& io) { if (!emulator_window_->emulator() || !emulator_window_->emulator()->kernel_state() || !emulator_window_->emulator()->kernel_state()->xam_state()) { return; } auto profile_manager = emulator_window_->emulator() ->kernel_state() ->xam_state() ->profile_manager(); if (!profile_manager) { return; } auto profiles = profile_manager->GetAccounts(); ImGui::SetNextWindowPos(ImVec2(40, 40), ImGuiCond_FirstUseEver); ImGui::SetNextWindowBgAlpha(0.8f); bool dialog_open = true; if (!ImGui::Begin("Profiles Menu", &dialog_open, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_HorizontalScrollbar)) { ImGui::End(); return; } // For whatever reason dialog wasn't opened. It's probably in closing state. // We need to handle it here before it will make icons allocation. if (!dialog_open) { ImGui::CloseCurrentPopup(); Close(); ImGui::End(); emulator_window_->ToggleProfilesConfigDialog(); return; } if (profiles->empty()) { ImGui::TextUnformatted("No profiles found!"); ImGui::Spacing(); ImGui::Separator(); } const ImVec2 next_window_position = ImVec2(ImGui::GetWindowPos().x + ImGui::GetWindowSize().x + 20.f, ImGui::GetWindowPos().y); for (auto& [xuid, account] : *profiles) { ImGui::PushID(fmt::format("{:016X}", xuid).c_str()); const uint8_t user_index = profile_manager->GetUserIndexAssignedToProfile(xuid); const auto profile_icon = profile_icon_.find(xuid) != profile_icon_.cend() ? profile_icon_[xuid].get() : nullptr; auto context_menu_fun = [=, this]() -> bool { if (ImGui::BeginPopupContextItem("Profile Menu")) { //*selected_xuid = xuid; if (user_index == XUserIndexAny) { if (ImGui::MenuItem("Login")) { profile_manager->Login(xuid); if (!profile_manager->GetProfile(xuid) ->GetProfileIcon(kernel::xam::XTileType::kGamerTile) .empty()) { LoadProfileIcon(xuid); } } if (ImGui::BeginMenu("Login to slot:")) { for (uint8_t i = 1; i <= XUserMaxUserCount; i++) { if (ImGui::MenuItem(fmt::format("slot {}", i).c_str())) { uint64_t current_slot_xuid = 0; if (const auto current_profile = profile_manager->GetProfile( static_cast(i - 1)); current_profile) { current_slot_xuid = current_profile->xuid(); } profile_manager->Login(xuid, i - 1); LoadProfileIcon(xuid); // Release resources if (current_slot_xuid) { LoadProfileIcon(current_slot_xuid); } } } ImGui::EndMenu(); } } else { if (ImGui::MenuItem("Logout")) { profile_manager->Logout(user_index); LoadProfileIcon(xuid); } } if (ImGui::MenuItem("Modify")) { new kernel::xam::ui::GamercardUI( emulator_window_->window(), emulator_window_->imgui_drawer(), emulator_window_->emulator()->kernel_state(), xuid); } const bool is_signedin = profile_manager->GetProfile(xuid) != nullptr; ImGui::BeginDisabled(!is_signedin); if (ImGui::MenuItem("Show Played Titles")) { new kernel::xam::ui::TitleListUI( emulator_window_->imgui_drawer(), next_window_position, profile_manager->GetProfile(user_index)); } ImGui::EndDisabled(); if (ImGui::MenuItem("Show Content Directory")) { const auto path = profile_manager->GetProfileContentPath( xuid, emulator_window_->emulator()->kernel_state()->title_id()); if (!std::filesystem::exists(path)) { std::filesystem::create_directories(path); } std::thread path_open(LaunchFileExplorer, path); path_open.detach(); } if (!emulator_window_->emulator()->is_title_open()) { ImGui::Separator(); if (ImGui::BeginMenu("Delete Profile")) { ImGui::BeginTooltip(); ImGui::TextUnformatted( fmt::format( "You're about to delete profile: {} (XUID: {:016X}). " "This will remove all data assigned to this profile " "including savefiles. Are you sure?", account.GetGamertagString(), xuid) .c_str()); ImGui::EndTooltip(); if (ImGui::MenuItem("Yes, delete it!")) { profile_manager->DeleteProfile(xuid); ImGui::EndMenu(); ImGui::EndPopup(); return false; } ImGui::EndMenu(); } } ImGui::EndPopup(); } return true; }; if (!kernel::xam::xeDrawProfileContent( imgui_drawer(), xuid, user_index, &account, profile_icon, context_menu_fun, [=, this]() { LoadProfileIcon(xuid); }, &selected_xuid_)) { ImGui::PopID(); ImGui::End(); return; } ImGui::PopID(); ImGui::Separator(); } ImGui::Spacing(); if (ImGui::Button("Create Profile")) { new kernel::xam::ui::CreateProfileUI(emulator_window_->imgui_drawer(), emulator_window_->emulator()); } ImGui::End(); } } // namespace app } // namespace xe