aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/logic/CMakeLists.txt20
-rw-r--r--api/logic/MMCZip.h2
-rw-r--r--api/logic/minecraft/MinecraftInstance.cpp26
-rw-r--r--api/logic/minecraft/MinecraftInstance.h20
-rw-r--r--api/logic/minecraft/Mod.cpp433
-rw-r--r--api/logic/minecraft/ParseUtils_test.cpp2
-rw-r--r--api/logic/minecraft/legacy/LegacyInstance.h3
-rw-r--r--api/logic/minecraft/mod/LocalModParseTask.cpp298
-rw-r--r--api/logic/minecraft/mod/LocalModParseTask.h37
-rw-r--r--api/logic/minecraft/mod/Mod.cpp169
-rw-r--r--api/logic/minecraft/mod/Mod.h (renamed from api/logic/minecraft/Mod.h)49
-rw-r--r--api/logic/minecraft/mod/ModDetails.h17
-rw-r--r--api/logic/minecraft/mod/ModFolderLoadTask.cpp18
-rw-r--r--api/logic/minecraft/mod/ModFolderLoadTask.h29
-rw-r--r--api/logic/minecraft/mod/ModFolderModel.cpp (renamed from api/logic/minecraft/SimpleModList.cpp)169
-rw-r--r--api/logic/minecraft/mod/ModFolderModel.h (renamed from api/logic/minecraft/SimpleModList.h)22
-rw-r--r--api/logic/minecraft/mod/ModFolderModel_test.cpp (renamed from api/logic/minecraft/SimpleModList_test.cpp)12
-rw-r--r--application/pages/instance/ModFolderPage.cpp26
-rw-r--r--application/pages/instance/ModFolderPage.h8
-rw-r--r--application/pages/instance/VersionPage.cpp2
-rw-r--r--application/widgets/MCModInfoFrame.h2
21 files changed, 821 insertions, 543 deletions
diff --git a/api/logic/CMakeLists.txt b/api/logic/CMakeLists.txt
index 1f795556..a762fb22 100644
--- a/api/logic/CMakeLists.txt
+++ b/api/logic/CMakeLists.txt
@@ -279,15 +279,21 @@ set(MINECRAFT_SOURCES
minecraft/VersionFile.h
minecraft/VersionFilterData.h
minecraft/VersionFilterData.cpp
- minecraft/Mod.h
- minecraft/Mod.cpp
- minecraft/SimpleModList.h
- minecraft/SimpleModList.cpp
minecraft/World.h
minecraft/World.cpp
minecraft/WorldList.h
minecraft/WorldList.cpp
+ minecraft/mod/Mod.h
+ minecraft/mod/Mod.cpp
+ minecraft/mod/ModDetails.h
+ minecraft/mod/ModFolderModel.h
+ minecraft/mod/ModFolderModel.cpp
+ minecraft/mod/ModFolderLoadTask.h
+ minecraft/mod/ModFolderLoadTask.cpp
+ minecraft/mod/LocalModParseTask.h
+ minecraft/mod/LocalModParseTask.cpp
+
# Assets
minecraft/AssetsUtils.h
minecraft/AssetsUtils.cpp
@@ -318,8 +324,8 @@ add_unit_test(Library
)
# FIXME: shares data with FileSystem test
-add_unit_test(SimpleModList
- SOURCES minecraft/SimpleModList_test.cpp
+add_unit_test(ModFolderModel
+ SOURCES minecraft/mod/ModFolderModel_test.cpp
DATA testdata
LIBS MultiMC_logic
)
@@ -479,8 +485,6 @@ set(LOGIC_SOURCES
${FLAME_SOURCES}
)
-message(STATUS "FOO! ${LOGIC_SOURCES}")
-
add_library(MultiMC_logic SHARED ${LOGIC_SOURCES})
set_target_properties(MultiMC_logic PROPERTIES CXX_VISIBILITY_PRESET hidden VISIBILITY_INLINES_HIDDEN 1)
diff --git a/api/logic/MMCZip.h b/api/logic/MMCZip.h
index ee9c5cc1..85ac7802 100644
--- a/api/logic/MMCZip.h
+++ b/api/logic/MMCZip.h
@@ -18,7 +18,7 @@
#include <QString>
#include <QFileInfo>
#include <QSet>
-#include "minecraft/Mod.h"
+#include "minecraft/mod/Mod.h"
#include <functional>
#include "multimc_logic_export.h"
diff --git a/api/logic/minecraft/MinecraftInstance.cpp b/api/logic/minecraft/MinecraftInstance.cpp
index 617d7431..9ca77798 100644
--- a/api/logic/minecraft/MinecraftInstance.cpp
+++ b/api/logic/minecraft/MinecraftInstance.cpp
@@ -26,7 +26,7 @@
#include "meta/Index.h"
#include "meta/VersionList.h"
-#include "SimpleModList.h"
+#include "mod/ModFolderModel.h"
#include "WorldList.h"
#include "icons/IIconList.h"
@@ -892,46 +892,46 @@ JavaVersion MinecraftInstance::getJavaVersion() const
return JavaVersion(settings()->get("JavaVersion").toString());
}
-std::shared_ptr<SimpleModList> MinecraftInstance::loaderModList() const
+std::shared_ptr<ModFolderModel> MinecraftInstance::loaderModList() const
{
if (!m_loader_mod_list)
{
- m_loader_mod_list.reset(new SimpleModList(loaderModsDir()));
+ m_loader_mod_list.reset(new ModFolderModel(loaderModsDir()));
m_loader_mod_list->disableInteraction(isRunning());
- connect(this, &BaseInstance::runningStatusChanged, m_loader_mod_list.get(), &SimpleModList::disableInteraction);
+ connect(this, &BaseInstance::runningStatusChanged, m_loader_mod_list.get(), &ModFolderModel::disableInteraction);
}
return m_loader_mod_list;
}
-std::shared_ptr<SimpleModList> MinecraftInstance::coreModList() const
+std::shared_ptr<ModFolderModel> MinecraftInstance::coreModList() const
{
if (!m_core_mod_list)
{
- m_core_mod_list.reset(new SimpleModList(coreModsDir()));
+ m_core_mod_list.reset(new ModFolderModel(coreModsDir()));
m_core_mod_list->disableInteraction(isRunning());
- connect(this, &BaseInstance::runningStatusChanged, m_core_mod_list.get(), &SimpleModList::disableInteraction);
+ connect(this, &BaseInstance::runningStatusChanged, m_core_mod_list.get(), &ModFolderModel::disableInteraction);
}
return m_core_mod_list;
}
-std::shared_ptr<SimpleModList> MinecraftInstance::resourcePackList() const
+std::shared_ptr<ModFolderModel> MinecraftInstance::resourcePackList() const
{
if (!m_resource_pack_list)
{
- m_resource_pack_list.reset(new SimpleModList(resourcePacksDir()));
+ m_resource_pack_list.reset(new ModFolderModel(resourcePacksDir()));
m_resource_pack_list->disableInteraction(isRunning());
- connect(this, &BaseInstance::runningStatusChanged, m_resource_pack_list.get(), &SimpleModList::disableInteraction);
+ connect(this, &BaseInstance::runningStatusChanged, m_resource_pack_list.get(), &ModFolderModel::disableInteraction);
}
return m_resource_pack_list;
}
-std::shared_ptr<SimpleModList> MinecraftInstance::texturePackList() const
+std::shared_ptr<ModFolderModel> MinecraftInstance::texturePackList() const
{
if (!m_texture_pack_list)
{
- m_texture_pack_list.reset(new SimpleModList(texturePacksDir()));
+ m_texture_pack_list.reset(new ModFolderModel(texturePacksDir()));
m_texture_pack_list->disableInteraction(isRunning());
- connect(this, &BaseInstance::runningStatusChanged, m_texture_pack_list.get(), &SimpleModList::disableInteraction);
+ connect(this, &BaseInstance::runningStatusChanged, m_texture_pack_list.get(), &ModFolderModel::disableInteraction);
}
return m_texture_pack_list;
}
diff --git a/api/logic/minecraft/MinecraftInstance.h b/api/logic/minecraft/MinecraftInstance.h
index 501697f7..dd14664f 100644
--- a/api/logic/minecraft/MinecraftInstance.h
+++ b/api/logic/minecraft/MinecraftInstance.h
@@ -1,13 +1,13 @@
#pragma once
#include "BaseInstance.h"
#include <java/JavaVersion.h>
-#include "minecraft/Mod.h"
+#include "minecraft/mod/Mod.h"
#include <QProcess>
#include <QDir>
#include "multimc_logic_export.h"
class ModsModel;
-class SimpleModList;
+class ModFolderModel;
class WorldList;
class GameOptions;
class LaunchStep;
@@ -69,10 +69,10 @@ public:
////// Mod Lists //////
std::shared_ptr<ModsModel> modsModel() const;
- std::shared_ptr<SimpleModList> loaderModList() const;
- std::shared_ptr<SimpleModList> coreModList() const;
- std::shared_ptr<SimpleModList> resourcePackList() const;
- std::shared_ptr<SimpleModList> texturePackList() const;
+ std::shared_ptr<ModFolderModel> loaderModList() const;
+ std::shared_ptr<ModFolderModel> coreModList() const;
+ std::shared_ptr<ModFolderModel> resourcePackList() const;
+ std::shared_ptr<ModFolderModel> texturePackList() const;
std::shared_ptr<WorldList> worldList() const;
std::shared_ptr<GameOptions> gameOptionsModel() const;
@@ -124,10 +124,10 @@ private:
protected: // data
std::shared_ptr<ComponentList> m_components;
mutable std::shared_ptr<ModsModel> m_mods_model;
- mutable std::shared_ptr<SimpleModList> m_loader_mod_list;
- mutable std::shared_ptr<SimpleModList> m_core_mod_list;
- mutable std::shared_ptr<SimpleModList> m_resource_pack_list;
- mutable std::shared_ptr<SimpleModList> m_texture_pack_list;
+ mutable std::shared_ptr<ModFolderModel> m_loader_mod_list;
+ mutable std::shared_ptr<ModFolderModel> m_core_mod_list;
+ mutable std::shared_ptr<ModFolderModel> m_resource_pack_list;
+ mutable std::shared_ptr<ModFolderModel> m_texture_pack_list;
mutable std::shared_ptr<WorldList> m_world_list;
mutable std::shared_ptr<GameOptions> m_game_options;
};
diff --git a/api/logic/minecraft/Mod.cpp b/api/logic/minecraft/Mod.cpp
deleted file mode 100644
index 936ca00a..00000000
--- a/api/logic/minecraft/Mod.cpp
+++ /dev/null
@@ -1,433 +0,0 @@
-/* Copyright 2013-2019 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <QDir>
-#include <QString>
-#include <QJsonDocument>
-#include <QJsonObject>
-#include <QJsonArray>
-#include <QJsonValue>
-#include <quazip.h>
-#include <quazipfile.h>
-
-#include "Mod.h"
-#include "settings/INIFile.h"
-#include <FileSystem.h>
-#include <QDebug>
-
-namespace {
-// NEW format
-// https://github.com/MinecraftForge/FML/wiki/FML-mod-information-file/6f62b37cea040daf350dc253eae6326dd9c822c3
-
-// OLD format:
-// https://github.com/MinecraftForge/FML/wiki/FML-mod-information-file/5bf6a2d05145ec79387acc0d45c958642fb049fc
-ModDetails ReadMCModInfo(QByteArray contents)
-{
- auto getInfoFromArray = [&](QJsonArray arr)->ModDetails
- {
- ModDetails details;
- if (!arr.at(0).isObject()) {
- return details;
- }
- auto firstObj = arr.at(0).toObject();
- details.mod_id = firstObj.value("modid").toString();
- auto name = firstObj.value("name").toString();
- // NOTE: ignore stupid example mods copies where the author didn't even bother to change the name
- if(name != "Example Mod") {
- details.name = name;
- }
- details.version = firstObj.value("version").toString();
- details.updateurl = firstObj.value("updateUrl").toString();
- auto homeurl = firstObj.value("url").toString().trimmed();
- if(!homeurl.isEmpty())
- {
- // fix up url.
- if (!homeurl.startsWith("http://") && !homeurl.startsWith("https://") && !homeurl.startsWith("ftp://"))
- {
- homeurl.prepend("http://");
- }
- }
- details.homeurl = homeurl;
- details.description = firstObj.value("description").toString();
- QJsonArray authors = firstObj.value("authorList").toArray();
- if (authors.size() == 0) {
- // FIXME: what is the format of this? is there any?
- authors = firstObj.value("authors").toArray();
- }
-
- for (auto author: authors)
- {
- details.authors.append(author.toString());
- }
- details.credits = firstObj.value("credits").toString();
- details.valid = true;
- return details;
- };
- QJsonParseError jsonError;
- QJsonDocument jsonDoc = QJsonDocument::fromJson(contents, &jsonError);
- // this is the very old format that had just the array
- if (jsonDoc.isArray())
- {
- return getInfoFromArray(jsonDoc.array());
- }
- else if (jsonDoc.isObject())
- {
- auto val = jsonDoc.object().value("modinfoversion");
- if(val.isUndefined()) {
- val = jsonDoc.object().value("modListVersion");
- }
- int version = val.toDouble();
- if (version != 2)
- {
- qCritical() << "BAD stuff happened to mod json:";
- qCritical() << contents;
- return ModDetails();
- }
- auto arrVal = jsonDoc.object().value("modlist");
- if(arrVal.isUndefined()) {
- arrVal = jsonDoc.object().value("modList");
- }
- if (arrVal.isArray())
- {
- return getInfoFromArray(arrVal.toArray());
- }
- }
- return ModDetails();
-}
-
-// https://fabricmc.net/wiki/documentation:fabric_mod_json
-ModDetails ReadFabricModInfo(QByteArray contents)
-{
- QJsonParseError jsonError;
- QJsonDocument jsonDoc = QJsonDocument::fromJson(contents, &jsonError);
- auto object = jsonDoc.object();
- auto schemaVersion = object.contains("schemaVersion") ? object.value("schemaVersion").toInt(0) : 0;
-
- ModDetails details;
-
- details.mod_id = object.value("id").toString();
- details.version = object.value("version").toString();
-
- details.name = object.contains("name") ? object.value("name").toString() : details.mod_id;
- details.description = object.value("description").toString();
-
- if (schemaVersion >= 1)
- {
- QJsonArray authors = object.value("authors").toArray();
- for (auto author: authors)
- {
- if(author.isObject()) {
- details.authors.append(author.toObject().value("name").toString());
- }
- else {
- details.authors.append(author.toString());
- }
- }
-
- if (object.contains("contact"))
- {
- QJsonObject contact = object.value("contact").toObject();
-
- if (contact.contains("homepage"))
- {
- details.homeurl = contact.value("homepage").toString();
- }
- }
- }
- details.valid = !details.name.isEmpty();
- return details;
-}
-
-ModDetails ReadForgeInfo(QByteArray contents)
-{
- ModDetails details;
- // Read the data
- details.name = "Minecraft Forge";
- details.mod_id = "Forge";
- details.homeurl = "http://www.minecraftforge.net/forum/";
- details.valid = true;
- INIFile ini;
- if (!ini.loadFile(contents))
- return details;
-
- QString major = ini.get("forge.major.number", "0").toString();
- QString minor = ini.get("forge.minor.number", "0").toString();
- QString revision = ini.get("forge.revision.number", "0").toString();
- QString build = ini.get("forge.build.number", "0").toString();
-
- details.version = major + "." + minor + "." + revision + "." + build;
- return details;
-}
-
-ModDetails ReadLiteModInfo(QByteArray contents)
-{
- ModDetails details;
- QJsonParseError jsonError;
- QJsonDocument jsonDoc = QJsonDocument::fromJson(contents, &jsonError);
- auto object = jsonDoc.object();
- if (object.contains("name"))
- {
- details.mod_id = details.name = object.value("name").toString();
- }
- if (object.contains("version"))
- {
- details.version = object.value("version").toString("");
- }
- else
- {
- details.version = object.value("revision").toString("");
- }
- details.mcversion = object.value("mcversion").toString();
- auto author = object.value("author").toString();
- if(!author.isEmpty()) {
- details.authors.append(author);
- }
- details.description = object.value("description").toString();
- details.homeurl = object.value("url").toString();
- return details;
-}
-
-ModDetails invalidDetails;
-
-}
-
-
-Mod::Mod(const QFileInfo &file)
-{
- repath(file);
- m_changedDateTime = file.lastModified();
-}
-
-void Mod::repath(const QFileInfo &file)
-{
- m_file = file;
- QString name_base = file.fileName();
-
- m_type = Mod::MOD_UNKNOWN;
-
- if (m_file.isDir())
- {
- m_type = MOD_FOLDER;
- m_name = name_base;
- m_mmc_id = name_base;
- }
- else if (m_file.isFile())
- {
- if (name_base.endsWith(".disabled"))
- {
- m_enabled = false;
- name_base.chop(9);
- }
- else
- {
- m_enabled = true;
- }
- m_mmc_id = name_base;
- if (name_base.endsWith(".zip") || name_base.endsWith(".jar"))
- {
- m_type = MOD_ZIPFILE;
- name_base.chop(4);
- }
- else if (name_base.endsWith(".litemod"))
- {
- m_type = MOD_LITEMOD;
- name_base.chop(8);
- }
- else
- {
- m_type = MOD_SINGLEFILE;
- }
- m_name = name_base;
- }
-
- if (m_type == MOD_ZIPFILE)
- {
- QuaZip zip(m_file.filePath());
- if (!zip.open(QuaZip::mdUnzip))
- return;
-
- QuaZipFile file(&zip);
-
- if (zip.setCurrentFile("mcmod.info"))
- {
- if (!file.open(QIODevice::ReadOnly))
- {
- zip.close();
- return;
- }
-
- m_localDetails = ReadMCModInfo(file.readAll());
- file.close();
- zip.close();
- return;
- }
- else if (zip.setCurrentFile("fabric.mod.json"))
- {
- if (!file.open(QIODevice::ReadOnly))
- {
- zip.close();
- return;
- }
-
- m_localDetails = ReadFabricModInfo(file.readAll());
- file.close();
- zip.close();
- return;
- }
- else if (zip.setCurrentFile("forgeversion.properties"))
- {
- if (!file.open(QIODevice::ReadOnly))
- {
- zip.close();
- return;
- }
-
- m_localDetails = ReadForgeInfo(file.readAll());
- file.close();
- zip.close();
- return;
- }
-
- zip.close();
- }
- else if (m_type == MOD_FOLDER)
- {
- QFileInfo mcmod_info(FS::PathCombine(m_file.filePath(), "mcmod.info"));
- if (mcmod_info.isFile())
- {
- QFile mcmod(mcmod_info.filePath());
- if (!mcmod.open(QIODevice::ReadOnly))
- return;
- auto data = mcmod.readAll();
- if (data.isEmpty() || data.isNull())
- return;
- m_localDetails = ReadMCModInfo(data);
- }
- }
- else if (m_type == MOD_LITEMOD)
- {
- QuaZip zip(m_file.filePath());
- if (!zip.open(QuaZip::mdUnzip))
- return;
-
- QuaZipFile file(&zip);
-
- if (zip.setCurrentFile("litemod.json"))
- {
- if (!file.open(QIODevice::ReadOnly))
- {
- zip.close();
- return;
- }
-
- m_localDetails = ReadLiteModInfo(file.readAll());
- file.close();
- }
- zip.close();
- }
-}
-
-bool Mod::enable(bool value)
-{
- if (m_type == Mod::MOD_UNKNOWN || m_type == Mod::MOD_FOLDER)
- return false;
-
- if (m_enabled == value)
- return false;
-
- QString path = m_file.absoluteFilePath();
- if (value)
- {
- QFile foo(path);
- if (!path.endsWith(".disabled"))
- return false;
- path.chop(9);
- if (!foo.rename(path))
- return false;
- }
- else
- {
- QFile foo(path);
- path += ".disabled";
- if (!foo.rename(path))
- return false;
- }
- m_file = QFileInfo(path);
- m_enabled = value;
- return true;
-}
-
-bool Mod::destroy()
-{
- if (m_type == MOD_FOLDER)
- {
- QDir d(m_file.filePath());
- if (d.removeRecursively())
- {
- m_type = MOD_UNKNOWN;
- return true;
- }
- return false;
- }
- else if (m_type == MOD_SINGLEFILE || m_type == MOD_ZIPFILE || m_type == MOD_LITEMOD)
- {
- QFile f(m_file.filePath());
- if (f.remove())
- {
- m_type = MOD_UNKNOWN;
- return true;
- }
- return false;
- }
- return true;
-}
-
-
-const ModDetails & Mod::details() const
-{
- if(!m_localDetails)
- return invalidDetails;
- return m_localDetails;
-}
-
-
-QString Mod::version() const
-{
- return details().version;
-}
-
-QString Mod::name() const
-{
- auto & d = details();
- if(d && !d.name.isEmpty()) {
- return d.name;
- }
- return m_name;
-}
-
-QString Mod::homeurl() const
-{
- return details().homeurl;
-}
-
-QString Mod::description() const
-{
- return details().description;
-}
-
-QStringList Mod::authors() const
-{
- return details().authors;
-}
diff --git a/api/logic/minecraft/ParseUtils_test.cpp b/api/logic/minecraft/ParseUtils_test.cpp
index fde9cdbf..fcc137e5 100644
--- a/api/logic/minecraft/ParseUtils_test.cpp
+++ b/api/logic/minecraft/ParseUtils_test.cpp
@@ -33,7 +33,7 @@ slots:
auto time_parsed = timeFromS3Time(timestamp);
auto time_serialized = timeToS3Time(time_parsed);
-
+
QCOMPARE(time_serialized, timestamp);
}
diff --git a/api/logic/minecraft/legacy/LegacyInstance.h b/api/logic/minecraft/legacy/LegacyInstance.h
index 46fca3e4..7c0b94e8 100644
--- a/api/logic/minecraft/legacy/LegacyInstance.h
+++ b/api/logic/minecraft/legacy/LegacyInstance.h
@@ -16,12 +16,11 @@
#pragma once
#include "BaseInstance.h"
-#include "minecraft/Mod.h"
#include "launch/LaunchTask.h"
#include "multimc_logic_export.h"
-class SimpleModList;
+class ModFolderModel;
class LegacyModList;
class WorldList;
class Task;
diff --git a/api/logic/minecraft/mod/LocalModParseTask.cpp b/api/logic/minecraft/mod/LocalModParseTask.cpp
new file mode 100644
index 00000000..22ebd7d4
--- /dev/null
+++ b/api/logic/minecraft/mod/LocalModParseTask.cpp
@@ -0,0 +1,298 @@
+#include "LocalModParseTask.h"
+
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QJsonArray>
+#include <QJsonValue>
+#include <quazip.h>
+#include <quazipfile.h>
+
+#include "settings/INIFile.h"
+#include "FileSystem.h"
+
+namespace {
+
+// NEW format
+// https://github.com/MinecraftForge/FML/wiki/FML-mod-information-file/6f62b37cea040daf350dc253eae6326dd9c822c3
+
+// OLD format:
+// https://github.com/MinecraftForge/FML/wiki/FML-mod-information-file/5bf6a2d05145ec79387acc0d45c958642fb049fc
+std::shared_ptr<ModDetails> ReadMCModInfo(QByteArray contents)
+{
+ auto getInfoFromArray = [&](QJsonArray arr)->std::shared_ptr<ModDetails>
+ {
+ if (!arr.at(0).isObject()) {
+ return nullptr;
+ }
+ std::shared_ptr<ModDetails> details = std::make_shared<ModDetails>();
+ auto firstObj = arr.at(0).toObject();
+ details->mod_id = firstObj.value("modid").toString();
+ auto name = firstObj.value("name").toString();
+ // NOTE: ignore stupid example mods copies where the author didn't even bother to change the name
+ if(name != "Example Mod") {
+ details->name = name;
+ }
+ details->version = firstObj.value("version").toString();
+ details->updateurl = firstObj.value("updateUrl").toString();
+ auto homeurl = firstObj.value("url").toString().trimmed();
+ if(!homeurl.isEmpty())
+ {
+ // fix up url.
+ if (!homeurl.startsWith("http://") && !homeurl.startsWith("https://") && !homeurl.startsWith("ftp://"))
+ {
+ homeurl.prepend("http://");
+ }
+ }
+ details->homeurl = homeurl;
+ details->description = firstObj.value("description").toString();
+ QJsonArray authors = firstObj.value("authorList").toArray();
+ if (authors.size() == 0) {
+ // FIXME: what is the format of this? is there any?
+ authors = firstObj.value("authors").toArray();
+ }
+
+ for (auto author: authors)
+ {
+ details->authors.append(author.toString());
+ }
+ details->credits = firstObj.value("credits").toString();
+ return details;
+ };
+ QJsonParseError jsonError;
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(contents, &jsonError);
+ // this is the very old format that had just the array
+ if (jsonDoc.isArray())
+ {
+ return getInfoFromArray(jsonDoc.array());
+ }
+ else if (jsonDoc.isObject())
+ {
+ auto val = jsonDoc.object().value("modinfoversion");
+ if(val.isUndefined()) {
+ val = jsonDoc.object().value("modListVersion");
+ }
+ int version = val.toDouble();
+ if (version != 2)
+ {
+ qCritical() << "BAD stuff happened to mod json:";
+ qCritical() << contents;
+ return nullptr;
+ }
+ auto arrVal = jsonDoc.object().value("modlist");
+ if(arrVal.isUndefined()) {
+ arrVal = jsonDoc.object().value("modList");
+ }
+ if (arrVal.isArray())
+ {
+ return getInfoFromArray(arrVal.toArray());
+ }
+ }
+ return nullptr;
+}
+
+// https://fabricmc.net/wiki/documentation:fabric_mod_json
+std::shared_ptr<ModDetails> ReadFabricModInfo(QByteArray contents)
+{
+ QJsonParseError jsonError;
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(contents, &jsonError);
+ auto object = jsonDoc.object();
+ auto schemaVersion = object.contains("schemaVersion") ? object.value("schemaVersion").toInt(0) : 0;
+
+ std::shared_ptr<ModDetails> details = std::make_shared<ModDetails>();
+
+ details->mod_id = object.value("id").toString();
+ details->version = object.value("version").toString();
+
+ details->name = object.contains("name") ? object.value("name").toString() : details->mod_id;
+ details->description = object.value("description").toString();
+
+ if (schemaVersion >= 1)
+ {
+ QJsonArray authors = object.value("authors").toArray();
+ for (auto author: authors)
+ {
+ if(author.isObject()) {
+ details->authors.append(author.toObject().value("name").toString());
+ }
+ else {
+ details->authors.append(author.toString());
+ }
+ }
+
+ if (object.contains("contact"))
+ {
+ QJsonObject contact = object.value("contact").toObject();
+
+ if (contact.contains("homepage"))
+ {
+ details->homeurl = contact.value("homepage").toString();
+ }
+ }
+ }
+ return details;
+}
+
+std::shared_ptr<ModDetails> ReadForgeInfo(QByteArray contents)
+{
+ std::shared_ptr<ModDetails> details = std::make_shared<ModDetails>();
+ // Read the data
+ details->name = "Minecraft Forge";
+ details->mod_id = "Forge";
+ details->homeurl = "http://www.minecraftforge.net/forum/";
+ INIFile ini;
+ if (!ini.loadFile(contents))
+ return details;
+
+ QString major = ini.get("forge.major.number", "0").toString();
+ QString minor = ini.get("forge.minor.number", "0").toString();
+ QString revision = ini.get("forge.revision.number", "0").toString();
+ QString build = ini.get("forge.build.number", "0").toString();
+
+ details->version = major + "." + minor + "." + revision + "." + build;
+ return details;
+}
+
+std::shared_ptr<ModDetails> ReadLiteModInfo(QByteArray contents)
+{
+ std::shared_ptr<ModDetails> details = std::make_shared<ModDetails>();
+ QJsonParseError jsonError;
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(contents, &jsonError);
+ auto object = jsonDoc.object();
+ if (object.contains("name"))
+ {
+ details->mod_id = details->name = object.value("name").toString();
+ }
+ if (object.contains("version"))
+ {
+ details->version = object.value("version").toString("");
+ }
+ else
+ {
+ details->version = object.value("revision").toString("");
+ }
+ details->mcversion = object.value("mcversion").toString();
+ auto author = object.value("author").toString();
+ if(!author.isEmpty()) {
+ details->authors.append(author);
+ }
+ details->description = object.value("description").toString();
+ details->homeurl = object.value("url").toString();
+ return details;
+}
+
+}
+
+LocalModParseTask::LocalModParseTask(int token, Mod::ModType type, const QFileInfo& modFile):
+ m_token(token),
+ m_type(type),
+ m_modFile(modFile),
+ m_result(new Result())
+{
+}
+
+void LocalModParseTask::processAsZip()
+{
+ QuaZip zip(m_modFile.filePath());
+ if (!zip.open(QuaZip::mdUnzip))
+ return;
+
+ QuaZipFile file(&zip);
+
+ if (zip.setCurrentFile("mcmod.info"))
+ {
+ if (!file.open(QIODevice::ReadOnly))
+ {
+ zip.close();
+ return;
+ }
+
+ m_result->details = ReadMCModInfo(file.readAll());
+ file.close();
+ zip.close();
+ return;
+ }
+ else if (zip.setCurrentFile("fabric.mod.json"))
+ {
+ if (!file.open(QIODevice::ReadOnly))
+ {
+ zip.close();
+ return;
+ }
+
+ m_result->details = ReadFabricModInfo(file.readAll());
+ file.close();
+ zip.close();
+ return;
+ }
+ else if (zip.setCurrentFile("forgeversion.properties"))
+ {
+ if (!file.open(QIODevice::ReadOnly))
+ {
+ zip.close();
+ return;
+ }
+
+ m_result->details = ReadForgeInfo(file.readAll());
+ file.close();
+ zip.close();
+ return;
+ }
+
+ zip.close();
+}
+
+void LocalModParseTask::processAsFolder()
+{
+