diff --git a/src/core/linker.cpp b/src/core/linker.cpp index 28d2eea7b..2461edcb2 100644 --- a/src/core/linker.cpp +++ b/src/core/linker.cpp @@ -52,7 +52,7 @@ Linker::Linker() : memory{Memory::Instance()} {} Linker::~Linker() = default; -void Linker::Execute() { +void Linker::Execute(const std::vector args) { if (Config::debugDump()) { DebugDump(); } @@ -101,7 +101,7 @@ void Linker::Execute() { memory->SetupMemoryRegions(fmem_size, use_extended_mem1, use_extended_mem2); - main_thread.Run([this, module](std::stop_token) { + main_thread.Run([this, module, args](std::stop_token) { Common::SetCurrentThreadName("GAME_MainThread"); LoadSharedLibraries(); @@ -109,6 +109,12 @@ void Linker::Execute() { EntryParams params{}; params.argc = 1; params.argv[0] = "eboot.bin"; + if (!args.empty()) { + params.argc = args.size() + 1; + for (int i = 0; i < args.size() && i < 32; i++) { + params.argv[i + 1] = args[i].c_str(); + } + } params.entry_addr = module->GetEntryAddress(); RunMainEntry(¶ms); }); diff --git a/src/core/linker.h b/src/core/linker.h index 7ef13ae56..00da3a08c 100644 --- a/src/core/linker.h +++ b/src/core/linker.h @@ -49,7 +49,7 @@ class Linker; struct EntryParams { int argc; u32 padding; - const char* argv[3]; + const char* argv[33]; VAddr entry_addr; }; @@ -143,7 +143,7 @@ public: void Relocate(Module* module); bool Resolve(const std::string& name, Loader::SymbolType type, Module* module, Loader::SymbolRecord* return_info); - void Execute(); + void Execute(const std::vector args = {}); void DebugDump(); private: diff --git a/src/emulator.cpp b/src/emulator.cpp index 61d6d3862..e77c2b87f 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -99,7 +99,7 @@ Emulator::~Emulator() { Config::saveMainWindow(config_dir / "config.toml"); } -void Emulator::Run(const std::filesystem::path& file) { +void Emulator::Run(const std::filesystem::path& file, const std::vector args) { // Applications expect to be run from /app0 so mount the file's parent path as app0. auto* mnt = Common::Singleton::Instance(); const auto game_folder = file.parent_path(); @@ -152,6 +152,15 @@ void Emulator::Run(const std::filesystem::path& file) { if (const auto raw_attributes = param_sfo->GetInteger("ATTRIBUTE")) { psf_attributes.raw = *raw_attributes; } + if (!args.empty()) { + int argc = std::min(args.size(), 32); + for (int i = 0; i < argc; i++) { + LOG_INFO(Loader, "Game argument {}: {}", i, args[i]); + } + if (args.size() > 32) { + LOG_ERROR(Loader, "Too many game arguments, only passing the first 32"); + } + } } const auto pic1_path = mnt->GetHostPath("/app0/sce_sys/pic1.png"); @@ -239,7 +248,7 @@ void Emulator::Run(const std::filesystem::path& file) { } #endif - linker->Execute(); + linker->Execute(args); window->InitTimers(); while (window->IsOpen()) { diff --git a/src/emulator.h b/src/emulator.h index a08ab43c3..08c2807a1 100644 --- a/src/emulator.h +++ b/src/emulator.h @@ -25,7 +25,7 @@ public: Emulator(); ~Emulator(); - void Run(const std::filesystem::path& file); + void Run(const std::filesystem::path& file, const std::vector args = {}); void UpdatePlayTime(const std::string& serial); private: diff --git a/src/main.cpp b/src/main.cpp index 54772870c..fad3b1f53 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -29,6 +29,7 @@ int main(int argc, char* argv[]) { bool has_game_argument = false; std::string game_path; + std::vector game_args{}; // Map of argument strings to lambda functions std::unordered_map> arg_map = { @@ -37,6 +38,9 @@ int main(int argc, char* argv[]) { std::cout << "Usage: shadps4 [options] \n" "Options:\n" " -g, --game Specify game path to launch\n" + " -- ... Parameters passed to the game ELF. " + "Needs to be at the end of the line, and everything after \"--\" is a " + "game argument.\n" " -p, --patch Apply specified patch file\n" " -f, --fullscreen Specify window initial fullscreen " "state. Does not overwrite the config file.\n" @@ -126,6 +130,21 @@ int main(int argc, char* argv[]) { // Assume the last argument is the game file if not specified via -g/--game game_path = argv[i]; has_game_argument = true; + } else if (std::string(argv[i]) == "--") { + if (i + 1 == argc) { + std::cerr << "Warning: -- is set, but no game arguments are added!\n"; + break; + } + for (int j = i + 1; j < argc; j++) { + game_args.push_back(argv[j]); + } + break; + } else if (i + 1 < argc && std::string(argv[i + 1]) == "--") { + if (!has_game_argument) { + game_path = argv[i]; + has_game_argument = true; + } + break; } else { std::cerr << "Unknown argument: " << cur_arg << ", see --help for info.\n"; return 1; @@ -166,7 +185,7 @@ int main(int argc, char* argv[]) { // Run the emulator with the resolved eboot path Core::Emulator emulator; - emulator.Run(eboot_path); + emulator.Run(eboot_path, game_args); return 0; } diff --git a/src/qt_gui/main.cpp b/src/qt_gui/main.cpp index 2d524e199..8babadc35 100644 --- a/src/qt_gui/main.cpp +++ b/src/qt_gui/main.cpp @@ -33,6 +33,7 @@ int main(int argc, char* argv[]) { bool has_command_line_argument = argc > 1; bool show_gui = false, has_game_argument = false; std::string game_path; + std::vector game_args{}; // Map of argument strings to lambda functions std::unordered_map> arg_map = { @@ -43,6 +44,9 @@ int main(int argc, char* argv[]) { " No arguments: Opens the GUI.\n" " -g, --game Specify or " " to launch\n" + " -- ... Parameters passed to the game ELF. " + "Needs to be at the end of the line, and everything after \"--\" is a " + "game argument.\n" " -p, --patch Apply specified patch file\n" " -s, --show-gui Show the GUI\n" " -f, --fullscreen Specify window initial fullscreen " @@ -131,6 +135,20 @@ int main(int argc, char* argv[]) { // Assume the last argument is the game file if not specified via -g/--game game_path = argv[i]; has_game_argument = true; + } else if (std::string(argv[i]) == "--") { + if (i + 1 == argc) { + std::cerr << "Warning: -- is set, but no game arguments are added!\n"; + break; + } + for (int j = i + 1; j < argc; j++) { + game_args.push_back(argv[j]); + } + break; + } else if (i + 1 < argc && std::string(argv[i + 1]) == "--") { + if (!has_game_argument) { + game_path = argv[i]; + has_game_argument = true; + } } else { std::cerr << "Unknown argument: " << cur_arg << ", see --help for info.\n"; return 1; @@ -181,7 +199,7 @@ int main(int argc, char* argv[]) { // Run the emulator with the resolved game path Core::Emulator emulator; - emulator.Run(game_file_path.string()); + emulator.Run(game_file_path.string(), game_args); if (!show_gui) { return 0; // Exit after running the emulator without showing the GUI }