diff --git a/premake5.lua b/premake5.lua index e62277a2c..8f2ca3ff7 100644 --- a/premake5.lua +++ b/premake5.lua @@ -49,13 +49,9 @@ fatalwarnings("All") -- TODO(DrChat): Find a way to disable this on other architectures. if ARCH ~= "ppc64" then - filter({"architecture:x86_64", "platforms:Windows"}) - -- Use AVX instead of AVX2 on Windows to prevent MSVC from emitting BMI2 instructions - -- MSVC with /arch:AVX2 generates BMI2 unconditionally, breaking Sandy/Ivy Bridge CPUs + filter({"architecture:x86_64"}) + -- AVX2 requires Haswell (2013+) or newer vectorextensions("AVX") - filter({"architecture:x86_64", "platforms:not Windows"}) - -- On non-Windows (Linux/Clang), AVX2 is safe as it can be combined with -mno-bmi2 - vectorextensions("AVX2") filter({}) end diff --git a/src/xenia/base/memory.cc b/src/xenia/base/memory.cc index 09d899ba6..8d9cb3dc6 100644 --- a/src/xenia/base/memory.cc +++ b/src/xenia/base/memory.cc @@ -308,6 +308,11 @@ void copy_and_swap_32_aligned(void* dest_ptr, const void* src_ptr, } } +// Enable AVX2 for this function even when building with -mavx +// The function has runtime detection to only use AVX2 on supported CPUs +#if defined(__GNUC__) || defined(__clang__) +__attribute__((target("avx2"))) +#endif void copy_and_swap_32_unaligned(void* dest_ptr, const void* src_ptr, size_t count) { auto dest = reinterpret_cast(dest_ptr); diff --git a/src/xenia/kernel/xam/profile_manager.cc b/src/xenia/kernel/xam/profile_manager.cc index 1035a9351..427d381c8 100644 --- a/src/xenia/kernel/xam/profile_manager.cc +++ b/src/xenia/kernel/xam/profile_manager.cc @@ -162,30 +162,46 @@ void ProfileManager::SyncProfilesWithConfig() { LoadAccount(account_xuid); } - // Then logout all currently logged in profiles - std::vector slots_to_logout; - for (const auto& [slot, profile] : logged_profiles_) { - slots_to_logout.push_back(slot); - } - for (uint8_t slot : slots_to_logout) { - Logout(slot, false); - } - - // Now login profiles based on the current cvar values + // Build desired login state from current cvar values const std::string* profile_cvars[4] = { &cvars::logged_profile_slot_0_xuid, &cvars::logged_profile_slot_1_xuid, &cvars::logged_profile_slot_2_xuid, &cvars::logged_profile_slot_3_xuid}; + std::map desired_profiles; for (uint8_t slot = 0; slot < 4; slot++) { if (!profile_cvars[slot]->empty()) { uint64_t xuid = xe::string_util::from_string(*profile_cvars[slot], true); if (xuid != 0) { - Login(xuid, slot, false); + desired_profiles[slot] = xuid; } } } + // Logout profiles that shouldn't be logged in anymore + std::vector slots_to_logout; + for (const auto& [slot, profile] : logged_profiles_) { + auto desired_it = desired_profiles.find(slot); + if (desired_it == desired_profiles.end() || + desired_it->second != profile->xuid()) { + // This slot should be logged out (either empty or different profile) + slots_to_logout.push_back(slot); + } + } + for (uint8_t slot : slots_to_logout) { + Logout(slot, false); + } + + // Login profiles that aren't already logged in + for (const auto& [slot, xuid] : desired_profiles) { + auto current_profile = logged_profiles_.find(slot); + if (current_profile == logged_profiles_.end() || + current_profile->second->xuid() != xuid) { + // This slot needs to be logged in (either empty or different profile) + Login(xuid, slot, false); + } + } + // Send a single notification after all changes kernel_state_->BroadcastNotification(kXNotificationSystemSignInChanged, GetUsedUserSlots().to_ulong()); diff --git a/src/xenia/ui/game_list_dialog_qt.cc b/src/xenia/ui/game_list_dialog_qt.cc index 00fc4623a..28dbc5985 100644 --- a/src/xenia/ui/game_list_dialog_qt.cc +++ b/src/xenia/ui/game_list_dialog_qt.cc @@ -805,35 +805,80 @@ void GameListDialogQt::OnGameRightClicked(const QPoint& pos) { launch_action = context_menu.addAction("Open"); } - // Achievements option (enabled if user is logged in) - QAction* achievements_action = nullptr; + // Get the primary profile (same one shown in profile button) bool is_signedin = false; uint64_t xuid = 0; - // TODO: Handle multiple signed-in profiles better - // For now, just use the first logged-in profile we find - for (uint8_t user_index = 0; user_index < XUserMaxUserCount; user_index++) { - const auto profile = profile_manager->GetProfile(user_index); - if (profile) { - is_signedin = true; - xuid = profile->xuid(); - break; + // Use the profile from slot 0 as primary (matches profile button logic) + const auto primary_profile = + profile_manager->GetProfile(static_cast(0)); + if (primary_profile) { + is_signedin = true; + xuid = primary_profile->xuid(); + } + + // Saves folder option (enabled if user is logged in, we have a valid + // title_id, and saves exist) + QAction* saves_action = nullptr; + if (title_id != 0) { + saves_action = context_menu.addAction("Saves"); + if (!is_signedin) { + saves_action->setEnabled(false); + } else { + // Check if saves folder exists + auto saves_path = profile_manager->GetProfileContentPath( + xuid, title_id, XContentType::kSavedGame); + if (!std::filesystem::exists(saves_path)) { + saves_action->setEnabled(false); + } } } - if (is_signedin && title_id != 0) { + // Title Updates folder option (enabled if we have a valid title_id and + // updates exist) + QAction* title_updates_action = nullptr; + if (title_id != 0) { + title_updates_action = context_menu.addAction("Title Updates"); + // Title updates use xuid=0 and content type kInstaller + auto tu_path = profile_manager->GetProfileContentPath( + 0, title_id, XContentType::kInstaller); + if (!std::filesystem::exists(tu_path)) { + title_updates_action->setEnabled(false); + } + } + + // DLC folder option (enabled if we have a valid title_id and DLC exists) + QAction* dlc_action = nullptr; + if (title_id != 0) { + dlc_action = context_menu.addAction("DLC"); + // DLC uses xuid=0 and content type kMarketplaceContent + auto dlc_path = profile_manager->GetProfileContentPath( + 0, title_id, XContentType::kMarketplaceContent); + if (!std::filesystem::exists(dlc_path)) { + dlc_action->setEnabled(false); + } + } + + // Achievements option (enabled if user is logged in and we have a valid + // title_id) + QAction* achievements_action = nullptr; + if (title_id != 0) { achievements_action = context_menu.addAction("Achievements"); - connect(achievements_action, &QAction::triggered, [=, this]() { - // Get the title name from the game entry - QString title_name; - for (const auto& entry : game_entries_) { - if (entry.title_id == title_id) { - title_name = QString::fromStdString(entry.title_name); - break; + if (!is_signedin) { + achievements_action->setEnabled(false); + } else { + connect(achievements_action, &QAction::triggered, [=, this]() { + // Get the title name from the game entry + QString title_name; + for (const auto& entry : game_entries_) { + if (entry.title_id == title_id) { + title_name = QString::fromStdString(entry.title_name); + break; + } } - } - ShowAchievementsDialog(xuid, title_id, title_name); - }); + ShowAchievementsDialog(xuid, title_id, title_name); + }); + } } // Game config overrides option (enabled if we have a valid title_id) @@ -868,6 +913,24 @@ void GameListDialogQt::OnGameRightClicked(const QPoint& pos) { } } else if (selected == open_folder_action && open_folder_action) { OpenContainingFolder(path); + } else if (selected == saves_action && saves_action) { + // Open the saves folder for the primary profile + auto saves_path = profile_manager->GetProfileContentPath( + xuid, title_id, XContentType::kSavedGame); + std::thread path_open(LaunchFileExplorer, saves_path); + path_open.detach(); + } else if (selected == title_updates_action && title_updates_action) { + // Open the title updates folder + auto tu_path = profile_manager->GetProfileContentPath( + 0, title_id, XContentType::kInstaller); + std::thread path_open(LaunchFileExplorer, tu_path); + path_open.detach(); + } else if (selected == dlc_action && dlc_action) { + // Open the DLC folder + auto dlc_path = profile_manager->GetProfileContentPath( + 0, title_id, XContentType::kMarketplaceContent); + std::thread path_open(LaunchFileExplorer, dlc_path); + path_open.detach(); } else if (selected == achievements_action && achievements_action) { // Achievements dialog will open in a separate call } else if (selected == config_action && config_action) {