diff --git a/src/core/system.cpp b/src/core/system.cpp index 632dcc650..cd7103d3a 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -125,6 +125,8 @@ static std::unique_ptr GetMemoryCardForSlot(u32 slot, MemoryCardType static void SetTimerResolutionIncreased(bool enabled); } // namespace System +static constexpr const float PERFORMANCE_COUNTER_UPDATE_INTERVAL = 1.0f; + static std::unique_ptr s_game_settings_interface; static std::unique_ptr s_input_settings_interface; static std::string s_input_profile_name; @@ -1103,20 +1105,24 @@ bool System::BootSystem(SystemBootParameters parameters) // Load CD image up and detect region. Common::Error error; std::unique_ptr media; - bool exe_boot = false; - bool psf_boot = false; + std::string exe_boot; + std::string psf_boot; if (!parameters.filename.empty()) { - exe_boot = IsExeFileName(parameters.filename.c_str()); - psf_boot = (!exe_boot && IsPsfFileName(parameters.filename.c_str())); - if (exe_boot || psf_boot) + const bool do_exe_boot = IsExeFileName(parameters.filename); + const bool do_psf_boot = (!do_exe_boot && IsPsfFileName(parameters.filename)); + if (do_exe_boot || do_psf_boot) { if (s_region == ConsoleRegion::Auto) { const DiscRegion file_region = - (exe_boot ? GetRegionForExe(parameters.filename.c_str()) : GetRegionForPsf(parameters.filename.c_str())); + (do_exe_boot ? GetRegionForExe(parameters.filename.c_str()) : GetRegionForPsf(parameters.filename.c_str())); Log_InfoPrintf("EXE/PSF Region: %s", Settings::GetDiscRegionDisplayName(file_region)); s_region = GetConsoleRegionForDiscRegion(file_region); + if (do_psf_boot) + psf_boot = std::move(parameters.filename); + else + exe_boot = std::move(parameters.filename); } } else @@ -1174,6 +1180,21 @@ bool System::BootSystem(SystemBootParameters parameters) // Update running game, this will apply settings as well. UpdateRunningGame(media ? media->GetFileName().c_str() : parameters.filename.c_str(), media.get(), true); + if (!parameters.override_exe.empty()) + { + if (!FileSystem::FileExists(parameters.override_exe.c_str()) || !IsExeFileName(parameters.override_exe)) + { + Host::ReportFormattedErrorAsync("Error", "File '%s' is not a valid executable to boot.", + parameters.override_exe.c_str()); + s_state = State::Shutdown; + Host::OnSystemDestroyed(); + return false; + } + + Log_InfoPrintf("Overriding boot executable: '%s'", parameters.override_exe.c_str()); + exe_boot = std::move(parameters.override_exe); + } + // Check for SBI. if (!CheckForSBIFile(media.get())) { @@ -1217,7 +1238,7 @@ bool System::BootSystem(SystemBootParameters parameters) } // Allow controller analog mode for EXEs and PSFs. - s_running_bios = s_running_game_path.empty() && !exe_boot && !psf_boot; + s_running_bios = s_running_game_path.empty() && exe_boot.empty() && psf_boot.empty(); Bus::SetBIOS(bios_image.value()); UpdateControllers(); @@ -1236,15 +1257,15 @@ bool System::BootSystem(SystemBootParameters parameters) } // Load EXE late after BIOS. - if (exe_boot && !LoadEXE(parameters.filename.c_str())) + if (!exe_boot.empty() && !LoadEXE(exe_boot.c_str())) { - Host::ReportFormattedErrorAsync("Error", "Failed to load EXE file '%s'", parameters.filename.c_str()); + Host::ReportFormattedErrorAsync("Error", "Failed to load EXE file '%s'", exe_boot.c_str()); DestroySystem(); return false; } - else if (psf_boot && !PSFLoader::Load(parameters.filename.c_str())) + else if (!psf_boot.empty() && !PSFLoader::Load(psf_boot.c_str())) { - Host::ReportFormattedErrorAsync("Error", "Failed to load PSF file '%s'", parameters.filename.c_str()); + Host::ReportFormattedErrorAsync("Error", "Failed to load PSF file '%s'", psf_boot.c_str()); DestroySystem(); return false; } @@ -2215,7 +2236,7 @@ void System::UpdatePerformanceCounters() const Common::Timer::Value now_ticks = Common::Timer::GetCurrentValue(); const Common::Timer::Value ticks_diff = now_ticks - s_fps_timer.GetStartValue(); const float time = static_cast(Common::Timer::ConvertValueToSeconds(ticks_diff)); - if (time < 1.0f) + if (time < PERFORMANCE_COUNTER_UPDATE_INTERVAL) return; const float frames_run = static_cast(s_frame_number - s_last_frame_number); diff --git a/src/core/system.h b/src/core/system.h index 614bbb90e..b451dc737 100644 --- a/src/core/system.h +++ b/src/core/system.h @@ -29,6 +29,7 @@ struct SystemBootParameters std::string filename; std::string save_state; + std::string override_exe; std::optional override_fast_boot; std::optional override_fullscreen; std::optional override_start_paused; diff --git a/src/duckstation-nogui/nogui_host.cpp b/src/duckstation-nogui/nogui_host.cpp index 1921d117a..0c5191f1d 100644 --- a/src/duckstation-nogui/nogui_host.cpp +++ b/src/duckstation-nogui/nogui_host.cpp @@ -1079,6 +1079,7 @@ void NoGUIHost::PrintCommandLineHelp(const char* progname) " a global state will be loaded.\n"); std::fprintf(stderr, " -statefile : Loads state from the specified filename.\n" " No boot filename is required with this option.\n"); + std::fprintf(stderr, " -exe : Boot the specified exe instead of loading from disc.\n"); std::fprintf(stderr, " -fullscreen: Enters fullscreen mode immediately after starting.\n"); std::fprintf(stderr, " -nofullscreen: Prevents fullscreen mode from triggering if enabled.\n"); std::fprintf(stderr, " -portable: Forces \"portable mode\", data in same directory.\n"); @@ -1174,6 +1175,12 @@ bool NoGUIHost::ParseCommandLineParametersAndInitializeConfig(int argc, char* ar Log_InfoPrintf("Command Line: Loading state file: '%s'", autoboot->save_state.c_str()); continue; } + else if (CHECK_ARG_PARAM("-exe")) + { + AutoBoot(autoboot)->override_exe = argv[++i]; + Log_InfoPrintf("Command Line: Overriding EXE file: '%s'", autoboot->override_exe.c_str()); + continue; + } else if (CHECK_ARG("-fullscreen")) { Log_InfoPrintf("Command Line: Using fullscreen."); diff --git a/src/duckstation-qt/qthost.cpp b/src/duckstation-qt/qthost.cpp index 5346bc2d6..e37be1587 100644 --- a/src/duckstation-qt/qthost.cpp +++ b/src/duckstation-qt/qthost.cpp @@ -1883,6 +1883,7 @@ void QtHost::PrintCommandLineHelp(const char* progname) " a global state will be loaded.\n"); std::fprintf(stderr, " -statefile : Loads state from the specified filename.\n" " No boot filename is required with this option.\n"); + std::fprintf(stderr, " -exe : Boot the specified exe instead of loading from disc.\n"); std::fprintf(stderr, " -fullscreen: Enters fullscreen mode immediately after starting.\n"); std::fprintf(stderr, " -nofullscreen: Prevents fullscreen mode from triggering if enabled.\n"); std::fprintf(stderr, " -nogui: Disables main window from being shown, exits on shutdown.\n"); @@ -1985,6 +1986,12 @@ bool QtHost::ParseCommandLineParametersAndInitializeConfig(QApplication& app, Log_InfoPrintf("Command Line: Loading state file: '%s'", autoboot->save_state.c_str()); continue; } + else if (CHECK_ARG_PARAM("-exe")) + { + AutoBoot(autoboot)->override_exe = args[++i].toStdString(); + Log_InfoPrintf("Command Line: Overriding EXE file: '%s'", autoboot->override_exe.c_str()); + continue; + } else if (CHECK_ARG("-fullscreen")) { Log_InfoPrintf("Command Line: Using fullscreen.");