2019-08-04 02:27:53 +01:00
|
|
|
#include "LocalModParseTask.h"
|
|
|
|
|
2023-07-26 21:56:01 +01:00
|
|
|
#include <qdcss.h>
|
2022-09-23 22:06:36 +01:00
|
|
|
#include <quazip/quazip.h>
|
|
|
|
#include <quazip/quazipfile.h>
|
|
|
|
#include <toml++/toml.h>
|
|
|
|
#include <QJsonArray>
|
2019-08-04 02:27:53 +01:00
|
|
|
#include <QJsonDocument>
|
|
|
|
#include <QJsonObject>
|
|
|
|
#include <QJsonValue>
|
2022-06-17 15:21:43 +01:00
|
|
|
#include <QString>
|
2019-08-04 02:27:53 +01:00
|
|
|
|
2022-09-23 22:06:36 +01:00
|
|
|
#include "FileSystem.h"
|
2022-05-15 22:00:09 +01:00
|
|
|
#include "Json.h"
|
2022-12-10 07:52:50 +00:00
|
|
|
#include "minecraft/mod/ModDetails.h"
|
2019-08-04 02:27:53 +01:00
|
|
|
#include "settings/INIFile.h"
|
|
|
|
|
2022-12-10 07:52:50 +00:00
|
|
|
namespace ModUtils {
|
2019-08-04 02:27:53 +01:00
|
|
|
|
|
|
|
// NEW format
|
2023-01-14 00:15:10 +00:00
|
|
|
// https://github.com/MinecraftForge/FML/wiki/FML-mod-information-file/c8d8f1929aff9979e322af79a59ce81f3e02db6a
|
2019-08-04 02:27:53 +01:00
|
|
|
|
|
|
|
// OLD format:
|
|
|
|
// https://github.com/MinecraftForge/FML/wiki/FML-mod-information-file/5bf6a2d05145ec79387acc0d45c958642fb049fc
|
2022-08-12 21:06:20 +01:00
|
|
|
ModDetails ReadMCModInfo(QByteArray contents)
|
2019-08-04 02:27:53 +01:00
|
|
|
{
|
2022-09-23 22:06:36 +01:00
|
|
|
auto getInfoFromArray = [&](QJsonArray arr) -> ModDetails {
|
2019-08-04 02:27:53 +01:00
|
|
|
if (!arr.at(0).isObject()) {
|
2022-08-12 21:06:20 +01:00
|
|
|
return {};
|
2019-08-04 02:27:53 +01:00
|
|
|
}
|
2022-08-12 21:06:20 +01:00
|
|
|
ModDetails details;
|
2019-08-04 02:27:53 +01:00
|
|
|
auto firstObj = arr.at(0).toObject();
|
2022-08-12 21:06:20 +01:00
|
|
|
details.mod_id = firstObj.value("modid").toString();
|
2019-08-04 02:27:53 +01:00
|
|
|
auto name = firstObj.value("name").toString();
|
|
|
|
// NOTE: ignore stupid example mods copies where the author didn't even bother to change the name
|
2022-09-23 22:06:36 +01:00
|
|
|
if (name != "Example Mod") {
|
2022-08-12 21:06:20 +01:00
|
|
|
details.name = name;
|
2019-08-04 02:27:53 +01:00
|
|
|
}
|
2022-08-12 21:06:20 +01:00
|
|
|
details.version = firstObj.value("version").toString();
|
2019-08-04 02:27:53 +01:00
|
|
|
auto homeurl = firstObj.value("url").toString().trimmed();
|
2022-09-23 22:06:36 +01:00
|
|
|
if (!homeurl.isEmpty()) {
|
2019-08-04 02:27:53 +01:00
|
|
|
// fix up url.
|
2022-09-23 22:06:36 +01:00
|
|
|
if (!homeurl.startsWith("http://") && !homeurl.startsWith("https://") && !homeurl.startsWith("ftp://")) {
|
2019-08-04 02:27:53 +01:00
|
|
|
homeurl.prepend("http://");
|
|
|
|
}
|
|
|
|
}
|
2022-08-12 21:06:20 +01:00
|
|
|
details.homeurl = homeurl;
|
|
|
|
details.description = firstObj.value("description").toString();
|
2019-08-04 02:27:53 +01:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2023-05-05 07:42:42 +01:00
|
|
|
if (firstObj.contains("logoFile")) {
|
|
|
|
details.icon_file = firstObj.value("logoFile").toString();
|
|
|
|
}
|
|
|
|
|
2022-09-23 22:06:36 +01:00
|
|
|
for (auto author : authors) {
|
2022-08-12 21:06:20 +01:00
|
|
|
details.authors.append(author.toString());
|
2019-08-04 02:27:53 +01:00
|
|
|
}
|
|
|
|
return details;
|
|
|
|
};
|
|
|
|
QJsonParseError jsonError;
|
|
|
|
QJsonDocument jsonDoc = QJsonDocument::fromJson(contents, &jsonError);
|
|
|
|
// this is the very old format that had just the array
|
2022-09-23 22:06:36 +01:00
|
|
|
if (jsonDoc.isArray()) {
|
2019-08-04 02:27:53 +01:00
|
|
|
return getInfoFromArray(jsonDoc.array());
|
2022-09-23 22:06:36 +01:00
|
|
|
} else if (jsonDoc.isObject()) {
|
2019-08-04 02:27:53 +01:00
|
|
|
auto val = jsonDoc.object().value("modinfoversion");
|
2022-09-23 22:06:36 +01:00
|
|
|
if (val.isUndefined()) {
|
2019-08-04 02:27:53 +01:00
|
|
|
val = jsonDoc.object().value("modListVersion");
|
|
|
|
}
|
2022-06-17 15:21:43 +01:00
|
|
|
|
|
|
|
int version = Json::ensureInteger(val, -1);
|
|
|
|
|
|
|
|
// Some mods set the number with "", so it's a String instead
|
|
|
|
if (version < 0)
|
|
|
|
version = Json::ensureString(val, "").toInt();
|
|
|
|
|
2022-09-23 22:06:36 +01:00
|
|
|
if (version != 2) {
|
2023-01-14 00:15:10 +00:00
|
|
|
qWarning() << QString(R"(The value of 'modListVersion' is "%1" (expected "2")! The file may be corrupted.)").arg(version);
|
|
|
|
qWarning() << "The contents of 'mcmod.info' are as follows:";
|
|
|
|
qWarning() << contents;
|
2019-08-04 02:27:53 +01:00
|
|
|
}
|
2023-01-14 00:15:10 +00:00
|
|
|
|
2019-08-04 02:27:53 +01:00
|
|
|
auto arrVal = jsonDoc.object().value("modlist");
|
2022-09-23 22:06:36 +01:00
|
|
|
if (arrVal.isUndefined()) {
|
2019-08-04 02:27:53 +01:00
|
|
|
arrVal = jsonDoc.object().value("modList");
|
|
|
|
}
|
2022-09-23 22:06:36 +01:00
|
|
|
if (arrVal.isArray()) {
|
2019-08-04 02:27:53 +01:00
|
|
|
return getInfoFromArray(arrVal.toArray());
|
|
|
|
}
|
|
|
|
}
|
2022-08-12 21:06:20 +01:00
|
|
|
return {};
|
2019-08-04 02:27:53 +01:00
|
|
|
}
|
|
|
|
|
2021-04-17 18:33:45 +01:00
|
|
|
// https://github.com/MinecraftForge/Documentation/blob/5ab4ba6cf9abc0ac4c0abd96ad187461aefd72af/docs/gettingstarted/structuring.md
|
2022-08-12 21:06:20 +01:00
|
|
|
ModDetails ReadMCModTOML(QByteArray contents)
|
2021-04-16 21:33:56 +01:00
|
|
|
{
|
2022-08-12 21:06:20 +01:00
|
|
|
ModDetails details;
|
2021-04-16 21:33:56 +01:00
|
|
|
|
2022-09-23 22:06:36 +01:00
|
|
|
toml::table tomlData;
|
|
|
|
#if TOML_EXCEPTIONS
|
|
|
|
try {
|
|
|
|
tomlData = toml::parse(contents.toStdString());
|
|
|
|
} catch (const toml::parse_error& err) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
tomlData = toml::parse(contents.toStdString());
|
|
|
|
if (!tomlData) {
|
2022-08-12 21:06:20 +01:00
|
|
|
return {};
|
2021-04-17 18:33:45 +01:00
|
|
|
}
|
2022-09-23 22:06:36 +01:00
|
|
|
#endif
|
2021-04-17 18:33:45 +01:00
|
|
|
|
|
|
|
// array defined by [[mods]]
|
2022-09-23 22:06:36 +01:00
|
|
|
auto tomlModsArr = tomlData["mods"].as_array();
|
|
|
|
if (!tomlModsArr) {
|
2021-11-06 21:42:10 +00:00
|
|
|
qWarning() << "Corrupted mods.toml? Couldn't find [[mods]] array!";
|
2022-08-12 21:06:20 +01:00
|
|
|
return {};
|
2021-11-06 21:42:10 +00:00
|
|
|
}
|
|
|
|
|
2021-04-17 18:33:45 +01:00
|
|
|
// we only really care about the first element, since multiple mods in one file is not supported by us at the moment
|
2022-09-23 22:06:36 +01:00
|
|
|
auto tomlModsTable0 = tomlModsArr->get(0);
|
|
|
|
if (!tomlModsTable0) {
|
2021-11-06 21:42:10 +00:00
|
|
|
qWarning() << "Corrupted mods.toml? [[mods]] didn't have an element at index 0!";
|
2022-08-12 21:06:20 +01:00
|
|
|
return {};
|
2021-11-06 21:42:10 +00:00
|
|
|
}
|
2022-09-23 22:06:36 +01:00
|
|
|
auto modsTable = tomlModsTable0->as_table();
|
2022-11-13 13:38:37 +00:00
|
|
|
if (!modsTable) {
|
2022-09-23 22:06:36 +01:00
|
|
|
qWarning() << "Corrupted mods.toml? [[mods]] was not a table!";
|
|
|
|
return {};
|
|
|
|
}
|
2021-04-17 18:33:45 +01:00
|
|
|
|
|
|
|
// mandatory properties - always in [[mods]]
|
2022-09-23 22:06:36 +01:00
|
|
|
if (auto modIdDatum = (*modsTable)["modId"].as_string()) {
|
|
|
|
details.mod_id = QString::fromStdString(modIdDatum->get());
|
2021-04-17 18:33:45 +01:00
|
|
|
}
|
2022-09-23 22:06:36 +01:00
|
|
|
if (auto versionDatum = (*modsTable)["version"].as_string()) {
|
|
|
|
details.version = QString::fromStdString(versionDatum->get());
|
2021-04-17 18:33:45 +01:00
|
|
|
}
|
2022-09-23 22:06:36 +01:00
|
|
|
if (auto displayNameDatum = (*modsTable)["displayName"].as_string()) {
|
|
|
|
details.name = QString::fromStdString(displayNameDatum->get());
|
2021-04-17 18:33:45 +01:00
|
|
|
}
|
2022-09-23 22:06:36 +01:00
|
|
|
if (auto descriptionDatum = (*modsTable)["description"].as_string()) {
|
|
|
|
details.description = QString::fromStdString(descriptionDatum->get());
|
2021-04-17 18:33:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// optional properties - can be in the root table or [[mods]]
|
|
|
|
QString authors = "";
|
2022-09-23 22:06:36 +01:00
|
|
|
if (auto authorsDatum = tomlData["authors"].as_string()) {
|
|
|
|
authors = QString::fromStdString(authorsDatum->get());
|
|
|
|
} else if (auto authorsDatum = (*modsTable)["authors"].as_string()) {
|
|
|
|
authors = QString::fromStdString(authorsDatum->get());
|
2021-04-17 18:33:45 +01:00
|
|
|
}
|
2022-09-23 22:06:36 +01:00
|
|
|
if (!authors.isEmpty()) {
|
2022-08-12 21:06:20 +01:00
|
|
|
details.authors.append(authors);
|
2021-04-17 18:33:45 +01:00
|
|
|
}
|
2022-04-16 02:07:35 +01:00
|
|
|
|
2021-04-17 18:33:45 +01:00
|
|
|
QString homeurl = "";
|
2022-09-23 22:06:36 +01:00
|
|
|
if (auto homeurlDatum = tomlData["displayURL"].as_string()) {
|
|
|
|
homeurl = QString::fromStdString(homeurlDatum->get());
|
|
|
|
} else if (auto homeurlDatum = (*modsTable)["displayURL"].as_string()) {
|
|
|
|
homeurl = QString::fromStdString(homeurlDatum->get());
|
2021-04-17 18:33:45 +01:00
|
|
|
}
|
2022-09-23 22:06:36 +01:00
|
|
|
// fix up url.
|
|
|
|
if (!homeurl.isEmpty() && !homeurl.startsWith("http://") && !homeurl.startsWith("https://") && !homeurl.startsWith("ftp://")) {
|
|
|
|
homeurl.prepend("http://");
|
2021-04-16 21:33:56 +01:00
|
|
|
}
|
2022-08-12 21:06:20 +01:00
|
|
|
details.homeurl = homeurl;
|
2021-04-17 18:33:45 +01:00
|
|
|
|
2023-05-05 07:42:42 +01:00
|
|
|
QString issueTrackerURL = "";
|
|
|
|
if (auto issueTrackerURLDatum = tomlData["issueTrackerURL"].as_string()) {
|
|
|
|
issueTrackerURL = QString::fromStdString(issueTrackerURLDatum->get());
|
|
|
|
} else if (auto issueTrackerURLDatum = (*modsTable)["issueTrackerURL"].as_string()) {
|
|
|
|
issueTrackerURL = QString::fromStdString(issueTrackerURLDatum->get());
|
|
|
|
}
|
|
|
|
details.issue_tracker = issueTrackerURL;
|
|
|
|
|
|
|
|
QString license = "";
|
|
|
|
if (auto licenseDatum = tomlData["license"].as_string()) {
|
|
|
|
license = QString::fromStdString(licenseDatum->get());
|
|
|
|
} else if (auto licenseDatum =(*modsTable)["license"].as_string()) {
|
|
|
|
license = QString::fromStdString(licenseDatum->get());
|
|
|
|
}
|
2023-05-05 21:46:38 +01:00
|
|
|
if (!license.isEmpty())
|
|
|
|
details.licenses.append(ModLicense(license));
|
2023-05-05 07:42:42 +01:00
|
|
|
|
|
|
|
QString logoFile = "";
|
|
|
|
if (auto logoFileDatum = tomlData["logoFile"].as_string()) {
|
|
|
|
logoFile = QString::fromStdString(logoFileDatum->get());
|
|
|
|
} else if (auto logoFileDatum =(*modsTable)["logoFile"].as_string()) {
|
|
|
|
logoFile = QString::fromStdString(logoFileDatum->get());
|
|
|
|
}
|
|
|
|
details.icon_file = logoFile;
|
|
|
|
|
2021-04-16 21:33:56 +01:00
|
|
|
return details;
|
|
|
|
}
|
2021-04-17 18:33:45 +01:00
|
|
|
|
2019-08-04 02:27:53 +01:00
|
|
|
// https://fabricmc.net/wiki/documentation:fabric_mod_json
|
2022-08-12 21:06:20 +01:00
|
|
|
ModDetails ReadFabricModInfo(QByteArray contents)
|
2019-08-04 02:27:53 +01:00
|
|
|
{
|
|
|
|
QJsonParseError jsonError;
|
|
|
|
QJsonDocument jsonDoc = QJsonDocument::fromJson(contents, &jsonError);
|
|
|
|
auto object = jsonDoc.object();
|
|
|
|
auto schemaVersion = object.contains("schemaVersion") ? object.value("schemaVersion").toInt(0) : 0;
|
|
|
|
|
2022-08-12 21:06:20 +01:00
|
|
|
ModDetails details;
|
2019-08-04 02:27:53 +01:00
|
|
|
|
2022-08-12 21:06:20 +01:00
|
|
|
details.mod_id = object.value("id").toString();
|
|
|
|
details.version = object.value("version").toString();
|
2019-08-04 02:27:53 +01:00
|
|
|
|
2022-08-12 21:06:20 +01:00
|
|
|
details.name = object.contains("name") ? object.value("name").toString() : details.mod_id;
|
|
|
|
details.description = object.value("description").toString();
|
2019-08-04 02:27:53 +01:00
|
|
|
|
2022-09-23 22:06:36 +01:00
|
|
|
if (schemaVersion >= 1) {
|
2019-08-04 02:27:53 +01:00
|
|
|
QJsonArray authors = object.value("authors").toArray();
|
2022-09-23 22:06:36 +01:00
|
|
|
for (auto author : authors) {
|
|
|
|
if (author.isObject()) {
|
2022-08-12 21:06:20 +01:00
|
|
|
details.authors.append(author.toObject().value("name").toString());
|
2022-09-23 22:06:36 +01:00
|
|
|
} else {
|
2022-08-12 21:06:20 +01:00
|
|
|
details.authors.append(author.toString());
|
2019-08-04 02:27:53 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-23 22:06:36 +01:00
|
|
|
if (object.contains("contact")) {
|
2019-08-04 02:27:53 +01:00
|
|
|
QJsonObject contact = object.value("contact").toObject();
|
|
|
|
|
2022-09-23 22:06:36 +01:00
|
|
|
if (contact.contains("homepage")) {
|
2022-08-12 21:06:20 +01:00
|
|
|
details.homeurl = contact.value("homepage").toString();
|
2019-08-04 02:27:53 +01:00
|
|
|
}
|
2023-05-05 07:42:42 +01:00
|
|
|
if (contact.contains("issues")) {
|
|
|
|
details.issue_tracker = contact.value("issues").toString();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (object.contains("license")) {
|
|
|
|
auto license = object.value("license");
|
|
|
|
if (license.isArray()) {
|
|
|
|
for (auto l : license.toArray()) {
|
|
|
|
if (l.isString()) {
|
|
|
|
details.licenses.append(ModLicense(l.toString()));
|
|
|
|
} else if (l.isObject()) {
|
|
|
|
auto obj = l.toObject();
|
|
|
|
details.licenses.append(ModLicense(obj.value("name").toString(), obj.value("id").toString(),
|
|
|
|
obj.value("url").toString(), obj.value("description").toString()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (license.isString()) {
|
|
|
|
details.licenses.append(ModLicense(license.toString()));
|
|
|
|
} else if (license.isObject()) {
|
|
|
|
auto obj = license.toObject();
|
|
|
|
details.licenses.append(ModLicense(obj.value("name").toString(), obj.value("id").toString(), obj.value("url").toString(),
|
|
|
|
obj.value("description").toString()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (object.contains("icon")) {
|
|
|
|
auto icon = object.value("icon");
|
|
|
|
if (icon.isObject()) {
|
|
|
|
auto obj = icon.toObject();
|
|
|
|
// take the largest icon
|
|
|
|
int largest = 0;
|
|
|
|
for (auto key : obj.keys()) {
|
|
|
|
auto size = key.split('x').first().toInt();
|
|
|
|
if (size > largest) {
|
|
|
|
largest = size;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (largest > 0) {
|
2023-05-05 19:13:36 +01:00
|
|
|
auto key = QString::number(largest) + "x" + QString::number(largest);
|
2023-05-05 07:42:42 +01:00
|
|
|
details.icon_file = obj.value(key).toString();
|
|
|
|
} else { // parsing the sizes failed
|
|
|
|
// take the first
|
2023-05-05 19:13:36 +01:00
|
|
|
for (auto i : obj) {
|
|
|
|
details.icon_file = i.toString();
|
2023-05-05 07:42:42 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (icon.isString()) {
|
|
|
|
details.icon_file = icon.toString();
|
|
|
|
}
|
2019-08-04 02:27:53 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return details;
|
|
|
|
}
|
|
|
|
|
2022-05-17 14:17:20 +01:00
|
|
|
// https://github.com/QuiltMC/rfcs/blob/master/specification/0002-quilt.mod.json.md
|
2022-08-12 21:06:20 +01:00
|
|
|
ModDetails ReadQuiltModInfo(QByteArray contents)
|
2022-05-15 22:00:09 +01:00
|
|
|
{
|
|
|
|
QJsonParseError jsonError;
|
|
|
|
QJsonDocument jsonDoc = QJsonDocument::fromJson(contents, &jsonError);
|
|
|
|
auto object = Json::requireObject(jsonDoc, "quilt.mod.json");
|
|
|
|
auto schemaVersion = Json::ensureInteger(object.value("schema_version"), 0, "Quilt schema_version");
|
|
|
|
|
2022-08-12 21:06:20 +01:00
|
|
|
ModDetails details;
|
2022-05-15 22:00:09 +01:00
|
|
|
|
2022-05-17 14:17:20 +01:00
|
|
|
// https://github.com/QuiltMC/rfcs/blob/be6ba280d785395fefa90a43db48e5bfc1d15eb4/specification/0002-quilt.mod.json.md
|
2022-09-23 22:06:36 +01:00
|
|
|
if (schemaVersion == 1) {
|
2022-05-15 22:00:09 +01:00
|
|
|
auto modInfo = Json::requireObject(object.value("quilt_loader"), "Quilt mod info");
|
|
|
|
|
2022-08-12 21:06:20 +01:00
|
|
|
details.mod_id = Json::requireString(modInfo.value("id"), "Mod ID");
|
|
|
|
details.version = Json::requireString(modInfo.value("version"), "Mod version");
|
2022-05-15 22:00:09 +01:00
|
|
|
|
|
|
|
auto modMetadata = Json::ensureObject(modInfo.value("metadata"));
|
|
|
|
|
2022-08-12 21:06:20 +01:00
|
|
|
details.name = Json::ensureString(modMetadata.value("name"), details.mod_id);
|
|
|
|
details.description = Json::ensureString(modMetadata.value("description"));
|
2022-05-15 22:00:09 +01:00
|
|
|
|
|
|
|
auto modContributors = Json::ensureObject(modMetadata.value("contributors"));
|
|
|
|
|
|
|
|
// We don't really care about the role of a contributor here
|
2022-08-12 21:06:20 +01:00
|
|
|
details.authors += modContributors.keys();
|
2022-05-15 22:00:09 +01:00
|
|
|
|
|
|
|
auto modContact = Json::ensureObject(modMetadata.value("contact"));
|
|
|
|
|
2022-09-23 22:06:36 +01:00
|
|
|
if (modContact.contains("homepage")) {
|
2022-08-12 21:06:20 +01:00
|
|
|
details.homeurl = Json::requireString(modContact.value("homepage"));
|
2022-05-15 22:00:09 +01:00
|
|
|
}
|
2023-05-05 07:42:42 +01:00
|
|
|
if (modContact.contains("issues")) {
|
|
|
|
details.issue_tracker = Json::requireString(modContact.value("issues"));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (modMetadata.contains("license")) {
|
|
|
|
auto license = modMetadata.value("license");
|
|
|
|
if (license.isArray()) {
|
|
|
|
for (auto l : license.toArray()) {
|
|
|
|
if (l.isString()) {
|
|
|
|
details.licenses.append(ModLicense(l.toString()));
|
|
|
|
} else if (l.isObject()) {
|
|
|
|
auto obj = l.toObject();
|
|
|
|
details.licenses.append(ModLicense(obj.value("name").toString(), obj.value("id").toString(),
|
|
|
|
obj.value("url").toString(), obj.value("description").toString()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (license.isString()) {
|
|
|
|
details.licenses.append(ModLicense(license.toString()));
|
|
|
|
} else if (license.isObject()) {
|
|
|
|
auto obj = license.toObject();
|
|
|
|
details.licenses.append(ModLicense(obj.value("name").toString(), obj.value("id").toString(), obj.value("url").toString(),
|
|
|
|
obj.value("description").toString()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (modMetadata.contains("icon")) {
|
|
|
|
auto icon = modMetadata.value("icon");
|
|
|
|
if (icon.isObject()) {
|
|
|
|
auto obj = icon.toObject();
|
|
|
|
// take the largest icon
|
|
|
|
int largest = 0;
|
|
|
|
for (auto key : obj.keys()) {
|
|
|
|
auto size = key.split('x').first().toInt();
|
|
|
|
if (size > largest) {
|
|
|
|
largest = size;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (largest > 0) {
|
2023-05-05 19:13:36 +01:00
|
|
|
auto key = QString::number(largest) + "x" + QString::number(largest);
|
2023-05-05 07:42:42 +01:00
|
|
|
details.icon_file = obj.value(key).toString();
|
|
|
|
} else { // parsing the sizes failed
|
|
|
|
// take the first
|
2023-05-05 19:13:36 +01:00
|
|
|
for (auto i : obj) {
|
|
|
|
details.icon_file = i.toString();
|
2023-05-05 07:42:42 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (icon.isString()) {
|
|
|
|
details.icon_file = icon.toString();
|
|
|
|
}
|
|
|
|
}
|
2022-05-15 22:00:09 +01:00
|
|
|
}
|
|
|
|
return details;
|
|
|
|
}
|
|
|
|
|
2023-07-26 21:56:01 +01:00
|
|
|
ModDetails ReadForgeInfo(QByteArray contents)
|
2019-08-04 02:27:53 +01:00
|
|
|
{
|
2022-08-12 21:06:20 +01:00
|
|
|
ModDetails details;
|
2019-08-04 02:27:53 +01:00
|
|
|
// Read the data
|
2022-08-12 21:06:20 +01:00
|
|
|
details.name = "Minecraft Forge";
|
|
|
|
details.mod_id = "Forge";
|
|
|
|
details.homeurl = "http://www.minecraftforge.net/forum/";
|
2019-08-04 02:27:53 +01:00
|
|
|
INIFile ini;
|
2023-07-26 21:56:01 +01:00
|
|
|
if (!ini.loadFile(contents))
|
2019-08-04 02:27:53 +01:00
|
|
|
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();
|
|
|
|
|
2022-08-12 21:06:20 +01:00
|
|
|
details.version = major + "." + minor + "." + revision + "." + build;
|
2019-08-04 02:27:53 +01:00
|
|
|
return details;
|
|
|
|
}
|
|
|
|
|
2022-08-12 21:06:20 +01:00
|
|
|
ModDetails ReadLiteModInfo(QByteArray contents)
|
2019-08-04 02:27:53 +01:00
|
|
|
{
|
2022-08-12 21:06:20 +01:00
|
|
|
ModDetails details;
|
2019-08-04 02:27:53 +01:00
|
|
|
QJsonParseError jsonError;
|
|
|
|
QJsonDocument jsonDoc = QJsonDocument::fromJson(contents, &jsonError);
|
|
|
|
auto object = jsonDoc.object();
|
2022-09-23 22:06:36 +01:00
|
|
|
if (object.contains("name")) {
|
2022-08-12 21:06:20 +01:00
|
|
|
details.mod_id = details.name = object.value("name").toString();
|
2019-08-04 02:27:53 +01:00
|
|
|
}
|
2022-09-23 22:06:36 +01:00
|
|
|
if (object.contains("version")) {
|
2022-08-12 21:06:20 +01:00
|
|
|
details.version = object.value("version").toString("");
|
2022-09-23 22:06:36 +01:00
|
|
|
} else {
|
2022-08-12 21:06:20 +01:00
|
|
|
details.version = object.value("revision").toString("");
|
2019-08-04 02:27:53 +01:00
|
|
|
}
|
2022-08-12 21:06:20 +01:00
|
|
|
details.mcversion = object.value("mcversion").toString();
|
2019-08-04 02:27:53 +01:00
|
|
|
auto author = object.value("author").toString();
|
2022-09-23 22:06:36 +01:00
|
|
|
if (!author.isEmpty()) {
|
2022-08-12 21:06:20 +01:00
|
|
|
details.authors.append(author);
|
2019-08-04 02:27:53 +01:00
|
|
|
}
|
2022-08-12 21:06:20 +01:00
|
|
|
details.description = object.value("description").toString();
|
|
|
|
details.homeurl = object.value("url").toString();
|
2019-08-04 02:27:53 +01:00
|
|
|
return details;
|
|
|
|
}
|
|
|
|
|
2023-02-11 22:35:08 +00:00
|
|
|
// https://git.sleeping.town/unascribed/NilLoader/src/commit/d7fc87b255fc31019ff90f80d45894927fac6efc/src/main/java/nilloader/api/NilMetadata.java#L64
|
|
|
|
ModDetails ReadNilModInfo(QByteArray contents, QString fname)
|
|
|
|
{
|
|
|
|
ModDetails details;
|
|
|
|
|
2023-02-12 22:23:15 +00:00
|
|
|
QDCSS cssData = QDCSS(contents);
|
|
|
|
auto name = cssData.get("@nilmod.name");
|
|
|
|
auto desc = cssData.get("@nilmod.description");
|
|
|
|
auto authors = cssData.get("@nilmod.authors");
|
|
|
|
|
|
|
|
if (name->has_value()) {
|
|
|
|
details.name = name->value();
|
2023-02-11 22:35:08 +00:00
|
|
|
}
|
2023-02-12 22:23:15 +00:00
|
|
|
if (desc->has_value()) {
|
|
|
|
details.description = desc->value();
|
2023-02-11 22:35:08 +00:00
|
|
|
}
|
2023-02-12 22:23:15 +00:00
|
|
|
if (authors->has_value()) {
|
|
|
|
details.authors.append(authors->value());
|
2023-02-11 22:35:08 +00:00
|
|
|
}
|
2023-02-12 22:23:15 +00:00
|
|
|
details.version = cssData.get("@nilmod.version")->value_or("?");
|
2023-02-11 22:35:08 +00:00
|
|
|
|
|
|
|
details.mod_id = fname.remove(".nilmod.css");
|
|
|
|
|
|
|
|
return details;
|
|
|
|
}
|
|
|
|
|
2022-12-26 21:29:13 +00:00
|
|
|
bool process(Mod& mod, ProcessingLevel level)
|
|
|
|
{
|
2022-12-10 07:52:50 +00:00
|
|
|
switch (mod.type()) {
|
|
|
|
case ResourceType::FOLDER:
|
|
|
|
return processFolder(mod, level);
|
|
|
|
case ResourceType::ZIPFILE:
|
|
|
|
return processZIP(mod, level);
|
|
|
|
case ResourceType::LITEMOD:
|
|
|
|
return processLitemod(mod);
|
|
|
|
default:
|
2022-12-26 21:29:13 +00:00
|
|
|
qWarning() << "Invalid type for mod parse task!";
|
2022-12-10 07:52:50 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2019-08-04 02:27:53 +01:00
|
|
|
|
2022-12-26 21:29:13 +00:00
|
|
|
bool processZIP(Mod& mod, ProcessingLevel level)
|
|
|
|
{
|
2022-12-10 07:52:50 +00:00
|
|
|
ModDetails details;
|
|
|
|
|
|
|
|
QuaZip zip(mod.fileinfo().filePath());
|
2019-08-04 02:27:53 +01:00
|
|
|
if (!zip.open(QuaZip::mdUnzip))
|
2022-12-10 07:52:50 +00:00
|
|
|
return false;
|
2019-08-04 02:27:53 +01:00
|
|
|
|
|
|
|
QuaZipFile file(&zip);
|
|
|
|
|
2022-09-23 22:06:36 +01:00
|
|
|
if (zip.setCurrentFile("META-INF/mods.toml")) {
|
|
|
|
if (!file.open(QIODevice::ReadOnly)) {
|
2021-04-16 21:33:56 +01:00
|
|
|
zip.close();
|
2022-12-10 07:52:50 +00:00
|
|
|
return false;
|
2021-04-16 21:33:56 +01:00
|
|
|
}
|
|
|
|
|
2022-12-10 07:52:50 +00:00
|
|
|
details = ReadMCModTOML(file.readAll());
|
2021-04-16 21:33:56 +01:00
|
|
|
file.close();
|
2022-12-26 21:29:13 +00:00
|
|
|
|
2021-04-17 01:45:55 +01:00
|
|
|
// to replace ${file.jarVersion} with the actual version, as needed
|
2022-12-10 07:52:50 +00:00
|
|
|
if (details.version == "${file.jarVersion}") {
|
2022-09-23 22:06:36 +01:00
|
|
|
if (zip.setCurrentFile("META-INF/MANIFEST.MF")) {
|
|
|
|
if (!file.open(QIODevice::ReadOnly)) {
|
2021-04-17 01:45:55 +01:00
|
|
|
zip.close();
|
2022-12-10 07:52:50 +00:00
|
|
|
return false;
|
2021-04-17 01:45:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// quick and dirty line-by-line parser
|
|
|
|
auto manifestLines = file.readAll().split('\n');
|
|
|
|
QString manifestVersion = "";
|
2022-09-23 22:06:36 +01:00
|
|
|
for (auto& line : manifestLines) {
|
|
|
|
if (QString(line).startsWith("Implementation-Version: ")) {
|
2021-04-17 01:45:55 +01:00
|
|
|
manifestVersion = QString(line).remove("Implementation-Version: ");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// some mods use ${projectversion} in their build.gradle, causing this mess to show up in MANIFEST.MF
|
|
|
|
// also keep with forge's behavior of setting the version to "NONE" if none is found
|
2022-09-23 22:06:36 +01:00
|
|
|
if (manifestVersion.contains("task ':jar' property 'archiveVersion'") || manifestVersion == "") {
|
2021-04-17 01:45:55 +01:00
|
|
|
manifestVersion = "NONE";
|
|
|
|
}
|
|
|
|
|
2022-12-10 07:52:50 +00:00
|
|
|
details.version = manifestVersion;
|
2021-04-17 01:45:55 +01:00
|
|
|
|
|
|
|
file.close();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-16 21:33:56 +01:00
|
|
|
zip.close();
|
2022-12-10 07:52:50 +00:00
|
|
|
mod.setDetails(details);
|
|
|
|
|
|
|
|
return true;
|
2022-09-23 22:06:36 +01:00
|
|
|
} else if (zip.setCurrentFile("mcmod.info")) {
|
|
|
|
if (!file.open(QIODevice::ReadOnly)) {
|
2019-08-04 02:27:53 +01:00
|
|
|
zip.close();
|
2022-12-10 07:52:50 +00:00
|
|
|
return false;
|
2019-08-04 02:27:53 +01:00
|
|
|
}
|
|
|
|
|
2022-12-10 07:52:50 +00:00
|
|
|
details = ReadMCModInfo(file.readAll());
|
2019-08-04 02:27:53 +01:00
|
|
|
file.close();
|
|
|
|
zip.close();
|
2022-12-10 07:52:50 +00:00
|
|
|
|
|
|
|
mod.setDetails(details);
|
|
|
|
return true;
|
2022-09-23 22:06:36 +01:00
|
|
|
} else if (zip.setCurrentFile("quilt.mod.json")) {
|
|
|
|
if (!file.open(QIODevice::ReadOnly)) {
|
2019-08-04 02:27:53 +01:00
|
|
|
zip.close();
|
2022-12-10 07:52:50 +00:00
|
|
|
return false;
|
2019-08-04 02:27:53 +01:00
|
|
|
}
|
|
|
|
|
2022-12-10 07:52:50 +00:00
|
|
|
details = ReadQuiltModInfo(file.readAll());
|
2019-08-04 02:27:53 +01:00
|
|
|
file.close();
|
|
|
|
zip.close();
|
2022-12-10 07:52:50 +00:00
|
|
|
|
|
|
|
mod.setDetails(details);
|
|
|
|
return true;
|
2022-09-23 22:06:36 +01:00
|
|
|
} else if (zip.setCurrentFile("fabric.mod.json")) {
|
|
|
|
if (!file.open(QIODevice::ReadOnly)) {
|
2022-05-15 22:00:09 +01:00
|
|
|
zip.close();
|
2022-12-10 07:52:50 +00:00
|
|
|
return false;
|
2022-05-15 22:00:09 +01:00
|
|
|
}
|
|
|
|
|
2022-12-10 07:52:50 +00:00
|
|
|
details = ReadFabricModInfo(file.readAll());
|
2022-05-15 22:00:09 +01:00
|
|
|
file.close();
|
|
|
|
zip.close();
|
2022-12-10 07:52:50 +00:00
|
|
|
|
|
|
|
mod.setDetails(details);
|
|
|
|
return true;
|
2022-09-23 22:06:36 +01:00
|
|
|
} else if (zip.setCurrentFile("forgeversion.properties")) {
|
|
|
|
if (!file.open(QIODevice::ReadOnly)) {
|
2019-08-04 02:27:53 +01:00
|
|
|
zip.close();
|
2022-12-10 07:52:50 +00:00
|
|
|
return false;
|
2019-08-04 02:27:53 +01:00
|
|
|
}
|
|
|
|
|
2023-07-26 21:56:01 +01:00
|
|
|
details = ReadForgeInfo(file.readAll());
|
2019-08-04 02:27:53 +01:00
|
|
|
file.close();
|
|
|
|
zip.close();
|
2022-12-10 07:52:50 +00:00
|
|
|
|
|
|
|
mod.setDetails(details);
|
|
|
|
return true;
|
2023-02-11 22:35:08 +00:00
|
|
|
} else if (zip.setCurrentFile("META-INF/nil/mappings.json")) {
|
|
|
|
// nilloader uses the filename of the metadata file for the modid, so we can't know the exact filename
|
|
|
|
// thankfully, there is a good file to use as a canary so we don't look for nil meta all the time
|
|
|
|
|
2023-02-22 18:20:13 +00:00
|
|
|
QString foundNilMeta;
|
2023-02-11 22:35:08 +00:00
|
|
|
for (auto& fname : zip.getFileNameList()) {
|
2023-02-16 17:57:35 +00:00
|
|
|
// nilmods can shade nilloader to be able to run as a standalone agent - which includes nilloader's own meta file
|
|
|
|
if (fname.endsWith(".nilmod.css") && fname != "nilloader.nilmod.css") {
|
2023-02-22 18:20:13 +00:00
|
|
|
foundNilMeta = fname;
|
|
|
|
break;
|
2023-02-11 22:35:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-22 18:20:13 +00:00
|
|
|
if (zip.setCurrentFile(foundNilMeta)) {
|
2023-02-11 22:35:08 +00:00
|
|
|
if (!file.open(QIODevice::ReadOnly)) {
|
|
|
|
zip.close();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-02-22 18:20:13 +00:00
|
|
|
details = ReadNilModInfo(file.readAll(), foundNilMeta);
|
2023-02-11 22:35:08 +00:00
|
|
|
file.close();
|
|
|
|
zip.close();
|
|
|
|
|
|
|
|
mod.setDetails(details);
|
|
|
|
return true;
|
|
|
|
}
|
2019-08-04 02:27:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
zip.close();
|
2022-12-26 21:29:13 +00:00
|
|
|
return false; // no valid mod found in archive
|
2019-08-04 02:27:53 +01:00
|
|
|
}
|
|
|
|
|
2022-12-26 21:29:13 +00:00
|
|
|
bool processFolder(Mod& mod, ProcessingLevel level)
|
|
|
|
{
|
2022-12-10 07:52:50 +00:00
|
|
|
ModDetails details;
|
|
|
|
|
|
|
|
QFileInfo mcmod_info(FS::PathCombine(mod.fileinfo().filePath(), "mcmod.info"));
|
|
|
|
if (mcmod_info.exists() && mcmod_info.isFile()) {
|
2019-08-04 02:27:53 +01:00
|
|
|
QFile mcmod(mcmod_info.filePath());
|
|
|
|
if (!mcmod.open(QIODevice::ReadOnly))
|
2022-12-10 07:52:50 +00:00
|
|
|
return false;
|
2019-08-04 02:27:53 +01:00
|
|
|
auto data = mcmod.readAll();
|
|
|
|
if (data.isEmpty() || data.isNull())
|
2022-12-10 07:52:50 +00:00
|
|
|
return false;
|
|
|
|
details = ReadMCModInfo(data);
|
|
|
|
|
|
|
|
mod.setDetails(details);
|
|
|
|
return true;
|
2019-08-04 02:27:53 +01:00
|
|
|
}
|
2022-12-10 07:52:50 +00:00
|
|
|
|
2022-12-26 21:29:13 +00:00
|
|
|
return false; // no valid mcmod.info file found
|
2019-08-04 02:27:53 +01:00
|
|
|
}
|
|
|
|
|
2022-12-26 21:29:13 +00:00
|
|
|
bool processLitemod(Mod& mod, ProcessingLevel level)
|
|
|
|
{
|
2022-12-10 07:52:50 +00:00
|
|
|
ModDetails details;
|
2022-12-26 21:29:13 +00:00
|
|
|
|
2022-12-10 07:52:50 +00:00
|
|
|
QuaZip zip(mod.fileinfo().filePath());
|
2019-08-04 02:27:53 +01:00
|
|
|
if (!zip.open(QuaZip::mdUnzip))
|
2022-12-10 07:52:50 +00:00
|
|
|
return false;
|
2019-08-04 02:27:53 +01:00
|
|
|
|
|
|
|
QuaZipFile file(&zip);
|
|
|
|
|
2022-09-23 22:06:36 +01:00
|
|
|
if (zip.setCurrentFile("litemod.json")) {
|
|
|
|
if (!file.open(QIODevice::ReadOnly)) {
|
2019-08-04 02:27:53 +01:00
|
|
|
zip.close();
|
2022-12-10 07:52:50 +00:00
|
|
|
return false;
|
2019-08-04 02:27:53 +01:00
|
|
|
}
|
|
|
|
|
2022-12-10 07:52:50 +00:00
|
|
|
details = ReadLiteModInfo(file.readAll());
|
2019-08-04 02:27:53 +01:00
|
|
|
file.close();
|
2022-12-10 07:52:50 +00:00
|
|
|
|
|
|
|
mod.setDetails(details);
|
|
|
|
return true;
|
2019-08-04 02:27:53 +01:00
|
|
|
}
|
|
|
|
zip.close();
|
2022-12-10 07:52:50 +00:00
|
|
|
|
2022-12-26 21:29:13 +00:00
|
|
|
return false; // no valid litemod.json found in archive
|
2019-08-04 02:27:53 +01:00
|
|
|
}
|
|
|
|
|
2022-12-10 07:52:50 +00:00
|
|
|
/** Checks whether a file is valid as a mod or not. */
|
2022-12-26 21:29:13 +00:00
|
|
|
bool validate(QFileInfo file)
|
|
|
|
{
|
2022-12-10 07:52:50 +00:00
|
|
|
Mod mod{ file };
|
|
|
|
return ModUtils::process(mod, ProcessingLevel::BasicInfoOnly) && mod.valid();
|
|
|
|
}
|
|
|
|
|
2023-05-05 07:42:42 +01:00
|
|
|
bool processIconPNG(const Mod& mod, QByteArray&& raw_data)
|
|
|
|
{
|
|
|
|
auto img = QImage::fromData(raw_data);
|
|
|
|
if (!img.isNull()) {
|
|
|
|
mod.setIcon(img);
|
|
|
|
} else {
|
|
|
|
qWarning() << "Failed to parse mod logo:" << mod.iconPath() << "from" << mod.name();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool loadIconFile(const Mod& mod) {
|
|
|
|
if (mod.iconPath().isEmpty()) {
|
|
|
|
qWarning() << "No Iconfile set, be sure to parse the mod first";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto png_invalid = [&mod]() {
|
|
|
|
qWarning() << "Mod at" << mod.fileinfo().filePath() << "does not have a valid icon";
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
|
|
|
|
switch (mod.type()) {
|
|
|
|
case ResourceType::FOLDER:
|
|
|
|
{
|
|
|
|
QFileInfo icon_info(FS::PathCombine(mod.fileinfo().filePath(), mod.iconPath()));
|
|
|
|
if (icon_info.exists() && icon_info.isFile()) {
|
|
|
|
QFile icon(icon_info.filePath());
|
|
|
|
if (!icon.open(QIODevice::ReadOnly))
|
|
|
|
return false;
|
|
|
|
auto data = icon.readAll();
|
|
|
|
|
|
|
|
bool icon_result = ModUtils::processIconPNG(mod, std::move(data));
|
|
|
|
|
|
|
|
icon.close();
|
|
|
|
|
|
|
|
if (!icon_result) {
|
|
|
|
return png_invalid(); // icon invalid
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case ResourceType::ZIPFILE:
|
|
|
|
{
|
|
|
|
QuaZip zip(mod.fileinfo().filePath());
|
|
|
|
if (!zip.open(QuaZip::mdUnzip))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
QuaZipFile file(&zip);
|
|
|
|
|
|
|
|
if (zip.setCurrentFile(mod.iconPath())) {
|
|
|
|
if (!file.open(QIODevice::ReadOnly)) {
|
|
|
|
qCritical() << "Failed to open file in zip.";
|
|
|
|
zip.close();
|
|
|
|
return png_invalid();
|
|
|
|
}
|
|
|
|
|
|
|
|
auto data = file.readAll();
|
|
|
|
|
|
|
|
bool icon_result = ModUtils::processIconPNG(mod, std::move(data));
|
|
|
|
|
|
|
|
file.close();
|
|
|
|
if (!icon_result) {
|
|
|
|
return png_invalid(); // icon png invalid
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return png_invalid(); // could not set icon as current file.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case ResourceType::LITEMOD:
|
|
|
|
{
|
|
|
|
return false; // can lightmods even have icons?
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
qWarning() << "Invalid type for mod, can not load icon.";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-10 07:52:50 +00:00
|
|
|
} // namespace ModUtils
|
|
|
|
|
|
|
|
LocalModParseTask::LocalModParseTask(int token, ResourceType type, const QFileInfo& modFile)
|
|
|
|
: Task(nullptr, false), m_token(token), m_type(type), m_modFile(modFile), m_result(new Result())
|
|
|
|
{}
|
|
|
|
|
2022-08-12 21:09:56 +01:00
|
|
|
bool LocalModParseTask::abort()
|
|
|
|
{
|
2022-09-03 17:25:05 +01:00
|
|
|
m_aborted.store(true);
|
2022-08-12 21:09:56 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
refactor: move general code from mod model to its own model
This aims to continue decoupling other types of resources (e.g. resource
packs, shader packs, etc) from mods, so that we don't have to
continuously watch our backs for changes to one of them affecting the
others.
To do so, this creates a more general list model for resources, based on
the mods one, that allows you to extend it with functionality for other
resources.
I had to do some template and preprocessor stuff to get around the
QObject limitation of not allowing templated classes, so that's sadge :c
On the other hand, I tried cleaning up most general-purpose code in the
mod model, and added some documentation, because it looks nice :D
Signed-off-by: flow <flowlnlnln@gmail.com>
2022-08-09 05:58:22 +01:00
|
|
|
void LocalModParseTask::executeTask()
|
2022-12-26 21:29:13 +00:00
|
|
|
{
|
2022-12-10 07:52:50 +00:00
|
|
|
Mod mod{ m_modFile };
|
|
|
|
ModUtils::process(mod, ModUtils::ProcessingLevel::Full);
|
|
|
|
|
|
|
|
m_result->details = mod.details();
|
2022-08-12 21:09:56 +01:00
|
|
|
|
|
|
|
if (m_aborted)
|
2022-09-03 17:25:05 +01:00
|
|
|
emit finished();
|
2022-08-12 21:09:56 +01:00
|
|
|
else
|
|
|
|
emitSucceeded();
|
2019-08-04 02:27:53 +01:00
|
|
|
}
|