#include "LaunchSettings.h" #include "util/helpers/helpers.h" #include "Cafe/Account/Account.h" #include "Cafe/OS/libs/coreinit/coreinit.h" #include "boost/program_options.hpp" #include #include "config/ActiveSettings.h" #include "config/NetworkSettings.h" #include "util/crypto/aes128.h" #include "Cafe/Filesystem/FST/FST.h" void requireConsole(); bool LaunchSettings::HandleCommandline(const wchar_t* lpCmdLine) { #if BOOST_OS_WINDOWS const std::vector args = boost::program_options::split_winmain(lpCmdLine); return HandleCommandline(args); #else cemu_assert_unimplemented(); return false; #endif } bool LaunchSettings::HandleCommandline(int argc, wchar_t* argv[]) { std::vector args; args.reserve(argc); for(int i = 0; i < argc; ++i) { args.emplace_back(argv[i]); } return HandleCommandline(args); } bool LaunchSettings::HandleCommandline(int argc, char* argv[]) { std::vector args; args.reserve(argc); for(int i = 0; i < argc; ++i) { args.emplace_back(boost::nowide::widen(argv[i])); } return HandleCommandline(args); } bool LaunchSettings::HandleCommandline(const std::vector& args) { namespace po = boost::program_options; po::options_description desc{ "Launch options" }; desc.add_options() ("help,h", "This help screen") ("version,v", "Displays the version of Cemu") ("game,g", po::wvalue(), "Path of game to launch") ("mlc,m", po::wvalue(), "Custom mlc folder location") ("fullscreen,f", po::value()->implicit_value(true), "Launch games in fullscreen mode") ("ud,u", po::value()->implicit_value(true), "Render output upside-down") ("account,a", po::value(), "Persistent id of account") ("force-interpreter", po::value()->implicit_value(true), "Force interpreter CPU emulation, disables recompiler") ("act-url", po::value(), "URL prefix for account server") ("ecs-url", po::value(), "URL for ECS service"); po::options_description hidden{ "Hidden options" }; hidden.add_options() ("nsight", po::value()->implicit_value(true), "NSight debugging options") ("legacy", po::value()->implicit_value(true), "Intel legacy graphic mode"); po::options_description extractor{ "Extractor tool" }; extractor.add_options() ("extract,e", po::wvalue(), "Path to WUD or WUX file for extraction") ("path,p", po::value(), "Path of file to extract (for example meta/meta.xml)") ("output,o", po::wvalue(), "Output path for extracted file."); po::options_description all; all.add(desc).add(hidden).add(extractor); po::options_description visible; visible.add(desc).add(extractor); try { po::wcommand_line_parser parser{ args }; parser.allow_unregistered().options(all).style(po::command_line_style::allow_long | po::command_line_style::allow_short | po::command_line_style::allow_dash_for_short | po::command_line_style::case_insensitive | po::command_line_style::long_allow_next | po::command_line_style::short_allow_next | po::command_line_style::allow_long_disguise); const auto parsed_options = parser.run(); po::variables_map vm; po::store(parsed_options, vm); notify(vm); if (vm.count("help")) { requireConsole(); std::cout << visible << std::endl; return false; // exit in main } if (vm.count("version")) { requireConsole(); std::string versionStr; #if EMULATOR_VERSION_MINOR == 0 versionStr = fmt::format("{}.{}{}", EMULATOR_VERSION_LEAD, EMULATOR_VERSION_MAJOR, EMULATOR_VERSION_SUFFIX); #else versionStr = fmt::format("{}.{}-{}{}", EMULATOR_VERSION_LEAD, EMULATOR_VERSION_MAJOR, EMULATOR_VERSION_MINOR, EMULATOR_VERSION_SUFFIX); #endif std::cout << versionStr << std::endl; return false; // exit in main } if (vm.count("game")) { std::wstring tmp = vm["game"].as(); // workaround for boost command_line_parser not trimming token for short name parameters despite short_allow_adjacent if (tmp.size() > 0 && tmp.front() == '=') tmp.erase(tmp.begin()+0); s_load_game_file = tmp; } if (vm.count("mlc")) { std::wstring tmp = vm["mlc"].as(); // workaround for boost command_line_parser not trimming token for short name parameters despite short_allow_adjacent if (tmp.size() > 0 && tmp.front() == '=') tmp.erase(tmp.begin() + 0); s_mlc_path = tmp; } if (vm.count("account")) { const auto id = ConvertString(vm["account"].as(), 16); if (id >= Account::kMinPersistendId) s_persistent_id = id; } if (vm.count("fullscreen")) s_fullscreen = vm["fullscreen"].as(); if (vm.count("ud")) s_render_upside_down = vm["ud"].as(); if (vm.count("nsight")) s_nsight_mode = vm["nsight"].as(); if (vm.count("legacy")) s_force_intel_legacy = vm["legacy"].as(); if(vm.count("force-interpreter")) s_force_interpreter = vm["force-interpreter"].as(); std::wstring extract_path, log_path; std::string output_path; if (vm.count("extract")) extract_path = vm["extract"].as(); if (vm.count("path")) output_path = vm["path"].as(); if (vm.count("output")) log_path = vm["output"].as(); // urls if (vm.count("act-url")) { serviceURL_ACT = vm["act-url"].as(); if (serviceURL_ACT.size() > 0 && serviceURL_ACT.back() == '/') serviceURL_ACT.pop_back(); } if (vm.count("ecs-url")) serviceURL_ECS = vm["ecs-url"].as(); if(!extract_path.empty()) { ExtractorTool(extract_path, output_path, log_path); return false; } return true; } catch (const std::exception& ex) { std::string errorMsg; errorMsg.append("Error while trying to parse command line parameter:\n"); errorMsg.append(ex.what()); wxMessageBox(errorMsg, wxT("Parameter error"), wxICON_ERROR); return false; } } bool LaunchSettings::ExtractorTool(std::wstring_view wud_path, std::string_view output_path, std::wstring_view log_path) { // extracting requires path of file if (output_path.empty()) { requireConsole(); puts("Cannot extract files because no source path was specified (-p)\n"); return false; } // mount wud AES128_init(); FSTVolume* srcVolume = FSTVolume::OpenFromDiscImage(fs::path(wud_path)); if (!srcVolume) { requireConsole(); puts(fmt::format("Unable to open \"%s\"\n", fs::path(wud_path).generic_string()).c_str()); return false; } bool fileFound = false; std::vector fileData = srcVolume->ExtractFile(output_path, &fileFound); delete srcVolume; if (!fileFound) { requireConsole(); puts(fmt::format("Unable to read file \"%s\"\n", output_path).c_str()); return false; } // output on console (if no output path was specified) if (!log_path.empty()) { try { fs::path filename(std::wstring{ log_path }); filename /= boost::nowide::widen(std::string(output_path)); fs::path p = filename; p.remove_filename(); fs::create_directories(p); std::ofstream file(filename, std::ios::out | std::ios::binary); file.write((const char*)fileData.data(), fileData.size()); file.flush(); file.close(); } catch (const std::exception& ex) { forceLog_printf("can't write file: %s", ex.what()); puts(fmt::format("can't write file: %s\n", ex.what()).c_str()); } } else { // output to console requireConsole(); printf("%.*s", (int)fileData.size(), fileData.data()); fflush(stdout); } return true; } void LaunchSettings::ChangeNetworkServiceURL(int ID){ NetworkService Network = static_cast(ID); switch (Network) { case NetworkService::Pretendo: serviceURL_ACT = PretendoURLs::ACTURL; serviceURL_ECS = PretendoURLs::ECSURL; break; case NetworkService::Custom: serviceURL_ACT = GetNetworkConfig().urls.ACT.GetValue(); serviceURL_ECS = GetNetworkConfig().urls.ECS.GetValue(); break; case NetworkService::Nintendo: default: serviceURL_ACT = NintendoURLs::ACTURL; serviceURL_ECS = NintendoURLs::ECSURL; break; } }