Merge branch 'develop' of https://github.com/PrismLauncher/PrismLauncher into import_zip
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
This commit is contained in:
		
							
								
								
									
										2
									
								
								.github/workflows/backport.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/backport.yml
									
									
									
									
										vendored
									
									
								
							| @@ -24,7 +24,7 @@ jobs: | |||||||
|         with: |         with: | ||||||
|           ref: ${{ github.event.pull_request.head.sha }} |           ref: ${{ github.event.pull_request.head.sha }} | ||||||
|       - name: Create backport PRs |       - name: Create backport PRs | ||||||
|         uses: korthout/backport-action@v1.3.1 |         uses: korthout/backport-action@v1.4.0 | ||||||
|         with: |         with: | ||||||
|           # Config README: https://github.com/korthout/backport-action#backport-action |           # Config README: https://github.com/korthout/backport-action#backport-action | ||||||
|           pull_description: |- |           pull_description: |- | ||||||
|   | |||||||
| @@ -67,5 +67,16 @@ | |||||||
|             <string>Alternate</string> |             <string>Alternate</string> | ||||||
|         </dict> |         </dict> | ||||||
|     </array> |     </array> | ||||||
|  |     <key>CFBundleURLTypes</key> | ||||||
|  |     <array> | ||||||
|  |         <dict> | ||||||
|  |             <key>CFBundleURLName</key> | ||||||
|  |             <string>Curseforge</string> | ||||||
|  |             <key>CFBundleURLSchemes</key> | ||||||
|  |             <array> | ||||||
|  |             <string>curseforge</string> | ||||||
|  |             </array> | ||||||
|  |         </dict> | ||||||
|  |     </array> | ||||||
| </dict> | </dict> | ||||||
| </plist> | </plist> | ||||||
|   | |||||||
| @@ -194,8 +194,11 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) | |||||||
|           { { "s", "server" }, "Join the specified server on launch (only valid in combination with --launch)", "address" }, |           { { "s", "server" }, "Join the specified server on launch (only valid in combination with --launch)", "address" }, | ||||||
|           { { "a", "profile" }, "Use the account specified by its profile name (only valid in combination with --launch)", "profile" }, |           { { "a", "profile" }, "Use the account specified by its profile name (only valid in combination with --launch)", "profile" }, | ||||||
|           { "alive", "Write a small '" + liveCheckFile + "' file after the launcher starts" }, |           { "alive", "Write a small '" + liveCheckFile + "' file after the launcher starts" }, | ||||||
|           { { "I", "import" }, "Import instance from specified zip (local path or URL)", "file" }, |           { { "I", "import" }, "Import instance or resource from specified local path or URL", "url" }, | ||||||
|           { "show", "Opens the window for the specified instance (by instance ID)", "show" } }); |           { "show", "Opens the window for the specified instance (by instance ID)", "show" } }); | ||||||
|  |     // Has to be positional for some OS to handle that properly | ||||||
|  |     parser.addPositionalArgument("URL", "Import the resource(s) at the given URL(s) (same as -I / --import)", "[URL...]"); | ||||||
|  |  | ||||||
|     parser.addHelpOption(); |     parser.addHelpOption(); | ||||||
|     parser.addVersionOption(); |     parser.addVersionOption(); | ||||||
|  |  | ||||||
| @@ -208,13 +211,13 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) | |||||||
|  |  | ||||||
|     m_instanceIdToShowWindowOf = parser.value("show"); |     m_instanceIdToShowWindowOf = parser.value("show"); | ||||||
|  |  | ||||||
|     for (auto zip_path : parser.values("import")) { |     for (auto url : parser.values("import")) { | ||||||
|         m_zipsToImport.append(QUrl::fromLocalFile(QFileInfo(zip_path).absoluteFilePath())); |         m_urlsToImport.append(normalizeImportUrl(url)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // treat unspecified positional arguments as import urls |     // treat unspecified positional arguments as import urls | ||||||
|     for (auto zip_path : parser.positionalArguments()) { |     for (auto url : parser.positionalArguments()) { | ||||||
|         m_zipsToImport.append(QUrl::fromLocalFile(QFileInfo(zip_path).absoluteFilePath())); |         m_urlsToImport.append(normalizeImportUrl(url)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // error if --launch is missing with --server or --profile |     // error if --launch is missing with --server or --profile | ||||||
| @@ -313,11 +316,11 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) | |||||||
|                 activate.command = "activate"; |                 activate.command = "activate"; | ||||||
|                 m_peerInstance->sendMessage(activate.serialize(), timeout); |                 m_peerInstance->sendMessage(activate.serialize(), timeout); | ||||||
|  |  | ||||||
|                 if (!m_zipsToImport.isEmpty()) { |                 if (!m_urlsToImport.isEmpty()) { | ||||||
|                     for (auto zip_url : m_zipsToImport) { |                     for (auto url : m_urlsToImport) { | ||||||
|                         ApplicationMessage import; |                         ApplicationMessage import; | ||||||
|                         import.command = "import"; |                         import.command = "import"; | ||||||
|                         import.args.insert("path", zip_url.toString()); |                         import.args.insert("url", url.toString()); | ||||||
|                         m_peerInstance->sendMessage(import.serialize(), timeout); |                         m_peerInstance->sendMessage(import.serialize(), timeout); | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
| @@ -978,9 +981,9 @@ void Application::performMainStartupAction() | |||||||
|         showMainWindow(false); |         showMainWindow(false); | ||||||
|         qDebug() << "<> Main window shown."; |         qDebug() << "<> Main window shown."; | ||||||
|     } |     } | ||||||
|     if (!m_zipsToImport.isEmpty()) { |     if (!m_urlsToImport.isEmpty()) { | ||||||
|         qDebug() << "<> Importing from zip:" << m_zipsToImport; |         qDebug() << "<> Importing from url:" << m_urlsToImport; | ||||||
|         m_mainWindow->processURLs(m_zipsToImport); |         m_mainWindow->processURLs(m_urlsToImport); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -1022,12 +1025,12 @@ void Application::messageReceived(const QByteArray& message) | |||||||
|     if (command == "activate") { |     if (command == "activate") { | ||||||
|         showMainWindow(); |         showMainWindow(); | ||||||
|     } else if (command == "import") { |     } else if (command == "import") { | ||||||
|         QString path = received.args["path"]; |         QString url = received.args["url"]; | ||||||
|         if (path.isEmpty()) { |         if (url.isEmpty()) { | ||||||
|             qWarning() << "Received" << command << "message without a zip path/URL."; |             qWarning() << "Received" << command << "message without a zip path/URL."; | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|         m_mainWindow->processURLs({ QUrl::fromLocalFile(QFileInfo(path).absoluteFilePath()) }); |         m_mainWindow->processURLs({ normalizeImportUrl(url) }); | ||||||
|     } else if (command == "launch") { |     } else if (command == "launch") { | ||||||
|         QString id = received.args["id"]; |         QString id = received.args["id"]; | ||||||
|         QString server = received.args["server"]; |         QString server = received.args["server"]; | ||||||
| @@ -1590,3 +1593,13 @@ void Application::triggerUpdateCheck() | |||||||
|         qDebug() << "Updater not available."; |         qDebug() << "Updater not available."; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | QUrl Application::normalizeImportUrl(QString const& url) | ||||||
|  | { | ||||||
|  |     auto local_file = QFileInfo(url); | ||||||
|  |     if (local_file.exists()) { | ||||||
|  |         return QUrl::fromLocalFile(local_file.absoluteFilePath()); | ||||||
|  |     } else { | ||||||
|  |         return QUrl::fromUserInput(url); | ||||||
|  |     } | ||||||
|  | } | ||||||
|   | |||||||
| @@ -177,6 +177,8 @@ class Application : public QApplication { | |||||||
|  |  | ||||||
|     int suitableMaxMem(); |     int suitableMaxMem(); | ||||||
|  |  | ||||||
|  |     QUrl normalizeImportUrl(QString const& url); | ||||||
|  |  | ||||||
|    signals: |    signals: | ||||||
|     void updateAllowedChanged(bool status); |     void updateAllowedChanged(bool status); | ||||||
|     void globalSettingsAboutToOpen(); |     void globalSettingsAboutToOpen(); | ||||||
| @@ -279,7 +281,7 @@ class Application : public QApplication { | |||||||
|     QString m_serverToJoin; |     QString m_serverToJoin; | ||||||
|     QString m_profileToUse; |     QString m_profileToUse; | ||||||
|     bool m_liveCheck = false; |     bool m_liveCheck = false; | ||||||
|     QList<QUrl> m_zipsToImport; |     QList<QUrl> m_urlsToImport; | ||||||
|     QString m_instanceIdToShowWindowOf; |     QString m_instanceIdToShowWindowOf; | ||||||
|     std::unique_ptr<QFile> logFile; |     std::unique_ptr<QFile> logFile; | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -50,6 +50,7 @@ | |||||||
| #include "modplatform/technic/TechnicPackProcessor.h" | #include "modplatform/technic/TechnicPackProcessor.h" | ||||||
|  |  | ||||||
| #include "settings/INISettingsObject.h" | #include "settings/INISettingsObject.h" | ||||||
|  | #include "tasks/Task.h" | ||||||
|  |  | ||||||
| #include "net/ApiDownload.h" | #include "net/ApiDownload.h" | ||||||
|  |  | ||||||
| @@ -83,6 +84,12 @@ void InstanceImportTask::executeTask() | |||||||
|     } else { |     } else { | ||||||
|         setStatus(tr("Downloading modpack:\n%1").arg(m_sourceUrl.toString())); |         setStatus(tr("Downloading modpack:\n%1").arg(m_sourceUrl.toString())); | ||||||
|  |  | ||||||
|  |         downloadFromUrl(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void InstanceImportTask::downloadFromUrl() | ||||||
|  | { | ||||||
|     const QString path(m_sourceUrl.host() + '/' + m_sourceUrl.path()); |     const QString path(m_sourceUrl.host() + '/' + m_sourceUrl.path()); | ||||||
|  |  | ||||||
|     auto entry = APPLICATION->metacache()->resolveEntry("general", path); |     auto entry = APPLICATION->metacache()->resolveEntry("general", path); | ||||||
| @@ -100,7 +107,6 @@ void InstanceImportTask::executeTask() | |||||||
|     task.reset(filesNetJob); |     task.reset(filesNetJob); | ||||||
|     filesNetJob->start(); |     filesNetJob->start(); | ||||||
| } | } | ||||||
| } |  | ||||||
|  |  | ||||||
| QString InstanceImportTask::getRootFromZip(QuaZip* zip, const QString& root) | QString InstanceImportTask::getRootFromZip(QuaZip* zip, const QString& root) | ||||||
| { | { | ||||||
|   | |||||||
| @@ -88,4 +88,5 @@ class InstanceImportTask : public InstanceTask { | |||||||
|  |  | ||||||
|     // FIXME: nuke |     // FIXME: nuke | ||||||
|     QWidget* m_parent; |     QWidget* m_parent; | ||||||
|  |     void downloadFromUrl(); | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -1005,15 +1005,30 @@ static Meta::Version::Ptr getComponentVersion(const QString& uid, const QString& | |||||||
|     if (!vlist) |     if (!vlist) | ||||||
|         return {}; |         return {}; | ||||||
|  |  | ||||||
|     if (!vlist->isLoaded()) |     if (!vlist->isLoaded()) { | ||||||
|         vlist->load(Net::Mode::Online); |         QEventLoop loadVersionLoop; | ||||||
|  |         auto task = vlist->getLoadTask(); | ||||||
|  |         QObject::connect(task.get(), &Task::finished, &loadVersionLoop, &QEventLoop::quit); | ||||||
|  |         if (!task->isRunning()) | ||||||
|  |             task->start(); | ||||||
|  |  | ||||||
|  |         loadVersionLoop.exec(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     auto ver = vlist->getVersion(version); |     auto ver = vlist->getVersion(version); | ||||||
|     if (!ver) |     if (!ver) | ||||||
|         return {}; |         return {}; | ||||||
|  |  | ||||||
|     if (!ver->isLoaded()) |     if (!ver->isLoaded()) { | ||||||
|  |         QEventLoop loadVersionLoop; | ||||||
|         ver->load(Net::Mode::Online); |         ver->load(Net::Mode::Online); | ||||||
|  |         auto task = ver->getCurrentTask(); | ||||||
|  |         QObject::connect(task.get(), &Task::finished, &loadVersionLoop, &QEventLoop::quit); | ||||||
|  |         if (!task->isRunning()) | ||||||
|  |             task->start(); | ||||||
|  |  | ||||||
|  |         loadVersionLoop.exec(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     return ver; |     return ver; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -204,6 +204,17 @@ Task::Ptr FlameAPI::getFiles(const QStringList& fileIds, std::shared_ptr<QByteAr | |||||||
|     return netJob; |     return netJob; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | Task::Ptr FlameAPI::getFile(const QString& addonId, const QString& fileId, std::shared_ptr<QByteArray> response) const | ||||||
|  | { | ||||||
|  |     auto netJob = makeShared<NetJob>(QString("Flame::GetFile"), APPLICATION->network()); | ||||||
|  |     netJob->addNetAction( | ||||||
|  |         Net::ApiDownload::makeByteArray(QUrl(QString("https://api.curseforge.com/v1/mods/%1/files/%2").arg(addonId, fileId)), response)); | ||||||
|  |  | ||||||
|  |     QObject::connect(netJob.get(), &NetJob::failed, [addonId, fileId] { qDebug() << "Flame API file failure" << addonId << fileId; }); | ||||||
|  |  | ||||||
|  |     return netJob; | ||||||
|  | } | ||||||
|  |  | ||||||
| // https://docs.curseforge.com/?python#tocS_ModsSearchSortField | // https://docs.curseforge.com/?python#tocS_ModsSearchSortField | ||||||
| static QList<ResourceAPI::SortingMethod> s_sorts = { { 1, "Featured", QObject::tr("Sort by Featured") }, | static QList<ResourceAPI::SortingMethod> s_sorts = { { 1, "Featured", QObject::tr("Sort by Featured") }, | ||||||
|                                                      { 2, "Popularity", QObject::tr("Sort by Popularity") }, |                                                      { 2, "Popularity", QObject::tr("Sort by Popularity") }, | ||||||
|   | |||||||
| @@ -20,6 +20,7 @@ class FlameAPI : public NetworkResourceAPI { | |||||||
|     Task::Ptr getProjects(QStringList addonIds, std::shared_ptr<QByteArray> response) const override; |     Task::Ptr getProjects(QStringList addonIds, std::shared_ptr<QByteArray> response) const override; | ||||||
|     Task::Ptr matchFingerprints(const QList<uint>& fingerprints, std::shared_ptr<QByteArray> response); |     Task::Ptr matchFingerprints(const QList<uint>& fingerprints, std::shared_ptr<QByteArray> response); | ||||||
|     Task::Ptr getFiles(const QStringList& fileIds, std::shared_ptr<QByteArray> response) const; |     Task::Ptr getFiles(const QStringList& fileIds, std::shared_ptr<QByteArray> response) const; | ||||||
|  |     Task::Ptr getFile(const QString& addonId, const QString& fileId, std::shared_ptr<QByteArray> response) const; | ||||||
|  |  | ||||||
|     [[nodiscard]] auto getSortingMethods() const -> QList<ResourceAPI::SortingMethod> override; |     [[nodiscard]] auto getSortingMethods() const -> QList<ResourceAPI::SortingMethod> override; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -85,7 +85,7 @@ | |||||||
| #include <launch/LaunchTask.h> | #include <launch/LaunchTask.h> | ||||||
| #include <minecraft/MinecraftInstance.h> | #include <minecraft/MinecraftInstance.h> | ||||||
| #include <minecraft/auth/AccountList.h> | #include <minecraft/auth/AccountList.h> | ||||||
| #include <net/Download.h> | #include <net/ApiDownload.h> | ||||||
| #include <net/NetJob.h> | #include <net/NetJob.h> | ||||||
| #include <news/NewsChecker.h> | #include <news/NewsChecker.h> | ||||||
| #include <tools/BaseProfiler.h> | #include <tools/BaseProfiler.h> | ||||||
| @@ -118,11 +118,15 @@ | |||||||
| #include "minecraft/mod/ShaderPackFolderModel.h" | #include "minecraft/mod/ShaderPackFolderModel.h" | ||||||
| #include "minecraft/mod/tasks/LocalResourceParse.h" | #include "minecraft/mod/tasks/LocalResourceParse.h" | ||||||
|  |  | ||||||
|  | #include "modplatform/flame/FlameAPI.h" | ||||||
|  |  | ||||||
| #include "KonamiCode.h" | #include "KonamiCode.h" | ||||||
|  |  | ||||||
| #include "InstanceCopyTask.h" | #include "InstanceCopyTask.h" | ||||||
| #include "InstanceImportTask.h" | #include "InstanceImportTask.h" | ||||||
|  |  | ||||||
|  | #include "Json.h" | ||||||
|  |  | ||||||
| #include "MMCTime.h" | #include "MMCTime.h" | ||||||
|  |  | ||||||
| namespace { | namespace { | ||||||
| @@ -929,7 +933,7 @@ void MainWindow::finalizeInstance(InstancePtr inst) | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| void MainWindow::addInstance(QString url) | void MainWindow::addInstance(const QString& url, const QMap<QString, QString>& extra_info) | ||||||
| { | { | ||||||
|     QString groupName; |     QString groupName; | ||||||
|     do { |     do { | ||||||
| @@ -949,7 +953,7 @@ void MainWindow::addInstance(QString url) | |||||||
|         groupName = APPLICATION->settings()->get("LastUsedGroupForNewInstance").toString(); |         groupName = APPLICATION->settings()->get("LastUsedGroupForNewInstance").toString(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     NewInstanceDialog newInstDlg(groupName, url, this); |     NewInstanceDialog newInstDlg(groupName, url, extra_info, this); | ||||||
|     if (!newInstDlg.exec()) |     if (!newInstDlg.exec()) | ||||||
|         return; |         return; | ||||||
|  |  | ||||||
| @@ -976,18 +980,101 @@ void MainWindow::processURLs(QList<QUrl> urls) | |||||||
|         if (url.scheme().isEmpty()) |         if (url.scheme().isEmpty()) | ||||||
|             url.setScheme("file"); |             url.setScheme("file"); | ||||||
|  |  | ||||||
|         if (!url.isLocalFile()) {  // probably instance/modpack |         QMap<QString, QString> extra_info; | ||||||
|             addInstance(url.toString()); |         QUrl local_url; | ||||||
|             break; |         if (!url.isLocalFile()) {  // download the remote resource and identify | ||||||
|  |             QUrl dl_url; | ||||||
|  |             if (url.scheme() == "curseforge") { | ||||||
|  |                 // need to find the download link for the modpack / resource | ||||||
|  |                 // format of url curseforge://install?addonId=IDHERE&fileId=IDHERE | ||||||
|  |                 QUrlQuery query(url); | ||||||
|  |  | ||||||
|  |                 auto addonId = query.allQueryItemValues("addonId")[0]; | ||||||
|  |                 auto fileId = query.allQueryItemValues("fileId")[0]; | ||||||
|  |  | ||||||
|  |                 extra_info.insert("pack_id", addonId); | ||||||
|  |                 extra_info.insert("pack_version_id", fileId); | ||||||
|  |  | ||||||
|  |                 auto array = std::make_shared<QByteArray>(); | ||||||
|  |  | ||||||
|  |                 auto api = FlameAPI(); | ||||||
|  |                 auto job = api.getFile(addonId, fileId, array); | ||||||
|  |  | ||||||
|  |                 QString resource_name; | ||||||
|  |  | ||||||
|  |                 connect(job.get(), &Task::failed, this, | ||||||
|  |                         [this](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); }); | ||||||
|  |                 connect(job.get(), &Task::succeeded, this, [this, array, addonId, fileId, &dl_url, &resource_name] { | ||||||
|  |                     qDebug() << "Returned CFURL Json:\n" << array->toStdString().c_str(); | ||||||
|  |                     auto doc = Json::requireDocument(*array); | ||||||
|  |                     auto data = Json::ensureObject(Json::ensureObject(doc.object()), "data"); | ||||||
|  |                     // No way to find out if it's a mod or a modpack before here | ||||||
|  |                     // And also we need to check if it ends with .zip, instead of any better way | ||||||
|  |                     auto fileName = Json::ensureString(data, "fileName"); | ||||||
|  |  | ||||||
|  |                     // Have to use ensureString then use QUrl to get proper url encoding | ||||||
|  |                     dl_url = QUrl(Json::ensureString(data, "downloadUrl", "", "downloadUrl")); | ||||||
|  |                     if (!dl_url.isValid()) { | ||||||
|  |                         CustomMessageBox::selectable( | ||||||
|  |                             this, tr("Error"), | ||||||
|  |                             tr("The modpack, mod, or resource %1 is blocked for third-parties! Please download it manually.").arg(fileName), | ||||||
|  |                             QMessageBox::Critical) | ||||||
|  |                             ->show(); | ||||||
|  |                         return; | ||||||
|                     } |                     } | ||||||
|  |  | ||||||
|         auto localFileName = QDir::toNativeSeparators(url.toLocalFile()); |                     QFileInfo dl_file(dl_url.fileName()); | ||||||
|  |                     resource_name = Json::ensureString(data, "displayName", dl_file.completeBaseName(), "displayName"); | ||||||
|  |                 }); | ||||||
|  |  | ||||||
|  |                 {  // drop stack | ||||||
|  |                     ProgressDialog dlUrlDialod(this); | ||||||
|  |                     dlUrlDialod.setSkipButton(true, tr("Abort")); | ||||||
|  |                     dlUrlDialod.execWithTask(job.get()); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |             } else { | ||||||
|  |                 dl_url = url; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (!dl_url.isValid()) { | ||||||
|  |                 continue;  // no valid url to download this resource | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             const QString path = dl_url.host() + '/' + dl_url.path(); | ||||||
|  |             auto entry = APPLICATION->metacache()->resolveEntry("general", path); | ||||||
|  |             entry->setStale(true); | ||||||
|  |             auto dl_job = unique_qobject_ptr<NetJob>(new NetJob(tr("Modpack download"), APPLICATION->network())); | ||||||
|  |             dl_job->addNetAction(Net::ApiDownload::makeCached(dl_url, entry)); | ||||||
|  |             auto archivePath = entry->getFullPath(); | ||||||
|  |  | ||||||
|  |             bool dl_success = false; | ||||||
|  |             connect(dl_job.get(), &Task::failed, this, | ||||||
|  |                     [this](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); }); | ||||||
|  |             connect(dl_job.get(), &Task::succeeded, this, [&dl_success] { dl_success = true; }); | ||||||
|  |  | ||||||
|  |             {  // drop stack | ||||||
|  |                 ProgressDialog dlUrlDialod(this); | ||||||
|  |                 dlUrlDialod.setSkipButton(true, tr("Abort")); | ||||||
|  |                 dlUrlDialod.execWithTask(dl_job.get()); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (!dl_success) { | ||||||
|  |                 continue;  // no local file to identify | ||||||
|  |             } | ||||||
|  |             local_url = QUrl::fromLocalFile(archivePath); | ||||||
|  |  | ||||||
|  |         } else { | ||||||
|  |             local_url = url; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         auto localFileName = QDir::toNativeSeparators(local_url.toLocalFile()); | ||||||
|         QFileInfo localFileInfo(localFileName); |         QFileInfo localFileInfo(localFileName); | ||||||
|  |  | ||||||
|         auto type = ResourceUtils::identify(localFileInfo); |         auto type = ResourceUtils::identify(localFileInfo); | ||||||
|  |  | ||||||
|         if (ResourceUtils::ValidResourceTypes.count(type) == 0) {  // probably instance/modpack |         if (ResourceUtils::ValidResourceTypes.count(type) == 0) {  // probably instance/modpack | ||||||
|             addInstance(localFileName); |             addInstance(localFileName, extra_info); | ||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -220,7 +220,7 @@ class MainWindow : public QMainWindow { | |||||||
|    private: |    private: | ||||||
|     void retranslateUi(); |     void retranslateUi(); | ||||||
|  |  | ||||||
|     void addInstance(QString url = QString()); |     void addInstance(const QString& url = QString(), const QMap<QString, QString>& extra_info = {}); | ||||||
|     void activateInstance(InstancePtr instance); |     void activateInstance(InstancePtr instance); | ||||||
|     void setCatBackground(bool enabled); |     void setCatBackground(bool enabled); | ||||||
|     void updateInstanceToolIcon(QString new_icon); |     void updateInstanceToolIcon(QString new_icon); | ||||||
|   | |||||||
| @@ -62,8 +62,10 @@ | |||||||
| #include "ui/pages/modplatform/modrinth/ModrinthPage.h" | #include "ui/pages/modplatform/modrinth/ModrinthPage.h" | ||||||
| #include "ui/pages/modplatform/technic/TechnicPage.h" | #include "ui/pages/modplatform/technic/TechnicPage.h" | ||||||
| #include "ui/widgets/PageContainer.h" | #include "ui/widgets/PageContainer.h" | ||||||
|  | NewInstanceDialog::NewInstanceDialog(const QString& initialGroup, | ||||||
| NewInstanceDialog::NewInstanceDialog(const QString& initialGroup, const QString& url, QWidget* parent) |                                      const QString& url, | ||||||
|  |                                      const QMap<QString, QString>& extra_info, | ||||||
|  |                                      QWidget* parent) | ||||||
|     : QDialog(parent), ui(new Ui::NewInstanceDialog) |     : QDialog(parent), ui(new Ui::NewInstanceDialog) | ||||||
| { | { | ||||||
|     ui->setupUi(this); |     ui->setupUi(this); | ||||||
| @@ -125,6 +127,7 @@ NewInstanceDialog::NewInstanceDialog(const QString& initialGroup, const QString& | |||||||
|         QUrl actualUrl(url); |         QUrl actualUrl(url); | ||||||
|         m_container->selectPage("import"); |         m_container->selectPage("import"); | ||||||
|         importPage->setUrl(url); |         importPage->setUrl(url); | ||||||
|  |         importPage->setExtraInfo(extra_info); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     updateDialogState(); |     updateDialogState(); | ||||||
|   | |||||||
| @@ -53,7 +53,10 @@ class NewInstanceDialog : public QDialog, public BasePageProvider { | |||||||
|     Q_OBJECT |     Q_OBJECT | ||||||
|  |  | ||||||
|    public: |    public: | ||||||
|     explicit NewInstanceDialog(const QString& initialGroup, const QString& url = QString(), QWidget* parent = 0); |     explicit NewInstanceDialog(const QString& initialGroup, | ||||||
|  |                                const QString& url = QString(), | ||||||
|  |                                const QMap<QString, QString>& extra_info = {}, | ||||||
|  |                                QWidget* parent = 0); | ||||||
|     ~NewInstanceDialog(); |     ~NewInstanceDialog(); | ||||||
|  |  | ||||||
|     void updateDialogState(); |     void updateDialogState(); | ||||||
|   | |||||||
| @@ -35,14 +35,22 @@ | |||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include "ImportPage.h" | #include "ImportPage.h" | ||||||
|  |  | ||||||
|  | #include "ui/dialogs/ProgressDialog.h" | ||||||
| #include "ui_ImportPage.h" | #include "ui_ImportPage.h" | ||||||
|  |  | ||||||
| #include <QFileDialog> | #include <QFileDialog> | ||||||
| #include <QMimeDatabase> | #include <QMimeDatabase> | ||||||
| #include <QValidator> | #include <QValidator> | ||||||
|  | #include <utility> | ||||||
|  |  | ||||||
|  | #include "ui/dialogs/CustomMessageBox.h" | ||||||
| #include "ui/dialogs/NewInstanceDialog.h" | #include "ui/dialogs/NewInstanceDialog.h" | ||||||
|  |  | ||||||
|  | #include "modplatform/flame/FlameAPI.h" | ||||||
|  |  | ||||||
|  | #include "Json.h" | ||||||
|  |  | ||||||
| #include "InstanceImportTask.h" | #include "InstanceImportTask.h" | ||||||
|  |  | ||||||
| class UrlValidator : public QValidator { | class UrlValidator : public QValidator { | ||||||
| @@ -107,10 +115,61 @@ void ImportPage::updateState() | |||||||
|             bool isMRPack = fi.suffix() == "mrpack"; |             bool isMRPack = fi.suffix() == "mrpack"; | ||||||
|  |  | ||||||
|             if (fi.exists() && (isZip || isMRPack)) { |             if (fi.exists() && (isZip || isMRPack)) { | ||||||
|                 QFileInfo file_info(url.fileName()); |                 auto extra_info = QMap(m_extra_info); | ||||||
|                 dialog->setSuggestedPack(file_info.completeBaseName(), new InstanceImportTask(url, this)); |                 qDebug() << "Pack Extra Info" << extra_info << m_extra_info; | ||||||
|  |                 dialog->setSuggestedPack(fi.completeBaseName(), new InstanceImportTask(url, this, std::move(extra_info))); | ||||||
|                 dialog->setSuggestedIcon("default"); |                 dialog->setSuggestedIcon("default"); | ||||||
|             } |             } | ||||||
|  |         } else if (url.scheme() == "curseforge") { | ||||||
|  |             // need to find the download link for the modpack | ||||||
|  |             // format of url curseforge://install?addonId=IDHERE&fileId=IDHERE | ||||||
|  |             QUrlQuery query(url); | ||||||
|  |             auto addonId = query.allQueryItemValues("addonId")[0]; | ||||||
|  |             auto fileId = query.allQueryItemValues("fileId")[0]; | ||||||
|  |             auto array = std::make_shared<QByteArray>(); | ||||||
|  |  | ||||||
|  |             auto api = FlameAPI(); | ||||||
|  |             auto job = api.getFile(addonId, fileId, array); | ||||||
|  |  | ||||||
|  |             connect(job.get(), &NetJob::failed, this, | ||||||
|  |                     [this](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); }); | ||||||
|  |             connect(job.get(), &NetJob::succeeded, this, [this, array, addonId, fileId] { | ||||||
|  |                 qDebug() << "Returned CFURL Json:\n" << array->toStdString().c_str(); | ||||||
|  |                 auto doc = Json::requireDocument(*array); | ||||||
|  |                 auto data = Json::ensureObject(Json::ensureObject(doc.object()), "data"); | ||||||
|  |                 // No way to find out if it's a mod or a modpack before here | ||||||
|  |                 // And also we need to check if it ends with .zip, instead of any better way | ||||||
|  |                 auto fileName = Json::ensureString(data, "fileName"); | ||||||
|  |                 if (fileName.endsWith(".zip")) { | ||||||
|  |                     // Have to use ensureString then use QUrl to get proper url encoding | ||||||
|  |                     auto dl_url = QUrl(Json::ensureString(data, "downloadUrl", "", "downloadUrl")); | ||||||
|  |                     if (!dl_url.isValid()) { | ||||||
|  |                         CustomMessageBox::selectable( | ||||||
|  |                             this, tr("Error"), | ||||||
|  |                             tr("The modpack %1 is blocked for third-parties! Please download it manually.").arg(fileName), | ||||||
|  |                             QMessageBox::Critical) | ||||||
|  |                             ->show(); | ||||||
|  |                         return; | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     QFileInfo dl_file(dl_url.fileName()); | ||||||
|  |                     QString pack_name = Json::ensureString(data, "displayName", dl_file.completeBaseName(), "displayName"); | ||||||
|  |  | ||||||
|  |                     QMap<QString, QString> extra_info; | ||||||
|  |                     extra_info.insert("pack_id", addonId); | ||||||
|  |                     extra_info.insert("pack_version_id", fileId); | ||||||
|  |  | ||||||
|  |                     dialog->setSuggestedPack(pack_name, new InstanceImportTask(dl_url, this, std::move(extra_info))); | ||||||
|  |                     dialog->setSuggestedIcon("default"); | ||||||
|  |  | ||||||
|  |                 } else { | ||||||
|  |                     CustomMessageBox::selectable(this, tr("Error"), tr("This url isn't a valid modpack !"), QMessageBox::Critical)->show(); | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |             ProgressDialog dlUrlDialod(this); | ||||||
|  |             dlUrlDialod.setSkipButton(true, tr("Abort")); | ||||||
|  |             dlUrlDialod.execWithTask(job.get()); | ||||||
|  |             return; | ||||||
|         } else { |         } else { | ||||||
|             if (input.endsWith("?client=y")) { |             if (input.endsWith("?client=y")) { | ||||||
|                 input.chop(9); |                 input.chop(9); | ||||||
| @@ -119,7 +178,8 @@ void ImportPage::updateState() | |||||||
|             } |             } | ||||||
|             // hook, line and sinker. |             // hook, line and sinker. | ||||||
|             QFileInfo fi(url.fileName()); |             QFileInfo fi(url.fileName()); | ||||||
|             dialog->setSuggestedPack(fi.completeBaseName(), new InstanceImportTask(url, this)); |             auto extra_info = QMap(m_extra_info); | ||||||
|  |             dialog->setSuggestedPack(fi.completeBaseName(), new InstanceImportTask(url, this, std::move(extra_info))); | ||||||
|             dialog->setSuggestedIcon("default"); |             dialog->setSuggestedIcon("default"); | ||||||
|         } |         } | ||||||
|     } else { |     } else { | ||||||
| @@ -133,6 +193,12 @@ void ImportPage::setUrl(const QString& url) | |||||||
|     updateState(); |     updateState(); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void ImportPage::setExtraInfo(const QMap<QString, QString>& extra_info) | ||||||
|  | { | ||||||
|  |     m_extra_info = extra_info; | ||||||
|  |     updateState(); | ||||||
|  | } | ||||||
|  |  | ||||||
| void ImportPage::on_modpackBtn_clicked() | void ImportPage::on_modpackBtn_clicked() | ||||||
| { | { | ||||||
|     auto filter = QMimeDatabase().mimeTypeForName("application/zip").filterString(); |     auto filter = QMimeDatabase().mimeTypeForName("application/zip").filterString(); | ||||||
|   | |||||||
| @@ -62,7 +62,7 @@ class ImportPage : public QWidget, public BasePage { | |||||||
|  |  | ||||||
|     void setUrl(const QString& url); |     void setUrl(const QString& url); | ||||||
|     void openedImpl() override; |     void openedImpl() override; | ||||||
|  |     void setExtraInfo(const QMap<QString, QString>& extra_info); | ||||||
|    private slots: |    private slots: | ||||||
|     void on_modpackBtn_clicked(); |     void on_modpackBtn_clicked(); | ||||||
|     void updateState(); |     void updateState(); | ||||||
| @@ -73,4 +73,5 @@ class ImportPage : public QWidget, public BasePage { | |||||||
|    private: |    private: | ||||||
|     Ui::ImportPage* ui = nullptr; |     Ui::ImportPage* ui = nullptr; | ||||||
|     NewInstanceDialog* dialog = nullptr; |     NewInstanceDialog* dialog = nullptr; | ||||||
|  |     QMap<QString, QString> m_extra_info = {}; | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -40,7 +40,7 @@ | |||||||
|      <item> |      <item> | ||||||
|       <widget class="QLabel" name="label_5"> |       <widget class="QLabel" name="label_5"> | ||||||
|        <property name="text"> |        <property name="text"> | ||||||
|         <string>- CurseForge modpacks (ZIP)</string> |         <string>- CurseForge modpacks (ZIP / curseforge:// URL)</string> | ||||||
|        </property> |        </property> | ||||||
|        <property name="alignment"> |        <property name="alignment"> | ||||||
|         <set>Qt::AlignCenter</set> |         <set>Qt::AlignCenter</set> | ||||||
|   | |||||||
| @@ -10,4 +10,4 @@ Icon=org.prismlauncher.PrismLauncher | |||||||
| Categories=Game;ActionGame;AdventureGame;Simulation; | Categories=Game;ActionGame;AdventureGame;Simulation; | ||||||
| Keywords=game;minecraft;mc; | Keywords=game;minecraft;mc; | ||||||
| StartupWMClass=PrismLauncher | StartupWMClass=PrismLauncher | ||||||
| MimeType=application/zip;application/x-modrinth-modpack+zip | MimeType=application/zip;application/x-modrinth-modpack+zip;x-scheme-handler/curseforge; | ||||||
|   | |||||||
| @@ -363,6 +363,10 @@ Section "@Launcher_DisplayName@" | |||||||
|   ; Write the installation path into the registry |   ; Write the installation path into the registry | ||||||
|   WriteRegStr HKCU Software\@Launcher_CommonName@ "InstallDir" "$INSTDIR" |   WriteRegStr HKCU Software\@Launcher_CommonName@ "InstallDir" "$INSTDIR" | ||||||
|  |  | ||||||
|  |   ; Write the URL Handler into registry for curseforge | ||||||
|  |   WriteRegStr HKCU Software\Classes\curseforge "URL Protocol" "" | ||||||
|  |   WriteRegStr HKCU Software\Classes\curseforge\shell\open\command "" '"$INSTDIR\@Launcher_APP_BINARY_NAME@.exe" "%1"' | ||||||
|  |  | ||||||
|   ; Write the uninstall keys for Windows |   ; Write the uninstall keys for Windows | ||||||
|   ${GetParameters} $R0 |   ${GetParameters} $R0 | ||||||
|   ${GetOptions} $R0 "/NoUninstaller" $R1 |   ${GetOptions} $R0 "/NoUninstaller" $R1 | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Trial97
					Trial97