Qt: Implement more command line arguments

This commit is contained in:
Connor McLaughlin 2022-05-03 14:26:49 +10:00 committed by refractionpcsx2
parent 119b6bdfd5
commit 378588c67d
10 changed files with 125 additions and 58 deletions

View File

@ -143,16 +143,15 @@ void EmuThread::setVMPaused(bool paused)
VMManager::SetPaused(paused);
}
bool EmuThread::shutdownVM(bool allow_save_to_state /* = true */)
void EmuThread::shutdownVM(bool allow_save_to_state /* = true */)
{
const VMState state = VMManager::GetState();
if (state == VMState::Paused)
m_event_loop->quit();
else if (state != VMState::Running)
return true;
return;
VMManager::SetState(VMState::Stopping);
return true;
}
void EmuThread::loadState(const QString& filename)

View File

@ -59,7 +59,7 @@ public Q_SLOTS:
void startVM(std::shared_ptr<VMBootParameters> boot_params);
void resetVM();
void setVMPaused(bool paused);
bool shutdownVM(bool allow_save_to_state = true);
void shutdownVM(bool allow_save_to_state = true);
void loadState(const QString& filename);
void loadStateFromSlot(qint32 slot);
void saveState(const QString& filename);

View File

@ -31,6 +31,7 @@
static void PrintCommandLineVersion()
{
QtHost::InitializeEarlyConsole();
std::fprintf(stderr, "PCSX2 Version %s\n", GIT_REV);
std::fprintf(stderr, "https://pcsx2.net/\n");
std::fprintf(stderr, "\n");
@ -46,19 +47,13 @@ static void PrintCommandLineHelp(const char* progname)
std::fprintf(stderr, " -batch: Enables batch mode (exits after shutting down).\n");
std::fprintf(stderr, " -elf <file>: Overrides the boot ELF with the specified filename.\n");
std::fprintf(stderr, " -disc <path>: Uses the specified host DVD drive as a source.\n");
std::fprintf(stderr, " -bios: Starts the BIOS (System Menu/OSDSYS).\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.\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, " -statefile <filename>: Loads state from the specified filename.\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, " -settings <filename>: Loads a custom settings configuration from the\n"
" specified filename. Default settings applied if file not found.\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");
@ -75,10 +70,6 @@ static std::shared_ptr<VMBootParameters>& AutoBoot(std::shared_ptr<VMBootParamet
static bool ParseCommandLineOptions(int argc, char* argv[], std::shared_ptr<VMBootParameters>& autoboot)
{
std::optional<bool> fast_boot;
std::optional<bool> start_fullscreen;
std::optional<s32> state_index;
std::string state_filename;
bool no_more_args = false;
for (int i = 1; i < argc; i++)
@ -100,30 +91,22 @@ static bool ParseCommandLineOptions(int argc, char* argv[], std::shared_ptr<VMBo
}
else if (CHECK_ARG("-batch"))
{
Console.WriteLn("Enabling batch mode.");
AutoBoot(autoboot)->batch_mode = true;
QtHost::SetBatchMode(true);
continue;
}
else if (CHECK_ARG("-fastboot"))
{
Console.WriteLn("Forcing fast boot.");
fast_boot = true;
AutoBoot(autoboot)->fast_boot = true;
continue;
}
else if (CHECK_ARG("-slowboot"))
{
Console.WriteLn("Forcing slow boot.");
fast_boot = false;
continue;
}
else if (CHECK_ARG("-resume"))
{
state_index = -1;
AutoBoot(autoboot)->fast_boot = false;
continue;
}
else if (CHECK_ARG_PARAM("-state"))
{
state_index = std::atoi(argv[++i]);
AutoBoot(autoboot)->state_index = std::atoi(argv[++i]);
continue;
}
else if (CHECK_ARG_PARAM("-statefile"))
@ -142,27 +125,19 @@ static bool ParseCommandLineOptions(int argc, char* argv[], std::shared_ptr<VMBo
AutoBoot(autoboot)->filename = argv[++i];
continue;
}
else if (CHECK_ARG("-bios"))
{
AutoBoot(autoboot)->source_type = CDVD_SourceType::NoDisc;
continue;
}
else if (CHECK_ARG("-fullscreen"))
{
Console.WriteLn("Going fullscreen after booting.");
start_fullscreen = true;
AutoBoot(autoboot)->fullscreen = true;
continue;
}
else if (CHECK_ARG("-nofullscreen"))
{
Console.WriteLn("Preventing fullscreen after booting.");
start_fullscreen = false;
continue;
}
else if (CHECK_ARG("-portable"))
{
Console.WriteLn("Using portable mode.");
// SetUserDirectoryToProgramDirectory();
continue;
}
else if (CHECK_ARG("-resume"))
{
state_index = -1;
AutoBoot(autoboot)->fullscreen = false;
continue;
}
else if (CHECK_ARG("--"))
@ -172,7 +147,8 @@ static bool ParseCommandLineOptions(int argc, char* argv[], std::shared_ptr<VMBo
}
else if (argv[i][0] == '-')
{
Console.Error("Unknown parameter: '%s'", argv[i]);
QtHost::InitializeEarlyConsole();
std::fprintf(stderr, "Unknown parameter: '%s'", argv[i]);
return false;
}
@ -186,6 +162,24 @@ static bool ParseCommandLineOptions(int argc, char* argv[], std::shared_ptr<VMBo
AutoBoot(autoboot)->filename += argv[i];
}
// check autoboot parameters, if we set something like fullscreen without a bios
// or disc, we don't want to actually start.
if (autoboot && !autoboot->source_type.has_value() && autoboot->filename.empty() && autoboot->elf_override.empty())
{
QtHost::InitializeEarlyConsole();
Console.Warning("Skipping autoboot due to no boot parameters.");
autoboot.reset();
}
// if we don't have autoboot, we definitely don't want batch mode (because that'll skip
// scanning the game list).
if (QtHost::InBatchMode() && !autoboot)
{
QtHost::InitializeEarlyConsole();
Console.Warning("Disabling batch mode, because we have no autoboot.");
QtHost::SetBatchMode(false);
}
return true;
}
@ -213,7 +207,10 @@ int main(int argc, char* argv[])
main_window->initialize();
EmuThread::start();
main_window->refreshGameList(false);
// skip scanning the game list when running in batch mode
if (!QtHost::InBatchMode())
main_window->refreshGameList(false);
main_window->show();
if (autoboot)

View File

@ -720,13 +720,19 @@ bool MainWindow::requestShutdown(bool allow_confirm /* = true */, bool allow_sav
g_emu_thread->shutdownVM(allow_save_to_state);
if (block_until_done)
if (block_until_done || QtHost::InBatchMode())
{
// we need to yield here, since the display gets destroyed
while (VMManager::HasValidVM())
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents, 1);
}
if (QtHost::InBatchMode())
{
// closing the window should shut down everything.
close();
}
return true;
}

View File

@ -68,6 +68,7 @@ static void SaveSettings();
//////////////////////////////////////////////////////////////////////////
static std::unique_ptr<QTimer> s_settings_save_timer;
static std::unique_ptr<INISettingsInterface> s_base_settings_interface;
static bool s_batch_mode = false;
//////////////////////////////////////////////////////////////////////////
// Initialization/Shutdown
@ -377,6 +378,16 @@ void QtHost::QueueSettingsSave()
s_settings_save_timer->start(SETTINGS_SAVE_DELAY);
}
bool QtHost::InBatchMode()
{
return s_batch_mode;
}
void QtHost::SetBatchMode(bool enabled)
{
s_batch_mode = enabled;
}
void QtHost::RunOnUIThread(const std::function<void()>& func, bool block /*= false*/)
{
// main window always exists, so it's fine to attach it to that.

View File

@ -45,6 +45,10 @@ namespace QtHost
/// Initializes early console logging (for printing command line arguments).
void InitializeEarlyConsole();
/// Sets batch mode (exit after game shutdown).
bool InBatchMode();
void SetBatchMode(bool enabled);
/// Executes a function on the UI thread.
void RunOnUIThread(const std::function<void()>& func, bool block = false);

View File

@ -48,7 +48,7 @@ namespace GameList
static Entry* GetMutableEntryForPath(const char* path);
static bool GetElfListEntry(const std::string& path, GameList::Entry* entry);
static bool GetGameListEntry(const std::string& path, GameList::Entry* entry);
static bool GetIsoListEntry(const std::string& path, GameList::Entry* entry);
static bool GetGameListEntryFromCache(const std::string& path, GameList::Entry* entry);
static void ScanDirectory(const char* path, bool recursive, const std::vector<std::string>& excluded_paths,
@ -169,11 +169,8 @@ bool GameList::GetElfListEntry(const std::string& path, GameList::Entry* entry)
return true;
}
bool GameList::GetGameListEntry(const std::string& path, GameList::Entry* entry)
bool GameList::GetIsoListEntry(const std::string& path, GameList::Entry* entry)
{
if (VMManager::IsElfFileName(path.c_str()))
return GetElfListEntry(path.c_str(), entry);
FILESYSTEM_STAT_DATA sd;
if (!FileSystem::StatFile(path.c_str(), &sd))
return false;
@ -241,6 +238,14 @@ bool GameList::GetGameListEntry(const std::string& path, GameList::Entry* entry)
return true;
}
bool GameList::PopulateEntryFromPath(const std::string& path, GameList::Entry* entry)
{
if (VMManager::IsElfFileName(path.c_str()))
return GetElfListEntry(path, entry);
else
return GetIsoListEntry(path, entry);
}
bool GameList::GetGameListEntryFromCache(const std::string& path, GameList::Entry* entry)
{
auto iter = m_cache_map.find(path);
@ -529,7 +534,7 @@ bool GameList::ScanFile(std::string path, std::time_t timestamp)
DevCon.WriteLn("Scanning '%s'...", path.c_str());
Entry entry;
if (!GetGameListEntry(path, &entry))
if (!PopulateEntryFromPath(path, &entry))
return false;
entry.path = std::move(path);

View File

@ -76,6 +76,10 @@ namespace GameList
/// Fills in boot parameters (iso or elf) based on the game list entry.
void FillBootParametersForEntry(VMBootParameters* params, const Entry* entry);
/// Populates a game list entry struct with information from the iso/elf.
/// Do *not* call while the system is running, it will mess with CDVD state.
bool PopulateEntryFromPath(const std::string& path, GameList::Entry* entry);
// Game list access. It's the caller's responsibility to hold the lock while manipulating the entry in any way.
std::unique_lock<std::recursive_mutex> GetLock();
const Entry* GetEntryByIndex(u32 index);

View File

@ -57,6 +57,7 @@
#include "Frontend/INISettingsInterface.h"
#include "Frontend/InputManager.h"
#include "Frontend/GameList.h"
#include "common/emitter/tools.h"
#ifdef _M_X86
@ -78,7 +79,7 @@ namespace VMManager
static void CheckForMemoryCardConfigChanges(const Pcsx2Config& old_config);
static bool AutoDetectSource(const std::string& filename);
static bool ApplyBootParameters(const VMBootParameters& params);
static bool ApplyBootParameters(const VMBootParameters& params, std::string* state_to_load);
static bool CheckBIOSAvailability();
static void UpdateRunningGame(bool force);
@ -515,14 +516,53 @@ bool VMManager::AutoDetectSource(const std::string& filename)
}
}
bool VMManager::ApplyBootParameters(const VMBootParameters& params)
bool VMManager::ApplyBootParameters(const VMBootParameters& params, std::string* state_to_load)
{
const bool default_fast_boot = Host::GetBoolSettingValue("EmuCore", "EnableFastBoot", true);
EmuConfig.UseBOOT2Injection = params.fast_boot.value_or(default_fast_boot);
s_elf_override = params.elf_override;
s_disc_path.clear();
if (!params.save_state.empty())
*state_to_load = params.save_state;
// if we're loading an indexed save state, we need to get the serial/crc from the disc.
if (params.state_index.has_value())
{
if (params.filename.empty())
{
Host::ReportErrorAsync("Error", "Cannot load an indexed save state without a boot filename.");
return false;
}
// try the game list first, but this won't work if we're in batch mode
{
auto lock = GameList::GetLock();
if (const GameList::Entry* entry = GameList::GetEntryForPath(params.filename.c_str()); entry)
{
*state_to_load = GetSaveStateFileName(entry->serial.c_str(), entry->crc, params.state_index.value());
}
else
{
// just scan it.. hopefully it'll come back okay
GameList::Entry temp_entry;
if (!GameList::PopulateEntryFromPath(params.filename.c_str(), &temp_entry))
{
Host::ReportFormattedErrorAsync("Error", "Could not scan path '%s' for indexed save state load.", params.filename.c_str());
return false;
}
*state_to_load = GetSaveStateFileName(temp_entry.serial.c_str(), temp_entry.crc, params.state_index.value());
}
if (state_to_load->empty())
{
Host::ReportFormattedErrorAsync("Error", "Could not resolve path indexed save state load.");
return false;
}
}
}
// resolve source type
if (params.source_type.has_value())
{
if (params.source_type.value() == CDVD_SourceType::Iso && !FileSystem::FileExists(params.filename.c_str()))
@ -591,7 +631,8 @@ bool VMManager::Initialize(const VMBootParameters& boot_params)
LoadSettings();
if (!ApplyBootParameters(boot_params))
std::string state_to_load;
if (!ApplyBootParameters(boot_params, &state_to_load))
return false;
EmuConfig.LimiterMode = GetInitialLimiterMode();
@ -727,9 +768,9 @@ bool VMManager::Initialize(const VMBootParameters& boot_params)
PerformanceMetrics::Clear();
// do we want to load state?
if (!GSDumpReplayer::IsReplayingDump() && !boot_params.save_state.empty())
if (!GSDumpReplayer::IsReplayingDump() && !state_to_load.empty())
{
if (!DoLoadState(boot_params.save_state.c_str()))
if (!DoLoadState(state_to_load.c_str()))
{
Shutdown();
return false;

View File

@ -42,11 +42,11 @@ struct VMBootParameters
std::string filename;
std::string elf_override;
std::string save_state;
std::optional<s32> state_index;
std::optional<CDVD_SourceType> source_type;
std::optional<bool> fast_boot;
std::optional<bool> fullscreen;
std::optional<bool> batch_mode;
};
namespace VMManager