diff --git a/src/addbinarydialog.cpp b/src/addbinarydialog.cpp index 0a0d70a..1774d9d 100644 --- a/src/addbinarydialog.cpp +++ b/src/addbinarydialog.cpp @@ -3,7 +3,8 @@ #include AddBinaryDialog::AddBinaryDialog( - const std::vector>& games, QWidget* parent) + const std::vector>& games, + const QStringList schemas, QWidget* parent) : QDialog(parent), ui(new Ui::AddBinaryDialog) { ui->setupUi(this); @@ -11,6 +12,10 @@ AddBinaryDialog::AddBinaryDialog( for (auto iter = games.begin(); iter != games.end(); ++iter) { addGame(std::get<0>(*iter), std::get<1>(*iter)); } + + for (auto schema : schemas) { + this->ui->schemaSelector->addItem(schema); + } } AddBinaryDialog::~AddBinaryDialog() @@ -45,6 +50,11 @@ QString AddBinaryDialog::arguments() return ui->argumentsEdit->text(); } +QString AddBinaryDialog::schema() +{ + return ui->schemaSelector->currentText(); +} + void AddBinaryDialog::on_browseButton_clicked() { ui->binaryEdit->setText(QFileDialog::getOpenFileName( diff --git a/src/addbinarydialog.h b/src/addbinarydialog.h index 6ec79ad..bbe5c24 100644 --- a/src/addbinarydialog.h +++ b/src/addbinarydialog.h @@ -16,11 +16,12 @@ class AddBinaryDialog : public QDialog public: explicit AddBinaryDialog( const std::vector>& handlers, - QWidget* parent = 0); + const QStringList schemas, QWidget* parent = 0); ~AddBinaryDialog(); QStringList gameIDs(); QString executable(); QString arguments(); + QString schema(); private slots: void on_browseButton_clicked(); diff --git a/src/addbinarydialog.ui b/src/addbinarydialog.ui index f84cab0..b3d7ba7 100644 --- a/src/addbinarydialog.ui +++ b/src/addbinarydialog.ui @@ -7,7 +7,7 @@ 0 0 575 - 160 + 219 @@ -28,7 +28,7 @@ - QAbstractItemView::ExtendedSelection + QAbstractItemView::SelectionMode::ExtendedSelection @@ -37,7 +37,7 @@ - + Select Handler Binary (i.e. ModOrganizer.exe) @@ -64,7 +64,17 @@ - + + + Assigned Schema + + + + + + + + Set Arguments (optional) @@ -76,7 +86,7 @@ - Qt::Vertical + Qt::Orientation::Vertical @@ -93,10 +103,10 @@ - Qt::Horizontal + Qt::Orientation::Horizontal - QDialogButtonBox::Cancel|QDialogButtonBox::Ok + QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok diff --git a/src/handlerstorage.cpp b/src/handlerstorage.cpp index 759aead..d23978d 100644 --- a/src/handlerstorage.cpp +++ b/src/handlerstorage.cpp @@ -23,29 +23,30 @@ void HandlerStorage::clear() m_Handlers.clear(); } -void HandlerStorage::registerProxy(const QString& proxyPath) +void HandlerStorage::registerSchemaProxy(const QString& proxyPath, + const QString& schema) { - QSettings settings("HKEY_CURRENT_USER\\Software\\Classes\\nxm\\", + QSettings settings("HKEY_CURRENT_USER\\Software\\Classes\\" + schema + "\\", QSettings::NativeFormat); QString myExe = QString("\"%1\" ").arg(QDir::toNativeSeparators(proxyPath)).append("\"%1\""); - settings.setValue("Default", "URL:NXM Protocol"); + settings.setValue("Default", "URL:" + schema.toUpper() + " Protocol"); settings.setValue("URL Protocol", ""); settings.setValue("shell/open/command/Default", myExe); settings.sync(); } -void HandlerStorage::registerHandler(const QString& executable, +void HandlerStorage::registerHandler(const QString& schema, const QString& executable, const QString& arguments, bool prepend) { QStringList games; for (const auto& game : this->knownGames()) { games.append(std::get<1>(game)); } - registerHandler(games, executable, arguments, prepend, false); + registerHandler(games, schema, executable, arguments, prepend, false); } -void HandlerStorage::registerHandler(const QStringList& games, +void HandlerStorage::registerHandler(const QStringList& games, const QString& schema, const QString& executable, const QString& arguments, bool prepend, bool rereg) { @@ -54,12 +55,14 @@ void HandlerStorage::registerHandler(const QStringList& games, gamesLower.append(game.toLower()); } for (auto iter = m_Handlers.begin(); iter != m_Handlers.end(); ++iter) { - if (iter->executable.compare(executable, Qt::CaseInsensitive) == 0) { - // executable already registered, update supported games and move it to top if - // requested + if (iter->executable.compare(executable, Qt::CaseInsensitive) == 0 && + iter->schema.compare(schema, Qt::CaseInsensitive) == 0) { + // executable already registered, update supported games and move it to + // top if requested if (rereg) { HandlerInfo info = *iter; info.games = gamesLower; + info.arguments = arguments; m_Handlers.erase(iter); if (prepend) { m_Handlers.push_front(info); @@ -69,6 +72,7 @@ void HandlerStorage::registerHandler(const QStringList& games, } else { iter->games.append(gamesLower); iter->games.removeDuplicates(); + iter->arguments = arguments; } return; // important: in the rereg-case we changed the list thus screwing up the // iterator @@ -79,6 +83,7 @@ void HandlerStorage::registerHandler(const QStringList& games, HandlerInfo info; info.ID = static_cast(m_Handlers.size()); info.games = gamesLower; + info.schema = schema; info.executable = executable; info.arguments = arguments; if (prepend) { @@ -88,7 +93,7 @@ void HandlerStorage::registerHandler(const QStringList& games, } } -QStringList HandlerStorage::getHandler(const QString& game) const +QStringList HandlerStorage::getHandler(const QString& game, const QString& schema) const { QString gameKey; QStringList results; @@ -102,12 +107,14 @@ QStringList HandlerStorage::getHandler(const QString& game) const } // look for an explictly registered handler for (const HandlerInfo& info : m_Handlers) { - for (auto handler : info.games) { - if (game.compare(handler, Qt::CaseInsensitive) == 0 || - gameKey.compare(handler, Qt::CaseInsensitive) == 0) { - results << info.executable; - results << info.arguments; - return results; + if (info.schema == schema) { + for (auto handler : info.games) { + if (game.compare(handler, Qt::CaseInsensitive) == 0 || + gameKey.compare(handler, Qt::CaseInsensitive) == 0) { + results << info.executable; + results << info.arguments; + return results; + } } } } @@ -115,10 +122,12 @@ QStringList HandlerStorage::getHandler(const QString& game) const // if no registered handler, look for the first "other" entry if (results.length() == 0) { for (const HandlerInfo& info : m_Handlers) { - if (info.games.contains("other", Qt::CaseInsensitive)) { - results << info.executable; - results << info.arguments; - return results; + if (info.schema == schema) { + if (info.games.contains("other", Qt::CaseInsensitive)) { + results << info.executable; + results << info.arguments; + return results; + } } } } @@ -136,18 +145,26 @@ std::vector> HandlerStorage::knownGames() return { std::make_tuple("Morrowind", "morrowind", "morrowind"), std::make_tuple("Oblivion", "oblivion", "oblivion"), + std::make_tuple( + "Oblivion Remastered", "oblivionremastered", "oblivionremastered"), std::make_tuple("Fallout 3", "fallout3", "fallout3"), std::make_tuple("Fallout 4", "fallout4", "fallout4"), std::make_tuple("Fallout NV", "falloutnv", "newvegas"), std::make_tuple("Skyrim", "skyrim", "skyrim"), std::make_tuple("SkyrimSE", "skyrimse", "skyrimspecialedition"), + std::make_tuple("Starfield", "starfield", "starfield"), std::make_tuple("Enderal", "enderal", "enderal"), std::make_tuple("EnderalSE", "enderalse", "enderalspecialedition"), std::make_tuple("Other", "other", "other")}; } +QStringList HandlerStorage::availableSchemas() const +{ + return {"nxm", "modl"}; +} + QStringList HandlerStorage::stripCall(const QString& call) { // results[0] is binary, results[1..n] are optional arguments @@ -213,6 +230,7 @@ void HandlerStorage::loadStore() if (!gameList.isEmpty()) { info.games = gameList.split(","); } + info.schema = settings.value("schema", "nxm").toString(); info.executable = settings.value("executable").toString(); info.arguments = settings.value("arguments").toString(); if (QFile::exists(info.executable)) { @@ -234,6 +252,7 @@ void HandlerStorage::loadStore() ids.append(std::get<1>(*iter)); } info.games = QStringList() << ids; + info.schema = "nxm"; info.executable = handlerValues.front(); handlerValues.pop_front(); info.arguments = handlerValues.join(" "); @@ -261,6 +280,7 @@ void HandlerStorage::saveStore() for (auto iter = m_Handlers.begin(); iter != m_Handlers.end(); ++iter) { settings.setArrayIndex(i++); settings.setValue("games", iter->games.join(",")); + settings.setValue("schema", iter->schema); settings.setValue("executable", iter->executable); settings.setValue("arguments", iter->arguments); } diff --git a/src/handlerstorage.h b/src/handlerstorage.h index 577ad76..e918eae 100644 --- a/src/handlerstorage.h +++ b/src/handlerstorage.h @@ -10,6 +10,7 @@ struct HandlerInfo { int ID; QStringList games; + QString schema; QString executable; QString arguments; }; @@ -22,16 +23,18 @@ class HandlerStorage : public QObject ~HandlerStorage(); void clear(); - /// register the primary proxy handler - void registerProxy(const QString& proxyPath); + /// register a proxy handler + void registerSchemaProxy(const QString& proxyPath, const QString& schema); /// register handler (for all games) - void registerHandler(const QString& executable, const QString& arguments, - bool prepend); + void registerHandler(const QString& schema, const QString& executable, + const QString& arguments, bool prepend); /// register handler for specified games - void registerHandler(const QStringList& games, const QString& executable, - const QString& arguments, bool prepend, bool rereg); - QStringList getHandler(const QString& game) const; + void registerHandler(const QStringList& games, const QString& schema, + const QString& executable, const QString& arguments, + bool prepend, bool rereg); + QStringList getHandler(const QString& game, const QString& schema) const; std::vector> knownGames() const; + QStringList availableSchemas() const; std::list handlers() const { return m_Handlers; } static QStringList stripCall(const QString& call); diff --git a/src/handlerwindow.cpp b/src/handlerwindow.cpp index a062c95..1aa06eb 100644 --- a/src/handlerwindow.cpp +++ b/src/handlerwindow.cpp @@ -10,6 +10,7 @@ enum { COL_GAMES, + COL_SCHEMA, COL_BINARY, COL_ARGUMENTS }; @@ -30,13 +31,23 @@ HandlerWindow::~HandlerWindow() delete ui; } -void HandlerWindow::setPrimaryHandler(const QString& handlerPath) +void HandlerWindow::setNXMHandler(const QString& handlerPath) { if (handlerPath == QCoreApplication::applicationFilePath()) { - ui->registerButton->setEnabled(false); - ui->handlerView->setText(tr("")); + ui->registerNXMButton->setEnabled(false); + ui->nxmHandlerView->setText(tr("")); } else { - ui->handlerView->setText(handlerPath); + ui->nxmHandlerView->setText(handlerPath); + } +} + +void HandlerWindow::setMODLHandler(const QString& handlerPath) +{ + if (handlerPath == QCoreApplication::applicationFilePath()) { + ui->registerMODLButton->setEnabled(false); + ui->modlHandlerView->setText(tr("")); + } else { + ui->modlHandlerView->setText(handlerPath); } } @@ -48,7 +59,7 @@ void HandlerWindow::setHandlerStorage(HandlerStorage* storage) auto list = storage->handlers(); for (auto iter = list.begin(); iter != list.end(); ++iter) { QTreeWidgetItem* newItem = new QTreeWidgetItem( - QStringList() << iter->games.join(",") + QStringList() << iter->games.join(",") << iter->schema << QDir::toNativeSeparators(iter->executable) << iter->arguments); newItem->setFlags(newItem->flags() | Qt::ItemIsEditable); @@ -63,14 +74,14 @@ void HandlerWindow::closeEvent(QCloseEvent* event) for (int i = 0; i < ui->handlersWidget->topLevelItemCount(); ++i) { QTreeWidgetItem* item = ui->handlersWidget->topLevelItem(i); m_Storage->registerHandler(item->text(0).split(","), item->text(1), item->text(2), - false, false); + item->text(3), false, false); } QMainWindow::closeEvent(event); } void HandlerWindow::addBinaryDialog() { - AddBinaryDialog dialog(m_Storage->knownGames()); + AddBinaryDialog dialog(m_Storage->knownGames(), m_Storage->availableSchemas()); if (dialog.exec() == QDialog::Accepted) { bool executableKnown = false; for (int i = 0; i < ui->handlersWidget->topLevelItemCount(); ++i) { @@ -80,6 +91,7 @@ void HandlerWindow::addBinaryDialog() games.append(dialog.gameIDs()); games.removeDuplicates(); iterItem->setText(COL_GAMES, games.join(",")); + iterItem->setText(COL_SCHEMA, dialog.schema()); if (iterItem->text(COL_ARGUMENTS) .compare(dialog.arguments(), Qt::CaseInsensitive) != 0) { iterItem->setText(COL_ARGUMENTS, dialog.arguments()); @@ -90,8 +102,8 @@ void HandlerWindow::addBinaryDialog() if (!executableKnown) { QTreeWidgetItem* newItem = new QTreeWidgetItem( - QStringList() << dialog.gameIDs().join(",") << dialog.executable() - << dialog.arguments()); + QStringList() << dialog.gameIDs().join(",") << dialog.schema() + << dialog.executable() << dialog.arguments()); newItem->setFlags(newItem->flags() | Qt::ItemIsEditable); ui->handlersWidget->insertTopLevelItem(0, newItem); } @@ -120,19 +132,36 @@ void HandlerWindow::on_handlersWidget_customContextMenuRequested(const QPoint& p contextMenu.exec(); } -void HandlerWindow::on_registerButton_clicked() +void HandlerWindow::on_registerNXMButton_clicked() +{ + if (QMessageBox::question( + this, tr("Change handler registration?"), + tr("This will make the nxmhandler.exe you called the NXM handler registered " + "in the system.\n" + "That has no immediate impact on how links are handled.\n" + "Use this if you moved Mod Organizer or if you uninstalled the Mod " + "Organizer installation that was previously registered. Continue?"), + QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { + ui->nxmHandlerView->setText(tr("")); + ui->registerNXMButton->setEnabled(false); + + m_Storage->registerSchemaProxy(QCoreApplication::applicationFilePath(), "nxm"); + } +} + +void HandlerWindow::on_registerMODLButton_clicked() { - if (QMessageBox::question(this, tr("Change handler registration?"), - tr("This will make the nxmhandler.exe you called the " - "primary handler registered in the system.\n" - "That has no immediate impact on how links are " - "handled.\nUse this if you moved Mod Organizer " - "or if you uninstalled the Mod Organizer installation " - "that was previously registered. Continue?"), - QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { - ui->handlerView->setText(tr("")); - ui->registerButton->setEnabled(false); - - m_Storage->registerProxy(QCoreApplication::applicationFilePath()); + if (QMessageBox::question( + this, tr("Change handler registration?"), + tr("This will make the nxmhandler.exe you called the MODL handler registered " + "in the system.\n" + "That has no immediate impact on how links are handled.\n" + "Use this if you moved Mod Organizer or if you uninstalled the Mod " + "Organizer installation that was previously registered. Continue?"), + QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { + ui->modlHandlerView->setText(tr("")); + ui->registerMODLButton->setEnabled(false); + + m_Storage->registerSchemaProxy(QCoreApplication::applicationFilePath(), "modl"); } } diff --git a/src/handlerwindow.h b/src/handlerwindow.h index 68c84bc..1273112 100644 --- a/src/handlerwindow.h +++ b/src/handlerwindow.h @@ -18,7 +18,8 @@ class HandlerWindow : public QMainWindow explicit HandlerWindow(QWidget* parent = 0); ~HandlerWindow(); - void setPrimaryHandler(const QString& handlerPath); + void setNXMHandler(const QString& handlerPath); + void setMODLHandler(const QString& handlerPath); void setHandlerStorage(HandlerStorage* storage); protected: @@ -27,7 +28,8 @@ private slots: void on_handlersWidget_customContextMenuRequested(const QPoint& pos); void addBinaryDialog(); void removeBinary(); - void on_registerButton_clicked(); + void on_registerNXMButton_clicked(); + void on_registerMODLButton_clicked(); private: Ui::HandlerWindow* ui; diff --git a/src/handlerwindow.ui b/src/handlerwindow.ui index 645b6e0..66c75df 100644 --- a/src/handlerwindow.ui +++ b/src/handlerwindow.ui @@ -7,18 +7,18 @@ 0 0 647 - 347 + 362 - NXM Handler + MO2 Download Handler - Use this list to configure programs to handle nxm links. Different Programs can be set up to handle links for different games. If the same game is supported by multiple binaries, the top-most is used. + Use this list to configure programs to handle download links. Different programs can be set up to handle links for different games. If the same game is supported by multiple binaries, the top-most is used. true @@ -28,13 +28,13 @@ - Qt::CustomContextMenu + Qt::ContextMenuPolicy::CustomContextMenu - QAbstractItemView::DragDrop + QAbstractItemView::DragDropMode::DragDrop - Qt::MoveAction + Qt::DropAction::MoveAction 0 @@ -42,6 +42,9 @@ false + + 4 + 150 @@ -50,6 +53,11 @@ Supported Games + + + Schema Handler + + Binary @@ -67,19 +75,40 @@ - Primary Handler + Current NXM Handler - + true - + + + Register active + + + + + + + + + + + Current MODL Handler + + + + + + + + Register active @@ -92,7 +121,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal diff --git a/src/main.cpp b/src/main.cpp index 3a6b126..98dc0d1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -43,8 +44,8 @@ void logHandler(QtMsgType type, const QMessageLogContext& context, QString("[%1] %2\n").arg(QDateTime::currentDateTime().toString()).arg(message))); } -void handleLink(const QString& executable, const QString& arguments, - const QString& link) +void handleNxmLink(const QString& executable, const QString& arguments, + const QString& link) { QString quotedExecutable(executable); if (!quotedExecutable.contains(QRegularExpression("^\".*\"$"))) { @@ -62,8 +63,29 @@ void handleLink(const QString& executable, const QString& arguments, SW_SHOWNORMAL); } -HandlerStorage* registerExecutable(const QDir& storagePath, const QString& handlerPath, - const QString& handlerArgs) +void handleModlLink(const QString& executable, const QString& arguments, + const QString& link) +{ + QString quotedExecutable(executable); + if (!quotedExecutable.contains(QRegularExpression("^\".*\"$"))) { + quotedExecutable = '"' + quotedExecutable + '"'; + } + + QString quotedLink(link); + if (!quotedLink.contains(QRegularExpression("^\".*\"$"))) { + quotedLink = '"' + quotedLink + '"'; + } + + ::ShellExecute(nullptr, TEXT("open"), ToWString(quotedExecutable).c_str(), + ToWString("download " + arguments + " " + quotedLink).c_str(), + ToWString(QFileInfo(quotedExecutable).absolutePath()).c_str(), + SW_SHOWNORMAL); +} + +HandlerStorage* registerSchemaExecutable(const QDir& storagePath, + const QString& handlerPath, + const QString& schema, + const QString& handlerArgs) { HandlerStorage* storage = nullptr; if (!handlerPath.isEmpty() && @@ -71,23 +93,19 @@ HandlerStorage* registerExecutable(const QDir& storagePath, const QString& handl // a foreign or global nxm handler, register ourself and use that handler as // an option - if this is another nxmhandler we could run into problems so skip it storage = new HandlerStorage(storagePath.path()); - storage->registerHandler(handlerPath, handlerArgs, false); - storage->registerProxy(QCoreApplication::applicationFilePath()); + storage->registerHandler(schema, handlerPath, handlerArgs, false); + storage->registerSchemaProxy(QCoreApplication::applicationFilePath(), schema); } else { // no handler registered yet or the existing handler is invalid -> overwrite storage = new HandlerStorage(storagePath.path()); - storage->registerProxy(QCoreApplication::applicationFilePath()); + storage->registerSchemaProxy(QCoreApplication::applicationFilePath(), schema); } return storage; } -// ensure a nxmhandler.exe is registered to handle nxm-links, then load the -// handler storage for that registered instance -// (even if it's different from the one actually being run) -HandlerStorage* loadStorage(bool forceReg) +HandlerStorage* registerHandler(HandlerStorage* storage, const QString& schema, + bool forceReg) { - HandlerStorage* storage = nullptr; - QDir globalStorage( QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation)); globalStorage.cd("../ModOrganizer"); @@ -97,8 +115,7 @@ HandlerStorage* loadStorage(bool forceReg) } else { baseDir = QDir(qApp->applicationDirPath()); } - NxmHandler::LoggerInit(baseDir.filePath("nxmhandler.log")); - QSettings handlerReg("HKEY_CURRENT_USER\\Software\\Classes\\nxm\\", + QSettings handlerReg("HKEY_CURRENT_USER\\Software\\Classes\\" + schema + "\\", QSettings::NativeFormat); QStringList handlerVals = HandlerStorage::stripCall( handlerReg.value("shell/open/command/Default").toString()); @@ -114,39 +131,44 @@ HandlerStorage* loadStorage(bool forceReg) handlerPath.endsWith("nxmhandler.exe", Qt::CaseInsensitive) && QFile::exists(handlerPath)) { // global configuration avaible - use it - storage = new HandlerStorage(globalStorage.path()); + if (storage == nullptr) + storage = new HandlerStorage(globalStorage.path()); } else if (handlerBaseDir.exists("nxmhandler.ini") && handlerPath.endsWith("nxmhandler.exe", Qt::CaseInsensitive) && QFile::exists(handlerPath)) { // a portable installation is registered to handle links, use its // configuration - storage = new HandlerStorage(QFileInfo(handlerPath).absolutePath()); + if (storage == nullptr) + storage = new HandlerStorage(globalStorage.path()); if (forceReg && (QString::compare(QDir::toNativeSeparators( QCoreApplication::applicationFilePath()), handlerPath, Qt::CaseInsensitive))) { if (QMessageBox::question( nullptr, QObject::tr("Change Handler?"), - QObject::tr("A nxm handler from a different Mod Organizer " + QObject::tr("A %1 handler from a different Mod Organizer " "installation has been registered. Do you want to " "replace it? This is usually not necessary unless " - "the other installation is defective."), + "the other installation is defective.") + .arg(schema), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { - storage->registerProxy(QCoreApplication::applicationFilePath()); + storage->registerSchemaProxy(QCoreApplication::applicationFilePath(), schema); } } } else if (!noRegister || forceReg) { - // no registration + // no handler registration QMessageBox registerBox( QMessageBox::Question, QObject::tr("Register?"), - QObject::tr("Mod Organizer is not set up to handle nxm links. " - "Associate it with nxm links?"), + QObject::tr("Mod Organizer is not set up to handle %1 links. " + "Associate it with %1 links?") + .arg(schema), QMessageBox::Yes | QMessageBox::No | QMessageBox::Save); registerBox.button(QMessageBox::Save)->setText(QObject::tr("No, don't ask again")); switch (registerBox.exec()) { case QMessageBox::Yes: { // base dir is either the global dir if it exists or the local application // dir - storage = registerExecutable(baseDir, handlerPath, handlerArgs); + if (storage == nullptr) + storage = registerSchemaExecutable(baseDir, handlerPath, schema, handlerArgs); } break; case QMessageBox::Save: { settings.setValue("noregister", true); @@ -159,6 +181,30 @@ HandlerStorage* loadStorage(bool forceReg) return storage; } +// ensure a nxmhandler.exe is registered to handle links, then load the +// handler storage for that registered instance +// (even if it's different from the one actually being run) +HandlerStorage* loadStorage(bool forceReg) +{ + HandlerStorage* storage = nullptr; + + QDir globalStorage( + QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation)); + globalStorage.cd("../ModOrganizer"); + QDir baseDir; + if (globalStorage.exists()) { + baseDir = globalStorage; + } else { + baseDir = QDir(qApp->applicationDirPath()); + } + NxmHandler::LoggerInit(baseDir.filePath("nxmhandler.log")); + QStringList schemas = {"nxm", "modl"}; + for (auto schema : schemas) { + storage = registerHandler(storage, schema, forceReg); + } + return storage; +} + static void applyChromeFix() { QString dataPath = QDir::fromNativeSeparators( @@ -189,7 +235,7 @@ static void applyChromeFix() // excluded_schemes exists, protocol_handler existed as well if (handlers.contains("excluded_schemes")) { QJsonObject schemes = handlers["excluded_schemes"].toObject(); - if (schemes["nxm"].toBool(true)) { + if (schemes["nxm"].toBool(true) || schemes["modl"].toBool(true)) { if (QMessageBox::question(nullptr, "Apply Chrome fix", "Chrome may not support nexus links even though the " "association is set up correctly. " @@ -201,6 +247,7 @@ static void applyChromeFix() return; } schemes["nxm"] = false; + schemes["modl"] = false; handlers["excluded_schemes"] = schemes; docMap["protocol_handler"] = handlers; QByteArray result = QJsonDocument(docMap).toJson(); @@ -231,7 +278,7 @@ int main(int argc, char* argv[]) } // Log the arguments - qDebug(qUtf8Printable("\"" + args.join("\" \"") + "\"")); + qDebug() << qUtf8Printable("\"" + args.join("\" \"") + "\""); // No other logs, close the log NxmHandler::LoggerDeinit(); @@ -241,7 +288,8 @@ int main(int argc, char* argv[]) // nxmhandler.exe // forces registration and spawns handler window // - // nxmhandler.exe reg|forcereg game1,game2,game3 C:/path/to/binary + // nxmhandler.exe reg|forcereg schema game1,game2,game3 C:/path/to/binary + // [arguments] // reg: register if noregister==false // forcereg: force registration // @@ -250,10 +298,11 @@ int main(int argc, char* argv[]) if (args.count() > 1) { if ((args.at(1) == "reg") || (args.at(1) == "forcereg")) { - if (args.count() == 4) { - storage->registerHandler(args.at(2).split(",", Qt::SkipEmptyParts), - QDir::toNativeSeparators(args.at(3)), "", true, - forceReg); + if (args.count() == 5 || args.count() == 6) { + auto arguments = args.count() == 6 ? args.at(5) : ""; + storage->registerHandler(args.at(3).split(",", Qt::SkipEmptyParts), + args.at(2), QDir::toNativeSeparators(args.at(4)), + arguments, true, forceReg); if (forceReg) { applyChromeFix(); } @@ -264,12 +313,12 @@ int main(int argc, char* argv[]) } } else if (args.at(1).startsWith("nxm://")) { NXMUrl url(args.at(1)); - QStringList handlerVals = storage->getHandler(url.game()); + QStringList handlerVals = storage->getHandler(url.game(), "nxm"); QString executable = handlerVals.front(); handlerVals.pop_front(); QString arguments = handlerVals.join(" "); if (!executable.isEmpty()) { - handleLink(executable, arguments, args.at(1)); + handleNxmLink(executable, arguments, args.at(1)); return 0; } else { QMessageBox::warning( @@ -284,6 +333,43 @@ int main(int argc, char* argv[]) .arg(url.game())); return 1; } + } else if (args.at(1).startsWith("modl://")) { + QUrl url(args.at(1)); + QUrlQuery params(url.query()); + QStringList handlerVals = storage->getHandler(url.host(), "modl"); + QString executable = handlerVals.front(); + QString downloadUrl = params.queryItemValue("url", QUrl::FullyDecoded); + handlerVals.pop_front(); + auto argumentChunks = handlerVals.first().split(" "); + QMap targetParams = { + {"name", ""}, {"modname", ""}, {"version", ""}, {"source", ""}}; + for (auto param : targetParams.asKeyValueRange()) { + auto value = params.hasQueryItem(param.first) + ? params.queryItemValue(param.first, QUrl::FullyDecoded) + : ""; + targetParams[param.first] = value; + } + for (auto param : targetParams.asKeyValueRange()) { + if (argumentChunks.contains("%" + param.first + "%")) { + argumentChunks.replace(argumentChunks.indexOf("%" + param.first + "%"), + "\"" + param.second.replace("\"", "\\\"") + "\""); + } + } + argumentChunks.append("-g " + url.host()); + QString arguments = argumentChunks.join(" "); + if (!executable.isEmpty()) { + handleModlLink(executable, arguments, downloadUrl); + return 0; + } else { + QMessageBox::warning( + nullptr, QObject::tr("No handler found"), + QObject::tr("No application registered to handle this game (%1).\n" + "If you expected Mod Organizer to handle the link, " + "you have to go to Settings and click the \"Associate " + "with MODL links\"-button.\n") + .arg(url.host())); + return 1; + } } else { QMessageBox::warning(nullptr, QObject::tr("Invalid Arguments"), QObject::tr("Invalid number of parameters")); @@ -293,14 +379,18 @@ int main(int argc, char* argv[]) } else { HandlerWindow win; win.setHandlerStorage(storage.get()); - QSettings handlerReg("HKEY_CURRENT_USER\\Software\\Classes\\nxm\\", - QSettings::NativeFormat); - QStringList handlerVals = HandlerStorage::stripCall( - handlerReg.value("shell/open/command/Default").toString()); - QString handlerPath = handlerVals.front(); - handlerVals.pop_front(); - QString handerArgs = handlerVals.join(" "); - win.setPrimaryHandler(handlerPath); + QSettings nxmHandlerReg("HKEY_CURRENT_USER\\Software\\Classes\\nxm\\", + QSettings::NativeFormat); + QStringList nxmHandlerVals = HandlerStorage::stripCall( + nxmHandlerReg.value("shell/open/command/Default").toString()); + QString nxmHandlerPath = nxmHandlerVals.front(); + win.setNXMHandler(nxmHandlerPath); + QSettings modlHandlerReg("HKEY_CURRENT_USER\\Software\\Classes\\modl\\", + QSettings::NativeFormat); + QStringList modlHandlerVals = HandlerStorage::stripCall( + modlHandlerReg.value("shell/open/command/Default").toString()); + QString modlHandlerPath = modlHandlerVals.front(); + win.setMODLHandler(modlHandlerPath); win.show(); return app.exec(); diff --git a/src/version.rc b/src/version.rc index e5cd977..ca52097 100644 --- a/src/version.rc +++ b/src/version.rc @@ -1,7 +1,7 @@ #include "Winver.h" -#define VER_FILEVERSION 1,2,0,0 -#define VER_FILEVERSION_STR "1,2,0,0\0" +#define VER_FILEVERSION 1,3,0,0 +#define VER_FILEVERSION_STR "1,3,0,0\0" VS_VERSION_INFO VERSIONINFO FILEVERSION VER_FILEVERSION @@ -17,10 +17,10 @@ BEGIN BLOCK "040904B0" BEGIN VALUE "FileVersion", VER_FILEVERSION_STR - VALUE "CompanyName", "Tannin\0" - VALUE "FileDescription", "NXM Link Proxy\0" + VALUE "CompanyName", "Mod Organizer 2 Team\0" + VALUE "FileDescription", "MO2 Download Proxy\0" VALUE "OriginalFilename", "nxmhandler.exe\0" - VALUE "ProductName", "NXM Handler\0" + VALUE "ProductName", "MO2 Download Proxy\0" VALUE "ProductVersion", VER_FILEVERSION_STR END END