mirror of
				https://github.com/RetroDECK/Duckstation.git
				synced 2025-04-10 19:15:14 +00:00 
			
		
		
		
	Frontends: Add shared command line interface
Also provides batch mode and automatic fullscreen switching.
  -help: Displays this information and exits.
  -version: Displays version information and exits.
  -batch: Enables batch mode (exits after powering off)
  -fastboot: Force fast boot for provided filename
  -slowboot: Force slow boot for provided filename
  -resume: Load resume save state. If a boot filename is provided,
    that game's resume state will be loaded, otherwise the most
    recent resume save state will be loaded.
  -state <index>: Loads specified save state by index. If a boot
    filename is provided, a per-game state will be loaded, otherwise
    a global state will be loaded.
  -statefile <filename>: Loads state from the specified filename.
    No boot filename is required with this option.
  -fullscreen: Enters fullscreen mode immediately after starting.
  -nofullscreen: Prevents fullscreen mode from triggering if enabled.
  -portable: Forces "portable mode", data in same directory.
  --: Signals that no more arguments will follow and the remaining
    parameters make up the filename. Use when the filename contains
    spaces or starts with a dash.
			
			
This commit is contained in:
		
							parent
							
								
									6a03bb2d15
								
							
						
					
					
						commit
						81cf4b469f
					
				|  | @ -61,6 +61,14 @@ void HostInterface::CreateAudioStream() | |||
| 
 | ||||
| bool HostInterface::BootSystem(const SystemBootParameters& parameters) | ||||
| { | ||||
|   if (parameters.filename.empty()) | ||||
|     Log_InfoPrintf("Boot Filename: <BIOS/Shell>"); | ||||
|   else | ||||
|     Log_InfoPrintf("Boot Filename: %s", parameters.filename.c_str()); | ||||
| 
 | ||||
|   if (!parameters.state_filename.empty()) | ||||
|     Log_InfoPrintf("Save State Filename: %s", parameters.filename.c_str()); | ||||
| 
 | ||||
|   if (!AcquireHostDisplay()) | ||||
|   { | ||||
|     ReportFormattedError("Failed to acquire host display"); | ||||
|  | @ -81,6 +89,9 @@ bool HostInterface::BootSystem(const SystemBootParameters& parameters) | |||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   if (!parameters.state_filename.empty()) | ||||
|     LoadState(parameters.state_filename.c_str()); | ||||
| 
 | ||||
|   OnSystemCreated(); | ||||
| 
 | ||||
|   m_paused = m_settings.start_paused; | ||||
|  | @ -611,6 +622,9 @@ void HostInterface::OnControllerTypeChanged(u32 slot) {} | |||
| 
 | ||||
| void HostInterface::SetUserDirectory() | ||||
| { | ||||
|   if (!m_user_directory.empty()) | ||||
|     return; | ||||
| 
 | ||||
|   const std::string program_path = FileSystem::GetProgramPath(); | ||||
|   const std::string program_directory = FileSystem::GetPathDirectory(program_path.c_str()); | ||||
|   Log_InfoPrintf("Program path: \"%s\" (directory \"%s\")", program_path.c_str(), program_directory.c_str()); | ||||
|  | @ -653,6 +667,13 @@ void HostInterface::SetUserDirectory() | |||
|   } | ||||
| } | ||||
| 
 | ||||
| void HostInterface::SetUserDirectoryToProgramDirectory() | ||||
| { | ||||
|   const std::string program_path = FileSystem::GetProgramPath(); | ||||
|   const std::string program_directory = FileSystem::GetPathDirectory(program_path.c_str()); | ||||
|   m_user_directory = program_directory; | ||||
| } | ||||
| 
 | ||||
| void HostInterface::InitializeUserDirectory() | ||||
| { | ||||
|   Log_InfoPrintf("User directory: \"%s\"", m_user_directory.c_str()); | ||||
|  |  | |||
|  | @ -50,10 +50,11 @@ public: | |||
|   /// Shuts down the emulator frontend.
 | ||||
|   virtual void Shutdown(); | ||||
| 
 | ||||
|   bool BootSystem(const SystemBootParameters& parameters); | ||||
|   virtual bool BootSystem(const SystemBootParameters& parameters); | ||||
|   virtual void PowerOffSystem(); | ||||
| 
 | ||||
|   void PauseSystem(bool paused); | ||||
|   void ResetSystem(); | ||||
|   void PowerOffSystem(); | ||||
|   void DestroySystem(); | ||||
| 
 | ||||
|   /// Loads state from the specified filename.
 | ||||
|  | @ -158,6 +159,9 @@ protected: | |||
|   /// Sets the base path for the user directory. Can be overridden by platform/frontend/command line.
 | ||||
|   virtual void SetUserDirectory(); | ||||
| 
 | ||||
|   /// Sets the user directory to the program directory, i.e. "portable mode".
 | ||||
|   void SetUserDirectoryToProgramDirectory(); | ||||
| 
 | ||||
|   /// Performs the initial load of settings. Should call CheckSettings() and m_settings.Load().
 | ||||
|   virtual void LoadSettings() = 0; | ||||
| 
 | ||||
|  | @ -229,14 +233,14 @@ protected: | |||
|   Settings m_settings; | ||||
|   std::string m_user_directory; | ||||
| 
 | ||||
|   std::deque<OSDMessage> m_osd_messages; | ||||
|   std::mutex m_osd_messages_lock; | ||||
| 
 | ||||
|   bool m_paused = false; | ||||
|   bool m_speed_limiter_temp_disabled = false; | ||||
|   bool m_speed_limiter_enabled = false; | ||||
|   bool m_timer_resolution_increased = false; | ||||
| 
 | ||||
|   std::deque<OSDMessage> m_osd_messages; | ||||
|   std::mutex m_osd_messages_lock; | ||||
| 
 | ||||
| private: | ||||
|   void InitializeUserDirectory(); | ||||
|   void CreateAudioStream(); | ||||
|  |  | |||
|  | @ -35,7 +35,9 @@ struct SystemBootParameters | |||
|   ~SystemBootParameters(); | ||||
| 
 | ||||
|   std::string filename; | ||||
|   std::string state_filename; | ||||
|   std::optional<bool> override_fast_boot; | ||||
|   std::optional<bool> override_fullscreen; | ||||
| }; | ||||
| 
 | ||||
| class System | ||||
|  |  | |||
|  | @ -3,12 +3,13 @@ | |||
| #include "qthostinterface.h" | ||||
| #include <QtWidgets/QApplication> | ||||
| #include <QtWidgets/QMessageBox> | ||||
| #include <cstdlib> | ||||
| #include <memory> | ||||
| 
 | ||||
| static void InitLogging() | ||||
| { | ||||
|   // set log flags
 | ||||
| #ifdef Y_BUILD_CONFIG_DEBUG | ||||
| #ifdef _DEBUG | ||||
|   Log::SetConsoleOutputParams(true, nullptr, LOGLEVEL_DEBUG); | ||||
|   Log::SetFilterLevel(LOGLEVEL_DEBUG); | ||||
| #else | ||||
|  | @ -35,18 +36,30 @@ int main(int argc, char* argv[]) | |||
| #endif | ||||
| 
 | ||||
|   std::unique_ptr<QtHostInterface> host_interface = std::make_unique<QtHostInterface>(); | ||||
|   std::unique_ptr<SystemBootParameters> boot_params; | ||||
|   if (!host_interface->parseCommandLineParameters(argc, argv, &boot_params)) | ||||
|     return EXIT_FAILURE; | ||||
| 
 | ||||
|   if (!host_interface->Initialize()) | ||||
|   { | ||||
|     host_interface->Shutdown(); | ||||
|     QMessageBox::critical(nullptr, QObject::tr("DuckStation Error"), | ||||
|                           QObject::tr("Failed to initialize host interface. Cannot continue."), QMessageBox::Ok); | ||||
|     return -1; | ||||
|     return EXIT_FAILURE; | ||||
|   } | ||||
| 
 | ||||
|   std::unique_ptr<MainWindow> window = std::make_unique<MainWindow>(host_interface.get()); | ||||
|   window->show(); | ||||
| 
 | ||||
|   host_interface->refreshGameList(); | ||||
|   // if we're in batch mode, don't bother refreshing the game list as it won't be used
 | ||||
|   if (!host_interface->inBatchMode()) | ||||
|     host_interface->refreshGameList(); | ||||
| 
 | ||||
|   if (boot_params) | ||||
|   { | ||||
|     host_interface->bootSystem(*boot_params); | ||||
|     boot_params.reset(); | ||||
|   } | ||||
| 
 | ||||
|   int result = app.exec(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -528,6 +528,7 @@ void MainWindow::connectSignals() | |||
|   connect(m_host_interface, &QtHostInterface::systemPerformanceCountersUpdated, this, | ||||
|           &MainWindow::onSystemPerformanceCountersUpdated); | ||||
|   connect(m_host_interface, &QtHostInterface::runningGameChanged, this, &MainWindow::onRunningGameChanged); | ||||
|   connect(m_host_interface, &QtHostInterface::exitRequested, this, &MainWindow::close); | ||||
| 
 | ||||
|   // These need to be queued connections to stop crashing due to menus opening/closing and switching focus.
 | ||||
|   connect(m_game_list_widget, &GameListWidget::entrySelected, this, &MainWindow::onGameListEntrySelected, | ||||
|  |  | |||
|  | @ -39,6 +39,11 @@ QtHostInterface::~QtHostInterface() | |||
|   Assert(!m_display_widget); | ||||
| } | ||||
| 
 | ||||
| const char* QtHostInterface::GetFrontendName() const | ||||
| { | ||||
|   return "DuckStation Qt Frontend"; | ||||
| } | ||||
| 
 | ||||
| bool QtHostInterface::Initialize() | ||||
| { | ||||
|   createThread(); | ||||
|  | @ -104,6 +109,12 @@ bool QtHostInterface::ConfirmMessage(const char* message) | |||
|   return result; | ||||
| } | ||||
| 
 | ||||
| bool QtHostInterface::parseCommandLineParameters(int argc, char* argv[], | ||||
|                                                  std::unique_ptr<SystemBootParameters>* out_boot_params) | ||||
| { | ||||
|   return CommonHostInterface::ParseCommandLineParameters(argc, argv, out_boot_params); | ||||
| } | ||||
| 
 | ||||
| QVariant QtHostInterface::getSettingValue(const QString& name, const QVariant& default_value) | ||||
| { | ||||
|   std::lock_guard<std::recursive_mutex> guard(m_qsettings_mutex); | ||||
|  | @ -320,6 +331,11 @@ bool QtHostInterface::SetFullscreen(bool enabled) | |||
|   return true; | ||||
| } | ||||
| 
 | ||||
| void QtHostInterface::RequestExit() | ||||
| { | ||||
|   emit exitRequested(); | ||||
| } | ||||
| 
 | ||||
| std::optional<CommonHostInterface::HostKeyCode> QtHostInterface::GetHostKeyCode(const std::string_view key_code) const | ||||
| { | ||||
|   const std::optional<int> code = | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| #pragma once | ||||
| #include "common/event.h" | ||||
| #include "core/host_interface.h" | ||||
| #include "core/system.h" | ||||
| #include "common/event.h" | ||||
| #include "frontend-common/common_host_interface.h" | ||||
| #include <QtCore/QByteArray> | ||||
| #include <QtCore/QObject> | ||||
|  | @ -37,6 +37,8 @@ public: | |||
|   explicit QtHostInterface(QObject* parent = nullptr); | ||||
|   ~QtHostInterface(); | ||||
| 
 | ||||
|   const char* GetFrontendName() const override; | ||||
| 
 | ||||
|   bool Initialize() override; | ||||
|   void Shutdown() override; | ||||
| 
 | ||||
|  | @ -44,6 +46,8 @@ public: | |||
|   void ReportMessage(const char* message) override; | ||||
|   bool ConfirmMessage(const char* message) override; | ||||
| 
 | ||||
|   bool parseCommandLineParameters(int argc, char* argv[], std::unique_ptr<SystemBootParameters>* out_boot_params); | ||||
| 
 | ||||
|   /// Thread-safe QSettings access.
 | ||||
|   QVariant getSettingValue(const QString& name, const QVariant& default_value = QVariant()); | ||||
|   void putSettingValue(const QString& name, const QVariant& value); | ||||
|  | @ -55,6 +59,7 @@ public: | |||
| 
 | ||||
|   ALWAYS_INLINE const HotkeyInfoList& getHotkeyInfoList() const { return GetHotkeyInfoList(); } | ||||
|   ALWAYS_INLINE ControllerInterface* getControllerInterface() const { return GetControllerInterface(); } | ||||
|   ALWAYS_INLINE bool inBatchMode() const { return InBatchMode(); } | ||||
| 
 | ||||
|   ALWAYS_INLINE bool isOnWorkerThread() const { return QThread::currentThread() == m_worker_thread; } | ||||
| 
 | ||||
|  | @ -84,6 +89,7 @@ Q_SIGNALS: | |||
|   void systemPerformanceCountersUpdated(float speed, float fps, float vps, float avg_frame_time, | ||||
|                                         float worst_frame_time); | ||||
|   void runningGameChanged(const QString& filename, const QString& game_code, const QString& game_title); | ||||
|   void exitRequested(); | ||||
| 
 | ||||
| public Q_SLOTS: | ||||
|   void setDefaultSettings(); | ||||
|  | @ -124,6 +130,7 @@ protected: | |||
|   bool IsFullscreen() const override; | ||||
|   bool SetFullscreen(bool enabled) override; | ||||
| 
 | ||||
|   void RequestExit() override; | ||||
|   std::optional<HostKeyCode> GetHostKeyCode(const std::string_view key_code) const override; | ||||
| 
 | ||||
|   void OnSystemCreated() override; | ||||
|  | @ -161,7 +168,7 @@ private: | |||
| 
 | ||||
|   private: | ||||
|     QtHostInterface* m_parent; | ||||
|     std::atomic_bool m_init_result{ false }; | ||||
|     std::atomic_bool m_init_result{false}; | ||||
|     Common::Event m_init_event; | ||||
|   }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -5,70 +5,8 @@ | |||
| #include "sdl_host_interface.h" | ||||
| #include <SDL.h> | ||||
| #include <cstdio> | ||||
| #include <cstdlib> | ||||
| 
 | ||||
| static int Run(int argc, char* argv[]) | ||||
| { | ||||
|   // parameters
 | ||||
|   std::optional<s32> state_index; | ||||
|   bool state_is_global = false; | ||||
|   const char* boot_filename = nullptr; | ||||
|   for (int i = 1; i < argc; i++) | ||||
|   { | ||||
| #define CHECK_ARG(str) !std::strcmp(argv[i], str) | ||||
| #define CHECK_ARG_PARAM(str) (!std::strcmp(argv[i], str) && ((i + 1) < argc)) | ||||
| 
 | ||||
|     if (CHECK_ARG_PARAM("-state") || CHECK_ARG_PARAM("-gstate")) | ||||
|     { | ||||
|       state_is_global = argv[i][1] == 'g'; | ||||
|       state_index = std::atoi(argv[++i]); | ||||
|     } | ||||
|     else if (CHECK_ARG_PARAM("-resume")) | ||||
|     { | ||||
|       state_index = -1; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|       boot_filename = argv[i]; | ||||
|     } | ||||
| 
 | ||||
| #undef CHECK_ARG | ||||
| #undef CHECK_ARG_PARAM | ||||
|   } | ||||
| 
 | ||||
|   // create display and host interface
 | ||||
|   std::unique_ptr<SDLHostInterface> host_interface = SDLHostInterface::Create(); | ||||
|   if (!host_interface->Initialize()) | ||||
|   { | ||||
|     host_interface->Shutdown(); | ||||
|     Panic("Failed to initialize host interface"); | ||||
|     SDL_Quit(); | ||||
|     return -1; | ||||
|   } | ||||
| 
 | ||||
|   // boot/load state
 | ||||
|   if (boot_filename) | ||||
|   { | ||||
|     SystemBootParameters boot_params; | ||||
|     boot_params.filename = boot_filename; | ||||
|     if (host_interface->BootSystem(boot_params) && state_index.has_value()) | ||||
|       host_interface->LoadState(state_is_global, state_index.value()); | ||||
|   } | ||||
|   else if (state_index.has_value()) | ||||
|   { | ||||
|     host_interface->LoadState(true, state_index.value()); | ||||
|   } | ||||
| 
 | ||||
|   // run
 | ||||
|   host_interface->Run(); | ||||
| 
 | ||||
|   // done
 | ||||
|   host_interface->Shutdown(); | ||||
|   host_interface.reset(); | ||||
|   SDL_Quit(); | ||||
|   return 0; | ||||
| } | ||||
| 
 | ||||
| // SDL requires the entry point declared without c++ decoration
 | ||||
| #undef main | ||||
| int main(int argc, char* argv[]) | ||||
| { | ||||
|  | @ -91,6 +29,38 @@ int main(int argc, char* argv[]) | |||
| 
 | ||||
|   FrontendCommon::EnsureSDLInitialized(); | ||||
| 
 | ||||
|   // return NoGUITest();
 | ||||
|   return Run(argc, argv); | ||||
|   std::unique_ptr<SDLHostInterface> host_interface = SDLHostInterface::Create(); | ||||
|   std::unique_ptr<SystemBootParameters> boot_params; | ||||
|   if (!host_interface->ParseCommandLineParameters(argc, argv, &boot_params)) | ||||
|   { | ||||
|     SDL_Quit(); | ||||
|     return EXIT_FAILURE; | ||||
|   } | ||||
| 
 | ||||
|   if (!host_interface->Initialize()) | ||||
|   { | ||||
|     host_interface->Shutdown(); | ||||
|     SDL_Quit(); | ||||
|     return EXIT_FAILURE; | ||||
|   } | ||||
| 
 | ||||
|   if (boot_params) | ||||
|   { | ||||
|     if (!host_interface->BootSystem(*boot_params) && host_interface->InBatchMode()) | ||||
|     { | ||||
|       host_interface->Shutdown(); | ||||
|       host_interface.reset(); | ||||
|       SDL_Quit(); | ||||
|       return EXIT_FAILURE; | ||||
|     } | ||||
| 
 | ||||
|     boot_params.reset(); | ||||
|   } | ||||
| 
 | ||||
|   host_interface->Run(); | ||||
|   host_interface->Shutdown(); | ||||
|   host_interface.reset(); | ||||
| 
 | ||||
|   SDL_Quit(); | ||||
|   return EXIT_SUCCESS; | ||||
| } | ||||
|  |  | |||
|  | @ -34,6 +34,11 @@ SDLHostInterface::SDLHostInterface() | |||
| 
 | ||||
| SDLHostInterface::~SDLHostInterface() = default; | ||||
| 
 | ||||
| const char* SDLHostInterface::GetFrontendName() const | ||||
| { | ||||
|   return "DuckStation SDL/ImGui Frontend"; | ||||
| } | ||||
| 
 | ||||
| float SDLHostInterface::GetDPIScaleFactor(SDL_Window* window) | ||||
| { | ||||
| #ifdef __APPLE__ | ||||
|  | @ -191,10 +196,6 @@ bool SDLHostInterface::AcquireHostDisplay() | |||
|   } | ||||
| #endif | ||||
| 
 | ||||
|   // Switch to fullscreen if requested.
 | ||||
|   if (m_settings.start_fullscreen) | ||||
|     SetFullscreen(true); | ||||
| 
 | ||||
|   return true; | ||||
| } | ||||
| 
 | ||||
|  | @ -274,6 +275,11 @@ void SDLHostInterface::OnRunningGameChanged() | |||
|     SDL_SetWindowTitle(m_window, "DuckStation"); | ||||
| } | ||||
| 
 | ||||
| void SDLHostInterface::RequestExit() | ||||
| { | ||||
|   m_quit_request = true; | ||||
| } | ||||
| 
 | ||||
| void SDLHostInterface::RunLater(std::function<void()> callback) | ||||
| { | ||||
|   SDL_Event ev = {}; | ||||
|  |  | |||
|  | @ -25,6 +25,8 @@ public: | |||
| 
 | ||||
|   static std::unique_ptr<SDLHostInterface> Create(); | ||||
| 
 | ||||
|   const char* GetFrontendName() const override; | ||||
| 
 | ||||
|   void ReportError(const char* message) override; | ||||
|   void ReportMessage(const char* message) override; | ||||
|   bool ConfirmMessage(const char* message) override; | ||||
|  | @ -47,6 +49,8 @@ protected: | |||
|   void OnSystemDestroyed() override; | ||||
|   void OnRunningGameChanged() override; | ||||
| 
 | ||||
|   void RequestExit() override; | ||||
| 
 | ||||
|   std::optional<HostKeyCode> GetHostKeyCode(const std::string_view key_code) const override; | ||||
|   void UpdateInputMap() override; | ||||
| 
 | ||||
|  |  | |||
|  | @ -13,6 +13,7 @@ | |||
| #include "sdl_audio_stream.h" | ||||
| #include "sdl_controller_interface.h" | ||||
| #endif | ||||
| #include <cstdio> | ||||
| #include <cstring> | ||||
| Log_SetChannel(CommonHostInterface); | ||||
| 
 | ||||
|  | @ -63,6 +64,228 @@ void CommonHostInterface::Shutdown() | |||
|   } | ||||
| } | ||||
| 
 | ||||
| bool CommonHostInterface::BootSystem(const SystemBootParameters& parameters) | ||||
| { | ||||
|   if (!HostInterface::BootSystem(parameters)) | ||||
|   { | ||||
|     // if in batch mode, exit immediately if booting failed
 | ||||
|     if (m_batch_mode) | ||||
|       RequestExit(); | ||||
| 
 | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   // enter fullscreen if requested in the parameters
 | ||||
|   if ((parameters.override_fullscreen.has_value() && *parameters.override_fullscreen) || | ||||
|       (!parameters.override_fullscreen.has_value() && m_settings.start_fullscreen)) | ||||
|   { | ||||
|     SetFullscreen(true); | ||||
|   } | ||||
| 
 | ||||
|   return true; | ||||
| } | ||||
| 
 | ||||
| void CommonHostInterface::PowerOffSystem() | ||||
| { | ||||
|   HostInterface::PowerOffSystem(); | ||||
| 
 | ||||
|   // TODO: Do we want to move the resume state saving here?
 | ||||
| 
 | ||||
|   if (m_batch_mode) | ||||
|     RequestExit(); | ||||
| } | ||||
| 
 | ||||
| static void PrintCommandLineVersion(const char* frontend_name) | ||||
| { | ||||
|   std::fprintf(stderr, "%s version <TODO>\n", frontend_name); | ||||
|   std::fprintf(stderr, "https://github.com/stenzek/duckstation\n"); | ||||
|   std::fprintf(stderr, "\n"); | ||||
| } | ||||
| 
 | ||||
| static void PrintCommandLineHelp(const char* progname, const char* frontend_name) | ||||
| { | ||||
|   PrintCommandLineVersion(frontend_name); | ||||
|   std::fprintf(stderr, "Usage: %s [parameters] [--] [boot filename]\n", progname); | ||||
|   std::fprintf(stderr, "\n"); | ||||
|   std::fprintf(stderr, "  -help: Displays this information and exits.\n"); | ||||
|   std::fprintf(stderr, "  -version: Displays version information and exits.\n"); | ||||
|   std::fprintf(stderr, "  -batch: Enables batch mode (exits after powering off)\n"); | ||||
|   std::fprintf(stderr, "  -fastboot: Force fast boot for provided filename\n"); | ||||
|   std::fprintf(stderr, "  -slowboot: Force slow boot for provided filename\n"); | ||||
|   std::fprintf(stderr, "  -resume: Load resume save state. If a boot filename is provided,\n" | ||||
|                        "    that game's resume state will be loaded, otherwise the most\n" | ||||
|                        "    recent resume save state will be loaded.\n"); | ||||
|   std::fprintf(stderr, "  -state <index>: Loads specified save state by index. If a boot\n" | ||||
|                        "    filename is provided, a per-game state will be loaded, otherwise\n" | ||||
|                        "    a global state will be loaded.\n"); | ||||
|   std::fprintf(stderr, "  -statefile <filename>: Loads state from the specified filename.\n" | ||||
|                        "    No boot filename is required with this option.\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"); | ||||
|   std::fprintf(stderr, "  --: Signals that no more arguments will follow and the remaining\n" | ||||
|                        "    parameters make up the filename. Use when the filename contains\n" | ||||
|                        "    spaces or starts with a dash.\n"); | ||||
|   std::fprintf(stderr, "\n"); | ||||
| } | ||||
| 
 | ||||
| bool CommonHostInterface::ParseCommandLineParameters(int argc, char* argv[], | ||||
|                                                      std::unique_ptr<SystemBootParameters>* out_boot_params) | ||||
| { | ||||
|   std::optional<bool> force_fast_boot; | ||||
|   std::optional<bool> force_fullscreen; | ||||
|   std::optional<s32> state_index; | ||||
|   std::string state_filename; | ||||
|   std::string boot_filename; | ||||
|   bool no_more_args = false; | ||||
| 
 | ||||
|   for (int i = 1; i < argc; i++) | ||||
|   { | ||||
|     if (!no_more_args) | ||||
|     { | ||||
| #define CHECK_ARG(str) !std::strcmp(argv[i], str) | ||||
| #define CHECK_ARG_PARAM(str) (!std::strcmp(argv[i], str) && ((i + 1) < argc)) | ||||
| 
 | ||||
|       if (CHECK_ARG("-help")) | ||||
|       { | ||||
|         PrintCommandLineHelp(argv[0], GetFrontendName()); | ||||
|         return false; | ||||
|       } | ||||
|       else if (CHECK_ARG("-version")) | ||||
|       { | ||||
|         PrintCommandLineVersion(GetFrontendName()); | ||||
|         return false; | ||||
|       } | ||||
|       else if (CHECK_ARG("-batch")) | ||||
|       { | ||||
|         Log_InfoPrintf("Enabling batch mode.\n"); | ||||
|         m_batch_mode = true; | ||||
|         continue; | ||||
|       } | ||||
|       else if (CHECK_ARG("-fastboot")) | ||||
|       { | ||||
|         Log_InfoPrintf("Forcing fast boot.\n"); | ||||
|         force_fast_boot = true; | ||||
|         continue; | ||||
|       } | ||||
|       else if (CHECK_ARG("-slowboot")) | ||||
|       { | ||||
|         Log_InfoPrintf("Forcing slow boot.\n"); | ||||
|         force_fast_boot = false; | ||||
|         continue; | ||||
|       } | ||||
|       else if (CHECK_ARG("-resume")) | ||||
|       { | ||||
|         state_index = -1; | ||||
|         continue; | ||||
|       } | ||||
|       else if (CHECK_ARG_PARAM("-state")) | ||||
|       { | ||||
|         state_index = std::atoi(argv[++i]); | ||||
|         continue; | ||||
|       } | ||||
|       else if (CHECK_ARG_PARAM("-statefile")) | ||||
|       { | ||||
|         state_filename = argv[++i]; | ||||
|         continue; | ||||
|       } | ||||
|       else if (CHECK_ARG("-fullscreen")) | ||||
|       { | ||||
|         Log_InfoPrintf("Going fullscreen after booting.\n"); | ||||
|         force_fullscreen = true; | ||||
|         continue; | ||||
|       } | ||||
|       else if (CHECK_ARG("-nofullscreen")) | ||||
|       { | ||||
|         Log_InfoPrintf("Preventing fullscreen after booting.\n"); | ||||
|         force_fullscreen = false; | ||||
|         continue; | ||||
|       } | ||||
|       else if (CHECK_ARG("-portable")) | ||||
|       { | ||||
|         Log_InfoPrintf("Using portable mode.\n"); | ||||
|         SetUserDirectoryToProgramDirectory(); | ||||
|         continue; | ||||
|       } | ||||
|       else if (CHECK_ARG_PARAM("-resume")) | ||||
|       { | ||||
|         state_index = -1; | ||||
|         continue; | ||||
|       } | ||||
|       else if (CHECK_ARG("--")) | ||||
|       { | ||||
|         no_more_args = true; | ||||
|         continue; | ||||
|       } | ||||
|       else if (argv[i][0] == '-') | ||||
|       { | ||||
|         Log_ErrorPrintf("Unknown parameter: '%s'", argv[i]); | ||||
|         return false; | ||||
|       } | ||||
| 
 | ||||
| #undef CHECK_ARG | ||||
| #undef CHECK_ARG_PARAM | ||||
|     } | ||||
| 
 | ||||
|     if (!boot_filename.empty()) | ||||
|       boot_filename += ' '; | ||||
|     boot_filename += argv[i]; | ||||
|   } | ||||
| 
 | ||||
|   if (state_index.has_value() || !boot_filename.empty() || !state_filename.empty()) | ||||
|   { | ||||
|     // init user directory early since we need it for save states
 | ||||
|     SetUserDirectory(); | ||||
| 
 | ||||
|     if (state_index.has_value() && !state_filename.empty()) | ||||
|     { | ||||
|       // if a save state is provided, whether a boot filename was provided determines per-game/local
 | ||||
|       if (boot_filename.empty()) | ||||
|       { | ||||
|         // loading a global state. if this is -1, we're loading the most recent resume state
 | ||||
|         if (*state_index < 0) | ||||
|           state_filename = GetMostRecentResumeSaveStatePath(); | ||||
|         else | ||||
|           state_filename = GetGlobalSaveStateFileName(*state_index); | ||||
| 
 | ||||
|         if (state_filename.empty() || !FileSystem::FileExists(state_filename.c_str())) | ||||
|         { | ||||
|           Log_ErrorPrintf("Could not find file for global save state %d", *state_index); | ||||
|           return false; | ||||
|         } | ||||
|       } | ||||
|       else | ||||
|       { | ||||
|         // find the game id, and get its save state path
 | ||||
|         std::string game_code = m_game_list->GetGameCodeForPath(boot_filename.c_str()); | ||||
|         if (game_code.empty()) | ||||
|         { | ||||
|           Log_WarningPrintf("Could not identify game code for '%s', cannot load save state %d.", boot_filename.c_str(), | ||||
|                             *state_index); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|           state_filename = GetGameSaveStateFileName(game_code.c_str(), *state_index); | ||||
|           if (state_filename.empty() || !FileSystem::FileExists(state_filename.c_str())) | ||||
|           { | ||||
|             Log_ErrorPrintf("Could not find file for game '%s' save state %d", game_code.c_str(), *state_index); | ||||
|             return false; | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     std::unique_ptr<SystemBootParameters> boot_params = std::make_unique<SystemBootParameters>(); | ||||
|     boot_params->filename = std::move(boot_filename); | ||||
|     boot_params->state_filename = std::move(state_filename); | ||||
|     boot_params->override_fast_boot = std::move(force_fast_boot); | ||||
|     boot_params->override_fullscreen = std::move(force_fullscreen); | ||||
|     *out_boot_params = std::move(boot_params); | ||||
|   } | ||||
| 
 | ||||
|   return true; | ||||
| } | ||||
| 
 | ||||
| bool CommonHostInterface::IsFullscreen() const | ||||
| { | ||||
|   return false; | ||||
|  | @ -401,7 +624,7 @@ void CommonHostInterface::RegisterGeneralHotkeys() | |||
|                  [this](bool pressed) { | ||||
|                    if (!pressed && m_system) | ||||
|                    { | ||||
|                      if (m_settings.confim_power_off) | ||||
|                      if (m_settings.confim_power_off && !m_batch_mode) | ||||
|                      { | ||||
|                        SmallString confirmation_message("Are you sure you want to stop emulation?"); | ||||
|                        if (m_settings.save_state_on_exit) | ||||
|  |  | |||
|  | @ -33,19 +33,34 @@ public: | |||
| 
 | ||||
|   using HotkeyInfoList = std::vector<HotkeyInfo>; | ||||
| 
 | ||||
|   /// Returns the name of the frontend.
 | ||||
|   virtual const char* GetFrontendName() const = 0; | ||||
| 
 | ||||
|   virtual bool Initialize() override; | ||||
|   virtual void Shutdown() override; | ||||
| 
 | ||||
|   virtual bool BootSystem(const SystemBootParameters& parameters) override; | ||||
|   virtual void PowerOffSystem() override; | ||||
| 
 | ||||
|   /// Returns a list of all available hotkeys.
 | ||||
|   ALWAYS_INLINE const HotkeyInfoList& GetHotkeyInfoList() const { return m_hotkeys; } | ||||
| 
 | ||||
|   /// Access to current controller interface.
 | ||||
|   ALWAYS_INLINE ControllerInterface* GetControllerInterface() const { return m_controller_interface.get(); } | ||||
| 
 | ||||
|   /// Returns true if running in batch mode, i.e. exit after emulation.
 | ||||
|   ALWAYS_INLINE bool InBatchMode() const { return m_batch_mode; } | ||||
| 
 | ||||
|   /// Parses command line parameters for all frontends.
 | ||||
|   bool ParseCommandLineParameters(int argc, char* argv[], std::unique_ptr<SystemBootParameters>* out_boot_params); | ||||
| 
 | ||||
| protected: | ||||
|   CommonHostInterface(); | ||||
|   ~CommonHostInterface(); | ||||
| 
 | ||||
|   /// Request the frontend to exit.
 | ||||
|   virtual void RequestExit() = 0; | ||||
| 
 | ||||
|   virtual bool IsFullscreen() const; | ||||
|   virtual bool SetFullscreen(bool enabled); | ||||
| 
 | ||||
|  | @ -85,4 +100,7 @@ private: | |||
| 
 | ||||
|   // input key maps
 | ||||
|   std::map<HostKeyCode, InputButtonHandler> m_keyboard_input_handlers; | ||||
| 
 | ||||
|   // running in batch mode? i.e. exit after stopping emulation
 | ||||
|   bool m_batch_mode = false; | ||||
| }; | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Connor McLaughlin
						Connor McLaughlin