summaryrefslogtreecommitdiffstats
path: root/logic
diff options
context:
space:
mode:
Diffstat (limited to 'logic')
-rw-r--r--logic/ForgeInstaller.cpp49
-rw-r--r--logic/OneSixFTBInstance.cpp2
-rw-r--r--logic/OneSixInstance.cpp30
-rw-r--r--logic/OneSixInstance.h8
-rw-r--r--logic/OneSixInstance_p.h1
-rw-r--r--logic/OneSixLibrary.cpp14
-rw-r--r--logic/OneSixLibrary.h2
-rw-r--r--logic/OneSixUpdate.cpp4
-rw-r--r--logic/OneSixVersion.cpp5
-rw-r--r--logic/OneSixVersion.h6
-rw-r--r--logic/OneSixVersionBuilder.cpp1002
-rw-r--r--logic/OneSixVersionBuilder.h18
12 files changed, 814 insertions, 327 deletions
diff --git a/logic/ForgeInstaller.cpp b/logic/ForgeInstaller.cpp
index 863c3dfd..c0340e95 100644
--- a/logic/ForgeInstaller.cpp
+++ b/logic/ForgeInstaller.cpp
@@ -21,6 +21,8 @@
#include <quazipfile.h>
#include <pathutils.h>
#include <QStringList>
+#include <QRegularExpression>
+#include <QRegularExpressionMatch>
#include "MultiMC.h"
#include "OneSixInstance.h"
@@ -146,36 +148,71 @@ bool ForgeInstaller::add(OneSixInstance *to)
QJsonObject libObj = lib->toJson();
bool found = false;
+ bool equals = false;
// find an entry that matches this one
- for (auto tolib : to->getFullVersion()->libraries)
+ for (auto tolib : to->getNonCustomVersion()->libraries)
{
if (tolib->name() != libName)
continue;
found = true;
+ if (tolib->toJson() == libObj)
+ {
+ equals = true;
+ }
// replace lib
libObj.insert("insert", QString("apply"));
break;
}
+ if (equals)
+ {
+ continue;
+ }
if (!found)
{
// add lib
QJsonObject insertObj;
- insertObj.insert("before", to->getFullVersion()->libraries.at(sliding_insert_window)->rawName());
+ insertObj.insert(
+ "before",
+ to->getFullVersion()->libraries.at(sliding_insert_window + 1)->rawName());
libObj.insert("insert", insertObj);
sliding_insert_window++;
}
- librariesPlus.append(libObj);
+ librariesPlus.prepend(libObj);
}
obj.insert("+libraries", librariesPlus);
obj.insert("mainClass", m_forge_version->mainClass);
- obj.insert("minecraftArguments", m_forge_version->minecraftArguments);
- obj.insert("processArguments", m_forge_version->processArguments);
+ QString args = m_forge_version->minecraftArguments;
+ QStringList tweakers;
+ {
+ QRegularExpression expression("--tweakClass ([a-zA-Z0-9\\.]*)");
+ QRegularExpressionMatch match = expression.match(args);
+ while (match.hasMatch())
+ {
+ tweakers.append(match.captured(1));
+ args.remove(match.capturedStart(), match.capturedLength());
+ match = expression.match(args);
+ }
+ }
+ if (!args.isEmpty() && args != to->getNonCustomVersion()->minecraftArguments)
+ {
+ obj.insert("minecraftArguments", args);
+ }
+ if (!tweakers.isEmpty())
+ {
+ obj.insert("+tweakers", QJsonArray::fromStringList(tweakers));
+ }
+ if (!m_forge_version->processArguments.isEmpty() &&
+ m_forge_version->processArguments != to->getNonCustomVersion()->processArguments)
+ {
+ obj.insert("processArguments", m_forge_version->processArguments);
+ }
}
QFile file(filename(to->instanceRoot()));
if (!file.open(QFile::WriteOnly))
{
- QLOG_ERROR() << "Error opening" << file.fileName() << "for reading:" << file.errorString();
+ QLOG_ERROR() << "Error opening" << file.fileName()
+ << "for reading:" << file.errorString();
return false;
}
file.write(QJsonDocument(obj).toJson());
diff --git a/logic/OneSixFTBInstance.cpp b/logic/OneSixFTBInstance.cpp
index 71bc1470..54bfbe2b 100644
--- a/logic/OneSixFTBInstance.cpp
+++ b/logic/OneSixFTBInstance.cpp
@@ -55,7 +55,7 @@ slots:
setStatus(tr("Installing Forge..."));
QString forgePath = entry->getFullPath();
ForgeInstaller forge(forgePath, forgeVersion->universal_url);
- if (!instance->reloadFullVersion())
+ if (!instance->reloadVersion())
{
emitFailed(tr("Couldn't load the version config"));
return;
diff --git a/logic/OneSixInstance.cpp b/logic/OneSixInstance.cpp
index c43a5187..19d5e112 100644
--- a/logic/OneSixInstance.cpp
+++ b/logic/OneSixInstance.cpp
@@ -35,13 +35,14 @@ OneSixInstance::OneSixInstance(const QString &rootDir, SettingsObject *settings,
d->m_settings->registerSetting("IntendedVersion", "");
d->m_settings->registerSetting("ShouldUpdate", false);
d->version.reset(new OneSixVersion(this, this));
+ d->nonCustomVersion.reset(new OneSixVersion(this, this));
if (QDir(instanceRoot()).exists("version.json"))
{
- reloadFullVersion();
+ reloadVersion();
}
else
{
- clearFullVersion();
+ clearVersion();
}
}
@@ -139,6 +140,10 @@ QStringList OneSixInstance::processMinecraftArgs(MojangAccountPtr account)
I_D(OneSixInstance);
auto version = d->version;
QString args_pattern = version->minecraftArguments;
+ for (auto tweaker : version->tweakers)
+ {
+ args_pattern += " --tweakClass " + tweaker;
+ }
QMap<QString, QString> token_mapping;
// yggdrasil!
@@ -287,7 +292,7 @@ bool OneSixInstance::setIntendedVersionId(QString version)
settings().set("IntendedVersion", version);
setShouldUpdate(true);
QFile::remove(PathCombine(instanceRoot(), "version.json"));
- clearFullVersion();
+ clearVersion();
return true;
}
@@ -323,28 +328,39 @@ QString OneSixInstance::currentVersionId() const
return intendedVersionId();
}
-bool OneSixInstance::reloadFullVersion(QWidget *widgetParent)
+bool OneSixInstance::reloadVersion(QWidget *widgetParent)
{
I_D(OneSixInstance);
bool ret = d->version->reload(widgetParent);
+ if (ret)
+ {
+ ret = d->nonCustomVersion->reload(widgetParent, true);
+ }
emit versionReloaded();
return ret;
}
-void OneSixInstance::clearFullVersion()
+void OneSixInstance::clearVersion()
{
I_D(OneSixInstance);
d->version->clear();
+ d->nonCustomVersion->clear();
emit versionReloaded();
}
-std::shared_ptr<OneSixVersion> OneSixInstance::getFullVersion()
+std::shared_ptr<OneSixVersion> OneSixInstance::getFullVersion() const
{
- I_D(OneSixInstance);
+ I_D(const OneSixInstance);
return d->version;
}
+std::shared_ptr<OneSixVersion> OneSixInstance::getNonCustomVersion() const
+{
+ I_D(const OneSixInstance);
+ return d->nonCustomVersion;
+}
+
QString OneSixInstance::defaultBaseJar() const
{
return "versions/" + intendedVersionId() + "/" + intendedVersionId() + ".jar";
diff --git a/logic/OneSixInstance.h b/logic/OneSixInstance.h
index 94577828..ee64e886 100644
--- a/logic/OneSixInstance.h
+++ b/logic/OneSixInstance.h
@@ -52,11 +52,13 @@ public:
virtual QDialog *createModEditDialog(QWidget *parent) override;
/// reload the full version json files. return true on success!
- bool reloadFullVersion(QWidget *widgetParent = 0);
+ bool reloadVersion(QWidget *widgetParent = 0);
/// clears all version information in preparation for an update
- void clearFullVersion();
+ void clearVersion();
/// get the current full version info
- std::shared_ptr<OneSixVersion> getFullVersion();
+ std::shared_ptr<OneSixVersion> getFullVersion() const;
+ /// gets the current version info, excluding custom.json
+ std::shared_ptr<OneSixVersion> getNonCustomVersion() const;
/// is the current version original, or custom?
virtual bool versionIsCustom() override;
diff --git a/logic/OneSixInstance_p.h b/logic/OneSixInstance_p.h
index a2b70ac9..6dc74e50 100644
--- a/logic/OneSixInstance_p.h
+++ b/logic/OneSixInstance_p.h
@@ -22,6 +22,7 @@
struct OneSixInstancePrivate : public BaseInstancePrivate
{
std::shared_ptr<OneSixVersion> version;
+ std::shared_ptr<OneSixVersion> nonCustomVersion;
std::shared_ptr<ModList> loader_mod_list;
std::shared_ptr<ModList> resource_pack_list;
};
diff --git a/logic/OneSixLibrary.cpp b/logic/OneSixLibrary.cpp
index 4d892d53..2b1f0600 100644
--- a/logic/OneSixLibrary.cpp
+++ b/logic/OneSixLibrary.cpp
@@ -93,6 +93,10 @@ void OneSixLibrary::addNative(OpSys os, const QString &suffix)
m_is_native = true;
m_native_suffixes[os] = suffix;
}
+void OneSixLibrary::clearSuffixes()
+{
+ m_native_suffixes.clear();
+}
void OneSixLibrary::setRules(QList<std::shared_ptr<Rule>> rules)
{
m_rules = rules;
@@ -176,7 +180,7 @@ bool OneSixLibrary::extractTo(QString target_dir)
cooked_storage.replace("${arch}", "32");
QString origin = PathCombine("libraries", cooked_storage);
QString target_dir_cooked = PathCombine(target_dir, "32");
- if(!ensureFolderPathExists(target_dir_cooked))
+ if (!ensureFolderPathExists(target_dir_cooked))
{
QLOG_ERROR() << "Couldn't create folder " + target_dir_cooked;
return false;
@@ -191,7 +195,7 @@ bool OneSixLibrary::extractTo(QString target_dir)
cooked_storage.replace("${arch}", "64");
origin = PathCombine("libraries", cooked_storage);
target_dir_cooked = PathCombine(target_dir, "64");
- if(!ensureFolderPathExists(target_dir_cooked))
+ if (!ensureFolderPathExists(target_dir_cooked))
{
QLOG_ERROR() << "Couldn't create folder " + target_dir_cooked;
return false;
@@ -205,7 +209,7 @@ bool OneSixLibrary::extractTo(QString target_dir)
}
else
{
- if(!ensureFolderPathExists(target_dir))
+ if (!ensureFolderPathExists(target_dir))
{
QLOG_ERROR() << "Couldn't create folder " + target_dir;
return false;
@@ -230,8 +234,10 @@ QJsonObject OneSixLibrary::toJson()
libRoot.insert("MMC-hint", m_hint);
if (m_base_url != "http://" + URLConstants::AWS_DOWNLOAD_LIBRARIES &&
m_base_url != "https://" + URLConstants::AWS_DOWNLOAD_LIBRARIES &&
- m_base_url != "https://" + URLConstants::LIBRARY_BASE)
+ m_base_url != "https://" + URLConstants::LIBRARY_BASE && !m_base_url.isEmpty())
+ {
libRoot.insert("url", m_base_url);
+ }
if (isNative() && m_native_suffixes.size())
{
QJsonObject nativeList;
diff --git a/logic/OneSixLibrary.h b/logic/OneSixLibrary.h
index 52f89969..5384015a 100644
--- a/logic/OneSixLibrary.h
+++ b/logic/OneSixLibrary.h
@@ -107,6 +107,8 @@ public:
void setIsNative();
/// Attach a name suffix to the specified OS native
void addNative(OpSys os, const QString &suffix);
+ /// Clears all suffixes
+ void clearSuffixes();
/// Set the load rules
void setRules(QList<std::shared_ptr<Rule>> rules);
diff --git a/logic/OneSixUpdate.cpp b/logic/OneSixUpdate.cpp
index f51b6c23..c056bc4c 100644
--- a/logic/OneSixUpdate.cpp
+++ b/logic/OneSixUpdate.cpp
@@ -137,7 +137,7 @@ void OneSixUpdate::versionFileFinished()
{
finfo.remove();
}
- inst->reloadFullVersion();
+ inst->reloadVersion();
jarlibStart();
}
@@ -235,7 +235,7 @@ void OneSixUpdate::jarlibStart()
setStatus(tr("Getting the library files from Mojang..."));
QLOG_INFO() << m_inst->name() << ": downloading libraries";
OneSixInstance *inst = (OneSixInstance *)m_inst;
- bool successful = inst->reloadFullVersion();
+ bool successful = inst->reloadVersion();
if (!successful)
{
emitFailed("Failed to load the version description file. It might be "
diff --git a/logic/OneSixVersion.cpp b/logic/OneSixVersion.cpp
index 3db3bd77..1b6bc9cb 100644
--- a/logic/OneSixVersion.cpp
+++ b/logic/OneSixVersion.cpp
@@ -25,9 +25,9 @@ OneSixVersion::OneSixVersion(OneSixInstance *instance, QObject *parent)
clear();
}
-bool OneSixVersion::reload(QWidget *widgetParent)
+bool OneSixVersion::reload(QWidget *widgetParent, const bool excludeCustom)
{
- return OneSixVersionBuilder::build(this, m_instance, widgetParent);
+ return OneSixVersionBuilder::build(this, m_instance, widgetParent, excludeCustom);
}
void OneSixVersion::clear()
@@ -42,6 +42,7 @@ void OneSixVersion::clear()
minimumLauncherVersion = 0xDEADBEAF;
mainClass.clear();
libraries.clear();
+ tweakers.clear();
}
void OneSixVersion::dump() const
diff --git a/logic/OneSixVersion.h b/logic/OneSixVersion.h
index 00afd010..767e05db 100644
--- a/logic/OneSixVersion.h
+++ b/logic/OneSixVersion.h
@@ -38,7 +38,7 @@ public:
virtual int columnCount(const QModelIndex &parent) const;
virtual Qt::ItemFlags flags(const QModelIndex &index) const;
- bool reload(QWidget *widgetParent);
+ bool reload(QWidget *widgetParent, const bool excludeCustom = false);
void clear();
void dump() const;
@@ -79,6 +79,10 @@ public:
*/
int minimumLauncherVersion = 0xDEADBEEF;
/**
+ * A list of all tweaker classes
+ */
+ QStringList tweakers;
+ /**
* The main class to load first
*/
QString mainClass;
diff --git a/logic/OneSixVersionBuilder.cpp b/logic/OneSixVersionBuilder.cpp
index 85f55a52..c031a94a 100644
--- a/logic/OneSixVersionBuilder.cpp
+++ b/logic/OneSixVersionBuilder.cpp
@@ -31,416 +31,844 @@
#include "OneSixRule.h"
#include "logger/QsLog.h"
-OneSixVersionBuilder::OneSixVersionBuilder()
-{
-
-}
-
-bool OneSixVersionBuilder::build(OneSixVersion *version, OneSixInstance *instance, QWidget *widgetParent)
+struct VersionFile
{
- OneSixVersionBuilder builder;
- builder.m_version = version;
- builder.m_instance = instance;
- builder.m_widgetParent = widgetParent;
- return builder.build();
-}
-
-bool OneSixVersionBuilder::read(OneSixVersion *version, const QJsonObject &obj)
-{
- OneSixVersionBuilder builder;
- builder.m_version = version;
- builder.m_instance = 0;
- builder.m_widgetParent = 0;
- return builder.read(obj);
-}
-
-bool OneSixVersionBuilder::build()
-{
- m_version->clear();
-
- QDir root(m_instance->instanceRoot());
- QDir patches(root.absoluteFilePath("patches/"));
-
- // version.json -> patches/*.json -> custom.json
+ int order;
+ QString id;
+ QString mainClass;
+ QString overwriteMinecraftArguments;
+ QString addMinecraftArguments;
+ QString removeMinecraftArguments;
+ QString processArguments;
+ QString type;
+ QString releaseTime;
+ QString time;
+ QString assets;
+ int minimumLauncherVersion = -1;
+
+ bool shouldOverwriteTweakers = false;
+ QStringList overwriteTweakers;
+ QStringList addTweakers;
+ QStringList removeTweakers;
+
+ struct Library
+ {
+ QString name;
+ QString url;
+ QString hint;
+ QString absoluteUrl;
+ bool applyExcludes = false;
+ QStringList excludes;
+ bool applyNatives = false;
+ QList<QPair<OpSys, QString>> natives;
+ bool applyRules = false;
+ QList<std::shared_ptr<Rule>> rules;
- // version.json
+ // user for '+' libraries
+ enum InsertType
+ {
+ Apply,
+ Append,
+ Prepend,
+ InsertBefore,
+ InsertAfter,
+ Replace
+ };
+ InsertType insertType;
+ QString insertData;
+ };
+ bool shouldOverwriteLibs = false;
+ QList<Library> overwriteLibs;
+ QList<Library> addLibs;
+ QList<QString> removeLibs;
+
+ static Library fromLibraryJson(const QJsonObject &libObj, const QString &filename,
+ bool &isError)
{
- QLOG_INFO() << "Reading version.json";
- QJsonObject obj;
- if (!read(QFileInfo(root.absoluteFilePath("version.json")), &obj))
+ isError = true;
+ Library out;
+ if (!libObj.contains("name"))
{
- return false;
+ QLOG_ERROR() << filename << "contains a library that doesn't have a 'name' field";
+ return out;
}
- if (!apply(obj))
- {
- return false;
- }
- }
+ out.name = libObj.value("name").toString();
- // patches/
- {
- // load all, put into map for ordering, apply in the right order
+ auto readString = [libObj, filename](const QString &key, QString &variable)
+ {
+ if (libObj.contains(key))
+ {
+ QJsonValue val = libObj.value(key);
+ if (!val.isString())
+ {
+ QLOG_WARN() << key << "is not a string in" << filename << "(skipping)";
+ }
+ else
+ {
+ variable = val.toString();
+ }
+ }
+ };
- QMap<int, QJsonObject> objects;
- for (auto info : patches.entryInfoList(QStringList() << "*.json", QDir::Files))
+ readString("url", out.url);
+ readString("MMC-hint", out.hint);
+ readString("MMC-absulute_url", out.absoluteUrl);
+ readString("MMC-absoluteUrl", out.absoluteUrl);
+ if (libObj.contains("extract"))
{
- QLOG_INFO() << "Reading" << info.fileName();
- QJsonObject obj;
- if (!read(info, &obj))
+ if (!libObj.value("extract").isObject())
{
- return false;
+ QLOG_ERROR()
+ << filename
+ << "contains a library with an 'extract' field that's not an object";
+ return out;
}
- if (!obj.contains("order") || !obj.value("order").isDouble())
+ QJsonObject extractObj = libObj.value("extract").toObject();
+ if (!extractObj.contains("exclude") || !extractObj.value("exclude").isArray())
{
- QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("Missing or invalid 'order' in %1").arg(info.absoluteFilePath()));
- return false;
+ QLOG_ERROR() << filename
+ << "contains a library with an invalid 'extract' field";
+ return out;
}
- objects.insert(obj.value("order").toDouble(), obj);
- }
- for (auto object : objects.values())
- {
- qDebug() << "Applying object with order" << objects.key(object);
- if (!apply(object))
+ out.applyExcludes = true;
+ QJsonArray excludeArray = extractObj.value("exclude").toArray();
+ for (auto excludeVal : excludeArray)
{
- return false;
+ if (!excludeVal.isString())
+ {
+ QLOG_WARN() << filename << "contains a library that contains an 'extract' "
+ "field that contains an invalid 'exclude' entry "
+ "(skipping)";
+ }
+ else
+ {
+ out.excludes.append(excludeVal.toString());
+ }
}
}
- }
-
- // custom.json
- {
- if (QFile::exists(root.absoluteFilePath("custom.json")))
+ if (libObj.contains("natives"))
{
- QLOG_INFO() << "Reading custom.json";
- QJsonObject obj;
- if (!read(QFileInfo(root.absoluteFilePath("custom.json")), &obj))
+ if (!libObj.value("natives").isObject())
{
- return false;
+ QLOG_ERROR()
+ << filename
+ << "contains a library with a 'natives' field that's not an object";
+ return out;
}
- if (!apply(obj))
+ out.applyNatives = true;
+ QJsonObject nativesObj = libObj.value("natives").toObject();
+ for (auto it = nativesObj.begin(); it != nativesObj.end(); ++it)
{
- return false;
+ if (!it.value().isString())
+ {
+ QLOG_WARN() << filename << "contains an invalid native (skipping)";
+ }
+ OpSys opSys = OpSys_fromString(it.key());
+ if (opSys != Os_Other)
+ {
+ out.natives.append(qMakePair(opSys, it.value().toString()));
+ }
}
}
+ if (libObj.contains("rules"))
+ {
+ out.applyRules = true;
+ out.rules = rulesFromJsonV4(libObj);
+ }
+ isError = false;
+ return out;
}
-
- return true;
-}
-
-bool OneSixVersionBuilder::read(const QJsonObject &obj)
-{
- m_version->clear();
-
- return apply(obj);
-}
-
-void applyString(const QJsonObject &obj, const QString &key, QString &out, const bool onlyOverride = true)
-{
- if (obj.contains(key) && obj.value(key).isString())
- {
- out = obj.value(key).toString();
- }
- else if (!onlyOverride)
+ static VersionFile fromJson(const QJsonDocument &doc, const QString &filename,
+ const bool requireOrder, bool &isError)
{
- if (obj.contains("+" + key) && obj.value("+" + key).isString())
+ VersionFile out;
+ isError = true;
+ if (doc.isEmpty() || doc.isNull())
{
- out += obj.value("+" + key).toString();
+ QLOG_ERROR() << filename << "is empty or null";
+ return out;
}
- else if (obj.contains("-" + key) && obj.value("-" + key).isString())
+ if (!doc.isObject())
{
- out.remove(obj.value("-" + key).toString());
+ QLOG_ERROR() << "The root of" << filename << "is not an object";
+ return out;
}
- }
-}
-void applyString(const QJsonObject &obj, const QString &key, std::shared_ptr<OneSixLibrary> lib, void(OneSixLibrary::*func)(const QString &val))
-{
- if (obj.contains(key) && obj.value(key).isString())
- {
- (lib.get()->*func)(obj.value(key).toString());
- }
-}
-bool OneSixVersionBuilder::apply(const QJsonObject &object)
-{
- applyString(object, "id", m_version->id);
- applyString(object, "mainClass", m_version->mainClass);
- applyString(object, "minecraftArguments", m_version->minecraftArguments, false);
- applyString(object, "processArguments", m_version->processArguments, false);
- if (m_version->minecraftArguments.isEmpty())
- {
- const QString toCompare = m_version->processArguments.toLower();
- if (toCompare == "legacy")
+
+ QJsonObject root = doc.object();
+
+ if (requireOrder)
{
- m_version->minecraftArguments = " ${auth_player_name} ${auth_session}";
+ if (root.contains("order"))
+ {
+ if (root.value("order").isDouble())
+ {
+ out.order = root.value("order").toDouble();
+ }
+ else
+ {
+ QLOG_ERROR() << "'order' field contains an invalid value in" << filename;
+ return out;
+ }
+ }
+ else
+ {
+ QLOG_ERROR() << filename << "doesn't contain an order field";
+ }
}
- else if (toCompare == "username_session")
+
+ auto readString = [root, filename](const QString &key, QString &variable)
+ {
+ if (root.contains(key))
+ {
+ QJsonValue val = root.value(key);
+ if (!val.isString())
+ {
+ QLOG_WARN() << key << "is not a string in" << filename << "(skipping)";
+ }
+ else
+ {
+ variable = val.toString();
+ }
+ }
+ };
+
+ readString("id", out.id);
+ readString("mainClass", out.mainClass);
+ readString("processArguments", out.processArguments);
+ readString("minecraftArguments", out.overwriteMinecraftArguments);
+ readString("+minecraftArguments", out.addMinecraftArguments);
+ readString("-minecraftArguments", out.removeMinecraftArguments);
+ readString("type", out.type);
+ readString("releaseTime", out.releaseTime);
+ readString("time", out.time);
+ readString("assets", out.assets);
+ if (root.contains("minimumLauncherVersion"))
{
- m_version->minecraftArguments = "--username ${auth_player_name} --session ${auth_session}";
+ QJsonValue val = root.value("minimumLauncherVersion");
+ if (!val.isDouble())
+ {
+ QLOG_WARN() << "minimumLauncherVersion is not an int in" << filename
+ << "(skipping)";
+ }
+ else
+ {
+ out.minimumLauncherVersion = val.toDouble();
+ }
}
- else if (toCompare == "username_session_version")
+
+ if (root.contains("tweakers"))
{
- m_version->minecraftArguments = "--username ${auth_player_name} --session ${auth_session} --version ${profile_name}";
+ QJsonValue tweakersVal = root.value("tweakers");
+ if (!tweakersVal.isArray())
+ {
+ QLOG_ERROR() << filename << "contains a 'tweakers' field, but it's not an array";
+ return out;
+ }
+ out.shouldOverwriteTweakers = true;
+ QJsonArray tweakers = root.value("tweakers").toArray();
+ for (auto tweakerVal : tweakers)
+ {
+ if (!tweakerVal.isString())
+ {
+ QLOG_ERROR() << filename << "contains a 'tweakers' field entry that's not a string";
+ return out;
+ }
+ out.overwriteTweakers.append(tweakerVal.toString());
+ }
}
- }
- applyString(object, "type", m_version->type);
- applyString(object, "releaseTime", m_version->releaseTime);
- applyString(object, "time", m_version->time);
- applyString(object, "assets", m_version->assets);
- {
- if (m_version->assets.isEmpty())
+ if (root.contains("+tweakers"))
{
- m_version->assets = "legacy";
+ QJsonValue tweakersVal = root.value("+tweakers");
+ if (!tweakersVal.isArray())
+ {
+ QLOG_ERROR() << filename << "contains a '+tweakers' field, but it's not an array";
+ return out;
+ }
+ QJsonArray tweakers = root.value("+tweakers").toArray();
+ for (auto tweakerVal : tweakers)
+ {
+ if (!tweakerVal.isString())
+ {
+ QLOG_ERROR() << filename << "contains a '+tweakers' field entry that's not a string";
+ return out;
+ }
+ out.addTweakers.append(tweakerVal.toString());
+ }
}
- }
- if (object.contains("minimumLauncherVersion"))
- {
- auto minLauncherVersionVal = object.value("minimumLauncherVersion");
- if (minLauncherVersionVal.isDouble())
+ if (root.contains("-tweakers"))
{
- m_version->minimumLauncherVersion = minLauncherVersionVal.toDouble();
+ QJsonValue tweakersVal = root.value("-tweakers");
+ if (!tweakersVal.isArray())
+ {
+ QLOG_ERROR() << filename << "contains a '-tweakers' field, but it's not an array";
+ return out;
+ }
+ out.shouldOverwriteTweakers = true;
+ QJsonArray tweakers = root.value("-tweakers").toArray();
+ for (auto tweakerVal : tweakers)
+ {
+ if (!tweakerVal.isString())
+ {
+ QLOG_ERROR() << filename << "contains a '-tweakers' field entry that's not a string";
+ return out;
+ }
+ out.removeTweakers.append(tweakerVal.toString());
+ }
}
- }
- // libraries
- if (object.contains("libraries"))
- {
- m_version->libraries.clear();
- auto librariesValue = object.value("libraries");
- if (!librariesValue.isArray())
+ if (root.contains("libraries"))
{
- QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("One json files contains a libraries field, but it's not an array"));
- return false;
+ out.shouldOverwriteLibs = true;
+ QJsonValue librariesVal = root.value("libraries");
+ if (!librariesVal.isArray())
+ {
+ QLOG_ERROR() << filename
+ << "contains a 'libraries' field, but its not an array";
+ return out;
+ }
+ QJsonArray librariesArray = librariesVal.toArray();
+ for (auto libVal : librariesArray)
+ {
+ if (!libVal.isObject())
+ {
+ QLOG_ERROR() << filename << "contains a library that's not an object";
+ return out;
+ }
+ QJsonObject libObj = libVal.toObject();
+ bool error;
+ Library lib = fromLibraryJson(libObj, filename, error);
+ if (error)
+ {
+ QLOG_ERROR() << "Error while reading a library entry in" << filename;
+ return out;
+ }
+ out.overwriteLibs.append(lib);
+ }
}
- auto array = librariesValue.toArray();
- for (auto libVal : array)
+ if (root.contains("+libraries"))
{
- if (libVal.isObject())
+ QJsonValue librariesVal = root.value("+libraries");
+ if (!librariesVal.isArray())
{
- if (!applyLibrary(libVal.toObject(), Override))
+ QLOG_ERROR() << filename
+ << "contains a '+libraries' field, but its not an array";
+ return out;
+ }
+ QJsonArray librariesArray = librariesVal.toArray();
+ for (auto libVal : librariesArray)
+ {
+ if (!libVal.isObject())
+ {
+ QLOG_ERROR() << filename << "contains a library that's not an object";
+ return out;
+ }
+ QJsonObject libObj = libVal.toObject();
+ bool error;
+ Library lib = fromLibraryJson(libObj, filename, error);
+ if (error)
+ {
+ QLOG_ERROR() << "Error while reading a library entry in" << filename;
+ return out;
+ }
+ if (!libObj.contains("insert"))
{
- return false;
+ QLOG_ERROR() << "Missing 'insert' field in '+libraries' field in"
+ << filename;
+ return out;
}
+ QJsonValue insertVal = libObj.value("insert");
+ QString insertString;
+ {
+ if (insertVal.isString())
+ {
+ insertString = insertVal.toString();
+ }
+ else if (insertVal.isObject())
+ {
+ QJsonObject insertObj = insertVal.toObject();
+ if (insertObj.isEmpty())
+ {
+ QLOG_ERROR() << "One library has an empty insert object in"
+ << filename;
+ return out;
+ }
+ insertString = insertObj.keys().first();
+ lib.insertData = insertObj.value(insertString).toString();
+ }
+ }
+ if (insertString == "apply")
+ {
+ lib.insertType = Library::Apply;
+ }
+ else if (insertString == "append")
+ {
+ lib.insertType = Library::Append;
+ }
+ else if (insertString == "prepend")
+ {
+ lib.insertType = Library::Prepend;
+ }
+ else if (insertString == "before")
+ {
+ lib.insertType = Library::InsertBefore;
+ }
+ else if (insertString == "after")
+ {
+ lib.insertType = Library::InsertAfter;
+ }
+ else if (insertString == "replace")
+ {
+ lib.insertType = Library::Replace;
+ }
+ else
+ {
+ QLOG_ERROR() << "A '+' library in" << filename
+ << "contains an invalid insert type";
+ return out;
+ }
+ out.addLibs.append(lib);
}
-
}
+ if (root.contains("-libraries"))
+ {
+ QJsonValue librariesVal = root.value("-libraries");
+ if (!librariesVal.isArray())
+ {
+ QLOG_ERROR() << filename
+ << "contains a '-libraries' field, but its not an array";
+ return out;
+ }
+ QJsonArray librariesArray = librariesVal.toArray();
+ for (auto libVal : librariesArray)
+ {
+ if (!libVal.isObject())
+ {
+ QLOG_ERROR() << filename << "contains a library that's not an object";
+ return out;
+ }
+ QJsonObject libObj = libVal.toObject();
+ if (!libObj.contains("name"))
+ {
+ QLOG_ERROR() << filename << "contains a library without a name";
+ return out;
+ }
+ if (!libObj.value("name").isString())
+ {
+ QLOG_ERROR() << filename
+ << "contains a library without a valid 'name' field";
+ return out;
+ }
+ out.removeLibs.append(libObj.value("name").toString());
+ }
+ }
+
+ isError = false;
+ return out;
}
- // +libraries
- if (object.contains("+libraries"))
+ static std::shared_ptr<OneSixLibrary> createLibrary(const Library &lib)
{
- auto librariesValue = object.value("+libraries");
- if (!librariesValue.isArray())
+ std::shared_ptr<OneSixLibrary> out(new OneSixLibrary(lib.name));
+ out->setBaseUrl(lib.url);
+ out->setHint(lib.hint);
+ out->setAbsoluteUrl(lib.absoluteUrl);
+ out->extract_excludes = lib.excludes;
+ for (auto native : lib.natives)
{
- QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("One json files contains a libraries field, but it's not an array"));
- return false;
+ out->addNative(native.first, native.second);
}
- for (auto libVal : librariesValue.toArray())
+ out->setRules(lib.rules);
+ out->finalize();
+ return out;
+ }
+ int findLibrary(QList<std::shared_ptr<OneSixLibrary>> haystack, const QString &needle)
+ {
+ for (int i = 0; i < haystack.size(); ++i)
{
- if (libVal.isObject())
+ if (QRegExp(needle, Qt::CaseSensitive, QRegExp::WildcardUnix)
+ .indexIn(haystack.at(i)->rawName()) != -1)
{
- applyLibrary(libVal.toObject(), Add);
+ return i;
}
-
}
+ return -1;
}
-
- // -libraries
- if (object.contains("-libraries"))
+ void applyTo(OneSixVersion *version, bool &isError)
{
- auto librariesValue = object.value("-libraries");
- if (!librariesValue.isArray())
+ isError = true;
+ if (!id.isNull())
{
- QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("One json files contains a libraries field, but it's not an array"));
- return false;
+ version->id = id;
+ }
+ if (!mainClass.isNull())
+ {
+ version->mainClass = mainClass;
+ }
+ if (!processArguments.isNull())
+ {
+ version->processArguments = processArguments;
+ }
+ if (!type.isNull())
+ {
+ version->type = type;
+ }
+ if (!releaseTime.isNull())
+ {
+ version->releaseTime = releaseTime;
+ }
+ if (!time.isNull())
+ {
+ version->time = time;
+ }
+ if (!assets.isNull())
+ {
+ version->assets = assets;
+ }
+ if (minimumLauncherVersion >= 0)
+ {
+ version->minimumLauncherVersion = minimumLauncherVersion;
+ }
+ if (!overwriteMinecraftArguments.isNull())
+ {
+ version->minecraftArguments = overwriteMinecraftArguments;
+ }
+ if (!addMinecraftArguments.isNull())
+ {
+ version->minecraftArguments += addMinecraftArguments;
}
- for (auto libVal : librariesValue.toArray())
+ if (!removeMinecraftArguments.isNull())
{
- if (libVal.isObject())
+ version->minecraftArguments.remove(removeMinecraftArguments);
+ }
+ if (shouldOverwriteTweakers)
+ {
+ version->tweakers = overwriteTweakers;
+ }
+ for (auto tweaker : addTweakers)
+ {
+ version->tweakers += tweaker;
+ }
+ for (auto tweaker : removeTweakers)
+ {
+ version->tweakers.removeAll(tweaker);
+ }
+ if (shouldOverwriteLibs)
+ {
+ version->libraries.clear();
+ for (auto lib : overwriteLibs)
+ {
+ version->libraries.append(createLibrary(lib));
+ }
+ }
+ for (auto lib : addLibs)
+ {
+ switch (lib.insertType)
+ {
+ case Library::Apply:
+ {
+
+ int index = findLibrary(version->libraries, lib.name);
+ if (index >= 0)
+ {
+ auto library = version->libraries[index];
+ if (!lib.url.isNull())
+ {
+ library->setBaseUrl(lib.url);
+ }
+ if (!lib.hint.isNull())
+ {
+ library->setHint(lib.hint);
+ }
+ if (!lib.absoluteUrl.isNull())
+ {
+ library->setAbsoluteUrl(lib.absoluteUrl);
+ }
+ if (lib.applyExcludes)
+ {
+ library->extract_excludes = lib.excludes;
+ }
+ if (lib.applyNatives)
+ {
+ library->clearSuffixes();
+ for (auto native : lib.natives)
+ {
+ library->addNative(native.first, native.second);
+ }
+ }
+ if (lib.applyRules)
+ {
+ library->setRules(lib.rules);
+ }
+ library->finalize();
+ }
+ else
+ {
+ QLOG_WARN() << "Couldn't find" << lib.insertData << "(skipping)";
+ }
+ break;
+ }
+ case Library::Append:
+ version->libraries.append(createLibrary(lib));
+ break;
+ case Library::Prepend:
+ version->libraries.prepend(createLibrary(lib));
+ break;
+ case Library::InsertBefore:
{
- applyLibrary(libVal.toObject(), Remove);
+
+ int index = findLibrary(version->libraries, lib.insertData);
+ if (index >= 0)
+ {
+ version->libraries.insert(index, createLibrary(lib));
+ }
+ else
+ {
+ QLOG_WARN() << "Couldn't find" << lib.insertData << "(skipping)";
+ }
+ break;
}
+ case Library::InsertAfter:
+ {
+ int index = findLibrary(version->libraries, lib.insertData);
+ if (index >= 0)
+ {
+ version->libraries.insert(index + 1, createLibrary(lib));
+ }
+ else
+ {
+ QLOG_WARN() << "Couldn't find" << lib.insertData << "(skipping)";
+ }
+ break;
+ }
+ case Library::Replace:
+ {
+ int index = findLibrary(version->libraries, lib.insertData);
+ if (index >= 0)
+ {
+ version->libraries.replace(index, createLibrary(lib));
+ }
+ else
+ {
+ QLOG_WARN() << "Couldn't find" << lib.insertData << "(skipping)";
+ }
+ break;
+ }
+ }
+ }
+ for (auto lib : removeLibs)
+ {
+ int index = findLibrary(version->libraries, lib);
+ if (index >= 0)
+ {
+ version->libraries.removeAt(index);
+ }
+ else
+ {
+ QLOG_WARN() << "Couldn't find" << lib << "(skipping)";
+ }
}
+
+ isError = false;
}
+};
- return true;
+OneSixVersionBuilder::OneSixVersionBuilder()
+{
}
-int findLibrary(QList<std::shared_ptr<OneSixLibrary> > haystack, const QString &needle)
+bool OneSixVersionBuilder::build(OneSixVersion *version, OneSixInstance *instance,
+ QWidget *widgetParent, const bool excludeCustom)
{
- for (int i = 0; i < haystack.size(); ++i)
- {
- if (QRegExp(needle, Qt::CaseSensitive, QRegExp::WildcardUnix).indexIn(haystack.at(i)->rawName()) != -1)
- {
- return i;
- }
- }
- return -1;
+ OneSixVersionBuilder builder;
+ builder.m_version = version;
+ builder.m_instance = instance;
+ builder.m_widgetParent = widgetParent;
+ return builder.build(excludeCustom);
}
-bool OneSixVersionBuilder::applyLibrary(const QJsonObject &lib, const OneSixVersionBuilder::Type type)
+bool OneSixVersionBuilder::read(OneSixVersion *version, const QJsonObject &obj)
{
- // Library name
- auto nameVal = lib.value("name");
- if (!nameVal.isString())
- {
- return false;
- }
- auto name = nameVal.toString();
+ OneSixVersionBuilder builder;
+ builder.m_version = version;
+ builder.m_instance = 0;
+ builder.m_widgetParent = 0;
+ return builder.read(obj);
+}
- if (type == Remove)
+bool OneSixVersionBuilder::build(const bool excludeCustom)
+{
+ m_version->clear();
+
+ QDir root(m_instance->instanceRoot());
+ QDir patches(root.absoluteFilePath("patches/"));
+
+ // version.json -> patches/*.json -> custom.json
+
+ // version.json
{
- int index = findLibrary(m_version->libraries, name);
- if (index >= 0)
+ QLOG_INFO() << "Reading version.json";
+ VersionFile file;
+ if (!read(QFileInfo(root.absoluteFilePath("version.json")), false, &file))
+ {
+ return false;
+ }
+ bool isError = false;
+ file.applyTo(m_version, isError);
+ if (isError)
{
- m_version->libraries.removeAt(index);
+ QMessageBox::critical(
+ m_widgetParent, QObject::tr("Error"),
+ QObject::tr(
+ "Error while applying %1. Please check MultiMC-0.log for more info.")
+ .arg(root.absoluteFilePath("version.json")));
+ return false;
}
- return true;
}
- if (type == Add && !lib.contains("insert"))
+ // patches/
{
- return false;
- }
-
- std::shared_ptr<OneSixLibrary> library;
+ // load all, put into map for ordering, apply in the right order
- if (lib.value("insert").toString() != "apply" && type == Add)
- {
- QMutableListIterator<std::shared_ptr<OneSixLibrary> > it(m_version->libraries);
- while (it.hasNext())
+ QMap<int, QPair<QString, VersionFile>> files;
+ for (auto info : patches.entryInfoList(QStringList() << "*.json", QDir::Files))
{
- if (it.next()->rawName() == name)
+ QLOG_INFO() << "Reading" << info.fileName();
+ VersionFile file;
+ if (!read(info, true, &file))
{
- it.remove();
+ return false;
}
+ files.insert(file.order, qMakePair(info.fileName(), file));
}
- }
-
- if (lib.value("insert").toString() == "apply" && type == Add)
- {
- library = m_version->libraries[findLibrary(m_version->libraries, name)];
- }
- else
- {
- library.reset(new OneSixLibrary(nameVal.toString()));
- }
-
- applyString(lib, "url", library, &OneSixLibrary::setBaseUrl);
- applyString(lib, "MMC-hint", library, &OneSixLibrary::setHint);
- applyString(lib, "MMC-absulute_url", library, &OneSixLibrary::setAbsoluteUrl);
- applyString(lib, "MMC-absoluteUrl", library, &OneSixLibrary::setAbsoluteUrl);
-
- auto extractVal = lib.value("extract");
- if (extractVal.isObject())
- {
- QStringList excludes;
- auto extractObj = extractVal.toObject();
- auto excludesVal = extractObj.value("exclude");
- if (excludesVal.isArray())
+ for (auto order : files.keys())
{
- auto excludesList = excludesVal.toArray();
- for (auto excludeVal : excludesList)
+ QLOG_DEBUG() << "Applying file with order" << order;
+ auto filePair = files[order];
+ bool isError = false;
+ filePair.second.applyTo(m_version, isError);
+ if (isError)
{
- if (excludeVal.isString())
- {
- excludes.append(excludeVal.toString());
- }
+ QMessageBox::critical(
+ m_widgetParent, QObject::tr("Error"),
+ QObject::tr(
+ "Error while applying %1. Please check MultiMC-0.log for more info.")
+ .arg(filePair.first));
+ return false;
}
- library->extract_excludes = excludes;
}
}
- auto nativesVal = lib.value("natives");
- if (nativesVal.isObject())
+ // custom.json
+ if (!excludeCustom)
{
- library->setIsNative();
- auto nativesObj = nativesVal.toObject();
- for (auto it = nativesObj.begin(); it != nativesObj.end(); ++it)
+ if (QFile::exists(root.absoluteFilePath("custom.json")))
{
- auto osType = OpSys_fromString(it.key());
- if (osType == Os_Other)
+ QLOG_INFO() << "Reading custom.json";
+ VersionFile file;
+ if (!read(QFileInfo(root.absoluteFilePath("custom.json")), false, &file))
{
- continue;
+ return false;
}
- if (!it.value().isString())
+ bool isError = false;
+ file.applyTo(m_version, isError);
+ if (isError)
{
- continue;
+ QMessageBox::critical(
+ m_widgetParent, QObject::tr("Error"),
+ QObject::tr(
+ "Error while applying %1. Please check MultiMC-0.log for more info.")
+ .arg(root.absoluteFilePath("custom.json")));
+ return false;
}
- library->addNative(osType, it.value().toString());
}
}
- if (lib.contains("rules"))
+ // some final touches
{
- library->setRules(rulesFromJsonV4(lib));
- }
- library->finalize();
- if (type == Override)
- {
- m_version->libraries.append(library);
- }
- else if (lib.value("insert").toString() != "apply")
- {
- if (lib.value("insert").toString() == "append")
- {
- m_version->libraries.append(library);
- }
- else if (lib.value("insert").toString() == "prepend")
+ if (m_version->assets.isEmpty())
{
- m_version->libraries.prepend(library);
+ m_version->assets = "legacy";
}
- else if (lib.value("insert").isObject())
+ if (m_version->minecraftArguments.isEmpty())
{
- QJsonObject insertObj = lib.value("insert").toObject();
- if (insertObj.isEmpty())
+ QString toCompare = m_version->processArguments.toLower();
+ if (toCompare == "legacy")
{
- QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("'insert' object empty"));
- return false;
+ m_version->minecraftArguments = " ${auth_player_name} ${auth_session}";
}
- const QString key = insertObj.keys().first();
- const QString value = insertObj.value(key).toString();
- const int index = findLibrary(m_version->libraries, value);
- if (index >= 0)
+ else if (toCompare == "username_session")
{
- if (key == "before")
- {
- m_version->libraries.insert(index, library);
- }
- else if (key == "after")
- {
- m_version->libraries.insert(index + 1, library);
- }
- else
- {
- QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("Invalid value for 'insert': %1").arg(lib.value("insert").toString()));
- return false;
- }
+ m_version->minecraftArguments =
+ "--username ${auth_player_name} --session ${auth_session}";
+ }
+ else if (toCompare == "username_session_version")
+ {
+ m_version->minecraftArguments = "--username ${auth_player_name} "
+ "--session ${auth_session} "
+ "--version ${profile_name}";
}
}
- else
- {
- QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("Invalid value for 'insert': %1").arg(lib.value("insert").toString()));
- return false;
- }
}
+
+ return true;
+}
+
+bool OneSixVersionBuilder::read(const QJsonObject &obj)
+{
+ m_version->clear();
+
+ bool isError = false;
+ VersionFile file = VersionFile::fromJson(QJsonDocument(obj), QString(), false, isError);
+ if (isError)
+ {
+ QMessageBox::critical(
+ m_widgetParent, QObject::tr("Error"),
+ QObject::tr("Error while reading. Please check MultiMC-0.log for more info."));
+ return false;
+ }
+ file.applyTo(m_version, isError);
+ if (isError)
+ {
+ QMessageBox::critical(
+ m_widgetParent, QObject::tr("Error"),
+ QObject::tr("Error while applying. Please check MultiMC-0.log for more info."));
+ return false;
+ }
+
return true;
}
-bool OneSixVersionBuilder::read(const QFileInfo &fileInfo, QJsonObject *out)
+bool OneSixVersionBuilder::read(const QFileInfo &fileInfo, const bool requireOrder,
+ VersionFile *out)
{
QFile file(fileInfo.absoluteFilePath());
if (!file.open(QFile::ReadOnly))
{
- QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("Unable to open %1: %2").arg(file.fileName(), file.errorString()));
+ QMessageBox::critical(
+ m_widgetParent, QObject::tr("Error"),
+ QObject::tr("Unable to open %1: %2").arg(file.fileName(), file.errorString()));
return false;
}
QJsonParseError error;
QJsonDocument doc = QJsonDocument::fromJson(file.readAll(), &error);
if (error.error != QJsonParseError::NoError)
{
- QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("Unable to parse %1: %2 at %3").arg(file.fileName(), error.errorString()).arg(error.offset));
+ QMessageBox::critical(m_widgetParent, QObject::tr("Error"),
+ QObject::tr("Unable to parse %1: %2 at %3")
+ .arg(file.fileName(), error.errorString())
+ .arg(error.offset));
return false;
}
- *out = doc.object();
+ bool isError = false;
+ *out = VersionFile::fromJson(doc, file.fileName(), requireOrder, isError);
+ if (isError)
+ {
+ QMessageBox::critical(
+ m_widgetParent, QObject::tr("Error"),
+ QObject::tr("Error while reading %1. Please check MultiMC-0.log for more info.")
+ .arg(file.fileName()));
+ ;
+ }
return true;
}
diff --git a/logic/OneSixVersionBuilder.h b/logic/OneSixVersionBuilder.h
index a3ba331c..ac8c13bf 100644
--- a/logic/OneSixVersionBuilder.h
+++ b/logic/OneSixVersionBuilder.h
@@ -22,12 +22,13 @@ class OneSixInstance;
class QWidget;
class QJsonObject;
class QFileInfo;
+class VersionFile;
class OneSixVersionBuilder
{
OneSixVersionBuilder();
public:
- static bool build(OneSixVersion *version, OneSixInstance *instance, QWidget *widgetParent);
+ static bool build(OneSixVersion *version, OneSixInstance *instance, QWidget *widgetParent, const bool excludeCustom);
static bool read(OneSixVersion *version, const QJsonObject &obj);
private:
@@ -35,19 +36,8 @@ private:
OneSixInstance *m_instance;
QWidget *m_widgetParent;
- enum Type
- {
- Override,
- Add,
- Remove
- };
-
- bool build();
+ bool build(const bool excludeCustom);
bool read(const QJsonObject &obj);
- void clear();
- bool apply(const QJsonObject &object);
- bool applyLibrary(const QJsonObject &lib, const Type type);
-
- bool read(const QFileInfo &fileInfo, QJsonObject *out);
+ bool read(const QFileInfo &fileInfo, const bool requireOrder, VersionFile *out);
};