From ccab9eae8df3cf5367ce5cf164c98d1534ac13cb Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sat, 18 Jul 2020 05:07:32 -0400 Subject: add warning when profile given with -p doesn't exist implemented moshortcut, nxm links and executable names as command line options --- src/commandline.cpp | 98 +++++++++++++++++++++++++++++++++-------- src/commandline.h | 13 ++++++ src/main.cpp | 119 ++++++++++++++++++++++---------------------------- src/moshortcut.cpp | 9 ++++ src/moshortcut.h | 8 ++-- src/organizercore.cpp | 9 +++- src/organizercore.h | 3 -- src/shared/util.cpp | 6 +++ src/shared/util.h | 2 + 9 files changed, 175 insertions(+), 92 deletions(-) (limited to 'src') diff --git a/src/commandline.cpp b/src/commandline.cpp index f84906e9..f5c4df60 100644 --- a/src/commandline.cpp +++ b/src/commandline.cpp @@ -1,5 +1,6 @@ #include "commandline.h" #include "env.h" +#include "shared/util.h" namespace cl { @@ -29,21 +30,22 @@ std::optional CommandLine::run(const std::wstring& line) po::store(parsed, m_vm); + auto opts = po::collect_unrecognized( + parsed.options, po::include_positional); + + if (m_vm.count("command")) { const auto commandName = m_vm["command"].as(); for (auto&& c : m_commands) { if (c->name() == commandName) { + // remove the command name itself + opts.erase(opts.begin()); + auto co = c->options(); co.add_options() ("help", "shows this message"); - auto opts = po::collect_unrecognized( - parsed.options, po::include_positional); - - // remove the command name itself - opts.erase(opts.begin()); - po::wcommand_line_parser parser(opts); parser.options(co); @@ -72,6 +74,26 @@ std::optional CommandLine::run(const std::wstring& line) return 0; } + if (!opts.empty()) { + const auto qs = QString::fromStdWString(opts[0]); + m_shortcut = qs; + + if (!m_shortcut.isValid()) { + if (isNxmLink(qs)) { + m_nxmLink = qs; + } else { + m_executable = qs; + } + } + + // remove the shortcut/nxm/executable + opts.erase(opts.begin()); + + for (auto&& o : opts) { + m_untouched.push_back(QString::fromStdWString(o)); + } + } + return {}; } catch(po::error& e) @@ -86,11 +108,19 @@ std::optional CommandLine::run(const std::wstring& line) } } +void CommandLine::clear() +{ + m_vm.clear(); + m_shortcut = {}; + m_nxmLink = {}; +} + void CommandLine::createOptions() { m_visibleOptions.add_options() - ("help", "shows this message") - ("multiple", "allows multiple instances of MO to run; see below"); + ("help", "show this message") + ("multiple", "allow multiple instances of MO to run; see below") + ("profile,p", po::value(), "use the given profile (defaults to last used)"); po::options_description options; options.add_options() @@ -148,20 +178,50 @@ bool CommandLine::multiple() const return (m_vm.count("multiple") > 0); } +std::optional CommandLine::profile() const +{ + if (m_vm.count("profile")) { + return QString::fromStdString(m_vm["profile"].as()); + } + + return {}; +} + +const MOShortcut& CommandLine::shortcut() const +{ + return m_shortcut; +} + +std::optional CommandLine::nxmLink() const +{ + return m_nxmLink; +} + +std::optional CommandLine::executable() const +{ + return m_executable; +} + +const QStringList& CommandLine::untouched() const +{ + return m_untouched; +} + std::string CommandLine::more() const { return - "--multiple can be used to allow multiple instances of MO to run\n" - "simultaneously. This is unsupported and can create all sorts of weird\n" - "problems. To minimize the problems:\n" - "\n" - " 1) Never have multiple MO instances opened that manage the same game\n" - " instance.\n" - " 2) If an executable is launched from an instance, only this instance\n" - " may launch executables until all instances are closed.\n" - "\n" - "It is recommended to close _all_ instances of MO as soon as multiple\n" - "instances become unnecessary."; + "Multiple instances\n" + " --multiple can be used to allow multiple instances of MO to run\n" + " simultaneously. This is unsupported and can create all sorts of weird\n" + " problems. To minimize the problems:\n" + " \n" + " 1) Never have multiple MO instances opened that manage the same\n" + " game instance.\n" + " 2) If an executable is launched from an instance, only this\n" + " instance may launch executables until all instances are closed.\n" + " \n" + " It is recommended to close _all_ instances of MO as soon as multiple\n" + " instances become unnecessary."; } diff --git a/src/commandline.h b/src/commandline.h index deeb9923..cdd1c917 100644 --- a/src/commandline.h +++ b/src/commandline.h @@ -1,5 +1,6 @@ #pragma once +#include "moshortcut.h" #include #include @@ -92,15 +93,27 @@ public: CommandLine(); std::optional run(const std::wstring& line); + void clear(); + std::string usage(const Command* c=nullptr) const; bool multiple() const; + std::optional profile() const; + const MOShortcut& shortcut() const; + std::optional nxmLink() const; + std::optional executable() const; + + const QStringList& untouched() const; private: po::options_description m_visibleOptions, m_allOptions; po::positional_options_description m_positional; std::vector> m_commands; po::variables_map m_vm; + MOShortcut m_shortcut; + std::optional m_nxmLink; + std::optional m_executable; + QStringList m_untouched; void createOptions(); std::string more() const; diff --git a/src/main.cpp b/src/main.cpp index a1a4be01..3c5bc92d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -225,18 +225,13 @@ static bool HaveWriteAccess(const std::wstring &path) } -QString determineProfile(QStringList &arguments, const Settings &settings) +QString determineProfile(const cl::CommandLine& cl, const Settings &settings) { auto selectedProfileName = settings.game().selectedProfileName(); - { // see if there is a profile on the command line - int profileIndex = arguments.indexOf("-p", 1); - if ((profileIndex != -1) && (profileIndex < arguments.size() - 1)) { - log::debug("profile overwritten on command line"); - selectedProfileName = arguments.at(profileIndex + 1); - } - arguments.removeAt(profileIndex); - arguments.removeAt(profileIndex); + if (cl.profile()) { + log::debug("profile overwritten on command line"); + selectedProfileName = *cl.profile(); } if (!selectedProfileName) { @@ -481,7 +476,7 @@ static QString getVersionDisplayString() int runApplication( - MOApplication &application, QStringList& arguments, + MOApplication &application, const cl::CommandLine& cl, SingleInstance &instance, const QString &splashPath) { TimeThis tt("runApplication() to exec()"); @@ -635,57 +630,51 @@ int runApplication( organizer.updateExecutablesList(); organizer.updateModInfoFromDisc(); - QString selectedProfileName = determineProfile(arguments, settings); + QString selectedProfileName = determineProfile(cl, settings); organizer.setCurrentProfile(selectedProfileName); // if we have a command line parameter, it is either a nxm link or // a binary to start - if (arguments.size() > 1) { - if (MOShortcut shortcut{ arguments.at(1) }) { - if (shortcut.hasExecutable()) { - try { - organizer.processRunner() - .setFromShortcut(shortcut) - .setWaitForCompletion() - .run(); - - return 0; - } - catch (const std::exception &e) { - reportError( - QObject::tr("failed to start shortcut: %1").arg(e.what())); - return 1; - } - } - } - else if (OrganizerCore::isNxmLink(arguments.at(1))) { - log::debug("starting download from command line: {}", arguments.at(1)); - organizer.externalMessage(arguments.at(1)); - } - else { - QString exeName = arguments.at(1); - log::debug("starting {} from command line", exeName); - - arguments.removeFirst(); // remove application name (ModOrganizer.exe) - arguments.removeFirst(); // remove binary name - - try - { - // pass the remaining parameters to the binary - organizer.processRunner() - .setFromFileOrExecutable(exeName, arguments) + if (cl.shortcut().isValid()) { + if (cl.shortcut().hasExecutable()) { + try { + organizer.processRunner() + .setFromShortcut(cl.shortcut()) .setWaitForCompletion() .run(); - return 0; - } - catch (const std::exception &e) - { - reportError( - QObject::tr("failed to start application: %1").arg(e.what())); - return 1; - } - } - } + + return 0; + } + catch (const std::exception &e) { + reportError( + QObject::tr("failed to start shortcut: %1").arg(e.what())); + return 1; + } + } + } else if (cl.nxmLink()) { + log::debug("starting download from command line: {}", *cl.nxmLink()); + organizer.externalMessage(*cl.nxmLink()); + } else if (cl.executable()) { + const QString exeName = *cl.executable(); + log::debug("starting {} from command line", exeName); + + try + { + // pass the remaining parameters to the binary + organizer.processRunner() + .setFromFileOrExecutable(exeName, cl.untouched()) + .setWaitForCompletion() + .run(); + + return 0; + } + catch (const std::exception &e) + { + reportError( + QObject::tr("failed to start application: %1").arg(e.what())); + return 1; + } + } QPixmap pixmap; @@ -857,15 +846,13 @@ int main(int argc, char *argv[]) siFlags |= SingleInstance::AllowMultiple; } - MOShortcut moshortcut{ arguments.size() > 1 ? arguments.at(1) : "" }; - SingleInstance instance(siFlags); if (instance.ephemeral()) { - if (moshortcut || - arguments.size() > 1 && OrganizerCore::isNxmLink(arguments.at(1))) - { - log::debug("not primary instance, sending shortcut/download message"); - instance.sendMessage(arguments.at(1)); + if (cl.shortcut().isValid()) { + instance.sendMessage(cl.shortcut().toString()); + return 0; + } else if (cl.nxmLink()) { + instance.sendMessage(*cl.nxmLink()); return 0; } else if (arguments.size() == 1) { QMessageBox::information( @@ -887,8 +874,8 @@ int main(int argc, char *argv[]) try { InstanceManager& instanceManager = InstanceManager::instance(); - if (moshortcut && moshortcut.hasInstance()) - instanceManager.overrideInstance(moshortcut.instance()); + if (cl.shortcut().isValid() && cl.shortcut().hasInstance()) + instanceManager.overrideInstance(cl.shortcut().instance()); dataPath = instanceManager.determineDataPath(); } catch (const std::exception &e) { if (strcmp(e.what(),"Canceled")) @@ -920,12 +907,12 @@ int main(int argc, char *argv[]) tt.stop(); - const int result = runApplication(application, arguments, instance, splash); + const int result = runApplication(application, cl, instance, splash); if (result != RestartExitCode) { return result; } argc = 1; - moshortcut = MOShortcut(""); + cl.clear(); } while (true); } diff --git a/src/moshortcut.cpp b/src/moshortcut.cpp index aad7380e..4efedbdb 100644 --- a/src/moshortcut.cpp +++ b/src/moshortcut.cpp @@ -40,3 +40,12 @@ MOShortcut::MOShortcut(const QString& link) m_hasExecutable=true; } } + +QString MOShortcut::toString() const +{ + if (m_hasInstance) { + return "moshortcut://" + m_instance + ":" + m_executable; + } else { + return "moshortcut://" + m_executable; + } +} diff --git a/src/moshortcut.h b/src/moshortcut.h index 2ce54910..0067b3bc 100644 --- a/src/moshortcut.h +++ b/src/moshortcut.h @@ -27,19 +27,21 @@ along with Mod Organizer. If not, see . class MOShortcut { public: - MOShortcut(const QString& link); + MOShortcut(const QString& link={}); /// true iff intialized using a valid moshortcut link - operator bool() const { return m_valid; } + bool isValid() const { return m_valid; } bool hasInstance() const { return m_hasInstance; } - + bool hasExecutable() const { return m_hasExecutable; } const QString& instance() const { return m_instance; } const QString& executable() const { return m_executable; } + QString toString() const; + private: QString m_instance; QString m_executable; diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 1c35720b..f9cafc95 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -38,6 +38,7 @@ #include "shared/directoryentry.h" #include "shared/filesorigin.h" #include "shared/fileentry.h" +#include "shared/util.h" #include #include @@ -395,7 +396,9 @@ void OrganizerCore::profileRemoved(QString const& profileName) void OrganizerCore::externalMessage(const QString &message) { - if (MOShortcut moshortcut{ message } ) { + MOShortcut moshortcut(message); + + if (moshortcut.isValid()) { if(moshortcut.hasExecutable()) { processRunner() .setFromShortcut(moshortcut) @@ -555,12 +558,16 @@ void OrganizerCore::setCurrentProfile(const QString &profileName) QString profileDir = profileBaseDir.absoluteFilePath(profileName); if (!QDir(profileDir).exists()) { + log::error("profile '{}' does not exist", profileName); + // selected profile doesn't exist. Ensure there is at least one profile, // then pick any one createDefaultProfile(); profileDir = profileBaseDir.absoluteFilePath( profileBaseDir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot).at(0)); + + log::error("picked profile '{}' instead", QDir(profileDir).dirName()); } // Keep the old profile to emit signal-changed: diff --git a/src/organizercore.h b/src/organizercore.h index 70ce94f5..1452bf08 100644 --- a/src/organizercore.h +++ b/src/organizercore.h @@ -191,9 +191,6 @@ public: }; public: - - static bool isNxmLink(const QString &link) { return link.startsWith("nxm://", Qt::CaseInsensitive); } - OrganizerCore(Settings &settings); ~OrganizerCore(); diff --git a/src/shared/util.cpp b/src/shared/util.cpp index ba500da9..f316549e 100644 --- a/src/shared/util.cpp +++ b/src/shared/util.cpp @@ -435,3 +435,9 @@ void ResetExitFlag() { g_exiting = false; } + + +bool isNxmLink(const QString& link) +{ + return link.startsWith("nxm://", Qt::CaseInsensitive); +} diff --git a/src/shared/util.h b/src/shared/util.h index 2761b64f..1688a931 100644 --- a/src/shared/util.h +++ b/src/shared/util.h @@ -83,4 +83,6 @@ bool ModOrganizerExiting(); bool ModOrganizerCanCloseNow(); void ResetExitFlag(); +bool isNxmLink(const QString& link); + #endif // UTIL_H -- cgit v1.3.1