From d6dc22d57c33497b5908abaf53770cde1773c0a7 Mon Sep 17 00:00:00 2001
From: Jamie Mansfield <jmansfield@cadixdev.org>
Date: Mon, 5 Apr 2021 21:19:05 +0100
Subject: NOISSUE Handle JSON exceptions in ATLauncher support

Thanks to phit for pointing this out :)
---
 application/CMakeLists.txt                         |   5 +-
 .../pages/modplatform/atlauncher/AtlListModel.cpp  | 194 +++++++++++++++++++++
 .../pages/modplatform/atlauncher/AtlListModel.h    |  52 ++++++
 .../pages/modplatform/atlauncher/AtlModel.cpp      | 185 --------------------
 .../pages/modplatform/atlauncher/AtlModel.h        |  52 ------
 application/pages/modplatform/atlauncher/AtlPage.h |   2 +-
 6 files changed, 249 insertions(+), 241 deletions(-)
 create mode 100644 application/pages/modplatform/atlauncher/AtlListModel.cpp
 create mode 100644 application/pages/modplatform/atlauncher/AtlListModel.h
 delete mode 100644 application/pages/modplatform/atlauncher/AtlModel.cpp
 delete mode 100644 application/pages/modplatform/atlauncher/AtlModel.h

(limited to 'application')

diff --git a/application/CMakeLists.txt b/application/CMakeLists.txt
index afd13574..fd49c267 100644
--- a/application/CMakeLists.txt
+++ b/application/CMakeLists.txt
@@ -125,13 +125,12 @@ SET(MULTIMC_SOURCES
     pages/modplatform/VanillaPage.cpp
     pages/modplatform/VanillaPage.h
 
-    pages/modplatform/atlauncher/AtlModel.cpp
-    pages/modplatform/atlauncher/AtlModel.h
     pages/modplatform/atlauncher/AtlFilterModel.cpp
     pages/modplatform/atlauncher/AtlFilterModel.h
+    pages/modplatform/atlauncher/AtlListModel.cpp
+    pages/modplatform/atlauncher/AtlListModel.h
     pages/modplatform/atlauncher/AtlPage.cpp
     pages/modplatform/atlauncher/AtlPage.h
-    pages/modplatform/atlauncher/AtlPage.h
 
     pages/modplatform/ftb/FtbFilterModel.cpp
     pages/modplatform/ftb/FtbFilterModel.h
diff --git a/application/pages/modplatform/atlauncher/AtlListModel.cpp b/application/pages/modplatform/atlauncher/AtlListModel.cpp
new file mode 100644
index 00000000..f3be6198
--- /dev/null
+++ b/application/pages/modplatform/atlauncher/AtlListModel.cpp
@@ -0,0 +1,194 @@
+#include "AtlListModel.h"
+
+#include <BuildConfig.h>
+#include <MultiMC.h>
+#include <Env.h>
+#include <Json.h>
+
+namespace Atl {
+
+ListModel::ListModel(QObject *parent) : QAbstractListModel(parent)
+{
+}
+
+ListModel::~ListModel()
+{
+}
+
+int ListModel::rowCount(const QModelIndex &parent) const
+{
+    return modpacks.size();
+}
+
+int ListModel::columnCount(const QModelIndex &parent) const
+{
+    return 1;
+}
+
+QVariant ListModel::data(const QModelIndex &index, int role) const
+{
+    int pos = index.row();
+    if(pos >= modpacks.size() || pos < 0 || !index.isValid())
+    {
+        return QString("INVALID INDEX %1").arg(pos);
+    }
+
+    ATLauncher::IndexedPack pack = modpacks.at(pos);
+    if(role == Qt::DisplayRole)
+    {
+        return pack.name;
+    }
+    else if (role == Qt::ToolTipRole)
+    {
+        return pack.name;
+    }
+    else if(role == Qt::DecorationRole)
+    {
+        if(m_logoMap.contains(pack.safeName))
+        {
+            return (m_logoMap.value(pack.safeName));
+        }
+        auto icon = MMC->getThemedIcon("atlauncher-placeholder");
+
+        auto url = QString(BuildConfig.ATL_DOWNLOAD_SERVER_URL + "launcher/images/%1.png").arg(pack.safeName.toLower());
+        ((ListModel *)this)->requestLogo(pack.safeName, url);
+
+        return icon;
+    }
+    else if(role == Qt::UserRole)
+    {
+        QVariant v;
+        v.setValue(pack);
+        return v;
+    }
+
+    return QVariant();
+}
+
+void ListModel::request()
+{
+    beginResetModel();
+    modpacks.clear();
+    endResetModel();
+
+    auto *netJob = new NetJob("Atl::Request");
+    auto url = QString(BuildConfig.ATL_DOWNLOAD_SERVER_URL + "launcher/json/packsnew.json");
+    netJob->addNetAction(Net::Download::makeByteArray(QUrl(url), &response));
+    jobPtr = netJob;
+    jobPtr->start();
+
+    QObject::connect(netJob, &NetJob::succeeded, this, &ListModel::requestFinished);
+    QObject::connect(netJob, &NetJob::failed, this, &ListModel::requestFailed);
+}
+
+void ListModel::requestFinished()
+{
+    jobPtr.reset();
+
+    QJsonParseError parse_error;
+    QJsonDocument doc = QJsonDocument::fromJson(response, &parse_error);
+    if(parse_error.error != QJsonParseError::NoError) {
+        qWarning() << "Error while parsing JSON response from ATL at " << parse_error.offset << " reason: " << parse_error.errorString();
+        qWarning() << response;
+        return;
+    }
+
+    QList<ATLauncher::IndexedPack> newList;
+
+    auto packs = doc.array();
+    for(auto packRaw : packs) {
+        auto packObj = packRaw.toObject();
+
+        ATLauncher::IndexedPack pack;
+
+        try {
+            ATLauncher::loadIndexedPack(pack, packObj);
+        }
+        catch (const JSONValidationError &e) {
+            qDebug() << QString::fromUtf8(response);
+            qWarning() << "Error while reading pack manifest from ATLauncher: " << e.cause();
+            return;
+        }
+
+        // ignore packs without a published version
+        if(pack.versions.length() == 0) continue;
+        // only display public packs (for now)
+        if(pack.type != ATLauncher::PackType::Public) continue;
+        // ignore "system" packs (Vanilla, Vanilla with Forge, etc)
+        if(pack.system) continue;
+
+        newList.append(pack);
+    }
+
+    beginInsertRows(QModelIndex(), modpacks.size(), modpacks.size() + newList.size() - 1);
+    modpacks.append(newList);
+    endInsertRows();
+}
+
+void ListModel::requestFailed(QString reason)
+{
+    jobPtr.reset();
+}
+
+void ListModel::getLogo(const QString &logo, const QString &logoUrl, LogoCallback callback)
+{
+    if(m_logoMap.contains(logo))
+    {
+        callback(ENV.metacache()->resolveEntry("ATLauncherPacks", QString("logos/%1").arg(logo.section(".", 0, 0)))->getFullPath());
+    }
+    else
+    {
+        requestLogo(logo, logoUrl);
+    }
+}
+
+void ListModel::logoFailed(QString logo)
+{
+    m_failedLogos.append(logo);
+    m_loadingLogos.removeAll(logo);
+}
+
+void ListModel::logoLoaded(QString logo, QIcon out)
+{
+    m_loadingLogos.removeAll(logo);
+    m_logoMap.insert(logo, out);
+
+    for(int i = 0; i < modpacks.size(); i++) {
+        if(modpacks[i].safeName == logo) {
+            emit dataChanged(createIndex(i, 0), createIndex(i, 0), {Qt::DecorationRole});
+        }
+    }
+}
+
+void ListModel::requestLogo(QString file, QString url)
+{
+    if(m_loadingLogos.contains(file) || m_failedLogos.contains(file))
+    {
+        return;
+    }
+
+    MetaEntryPtr entry = ENV.metacache()->resolveEntry("ATLauncherPacks", QString("logos/%1").arg(file.section(".", 0, 0)));
+    NetJob *job = new NetJob(QString("ATLauncher Icon Download %1").arg(file));
+    job->addNetAction(Net::Download::makeCached(QUrl(url), entry));
+
+    auto fullPath = entry->getFullPath();
+    QObject::connect(job, &NetJob::succeeded, this, [this, file, fullPath]
+    {
+        emit logoLoaded(file, QIcon(fullPath));
+        if(waitingCallbacks.contains(file))
+        {
+            waitingCallbacks.value(file)(fullPath);
+        }
+    });
+
+    QObject::connect(job, &NetJob::failed, this, [this, file]
+    {
+        emit logoFailed(file);
+    });
+
+    job->start();
+
+    m_loadingLogos.append(file);
+}
+
+}
diff --git a/application/pages/modplatform/atlauncher/AtlListModel.h b/application/pages/modplatform/atlauncher/AtlListModel.h
new file mode 100644
index 00000000..2d30a64e
--- /dev/null
+++ b/application/pages/modplatform/atlauncher/AtlListModel.h
@@ -0,0 +1,52 @@
+#pragma once
+
+#include <QAbstractListModel>
+
+#include "net/NetJob.h"
+#include <QIcon>
+#include <modplatform/atlauncher/ATLPackIndex.h>
+
+namespace Atl {
+
+typedef QMap<QString, QIcon> LogoMap;
+typedef std::function<void(QString)> LogoCallback;
+
+class ListModel : public QAbstractListModel
+{
+    Q_OBJECT
+
+public:
+    ListModel(QObject *parent);
+    virtual ~ListModel();
+
+    int rowCount(const QModelIndex &parent) const override;
+    int columnCount(const QModelIndex &parent) const override;
+    QVariant data(const QModelIndex &index, int role) const override;
+
+    void request();
+
+    void getLogo(const QString &logo, const QString &logoUrl, LogoCallback callback);
+
+private slots:
+    void requestFinished();
+    void requestFailed(QString reason);
+
+    void logoFailed(QString logo);
+    void logoLoaded(QString logo, QIcon out);
+
+private:
+    void requestLogo(QString file, QString url);
+
+private:
+    QList<ATLauncher::IndexedPack> modpacks;
+
+    QStringList m_failedLogos;
+    QStringList m_loadingLogos;
+    LogoMap m_logoMap;
+    QMap<QString, LogoCallback> waitingCallbacks;
+
+    NetJobPtr jobPtr;
+    QByteArray response;
+};
+
+}
diff --git a/application/pages/modplatform/atlauncher/AtlModel.cpp b/application/pages/modplatform/atlauncher/AtlModel.cpp
deleted file mode 100644
index 4b1b1c8e..00000000
--- a/application/pages/modplatform/atlauncher/AtlModel.cpp
+++ /dev/null
@@ -1,185 +0,0 @@
-#include "AtlModel.h"
-
-#include <BuildConfig.h>
-#include <MultiMC.h>
-#include <Env.h>
-
-namespace Atl {
-
-ListModel::ListModel(QObject *parent) : QAbstractListModel(parent)
-{
-}
-
-ListModel::~ListModel()
-{
-}
-
-int ListModel::rowCount(const QModelIndex &parent) const
-{
-    return modpacks.size();
-}
-
-int ListModel::columnCount(const QModelIndex &parent) const
-{
-    return 1;
-}
-
-QVariant ListModel::data(const QModelIndex &index, int role) const
-{
-    int pos = index.row();
-    if(pos >= modpacks.size() || pos < 0 || !index.isValid())
-    {
-        return QString("INVALID INDEX %1").arg(pos);
-    }
-
-    ATLauncher::IndexedPack pack = modpacks.at(pos);
-    if(role == Qt::DisplayRole)
-    {
-        return pack.name;
-    }
-    else if (role == Qt::ToolTipRole)
-    {
-        return pack.name;
-    }
-    else if(role == Qt::DecorationRole)
-    {
-        if(m_logoMap.contains(pack.safeName))
-        {
-            return (m_logoMap.value(pack.safeName));
-        }
-        auto icon = MMC->getThemedIcon("atlauncher-placeholder");
-
-        auto url = QString(BuildConfig.ATL_DOWNLOAD_SERVER_URL + "launcher/images/%1.png").arg(pack.safeName.toLower());
-        ((ListModel *)this)->requestLogo(pack.safeName, url);
-
-        return icon;
-    }
-    else if(role == Qt::UserRole)
-    {
-        QVariant v;
-        v.setValue(pack);
-        return v;
-    }
-
-    return QVariant();
-}
-
-void ListModel::request()
-{
-    beginResetModel();
-    modpacks.clear();
-    endResetModel();
-
-    auto *netJob = new NetJob("Atl::Request");
-    auto url = QString(BuildConfig.ATL_DOWNLOAD_SERVER_URL + "launcher/json/packsnew.json");
-    netJob->addNetAction(Net::Download::makeByteArray(QUrl(url), &response));
-    jobPtr = netJob;
-    jobPtr->start();
-
-    QObject::connect(netJob, &NetJob::succeeded, this, &ListModel::requestFinished);
-    QObject::connect(netJob, &NetJob::failed, this, &ListModel::requestFailed);
-}
-
-void ListModel::requestFinished()
-{
-    jobPtr.reset();
-
-    QJsonParseError parse_error;
-    QJsonDocument doc = QJsonDocument::fromJson(response, &parse_error);
-    if(parse_error.error != QJsonParseError::NoError) {
-        qWarning() << "Error while parsing JSON response from ATL at " << parse_error.offset << " reason: " << parse_error.errorString();
-        qWarning() << response;
-        return;
-    }
-
-    QList<ATLauncher::IndexedPack> newList;
-
-    auto packs = doc.array();
-    for(auto packRaw : packs) {
-        auto packObj = packRaw.toObject();
-
-        ATLauncher::IndexedPack pack;
-        ATLauncher::loadIndexedPack(pack, packObj);
-
-        // ignore packs without a published version
-        if(pack.versions.length() == 0) continue;
-        // only display public packs (for now)
-        if(pack.type != ATLauncher::PackType::Public) continue;
-        // ignore "system" packs (Vanilla, Vanilla with Forge, etc)
-        if(pack.system) continue;
-
-        newList.append(pack);
-    }
-
-    beginInsertRows(QModelIndex(), modpacks.size(), modpacks.size() + newList.size() - 1);
-    modpacks.append(newList);
-    endInsertRows();
-}
-
-void ListModel::requestFailed(QString reason)
-{
-    jobPtr.reset();
-}
-
-void ListModel::getLogo(const QString &logo, const QString &logoUrl, LogoCallback callback)
-{
-    if(m_logoMap.contains(logo))
-    {
-        callback(ENV.metacache()->resolveEntry("ATLauncherPacks", QString("logos/%1").arg(logo.section(".", 0, 0)))->getFullPath());
-    }
-    else
-    {
-        requestLogo(logo, logoUrl);
-    }
-}
-
-void ListModel::logoFailed(QString logo)
-{
-    m_failedLogos.append(logo);
-    m_loadingLogos.removeAll(logo);
-}
-
-void ListModel::logoLoaded(QString logo, QIcon out)
-{
-    m_loadingLogos.removeAll(logo);
-    m_logoMap.insert(logo, out);
-
-    for(int i = 0; i < modpacks.size(); i++) {
-        if(modpacks[i].safeName == logo) {
-            emit dataChanged(createIndex(i, 0), createIndex(i, 0), {Qt::DecorationRole});
-        }
-    }
-}
-
-void ListModel::requestLogo(QString file, QString url)
-{
-    if(m_loadingLogos.contains(file) || m_failedLogos.contains(file))
-    {
-        return;
-    }
-
-    MetaEntryPtr entry = ENV.metacache()->resolveEntry("ATLauncherPacks", QString("logos/%1").arg(file.section(".", 0, 0)));
-    NetJob *job = new NetJob(QString("ATLauncher Icon Download %1").arg(file));
-    job->addNetAction(Net::Download::makeCached(QUrl(url), entry));
-
-    auto fullPath = entry->getFullPath();
-    QObject::connect(job, &NetJob::succeeded, this, [this, file, fullPath]
-    {
-        emit logoLoaded(file, QIcon(fullPath));
-        if(waitingCallbacks.contains(file))
-        {
-            waitingCallbacks.value(file)(fullPath);
-        }
-    });
-
-    QObject::connect(job, &NetJob::failed, this, [this, file]
-    {
-        emit logoFailed(file);
-    });
-
-    job->start();
-
-    m_loadingLogos.append(file);
-}
-
-}
diff --git a/application/pages/modplatform/atlauncher/AtlModel.h b/application/pages/modplatform/atlauncher/AtlModel.h
deleted file mode 100644
index 2d30a64e..00000000
--- a/application/pages/modplatform/atlauncher/AtlModel.h
+++ /dev/null
@@ -1,52 +0,0 @@
-#pragma once
-
-#include <QAbstractListModel>
-
-#include "net/NetJob.h"
-#include <QIcon>
-#include <modplatform/atlauncher/ATLPackIndex.h>
-
-namespace Atl {
-
-typedef QMap<QString, QIcon> LogoMap;
-typedef std::function<void(QString)> LogoCallback;
-
-class ListModel : public QAbstractListModel
-{
-    Q_OBJECT
-
-public:
-    ListModel(QObject *parent);
-    virtual ~ListModel();
-
-    int rowCount(const QModelIndex &parent) const override;
-    int columnCount(const QModelIndex &parent) const override;
-    QVariant data(const QModelIndex &index, int role) const override;
-
-    void request();
-
-    void getLogo(const QString &logo, const QString &logoUrl, LogoCallback callback);
-
-private slots:
-    void requestFinished();
-    void requestFailed(QString reason);
-
-    void logoFailed(QString logo);
-    void logoLoaded(QString logo, QIcon out);
-
-private:
-    void requestLogo(QString file, QString url);
-
-private:
-    QList<ATLauncher::IndexedPack> modpacks;
-
-    QStringList m_failedLogos;
-    QStringList m_loadingLogos;
-    LogoMap m_logoMap;
-    QMap<QString, LogoCallback> waitingCallbacks;
-
-    NetJobPtr jobPtr;
-    QByteArray response;
-};
-
-}
diff --git a/application/pages/modplatform/atlauncher/AtlPage.h b/application/pages/modplatform/atlauncher/AtlPage.h
index 368de666..715cf5f4 100644
--- a/application/pages/modplatform/atlauncher/AtlPage.h
+++ b/application/pages/modplatform/atlauncher/AtlPage.h
@@ -16,7 +16,7 @@
 #pragma once
 
 #include "AtlFilterModel.h"
-#include "AtlModel.h"
+#include "AtlListModel.h"
 
 #include <QWidget>
 
-- 
cgit