FullscreenUI: Fix several more unsafe reads

This commit is contained in:
Stenzek 2025-01-20 19:00:51 +10:00
parent cc0ccf2648
commit 78902d7632
No known key found for this signature in database
3 changed files with 95 additions and 91 deletions

View File

@ -419,9 +419,9 @@ static bool InitializeSaveStateListEntryFromSerial(SaveStateListEntry* li, const
bool global); bool global);
static bool InitializeSaveStateListEntryFromPath(SaveStateListEntry* li, std::string path, s32 slot, bool global); static bool InitializeSaveStateListEntryFromPath(SaveStateListEntry* li, std::string path, s32 slot, bool global);
static void ClearSaveStateEntryList(); static void ClearSaveStateEntryList();
static u32 PopulateSaveStateListEntries(const std::string& title, const std::string& serial); static u32 PopulateSaveStateListEntries(const std::string& serial,
static bool OpenLoadStateSelectorForGame(const std::string& game_path); std::optional<ExtendedSaveStateInfo> undo_save_state);
static bool OpenSaveStateSelector(bool is_loading); static void OpenSaveStateSelector(const std::string& serial, const std::string& path, bool is_loading);
static void CloseSaveStateSelector(); static void CloseSaveStateSelector();
static void DrawSaveStateSelector(bool is_loading); static void DrawSaveStateSelector(bool is_loading);
static bool OpenLoadStateSelectorForGameResume(const GameList::Entry* entry); static bool OpenLoadStateSelectorForGameResume(const GameList::Entry* entry);
@ -486,7 +486,9 @@ struct ALIGN_TO_CACHE_LINE UIState
bool was_paused_on_quick_menu_open = false; bool was_paused_on_quick_menu_open = false;
bool about_window_open = false; bool about_window_open = false;
bool achievements_login_window_open = false; bool achievements_login_window_open = false;
std::string current_game_subtitle; std::string current_game_title;
std::string current_game_serial;
std::string current_game_path;
std::string achievements_user_badge_path; std::string achievements_user_badge_path;
// Resources // Resources
@ -672,6 +674,15 @@ bool FullscreenUI::Initialize()
SwitchToLanding(); SwitchToLanding();
} }
// in case we open the pause menu while the game is running
if (GPUThread::HasGPUBackend())
{
Host::RunOnCPUThread([]() {
if (System::IsValid())
FullscreenUI::OnRunningGameChanged(System::GetDiscPath(), System::GetGameSerial(), System::GetGameTitle());
});
}
LoadBackground(); LoadBackground();
UpdateRunIdleState(); UpdateRunIdleState();
ForceKeyNavEnabled(); ForceKeyNavEnabled();
@ -758,32 +769,25 @@ void FullscreenUI::OnSystemDestroyed()
}); });
} }
void FullscreenUI::OnRunningGameChanged() void FullscreenUI::OnRunningGameChanged(const std::string& path, const std::string& serial, const std::string& title)
{ {
// NOTE: Called on CPU thread. // NOTE: Called on CPU thread.
if (!IsInitialized()) if (!IsInitialized())
return; return;
const std::string& path = System::GetDiscPath(); GPUThread::RunOnThread([path = path, title = title, serial = serial]() mutable {
const std::string& serial = System::GetGameSerial();
std::string subtitle;
if (!serial.empty())
subtitle = fmt::format("{0} - {1}", serial, Path::GetFileName(path));
else
subtitle = {};
GPUThread::RunOnThread([subtitle = std::move(subtitle)]() mutable {
if (!IsInitialized()) if (!IsInitialized())
return; return;
s_state.current_game_subtitle = std::move(subtitle); s_state.current_game_title = std::move(title);
s_state.current_game_serial = std::move(serial);
s_state.current_game_path = std::move(path);
}); });
} }
void FullscreenUI::PauseForMenuOpen(bool set_pause_menu_open) void FullscreenUI::PauseForMenuOpen(bool set_pause_menu_open)
{ {
s_state.was_paused_on_quick_menu_open = (System::GetState() == System::State::Paused); s_state.was_paused_on_quick_menu_open = GPUThread::IsSystemPaused();
if (!s_state.was_paused_on_quick_menu_open) if (!s_state.was_paused_on_quick_menu_open)
Host::RunOnCPUThread([]() { System::PauseSystem(true); }); Host::RunOnCPUThread([]() { System::PauseSystem(true); });
@ -894,7 +898,9 @@ void FullscreenUI::Shutdown(bool clear_state)
s_state.fullscreen_mode_list_cache = {}; s_state.fullscreen_mode_list_cache = {};
s_state.graphics_adapter_list_cache = {}; s_state.graphics_adapter_list_cache = {};
s_state.hotkey_list_cache = {}; s_state.hotkey_list_cache = {};
s_state.current_game_subtitle = {}; s_state.current_game_path = {};
s_state.current_game_serial = {};
s_state.current_game_title = {};
} }
DestroyResources(); DestroyResources();
@ -1301,7 +1307,7 @@ void FullscreenUI::DoChangeDiscFromFile()
}; };
OpenFileSelector(FSUI_ICONSTR(ICON_FA_COMPACT_DISC, "Select Disc Image"), false, std::move(callback), OpenFileSelector(FSUI_ICONSTR(ICON_FA_COMPACT_DISC, "Select Disc Image"), false, std::move(callback),
GetDiscImageFilters(), std::string(Path::GetDirectory(System::GetDiscPath()))); GetDiscImageFilters(), std::string(Path::GetDirectory(s_state.current_game_path)));
}); });
} }
@ -1897,7 +1903,7 @@ void FullscreenUI::DrawStartGameWindow()
if (!AreAnyDialogsOpen()) if (!AreAnyDialogsOpen())
{ {
if (ImGui::IsKeyPressed(ImGuiKey_NavGamepadMenu, false) || ImGui::IsKeyPressed(ImGuiKey_F1, false)) if (ImGui::IsKeyPressed(ImGuiKey_NavGamepadMenu, false) || ImGui::IsKeyPressed(ImGuiKey_F1, false))
OpenSaveStateSelector(true); OpenSaveStateSelector(std::string(), std::string(), true);
} }
if (IsGamepadInputSource()) if (IsGamepadInputSource())
@ -3311,14 +3317,14 @@ void FullscreenUI::SwitchToGameSettingsForSerial(std::string_view serial)
bool FullscreenUI::SwitchToGameSettings() bool FullscreenUI::SwitchToGameSettings()
{ {
if (System::GetGameSerial().empty()) if (s_state.current_game_serial.empty())
return false; return false;
auto lock = GameList::GetLock(); auto lock = GameList::GetLock();
const GameList::Entry* entry = GameList::GetEntryForPath(System::GetDiscPath()); const GameList::Entry* entry = GameList::GetEntryForPath(s_state.current_game_path);
if (!entry) if (!entry)
{ {
SwitchToGameSettingsForSerial(System::GetGameSerial()); SwitchToGameSettingsForSerial(s_state.current_game_serial);
return true; return true;
} }
else else
@ -6477,20 +6483,19 @@ void FullscreenUI::DrawPauseMenu()
// title info // title info
{ {
const std::string& title = System::GetGameTitle(); if (!s_state.current_game_serial.empty())
const std::string& serial = System::GetGameSerial(); buffer.format("{} - {}", s_state.current_game_serial, Path::GetFileName(s_state.current_game_path));
else
if (!serial.empty()) buffer.assign(Path::GetFileName(s_state.current_game_path));
buffer.format("{} - ", serial);
buffer.append(Path::GetFileName(System::GetDiscPath()));
const float image_width = 60.0f; const float image_width = 60.0f;
const float image_height = 60.0f; const float image_height = 60.0f;
const ImVec2 title_size(UIStyle.LargeFont->CalcTextSizeA(UIStyle.LargeFont->FontSize, const ImVec2 title_size(UIStyle.LargeFont->CalcTextSizeA(
std::numeric_limits<float>::max(), -1.0f, title.c_str())); UIStyle.LargeFont->FontSize, std::numeric_limits<float>::max(), -1.0f, s_state.current_game_title.c_str(),
s_state.current_game_title.c_str() + s_state.current_game_title.size()));
const ImVec2 subtitle_size(UIStyle.MediumFont->CalcTextSizeA( const ImVec2 subtitle_size(UIStyle.MediumFont->CalcTextSizeA(
UIStyle.MediumFont->FontSize, std::numeric_limits<float>::max(), -1.0f, buffer.c_str())); UIStyle.MediumFont->FontSize, std::numeric_limits<float>::max(), -1.0f, buffer.c_str(), buffer.end_ptr()));
ImVec2 title_pos(display_size.x - LayoutScale(10.0f + image_width + 20.0f) - title_size.x, ImVec2 title_pos(display_size.x - LayoutScale(10.0f + image_width + 20.0f) - title_size.x,
display_size.y - LayoutScale(LAYOUT_FOOTER_HEIGHT) - LayoutScale(10.0f + image_height)); display_size.y - LayoutScale(LAYOUT_FOOTER_HEIGHT) - LayoutScale(10.0f + image_height));
@ -6522,8 +6527,9 @@ void FullscreenUI::DrawPauseMenu()
} }
} }
DrawShadowedText(dl, UIStyle.LargeFont, title_pos, text_color, title.c_str()); DrawShadowedText(dl, UIStyle.LargeFont, title_pos, text_color, s_state.current_game_title.c_str(),
DrawShadowedText(dl, UIStyle.MediumFont, subtitle_pos, text_color, buffer.c_str()); s_state.current_game_title.c_str() + s_state.current_game_title.size());
DrawShadowedText(dl, UIStyle.MediumFont, subtitle_pos, text_color, buffer.c_str(), buffer.end_ptr());
GPUTexture* const cover = GetCoverForCurrentGame(); GPUTexture* const cover = GetCoverForCurrentGame();
const ImVec2 image_min(display_size.x - LayoutScale(10.0f + image_width), const ImVec2 image_min(display_size.x - LayoutScale(10.0f + image_width),
@ -6544,10 +6550,9 @@ void FullscreenUI::DrawPauseMenu()
const ImVec2 time_pos(display_size.x - LayoutScale(10.0f) - time_size.x, LayoutScale(10.0f)); const ImVec2 time_pos(display_size.x - LayoutScale(10.0f) - time_size.x, LayoutScale(10.0f));
DrawShadowedText(dl, UIStyle.LargeFont, time_pos, text_color, buffer.c_str(), buffer.end_ptr()); DrawShadowedText(dl, UIStyle.LargeFont, time_pos, text_color, buffer.c_str(), buffer.end_ptr());
const std::string& serial = System::GetGameSerial(); if (!s_state.current_game_serial.empty())
if (!serial.empty())
{ {
const std::time_t cached_played_time = GameList::GetCachedPlayedTimeForSerial(serial); const std::time_t cached_played_time = GameList::GetCachedPlayedTimeForSerial(s_state.current_game_serial);
const std::time_t session_time = static_cast<std::time_t>(System::GetSessionPlayedTime()); const std::time_t session_time = static_cast<std::time_t>(System::GetSessionPlayedTime());
buffer.format(FSUI_FSTR("Session: {}"), GameList::FormatTimespan(session_time, true)); buffer.format(FSUI_FSTR("Session: {}"), GameList::FormatTimespan(session_time, true));
@ -6588,7 +6593,7 @@ void FullscreenUI::DrawPauseMenu()
case PauseSubMenu::None: case PauseSubMenu::None:
{ {
// NOTE: Menu close must come first, because otherwise VM destruction options will race. // NOTE: Menu close must come first, because otherwise VM destruction options will race.
const bool has_game = GPUThread::HasGPUBackend() && !System::GetGameSerial().empty(); const bool has_game = GPUThread::HasGPUBackend();
if (DefaultActiveButton(FSUI_ICONSTR(ICON_FA_PLAY, "Resume Game"), false) || WantsToCloseMenu()) if (DefaultActiveButton(FSUI_ICONSTR(ICON_FA_PLAY, "Resume Game"), false) || WantsToCloseMenu())
ClosePauseMenu(); ClosePauseMenu();
@ -6600,16 +6605,10 @@ void FullscreenUI::DrawPauseMenu()
} }
if (ActiveButton(FSUI_ICONSTR(ICON_FA_UNDO, "Load State"), false, has_game)) if (ActiveButton(FSUI_ICONSTR(ICON_FA_UNDO, "Load State"), false, has_game))
{ OpenSaveStateSelector(s_state.current_game_serial, s_state.current_game_path, true);
if (OpenSaveStateSelector(true))
s_state.current_main_window = MainWindowType::None;
}
if (ActiveButton(FSUI_ICONSTR(ICON_FA_DOWNLOAD, "Save State"), false, has_game)) if (ActiveButton(FSUI_ICONSTR(ICON_FA_DOWNLOAD, "Save State"), false, has_game))
{ OpenSaveStateSelector(s_state.current_game_serial, s_state.current_game_path, false);
if (OpenSaveStateSelector(false))
s_state.current_main_window = MainWindowType::None;
}
if (ActiveButton(FSUI_ICONSTR(ICON_FA_GAMEPAD, "Toggle Analog"), false)) if (ActiveButton(FSUI_ICONSTR(ICON_FA_GAMEPAD, "Toggle Analog"), false))
{ {
@ -6634,7 +6633,7 @@ void FullscreenUI::DrawPauseMenu()
if (ActiveButton(FSUI_ICONSTR(ICON_FA_CAMERA, "Save Screenshot"), false)) if (ActiveButton(FSUI_ICONSTR(ICON_FA_CAMERA, "Save Screenshot"), false))
{ {
System::SaveScreenshot(); Host::RunOnCPUThread([]() { System::SaveScreenshot(); });
ClosePauseMenu(); ClosePauseMenu();
} }
@ -6779,23 +6778,20 @@ void FullscreenUI::ClearSaveStateEntryList()
s_state.save_state_selector_slots.clear(); s_state.save_state_selector_slots.clear();
} }
u32 FullscreenUI::PopulateSaveStateListEntries(const std::string& title, const std::string& serial) u32 FullscreenUI::PopulateSaveStateListEntries(const std::string& serial,
std::optional<ExtendedSaveStateInfo> undo_save_state)
{ {
ClearSaveStateEntryList(); ClearSaveStateEntryList();
if (s_state.save_state_selector_loading) if (undo_save_state.has_value())
{
std::optional<ExtendedSaveStateInfo> ssi = System::GetUndoSaveStateInfo();
if (ssi)
{ {
SaveStateListEntry li; SaveStateListEntry li;
li.title = FSUI_STR("Undo Load State"); li.title = FSUI_STR("Undo Load State");
li.summary = FSUI_STR("Restores the state of the system prior to the last state loaded."); li.summary = FSUI_STR("Restores the state of the system prior to the last state loaded.");
if (ssi->screenshot.IsValid()) if (undo_save_state->screenshot.IsValid())
li.preview_texture = g_gpu_device->FetchAndUploadTextureImage(ssi->screenshot); li.preview_texture = g_gpu_device->FetchAndUploadTextureImage(undo_save_state->screenshot);
s_state.save_state_selector_slots.push_back(std::move(li)); s_state.save_state_selector_slots.push_back(std::move(li));
} }
}
if (!serial.empty()) if (!serial.empty())
{ {
@ -6817,40 +6813,45 @@ u32 FullscreenUI::PopulateSaveStateListEntries(const std::string& title, const s
return static_cast<u32>(s_state.save_state_selector_slots.size()); return static_cast<u32>(s_state.save_state_selector_slots.size());
} }
bool FullscreenUI::OpenLoadStateSelectorForGame(const std::string& game_path) void FullscreenUI::OpenSaveStateSelector(const std::string& serial, const std::string& path, bool is_loading)
{ {
auto lock = GameList::GetLock(); if (GPUThread::HasGPUBackend())
const GameList::Entry* entry = GameList::GetEntryForPath(game_path);
if (entry)
{ {
s_state.save_state_selector_loading = true; // need to get the undo state, if any
if (PopulateSaveStateListEntries(entry->title, entry->serial) > 0) Host::RunOnCPUThread([serial = serial, is_loading]() {
{ std::optional<ExtendedSaveStateInfo> undo_state = System::GetUndoSaveStateInfo();
s_state.save_state_selector_open = true; GPUThread::RunOnThread([serial = std::move(serial), undo_state = std::move(undo_state), is_loading]() mutable {
s_state.save_state_selector_resuming = false;
s_state.save_state_selector_game_path = game_path;
return true;
}
}
ShowToast({}, FSUI_STR("No save states found."), 5.0f);
return false;
}
bool FullscreenUI::OpenSaveStateSelector(bool is_loading)
{
s_state.save_state_selector_game_path = {};
s_state.save_state_selector_loading = is_loading; s_state.save_state_selector_loading = is_loading;
s_state.save_state_selector_resuming = false; s_state.save_state_selector_resuming = false;
if (PopulateSaveStateListEntries(System::GetGameTitle(), System::GetGameSerial()) > 0) s_state.save_state_selector_game_path = {};
if (PopulateSaveStateListEntries(serial, std::move(undo_state)) > 0)
{ {
s_state.current_main_window = MainWindowType::None;
s_state.save_state_selector_open = true; s_state.save_state_selector_open = true;
QueueResetFocus(FocusResetType::PopupOpened); QueueResetFocus(FocusResetType::PopupOpened);
return true;
} }
else
ShowToast({}, FSUI_STR("No save states found."), 5.0f); {
return false; ShowToast({}, FSUI_STR("No save states found."), Host::OSD_INFO_DURATION);
}
});
});
}
else
{
s_state.save_state_selector_loading = is_loading;
s_state.save_state_selector_resuming = false;
if (PopulateSaveStateListEntries(serial, std::nullopt) > 0)
{
s_state.save_state_selector_game_path = path;
s_state.save_state_selector_open = true;
}
else
{
s_state.save_state_selector_game_path = {};
ShowToast({}, FSUI_STR("No save states found."), Host::OSD_INFO_DURATION);
}
}
} }
void FullscreenUI::CloseSaveStateSelector() void FullscreenUI::CloseSaveStateSelector()
@ -7210,7 +7211,7 @@ bool FullscreenUI::OpenLoadStateSelectorForGameResume(const GameList::Entry* ent
void FullscreenUI::DrawResumeStateSelector() void FullscreenUI::DrawResumeStateSelector()
{ {
ImGui::SetNextWindowSize(LayoutScale(800.0f, 602.0f)); ImGui::SetNextWindowSize(LayoutScale(800.0f, 605.0f));
ImGui::SetNextWindowPos(ImGui::GetIO().DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f)); ImGui::SetNextWindowPos(ImGui::GetIO().DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
ImGui::OpenPopup(FSUI_CSTR("Load Resume State")); ImGui::OpenPopup(FSUI_CSTR("Load Resume State"));
@ -7934,7 +7935,7 @@ void FullscreenUI::HandleGameListOptions(const GameList::Entry* entry)
DoStartPath(entry_path, System::GetGameSaveStateFileName(entry_serial, -1)); DoStartPath(entry_path, System::GetGameSaveStateFileName(entry_serial, -1));
break; break;
case 4: // Load State case 4: // Load State
OpenLoadStateSelectorForGame(entry_path); OpenSaveStateSelector(entry_serial, entry_path, true);
break; break;
case 5: // Default Boot case 5: // Default Boot
DoStartPath(entry_path); DoStartPath(entry_path);
@ -8259,7 +8260,7 @@ GPUTexture* FullscreenUI::GetCoverForCurrentGame()
{ {
auto lock = GameList::GetLock(); auto lock = GameList::GetLock();
const GameList::Entry* entry = GameList::GetEntryForPath(System::GetDiscPath()); const GameList::Entry* entry = GameList::GetEntryForPath(s_state.current_game_path);
if (!entry) if (!entry)
return s_state.fallback_disc_texture.get(); return s_state.fallback_disc_texture.get();

View File

@ -23,7 +23,7 @@ void CheckForConfigChanges(const GPUSettings& old_settings);
void OnSystemStarted(); void OnSystemStarted();
void OnSystemResumed(); void OnSystemResumed();
void OnSystemDestroyed(); void OnSystemDestroyed();
void OnRunningGameChanged(); void OnRunningGameChanged(const std::string& path, const std::string& serial, const std::string& title);
#ifndef __ANDROID__ #ifndef __ANDROID__
void OpenPauseMenu(); void OpenPauseMenu();

View File

@ -4208,6 +4208,9 @@ void System::UpdateRunningGame(const std::string& path, CDImage* image, bool boo
UpdateRichPresence(booting); UpdateRichPresence(booting);
FullscreenUI::OnRunningGameChanged(s_state.running_game_path, s_state.running_game_serial,
s_state.running_game_title);
Host::OnGameChanged(s_state.running_game_path, s_state.running_game_serial, s_state.running_game_title); Host::OnGameChanged(s_state.running_game_path, s_state.running_game_serial, s_state.running_game_title);
} }