diff --git a/src/core/fullscreen_ui.cpp b/src/core/fullscreen_ui.cpp index ac43743aa..67ed84036 100644 --- a/src/core/fullscreen_ui.cpp +++ b/src/core/fullscreen_ui.cpp @@ -706,7 +706,8 @@ bool FullscreenUI::AreAnyDialogsOpen() { return (s_state.save_state_selector_open || s_state.about_window_open || s_state.input_binding_type != InputBindingInfo::Type::Unknown || ImGuiFullscreen::IsChoiceDialogOpen() || - ImGuiFullscreen::IsFileSelectorOpen()); + ImGuiFullscreen::IsInputDialogOpen() || ImGuiFullscreen::IsFileSelectorOpen() || + ImGuiFullscreen::IsMessageBoxDialogOpen()); } void FullscreenUI::CheckForConfigChanges(const GPUSettings& old_settings) @@ -720,7 +721,7 @@ void FullscreenUI::UpdateRunIdleState() GPUThread::SetRunIdleReason(GPUThread::RunIdleReason::FullscreenUIActive, new_run_idle); } -void FullscreenUI::OnSystemStarted() +void FullscreenUI::OnSystemStarting() { // NOTE: Called on CPU thread. if (!IsInitialized()) @@ -1109,34 +1110,37 @@ void FullscreenUI::DoStartPath(std::string path, std::string state, std::optiona if (GPUThread::HasGPUBackend()) return; - // Switch to nothing, we'll get called back via OnSystemDestroyed() if startup fails. - const MainWindowType prev_main_window = std::exchange(s_state.current_main_window, MainWindowType::None); - QueueResetFocus(FocusResetType::ViewChanged); + // Stop running idle to prevent game list from being redrawn until we know if startup succeeded. GPUThread::SetRunIdleReason(GPUThread::RunIdleReason::FullscreenUIActive, false); SystemBootParameters params; params.filename = std::move(path); params.save_state = std::move(state); params.override_fast_boot = std::move(fast_boot); - Host::RunOnCPUThread([params = std::move(params), prev_main_window]() { + Host::RunOnCPUThread([params = std::move(params)]() mutable { if (System::IsValid()) return; + // This can "fail" if HC mode is enabled and the user cancels, or other startup cancel paths. + // In that case, we need to re-trigger the idle state so the user can interact with the message. + // But we can skip it if we have a system, because OnSystemStarted() fixed it up. Error error; - if (!System::BootSystem(std::move(params), &error)) - { - GPUThread::RunOnThread([error_desc = error.TakeDescription(), prev_main_window]() { - if (!IsInitialized()) - return; + const bool result = System::BootSystem(std::move(params), &error); + if (result && System::IsValid()) + return; + GPUThread::RunOnThread([error_desc = error.TakeDescription(), result]() { + if (!IsInitialized()) + return; + + if (!result) + { OpenInfoMessageDialog(TRANSLATE_STR("System", "Error"), fmt::format(TRANSLATE_FS("System", "Failed to boot system: {}"), error_desc)); - // ReturnToPreviousWindow(); - s_state.current_main_window = prev_main_window; - QueueResetFocus(FocusResetType::ViewChanged); - UpdateRunIdleState(); - }); - } + } + + UpdateRunIdleState(); + }); }); } diff --git a/src/core/fullscreen_ui.h b/src/core/fullscreen_ui.h index 96db950ab..f94122aa7 100644 --- a/src/core/fullscreen_ui.h +++ b/src/core/fullscreen_ui.h @@ -20,7 +20,7 @@ bool Initialize(); bool IsInitialized(); bool HasActiveWindow(); void CheckForConfigChanges(const GPUSettings& old_settings); -void OnSystemStarted(); +void OnSystemStarting(); void OnSystemResumed(); void OnSystemDestroyed(); void OnRunningGameChanged(const std::string& path, const std::string& serial, const std::string& title); diff --git a/src/core/system.cpp b/src/core/system.cpp index 0721d823b..d17df9f19 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -223,7 +223,6 @@ static void DoRewind(); static bool DoRunahead(); -static bool OpenGPUDump(std::string path, Error* error); static bool ChangeGPUDump(std::string new_path); static void UpdateSessionTime(const std::string& prev_serial); @@ -1657,18 +1656,13 @@ bool System::BootSystem(SystemBootParameters parameters, Error* error) else INFO_LOG("Boot Filename: {}", parameters.filename); - Assert(s_state.state == State::Shutdown); - s_state.state = State::Starting; - s_state.startup_cancelled.store(false, std::memory_order_relaxed); - s_state.region = g_settings.region; - std::atomic_thread_fence(std::memory_order_release); - Host::OnSystemStarting(); - // Load CD image up and detect region. std::unique_ptr disc; + ConsoleRegion console_region = ConsoleRegion::Auto; DiscRegion disc_region = DiscRegion::NonPS1; BootMode boot_mode = BootMode::FullBoot; std::string exe_override; + std::unique_ptr gpu_dump; if (!parameters.filename.empty()) { if (IsExePath(parameters.filename)) @@ -1683,24 +1677,22 @@ bool System::BootSystem(SystemBootParameters parameters, Error* error) } else if (IsGPUDumpPath(parameters.filename)) { - if (!OpenGPUDump(parameters.filename, error)) - { - DestroySystem(); + gpu_dump = GPUDump::Player::Open(parameters.filename, error); + if (!gpu_dump) return false; - } boot_mode = BootMode::ReplayGPUDump; } if (boot_mode == BootMode::BootEXE || boot_mode == BootMode::BootPSF) { - if (s_state.region == ConsoleRegion::Auto) + if (console_region == ConsoleRegion::Auto) { const DiscRegion file_region = ((boot_mode == BootMode::BootEXE) ? GetRegionForExe(parameters.filename.c_str()) : GetRegionForPsf(parameters.filename.c_str())); INFO_LOG("EXE/PSF Region: {}", Settings::GetDiscRegionDisplayName(file_region)); - s_state.region = GetConsoleRegionForDiscRegion(file_region); + console_region = GetConsoleRegionForDiscRegion(file_region); } } else if (boot_mode != BootMode::ReplayGPUDump) @@ -1710,25 +1702,24 @@ bool System::BootSystem(SystemBootParameters parameters, Error* error) if (!disc) { Error::AddPrefixFmt(error, "Failed to open CD image '{}':\n", Path::GetFileName(parameters.filename)); - DestroySystem(); return false; } disc_region = GameList::GetCustomRegionForPath(parameters.filename).value_or(GetRegionForImage(disc.get())); - if (s_state.region == ConsoleRegion::Auto) + if (console_region == ConsoleRegion::Auto) { if (disc_region != DiscRegion::Other) { - s_state.region = GetConsoleRegionForDiscRegion(disc_region); + console_region = GetConsoleRegionForDiscRegion(disc_region); INFO_LOG("Auto-detected console {} region for '{}' (region {})", - Settings::GetConsoleRegionName(s_state.region), parameters.filename, + Settings::GetConsoleRegionName(console_region), parameters.filename, Settings::GetDiscRegionName(disc_region)); } else { - s_state.region = ConsoleRegion::NTSC_U; + console_region = ConsoleRegion::NTSC_U; WARNING_LOG("Could not determine console region for disc region {}. Defaulting to {}.", - Settings::GetDiscRegionName(disc_region), Settings::GetConsoleRegionName(s_state.region)); + Settings::GetDiscRegionName(disc_region), Settings::GetConsoleRegionName(console_region)); } } } @@ -1736,21 +1727,72 @@ bool System::BootSystem(SystemBootParameters parameters, Error* error) else { // Default to NTSC for BIOS boot. - if (s_state.region == ConsoleRegion::Auto) - s_state.region = ConsoleRegion::NTSC_U; + if (console_region == ConsoleRegion::Auto) + console_region = ConsoleRegion::NTSC_U; } - INFO_LOG("Console Region: {}", Settings::GetConsoleRegionDisplayName(s_state.region)); + INFO_LOG("Console Region: {}", Settings::GetConsoleRegionDisplayName(console_region)); // Switch subimage. if (disc && parameters.media_playlist_index != 0 && !disc->SwitchSubImage(parameters.media_playlist_index, error)) { Error::AddPrefixFmt(error, "Failed to switch to subimage {} in '{}':\n", parameters.media_playlist_index, Path::GetFileName(parameters.filename)); - DestroySystem(); return false; } + // Achievement hardcore checks before committing to anything. + if (!IsReplayingGPUDump()) + { + // Check for resuming with hardcore mode. + if (parameters.disable_achievements_hardcore_mode) + Achievements::DisableHardcoreMode(); + else + Achievements::ResetHardcoreMode(true); + + if ((!parameters.save_state.empty() || !exe_override.empty()) && Achievements::IsHardcoreModeActive()) + { + const bool is_exe_override_boot = parameters.save_state.empty(); + bool cancelled; + if (FullscreenUI::IsInitialized()) + { + Achievements::ConfirmHardcoreModeDisableAsync(is_exe_override_boot ? + TRANSLATE("Achievements", "Overriding executable") : + TRANSLATE("Achievements", "Resuming state"), + [parameters = std::move(parameters)](bool approved) mutable { + if (approved) + { + parameters.disable_achievements_hardcore_mode = true; + BootSystem(std::move(parameters), nullptr); + } + }); + cancelled = true; + } + else + { + cancelled = !Achievements::ConfirmHardcoreModeDisable(is_exe_override_boot ? + TRANSLATE("Achievements", "Overriding executable") : + TRANSLATE("Achievements", "Resuming state")); + } + + if (cancelled) + { + // Technically a failure, but user-initiated. Returning false here would try to display a non-existent error. + return true; + } + } + } + + // Can't early cancel without destroying past this point. + Assert(s_state.state == State::Shutdown); + s_state.state = State::Starting; + s_state.region = console_region; + s_state.startup_cancelled.store(false, std::memory_order_relaxed); + s_state.gpu_dump_player = std::move(gpu_dump); + std::atomic_thread_fence(std::memory_order_release); + Host::OnSystemStarting(); + FullscreenUI::OnSystemStarting(); + // Update running game, this will apply settings as well. UpdateRunningGame(disc ? disc->GetPath() : parameters.filename, disc.get(), true); @@ -1770,42 +1812,6 @@ bool System::BootSystem(SystemBootParameters parameters, Error* error) exe_override = std::move(parameters.override_exe); } - // Check for resuming with hardcore mode. - if (parameters.disable_achievements_hardcore_mode) - Achievements::DisableHardcoreMode(); - if ((!parameters.save_state.empty() || !exe_override.empty()) && Achievements::IsHardcoreModeActive()) - { - const bool is_exe_override_boot = parameters.save_state.empty(); - bool cancelled; - if (FullscreenUI::IsInitialized()) - { - Achievements::ConfirmHardcoreModeDisableAsync(is_exe_override_boot ? - TRANSLATE("Achievements", "Overriding executable") : - TRANSLATE("Achievements", "Resuming state"), - [parameters = std::move(parameters)](bool approved) mutable { - if (approved) - { - parameters.disable_achievements_hardcore_mode = true; - BootSystem(std::move(parameters), nullptr); - } - }); - cancelled = true; - } - else - { - cancelled = !Achievements::ConfirmHardcoreModeDisable(is_exe_override_boot ? - TRANSLATE("Achievements", "Overriding executable") : - TRANSLATE("Achievements", "Resuming state")); - } - - if (cancelled) - { - // Technically a failure, but user-initiated. Returning false here would try to display a non-existent error. - DestroySystem(); - return true; - } - } - // Are we fast booting? Must be checked after updating game settings. if (boot_mode == BootMode::FullBoot && disc_region != DiscRegion::NonPS1 && parameters.override_fast_boot.value_or(static_cast(g_settings.bios_patch_fast_boot))) @@ -1847,8 +1853,6 @@ bool System::BootSystem(SystemBootParameters parameters, Error* error) return false; } - FullscreenUI::OnSystemStarted(); - InputManager::UpdateHostMouseMode(); if (g_settings.inhibit_screensaver) @@ -4189,9 +4193,6 @@ void System::UpdateRunningGame(const std::string& path, CDImage* image, bool boo if (!IsReplayingGPUDump()) { - if (booting) - Achievements::ResetHardcoreMode(true); - Achievements::GameChanged(s_state.running_game_path, image, booting); // game layer reloads cheats, but only the active list, we need new files @@ -5848,27 +5849,18 @@ void System::UpdateGTEAspectRatio() GTE::SetAspectRatio(gte_ar, custom_num, custom_denom); } -bool System::OpenGPUDump(std::string path, Error* error) -{ - std::unique_ptr new_dump = GPUDump::Player::Open(std::move(path), error); - if (!new_dump) - return false; - - // set properties - s_state.gpu_dump_player = std::move(new_dump); - s_state.region = s_state.gpu_dump_player->GetRegion(); - return true; -} - bool System::ChangeGPUDump(std::string new_path) { Error error; - if (!OpenGPUDump(std::move(new_path), &error)) + std::unique_ptr new_dump = GPUDump::Player::Open(std::move(new_path), &error); + if (!new_dump) { Host::ReportErrorAsync("Error", fmt::format(TRANSLATE_FS("Failed to change GPU dump: {}", error.GetDescription()))); return false; } + s_state.gpu_dump_player = std::move(new_dump); + s_state.region = s_state.gpu_dump_player->GetRegion(); UpdateRunningGame(s_state.gpu_dump_player->GetPath(), nullptr, false); // current player object has been changed out, toss call stack diff --git a/src/util/cd_image_chd.cpp b/src/util/cd_image_chd.cpp index 84735afc8..c22312d9c 100644 --- a/src/util/cd_image_chd.cpp +++ b/src/util/cd_image_chd.cpp @@ -449,7 +449,7 @@ CDImage::PrecacheResult CDImageCHD::Precache(ProgressCallback* progress) if (m_precached) return CDImage::PrecacheResult::Success; - progress->SetStatusText(fmt::format("Precaching {}...", FileSystem::GetDisplayNameFromPath(m_filename)).c_str()); + progress->SetStatusText("Precaching CHD..."); progress->SetProgressRange(100); auto callback = [](size_t pos, size_t total, void* param) {