From 7c24bcc83476dcbdd7f7acbe14ecef4398962689 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sat, 1 Mar 2014 23:06:47 +0100 Subject: Reorganize the version-related code. --- CMakeLists.txt | 21 +- gui/dialogs/OneSixModEditDialog.cpp | 4 +- gui/dialogs/OneSixModEditDialog.h | 2 +- logic/BaseInstaller.cpp | 2 +- logic/ForgeInstaller.cpp | 6 +- logic/ForgeInstaller.h | 4 +- logic/LiteLoaderInstaller.cpp | 2 +- logic/OneSixFTBInstance.cpp | 2 +- logic/OneSixInstance.cpp | 12 +- logic/OneSixInstance.h | 8 +- logic/OneSixInstance_p.h | 6 +- logic/OneSixUpdate.cpp | 8 +- logic/OneSixVersion.cpp | 221 ---------- logic/OneSixVersion.h | 137 ------- logic/OneSixVersionBuilder.cpp | 797 +----------------------------------- logic/OneSixVersionBuilder.h | 15 +- logic/VersionFile.cpp | 702 +++++++++++++++++++++++++++++++ logic/VersionFile.h | 90 ++++ logic/VersionFinal.cpp | 221 ++++++++++ logic/VersionFinal.h | 137 +++++++ 20 files changed, 1214 insertions(+), 1183 deletions(-) delete mode 100644 logic/OneSixVersion.cpp delete mode 100644 logic/OneSixVersion.h create mode 100644 logic/VersionFile.cpp create mode 100644 logic/VersionFile.h create mode 100644 logic/VersionFinal.cpp create mode 100644 logic/VersionFinal.h diff --git a/CMakeLists.txt b/CMakeLists.txt index f070684d..229179cb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -414,31 +414,38 @@ logic/LegacyInstance.cpp logic/LegacyInstance_p.h logic/LegacyUpdate.h logic/LegacyUpdate.cpp + logic/LegacyForge.h logic/LegacyForge.cpp # OneSix instances logic/OneSixUpdate.h logic/OneSixUpdate.cpp -logic/OneSixVersion.h -logic/OneSixVersion.cpp +logic/OneSixInstance.h +logic/OneSixInstance.cpp +logic/OneSixInstance_p.h + +# OneSix version json infrastructure +logic/OneSixVersionBuilder.h +logic/OneSixVersionBuilder.cpp +logic/VersionFile.h +logic/VersionFile.cpp +logic/VersionFinal.h +logic/VersionFinal.cpp logic/OneSixLibrary.h logic/OneSixLibrary.cpp logic/OneSixRule.h logic/OneSixRule.cpp logic/OpSys.h logic/OpSys.cpp + +# Mod installers logic/BaseInstaller.h logic/BaseInstaller.cpp logic/ForgeInstaller.h logic/ForgeInstaller.cpp logic/LiteLoaderInstaller.h logic/LiteLoaderInstaller.cpp -logic/OneSixInstance.h -logic/OneSixInstance.cpp -logic/OneSixInstance_p.h -logic/OneSixVersionBuilder.h -logic/OneSixVersionBuilder.cpp # Nostalgia logic/NostalgiaInstance.h diff --git a/gui/dialogs/OneSixModEditDialog.cpp b/gui/dialogs/OneSixModEditDialog.cpp index fe621a9a..67210217 100644 --- a/gui/dialogs/OneSixModEditDialog.cpp +++ b/gui/dialogs/OneSixModEditDialog.cpp @@ -34,7 +34,7 @@ #include "gui/dialogs/ProgressDialog.h" #include "logic/ModList.h" -#include "logic/OneSixVersion.h" +#include "logic/VersionFinal.h" #include "logic/EnabledItemFilter.h" #include "logic/lists/ForgeVersionList.h" #include "logic/lists/LiteLoaderVersionList.h" @@ -353,7 +353,7 @@ QMap OneSixModEditDialog::getExistingOrder() const QMap order; // default { - for (OneSixVersion::VersionFile file : m_version->versionFiles) + for (VersionFinal::VersionFile file : m_version->versionFiles) { if (file.id.startsWith("org.multimc.")) { diff --git a/gui/dialogs/OneSixModEditDialog.h b/gui/dialogs/OneSixModEditDialog.h index f44b336b..1f3f9f67 100644 --- a/gui/dialogs/OneSixModEditDialog.h +++ b/gui/dialogs/OneSixModEditDialog.h @@ -60,7 +60,7 @@ protected: private: Ui::OneSixModEditDialog *ui; - std::shared_ptr m_version; + std::shared_ptr m_version; std::shared_ptr m_mods; std::shared_ptr m_resourcepacks; EnabledItemFilter *main_model; diff --git a/logic/BaseInstaller.cpp b/logic/BaseInstaller.cpp index 92aa0c92..669fd0ac 100644 --- a/logic/BaseInstaller.cpp +++ b/logic/BaseInstaller.cpp @@ -17,7 +17,7 @@ #include -#include "OneSixVersion.h" +#include "VersionFinal.h" #include "OneSixLibrary.h" #include "OneSixInstance.h" diff --git a/logic/ForgeInstaller.cpp b/logic/ForgeInstaller.cpp index 3e18d17f..6f238c21 100644 --- a/logic/ForgeInstaller.cpp +++ b/logic/ForgeInstaller.cpp @@ -14,7 +14,7 @@ */ #include "ForgeInstaller.h" -#include "OneSixVersion.h" +#include "VersionFinal.h" #include "OneSixLibrary.h" #include "net/HttpMetaCache.h" #include @@ -33,7 +33,7 @@ ForgeInstaller::ForgeInstaller(QString filename, QString universal_url) { - std::shared_ptr newVersion; + std::shared_ptr newVersion; m_universal_url = universal_url; QuaZip zip(filename); @@ -66,7 +66,7 @@ ForgeInstaller::ForgeInstaller(QString filename, QString universal_url) // read the forge version info { - newVersion = OneSixVersion::fromJson(versionInfoVal.toObject()); + newVersion = VersionFinal::fromJson(versionInfoVal.toObject()); if (!newVersion) return; } diff --git a/logic/ForgeInstaller.h b/logic/ForgeInstaller.h index c5052092..df029f38 100644 --- a/logic/ForgeInstaller.h +++ b/logic/ForgeInstaller.h @@ -20,7 +20,7 @@ #include #include -class OneSixVersion; +class VersionFinal; class ForgeInstaller : public BaseInstaller { @@ -33,7 +33,7 @@ public: private: // the version, read from the installer - std::shared_ptr m_forge_version; + std::shared_ptr m_forge_version; QString internalPath; QString finalPath; QString realVersionId; diff --git a/logic/LiteLoaderInstaller.cpp b/logic/LiteLoaderInstaller.cpp index 126027fb..44400e8a 100644 --- a/logic/LiteLoaderInstaller.cpp +++ b/logic/LiteLoaderInstaller.cpp @@ -20,7 +20,7 @@ #include "logger/QsLog.h" -#include "OneSixVersion.h" +#include "VersionFinal.h" #include "OneSixLibrary.h" #include "OneSixInstance.h" diff --git a/logic/OneSixFTBInstance.cpp b/logic/OneSixFTBInstance.cpp index 8b8465b9..cdb3f53e 100644 --- a/logic/OneSixFTBInstance.cpp +++ b/logic/OneSixFTBInstance.cpp @@ -1,6 +1,6 @@ #include "OneSixFTBInstance.h" -#include "OneSixVersion.h" +#include "VersionFinal.h" #include "OneSixLibrary.h" #include "tasks/SequentialTask.h" #include "ForgeInstaller.h" diff --git a/logic/OneSixInstance.cpp b/logic/OneSixInstance.cpp index 10411c56..bd5d559e 100644 --- a/logic/OneSixInstance.cpp +++ b/logic/OneSixInstance.cpp @@ -19,7 +19,7 @@ #include "OneSixInstance_p.h" #include "OneSixUpdate.h" -#include "OneSixVersion.h" +#include "VersionFinal.h" #include "pathutils.h" #include "logger/QsLog.h" #include "assets/AssetsUtils.h" @@ -34,8 +34,8 @@ OneSixInstance::OneSixInstance(const QString &rootDir, SettingsObject *settings, I_D(OneSixInstance); d->m_settings->registerSetting("IntendedVersion", ""); d->m_settings->registerSetting("ShouldUpdate", false); - d->version.reset(new OneSixVersion(this, this)); - d->vanillaVersion.reset(new OneSixVersion(this, this)); + d->version.reset(new VersionFinal(this, this)); + d->vanillaVersion.reset(new VersionFinal(this, this)); } void OneSixInstance::init() @@ -79,7 +79,7 @@ QString replaceTokensIn(QString text, QMap with) return result; } -QDir OneSixInstance::reconstructAssets(std::shared_ptr version) +QDir OneSixInstance::reconstructAssets(std::shared_ptr version) { QDir assetsDir = QDir("assets/"); QDir indexDir = QDir(PathCombine(assetsDir.path(), "indexes")); @@ -345,13 +345,13 @@ void OneSixInstance::clearVersion() emit versionReloaded(); } -std::shared_ptr OneSixInstance::getFullVersion() const +std::shared_ptr OneSixInstance::getFullVersion() const { I_D(const OneSixInstance); return d->version; } -std::shared_ptr OneSixInstance::getVanillaVersion() const +std::shared_ptr OneSixInstance::getVanillaVersion() const { I_D(const OneSixInstance); return d->vanillaVersion; diff --git a/logic/OneSixInstance.h b/logic/OneSixInstance.h index 06fd9de3..75edec1e 100644 --- a/logic/OneSixInstance.h +++ b/logic/OneSixInstance.h @@ -17,7 +17,7 @@ #include "BaseInstance.h" -#include "OneSixVersion.h" +#include "VersionFinal.h" #include "ModList.h" class OneSixInstance : public BaseInstance @@ -58,9 +58,9 @@ public: /// clears all version information in preparation for an update void clearVersion(); /// get the current full version info - std::shared_ptr getFullVersion() const; + std::shared_ptr getFullVersion() const; /// gets the current version info, but only for version.json - std::shared_ptr getVanillaVersion() const; + std::shared_ptr getVanillaVersion() const; /// is the current version original, or custom? virtual bool versionIsCustom() override; @@ -80,5 +80,5 @@ signals: private: QStringList processMinecraftArgs(AuthSessionPtr account); - QDir reconstructAssets(std::shared_ptr version); + QDir reconstructAssets(std::shared_ptr version); }; diff --git a/logic/OneSixInstance_p.h b/logic/OneSixInstance_p.h index 0cc46f33..2dffa62c 100644 --- a/logic/OneSixInstance_p.h +++ b/logic/OneSixInstance_p.h @@ -16,13 +16,13 @@ #pragma once #include "BaseInstance_p.h" -#include "OneSixVersion.h" +#include "VersionFinal.h" #include "ModList.h" struct OneSixInstancePrivate : public BaseInstancePrivate { - std::shared_ptr version; - std::shared_ptr vanillaVersion; + std::shared_ptr version; + std::shared_ptr vanillaVersion; std::shared_ptr loader_mod_list; std::shared_ptr resource_pack_list; }; diff --git a/logic/OneSixUpdate.cpp b/logic/OneSixUpdate.cpp index f87c65e7..750aeabb 100644 --- a/logic/OneSixUpdate.cpp +++ b/logic/OneSixUpdate.cpp @@ -25,7 +25,7 @@ #include "BaseInstance.h" #include "lists/MinecraftVersionList.h" -#include "OneSixVersion.h" +#include "VersionFinal.h" #include "OneSixLibrary.h" #include "OneSixInstance.h" #include "net/ForgeMirrors.h" @@ -150,7 +150,7 @@ void OneSixUpdate::assetIndexStart() { setStatus(tr("Updating assets index...")); OneSixInstance *inst = (OneSixInstance *)m_inst; - std::shared_ptr version = inst->getFullVersion(); + std::shared_ptr version = inst->getFullVersion(); QString assetName = version->assets; QUrl indexUrl = "http://" + URLConstants::AWS_DOWNLOAD_INDEXES + assetName + ".json"; QString localPath = assetName + ".json"; @@ -174,7 +174,7 @@ void OneSixUpdate::assetIndexFinished() AssetsIndex index; OneSixInstance *inst = (OneSixInstance *)m_inst; - std::shared_ptr version = inst->getFullVersion(); + std::shared_ptr version = inst->getFullVersion(); QString assetName = version->assets; QString asset_fname = "assets/indexes/" + assetName + ".json"; @@ -243,7 +243,7 @@ void OneSixUpdate::jarlibStart() } // Build a list of URLs that will need to be downloaded. - std::shared_ptr version = inst->getFullVersion(); + std::shared_ptr version = inst->getFullVersion(); // minecraft.jar for this version { QString version_id = version->id; diff --git a/logic/OneSixVersion.cpp b/logic/OneSixVersion.cpp deleted file mode 100644 index 06e748bd..00000000 --- a/logic/OneSixVersion.cpp +++ /dev/null @@ -1,221 +0,0 @@ -/* Copyright 2013 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 "OneSixVersion.h" - -#include -#include - -#include "OneSixVersionBuilder.h" - -OneSixVersion::OneSixVersion(OneSixInstance *instance, QObject *parent) - : QAbstractListModel(parent), m_instance(instance) -{ - clear(); -} - -bool OneSixVersion::reload(QWidget *widgetParent, const bool onlyVanilla, const QStringList &external) -{ - beginResetModel(); - bool ret = OneSixVersionBuilder::build(this, m_instance, widgetParent, onlyVanilla, external); - endResetModel(); - return ret; -} - -void OneSixVersion::clear() -{ - beginResetModel(); - id.clear(); - time.clear(); - releaseTime.clear(); - type.clear(); - assets.clear(); - processArguments.clear(); - minecraftArguments.clear(); - minimumLauncherVersion = 0xDEADBEAF; - mainClass.clear(); - libraries.clear(); - tweakers.clear(); - versionFiles.clear(); - endResetModel(); -} - -void OneSixVersion::dump() const -{ - qDebug().nospace() << "OneSixVersion(" - << "\n\tid=" << id - << "\n\ttime=" << time - << "\n\treleaseTime=" << releaseTime - << "\n\ttype=" << type - << "\n\tassets=" << assets - << "\n\tprocessArguments=" << processArguments - << "\n\tminecraftArguments=" << minecraftArguments - << "\n\tminimumLauncherVersion=" << minimumLauncherVersion - << "\n\tmainClass=" << mainClass - << "\n\tlibraries="; - for (auto lib : libraries) - { - qDebug().nospace() << "\n\t\t" << lib.get(); - } - qDebug().nospace() << "\n)"; -} - -bool OneSixVersion::canRemove(const int index) const -{ - if (index < versionFiles.size()) - { - return versionFiles.at(index).id != "org.multimc.version.json"; - } - return false; -} - -QString OneSixVersion::versionFileId(const int index) const -{ - if (index < 0 || index >= versionFiles.size()) - { - return QString(); - } - return versionFiles.at(index).id; -} - -bool OneSixVersion::remove(const int index) -{ - if (canRemove(index)) - { - return QFile::remove(versionFiles.at(index).filename); - } - return false; -} - -QList > OneSixVersion::getActiveNormalLibs() -{ - QList > output; - for (auto lib : libraries) - { - if (lib->isActive() && !lib->isNative()) - { - output.append(lib); - } - } - return output; -} - -QList > OneSixVersion::getActiveNativeLibs() -{ - QList > output; - for (auto lib : libraries) - { - if (lib->isActive() && lib->isNative()) - { - output.append(lib); - } - } - return output; -} - -std::shared_ptr OneSixVersion::fromJson(const QJsonObject &obj) -{ - std::shared_ptr version(new OneSixVersion(0)); - if (OneSixVersionBuilder::read(version.get(), obj)) - { - return version; - } - return 0; -} - -QVariant OneSixVersion::data(const QModelIndex &index, int role) const -{ - if (!index.isValid()) - return QVariant(); - - int row = index.row(); - int column = index.column(); - - if (row < 0 || row >= versionFiles.size()) - return QVariant(); - - if (role == Qt::DisplayRole) - { - switch (column) - { - case 0: - return versionFiles.at(row).name; - case 1: - return versionFiles.at(row).version; - default: - return QVariant(); - } - } - return QVariant(); -} - -QVariant OneSixVersion::headerData(int section, Qt::Orientation orientation, int role) const -{ - if (orientation == Qt::Horizontal) - { - if (role == Qt::DisplayRole) - { - switch (section) - { - case 0: - return tr("Name"); - case 1: - return tr("Version"); - default: - return QVariant(); - } - } - } - return QVariant(); -} - -Qt::ItemFlags OneSixVersion::flags(const QModelIndex &index) const -{ - if (!index.isValid()) - return Qt::NoItemFlags; - return Qt::ItemIsSelectable | Qt::ItemIsEnabled; -} - -int OneSixVersion::rowCount(const QModelIndex &parent) const -{ - return versionFiles.size(); -} - -int OneSixVersion::columnCount(const QModelIndex &parent) const -{ - return 2; -} - -QDebug operator<<(QDebug &dbg, const OneSixVersion *version) -{ - version->dump(); - return dbg.maybeSpace(); -} -QDebug operator<<(QDebug &dbg, const OneSixLibrary *library) -{ - dbg.nospace() << "OneSixLibrary(" - << "\n\t\t\trawName=" << library->rawName() - << "\n\t\t\tname=" << library->name() - << "\n\t\t\tversion=" << library->version() - << "\n\t\t\ttype=" << library->type() - << "\n\t\t\tisActive=" << library->isActive() - << "\n\t\t\tisNative=" << library->isNative() - << "\n\t\t\tdownloadUrl=" << library->downloadUrl() - << "\n\t\t\tstoragePath=" << library->storagePath() - << "\n\t\t\tabsolutePath=" << library->absoluteUrl() - << "\n\t\t\thint=" << library->hint(); - dbg.nospace() << "\n\t\t)"; - return dbg.maybeSpace(); -} diff --git a/logic/OneSixVersion.h b/logic/OneSixVersion.h deleted file mode 100644 index fee47fa3..00000000 --- a/logic/OneSixVersion.h +++ /dev/null @@ -1,137 +0,0 @@ -/* Copyright 2013 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. - */ - -#pragma once - -#include - -#include -#include -#include - -#include "OneSixLibrary.h" - -class OneSixInstance; - -class OneSixVersion : public QAbstractListModel -{ - Q_OBJECT -public: - explicit OneSixVersion(OneSixInstance *instance, QObject *parent = 0); - - virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; - virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const; - virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; - virtual int columnCount(const QModelIndex &parent) const; - virtual Qt::ItemFlags flags(const QModelIndex &index) const; - - bool reload(QWidget *widgetParent, const bool onlyVanilla = false, const QStringList &external = QStringList()); - void clear(); - - void dump() const; - - bool canRemove(const int index) const; - - QString versionFileId(const int index) const; - -public -slots: - bool remove(const int index); - -public: - QList> getActiveNormalLibs(); - QList> getActiveNativeLibs(); - - static std::shared_ptr fromJson(const QJsonObject &obj); - - // data members -public: - /// the ID - determines which jar to use! ACTUALLY IMPORTANT! - QString id; - /// Last updated time - as a string - QString time; - /// Release time - as a string - QString releaseTime; - /// Release type - "release" or "snapshot" - QString type; - /// Assets type - "legacy" or a version ID - QString assets; - /** - * DEPRECATED: Old versions of the new vanilla launcher used this - * ex: "username_session_version" - */ - QString processArguments; - /** - * arguments that should be used for launching minecraft - * - * ex: "--username ${auth_player_name} --session ${auth_session} - * --version ${version_name} --gameDir ${game_directory} --assetsDir ${game_assets}" - */ - QString minecraftArguments; - /** - * the minimum launcher version required by this version ... current is 4 (at point of - * writing) - */ - int minimumLauncherVersion = 0xDEADBEEF; - /** - * A list of all tweaker classes - */ - QStringList tweakers; - /** - * The main class to load first - */ - QString mainClass; - - /// the list of libs - both active and inactive, native and java - QList> libraries; - - /* - FIXME: add support for those rules here? Looks like a pile of quick hacks to me though. - - "rules": [ - { - "action": "allow" - }, - { - "action": "disallow", - "os": { - "name": "osx", - "version": "^10\\.5\\.\\d$" - } - } - ], - "incompatibilityReason": "There is a bug in LWJGL which makes it incompatible with OSX - 10.5.8. Please go to New Profile and use 1.5.2 for now. Sorry!" - } - */ - // QList rules; - - struct VersionFile - { - QString name; - QString id; - QString version; - QString mcVersion; - QString filename; - int order; - }; - QList versionFiles; - -private: - OneSixInstance *m_instance; -}; - -QDebug operator<<(QDebug &dbg, const OneSixVersion *version); -QDebug operator<<(QDebug &dbg, const OneSixLibrary *library); diff --git a/logic/OneSixVersionBuilder.cpp b/logic/OneSixVersionBuilder.cpp index f6917697..0d4d66a2 100644 --- a/logic/OneSixVersionBuilder.cpp +++ b/logic/OneSixVersionBuilder.cpp @@ -26,806 +26,37 @@ #include #include -#include "OneSixVersion.h" +#include "VersionFinal.h" #include "OneSixInstance.h" #include "OneSixRule.h" +#include "VersionFile.h" #include "modutils.h" #include "logger/QsLog.h" -#define CURRENT_MINIMUM_LAUNCHER_VERSION 14 - -struct VersionFile -{ - int order; - QString name; - QString fileId; - QString version; - // TODO use the mcVersion to determine if a version file should be removed on update - QString mcVersion; - QString filename; - // TODO requirements - // QMap requirements; - 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> natives; - bool applyRules = false; - QList> rules; - - // user for '+' libraries - enum InsertType - { - Apply, - Append, - Prepend, - Replace - }; - InsertType insertType = Append; - QString insertData; - enum DependType - { - Soft, - Hard - }; - DependType dependType = Soft; - }; - bool shouldOverwriteLibs = false; - QList overwriteLibs; - QList addLibs; - QList removeLibs; - - enum ApplyError - { - LauncherVersionError, - OtherError, - NoApplyError - }; - - static Library fromLibraryJson(const QJsonObject &libObj, const QString &filename, - bool &isError) - { - isError = true; - Library out; - if (!libObj.contains("name")) - { - QLOG_ERROR() << filename << "contains a library that doesn't have a 'name' field"; - return out; - } - out.name = libObj.value("name").toString(); - - 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(); - } - } - }; - - readString("url", out.url); - readString("MMC-hint", out.hint); - readString("MMC-absulute_url", out.absoluteUrl); - readString("MMC-absoluteUrl", out.absoluteUrl); - if (libObj.contains("extract")) - { - if (!libObj.value("extract").isObject()) - { - QLOG_ERROR() - << filename - << "contains a library with an 'extract' field that's not an object"; - return out; - } - QJsonObject extractObj = libObj.value("extract").toObject(); - if (!extractObj.contains("exclude") || !extractObj.value("exclude").isArray()) - { - QLOG_ERROR() << filename - << "contains a library with an invalid 'extract' field"; - return out; - } - out.applyExcludes = true; - QJsonArray excludeArray = extractObj.value("exclude").toArray(); - for (auto excludeVal : excludeArray) - { - 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()); - } - } - } - if (libObj.contains("natives")) - { - if (!libObj.value("natives").isObject()) - { - QLOG_ERROR() - << filename - << "contains a library with a 'natives' field that's not an object"; - return out; - } - out.applyNatives = true; - QJsonObject nativesObj = libObj.value("natives").toObject(); - for (auto it = nativesObj.begin(); it != nativesObj.end(); ++it) - { - 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; - } - static VersionFile fromJson(const QJsonDocument &doc, const QString &filename, - const bool requireOrder, bool &isError, const OneSixVersionBuilder::ParseFlags flags = OneSixVersionBuilder::NoFlags) - { - VersionFile out; - isError = true; - if (doc.isEmpty() || doc.isNull()) - { - QLOG_ERROR() << filename << "is empty or null"; - return out; - } - if (!doc.isObject()) - { - QLOG_ERROR() << "The root of" << filename << "is not an object"; - return out; - } - - QJsonObject root = doc.object(); - - if (requireOrder) - { - 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"; - } - } - - out.name = root.value("name").toString(); - out.fileId = root.value("fileId").toString(); - out.version = root.value("version").toString(); - out.mcVersion = root.value("mcVersion").toString(); - out.filename = filename; - - 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(); - } - } - }; - - if (!(flags & OneSixVersionBuilder::IsFTBPackJson)) - { - 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")) - { - QJsonValue val = root.value("minimumLauncherVersion"); - if (!val.isDouble()) - { - QLOG_WARN() << "minimumLauncherVersion is not an int in" << filename - << "(skipping)"; - } - else - { - out.minimumLauncherVersion = val.toDouble(); - } - } - - if (root.contains("tweakers")) - { - 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()); - } - } - if (root.contains("+tweakers")) - { - 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 (root.contains("-tweakers")) - { - 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()); - } - } - - if (root.contains("libraries")) - { - out.shouldOverwriteLibs = !(flags & OneSixVersionBuilder::IsFTBPackJson); - 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; - } - if (flags & OneSixVersionBuilder::IsFTBPackJson) - { - lib.hint = "local"; - lib.insertType = Library::Prepend; - out.addLibs.prepend(lib); - } - else - { - out.overwriteLibs.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(); - 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")) - { - 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 == "prepend") - { - lib.insertType = Library::Prepend; - } - else if (insertString == "append") - { - lib.insertType = Library::Prepend; - } - else if (insertString == "replace") - { - lib.insertType = Library::Replace; - } - else - { - QLOG_ERROR() << "A '+' library in" << filename - << "contains an invalid insert type"; - return out; - } - if (libObj.contains("MMC-depend") && libObj.value("MMC-depend").isString()) - { - const QString dependString = libObj.value("MMC-depend").toString(); - if (dependString == "hard") - { - lib.dependType = Library::Hard; - } - else if (dependString == "soft") - { - lib.dependType = Library::Soft; - } - else - { - QLOG_ERROR() << "A '+' library in" << filename - << "contains an invalid depend 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; - } - - static std::shared_ptr createLibrary(const Library &lib) - { - std::shared_ptr out(new OneSixLibrary(lib.name)); - if (!lib.url.isEmpty()) - { - out->setBaseUrl(lib.url); - } - out->setHint(lib.hint); - if (!lib.absoluteUrl.isEmpty()) - { - out->setAbsoluteUrl(lib.absoluteUrl); - } - out->setAbsoluteUrl(lib.absoluteUrl); - out->extract_excludes = lib.excludes; - for (auto native : lib.natives) - { - out->addNative(native.first, native.second); - } - out->setRules(lib.rules); - out->finalize(); - return out; - } - int findLibrary(QList> haystack, const QString &needle) - { - 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; - } - ApplyError applyTo(OneSixVersion *version) - { - if (minimumLauncherVersion != -1) - { - if (minimumLauncherVersion > CURRENT_MINIMUM_LAUNCHER_VERSION) - { - QLOG_ERROR() << filename << "is for a different launcher version (" - << minimumLauncherVersion << "), current supported is" - << CURRENT_MINIMUM_LAUNCHER_VERSION; - return LauncherVersionError; - } - } - - if (!version->id.isNull() && !mcVersion.isNull()) - { - if (QRegExp(mcVersion, Qt::CaseInsensitive, QRegExp::Wildcard) - .indexIn(version->id) == -1) - { - QLOG_ERROR() << filename << "is for a different version of Minecraft"; - return OtherError; - } - } - - if (!id.isNull()) - { - 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; - } - if (!removeMinecraftArguments.isNull()) - { - 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: - case Library::Prepend: - { - - const int startOfVersion = lib.name.lastIndexOf(':') + 1; - const int index = - findLibrary(version->libraries, - QString(lib.name).replace(startOfVersion, INT_MAX, '*')); - if (index < 0) - { - if (lib.insertType == Library::Append) - { - version->libraries.append(createLibrary(lib)); - } - else - { - version->libraries.prepend(createLibrary(lib)); - } - } - else - { - auto otherLib = version->libraries.at(index); - const Util::Version ourVersion = lib.name.mid(startOfVersion, INT_MAX); - const Util::Version otherVersion = otherLib->version(); - // if the existing version is a hard dependency we can either use it or - // fail, but we can't change it - if (otherLib->dependType == OneSixLibrary::Hard) - { - // we need a higher version, or we're hard to and the versions aren't - // equal - if (ourVersion > otherVersion || - (lib.dependType == Library::Hard && ourVersion != otherVersion)) - { - QLOG_ERROR() << "Error resolving library dependencies between" - << otherLib->rawName() << "and" << lib.name << "in" - << filename; - return OtherError; - } - else - { - // the library is already existing, so we don't have to do anything - } - } - else if (otherLib->dependType == OneSixLibrary::Soft) - { - // if we are higher it means we should update - if (ourVersion > otherVersion) - { - auto library = createLibrary(lib); - if (Util::Version(otherLib->minVersion) < ourVersion) - { - library->minVersion = ourVersion.toString(); - } - version->libraries.replace(index, library); - } - else - { - // our version is smaller than the existing version, but we require - // it: fail - if (lib.dependType == Library::Hard) - { - QLOG_ERROR() << "Error resolving library dependencies between" - << otherLib->rawName() << "and" << lib.name << "in" - << filename; - return OtherError; - } - } - } - } - 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)"; - } - } - - OneSixVersion::VersionFile versionFile; - versionFile.name = name; - versionFile.id = fileId; - versionFile.version = this->version; - versionFile.mcVersion = mcVersion; - versionFile.filename = filename; - versionFile.order = order; - version->versionFiles.append(versionFile); - - return NoApplyError; - } -}; - OneSixVersionBuilder::OneSixVersionBuilder() { } -bool OneSixVersionBuilder::build(OneSixVersion *version, OneSixInstance *instance, +bool OneSixVersionBuilder::build(VersionFinal *version, OneSixInstance *instance, QWidget *widgetParent, const bool onlyVanilla, const QStringList &external) { OneSixVersionBuilder builder; builder.m_version = version; builder.m_instance = instance; builder.m_widgetParent = widgetParent; - return builder.build(onlyVanilla, external); + return builder.buildInternal(onlyVanilla, external); } -bool OneSixVersionBuilder::read(OneSixVersion *version, const QJsonObject &obj) +bool OneSixVersionBuilder::readJsonAndApplyToVersion(VersionFinal *version, const QJsonObject &obj) { OneSixVersionBuilder builder; builder.m_version = version; builder.m_instance = 0; builder.m_widgetParent = 0; - return builder.read(obj); + return builder.readJsonAndApply(obj); } -bool OneSixVersionBuilder::build(const bool onlyVanilla, const QStringList &external) +bool OneSixVersionBuilder::buildInternal(const bool onlyVanilla, const QStringList &external) { m_version->clear(); @@ -837,8 +68,7 @@ bool OneSixVersionBuilder::build(const bool onlyVanilla, const QStringList &exte { QLOG_INFO() << "Reading" << fileName; VersionFile file; - ParseFlags flags = fileName.endsWith("pack.json") ? IsFTBPackJson : NoFlags; - if (!read(QFileInfo(fileName), false, &file, flags)) + if (!parseJsonFile(QFileInfo(fileName), false, &file, fileName.endsWith("pack.json") ? IsFTBPackJson : NoFlags)) { return false; } @@ -856,7 +86,7 @@ bool OneSixVersionBuilder::build(const bool onlyVanilla, const QStringList &exte { QLOG_INFO() << "Reading custom.json"; VersionFile file; - if (!read(QFileInfo(root.absoluteFilePath("custom.json")), false, &file)) + if (!parseJsonFile(QFileInfo(root.absoluteFilePath("custom.json")), false, &file)) { return false; } @@ -876,7 +106,7 @@ bool OneSixVersionBuilder::build(const bool onlyVanilla, const QStringList &exte // version.json QLOG_INFO() << "Reading version.json"; VersionFile file; - if (!read(QFileInfo(root.absoluteFilePath("version.json")), false, &file)) + if (!parseJsonFile(QFileInfo(root.absoluteFilePath("version.json")), false, &file)) { return false; } @@ -907,7 +137,7 @@ bool OneSixVersionBuilder::build(const bool onlyVanilla, const QStringList &exte { QLOG_INFO() << "Reading" << info.fileName(); VersionFile file; - if (!read(info, true, &file)) + if (!parseJsonFile(info, true, &file)) { return false; } @@ -968,7 +198,7 @@ bool OneSixVersionBuilder::build(const bool onlyVanilla, const QStringList &exte return true; } -bool OneSixVersionBuilder::read(const QJsonObject &obj) +bool OneSixVersionBuilder::readJsonAndApply(const QJsonObject &obj) { m_version->clear(); @@ -1000,7 +230,7 @@ bool OneSixVersionBuilder::read(const QJsonObject &obj) return true; } -bool OneSixVersionBuilder::read(const QFileInfo &fileInfo, const bool requireOrder, +bool OneSixVersionBuilder::parseJsonFile(const QFileInfo &fileInfo, const bool requireOrder, VersionFile *out, const ParseFlags flags) { QFile file(fileInfo.absoluteFilePath()); @@ -1072,6 +302,7 @@ QMap OneSixVersionBuilder::readOverrideOrders(OneSixInstance *inst } return out; } + bool OneSixVersionBuilder::writeOverrideOrders(const QMap &order, OneSixInstance *instance) { diff --git a/logic/OneSixVersionBuilder.h b/logic/OneSixVersionBuilder.h index 8cf6f32f..8ca2551a 100644 --- a/logic/OneSixVersionBuilder.h +++ b/logic/OneSixVersionBuilder.h @@ -18,7 +18,7 @@ #include #include -class OneSixVersion; +class VersionFinal; class OneSixInstance; class QWidget; class QJsonObject; @@ -29,8 +29,9 @@ class OneSixVersionBuilder { OneSixVersionBuilder(); public: - static bool build(OneSixVersion *version, OneSixInstance *instance, QWidget *widgetParent, const bool onlyVanilla, const QStringList &external); - static bool read(OneSixVersion *version, const QJsonObject &obj); + static bool build(VersionFinal *version, OneSixInstance *instance, QWidget *widgetParent, const bool onlyVanilla, const QStringList &external); + static bool readJsonAndApplyToVersion(VersionFinal *version, const QJsonObject &obj); + static QMap readOverrideOrders(OneSixInstance *instance); static bool writeOverrideOrders(const QMap &order, OneSixInstance *instance); @@ -42,14 +43,14 @@ public: Q_DECLARE_FLAGS(ParseFlags, ParseFlag) private: - OneSixVersion *m_version; + VersionFinal *m_version; OneSixInstance *m_instance; QWidget *m_widgetParent; - bool build(const bool onlyVanilla, const QStringList &external); - bool read(const QJsonObject &obj); + bool buildInternal(const bool onlyVanilla, const QStringList &external); + bool readJsonAndApply(const QJsonObject &obj); - bool read(const QFileInfo &fileInfo, const bool requireOrder, VersionFile *out, const ParseFlags flags = NoFlags); + bool parseJsonFile(const QFileInfo &fileInfo, const bool requireOrder, VersionFile *out, const ParseFlags flags = NoFlags); }; Q_DECLARE_OPERATORS_FOR_FLAGS(OneSixVersionBuilder::ParseFlags) diff --git a/logic/VersionFile.cpp b/logic/VersionFile.cpp new file mode 100644 index 00000000..76cf9279 --- /dev/null +++ b/logic/VersionFile.cpp @@ -0,0 +1,702 @@ +#include +#include + +#include + +#include "logger/QsLog.h" +#include "logic/VersionFile.h" +#include "logic/OneSixLibrary.h" +#include "logic/VersionFinal.h" + + +#define CURRENT_MINIMUM_LAUNCHER_VERSION 14 + +VersionFile::Library VersionFile::Library::fromJson(const QJsonObject &libObj, + const QString &filename, bool &isError) +{ + isError = true; + Library out; + if (!libObj.contains("name")) + { + QLOG_ERROR() << filename << "contains a library that doesn't have a 'name' field"; + return out; + } + out.name = libObj.value("name").toString(); + + 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(); + } + } + }; + + readString("url", out.url); + readString("MMC-hint", out.hint); + readString("MMC-absulute_url", out.absoluteUrl); + readString("MMC-absoluteUrl", out.absoluteUrl); + if (libObj.contains("extract")) + { + if (!libObj.value("extract").isObject()) + { + QLOG_ERROR() << filename + << "contains a library with an 'extract' field that's not an object"; + return out; + } + QJsonObject extractObj = libObj.value("extract").toObject(); + if (!extractObj.contains("exclude") || !extractObj.value("exclude").isArray()) + { + QLOG_ERROR() << filename << "contains a library with an invalid 'extract' field"; + return out; + } + out.applyExcludes = true; + QJsonArray excludeArray = extractObj.value("exclude").toArray(); + for (auto excludeVal : excludeArray) + { + 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()); + } + } + } + if (libObj.contains("natives")) + { + if (!libObj.value("natives").isObject()) + { + QLOG_ERROR() << filename + << "contains a library with a 'natives' field that's not an object"; + return out; + } + out.applyNatives = true; + QJsonObject nativesObj = libObj.value("natives").toObject(); + for (auto it = nativesObj.begin(); it != nativesObj.end(); ++it) + { + 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; +} + +VersionFile VersionFile::fromJson(const QJsonDocument &doc, const QString &filename, + const bool requireOrder, bool &isError, const bool isFTB) +{ + VersionFile out; + isError = true; + if (doc.isEmpty() || doc.isNull()) + { + QLOG_ERROR() << filename << "is empty or null"; + return out; + } + if (!doc.isObject()) + { + QLOG_ERROR() << "The root of" << filename << "is not an object"; + return out; + } + + QJsonObject root = doc.object(); + + if (requireOrder) + { + 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"; + } + } + + out.name = root.value("name").toString(); + out.fileId = root.value("fileId").toString(); + out.version = root.value("version").toString(); + out.mcVersion = root.value("mcVersion").toString(); + out.filename = filename; + + 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(); + } + } + }; + + // FTB id attribute is completely bogus. We ignore it. + if (!isFTB) + { + 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")) + { + QJsonValue val = root.value("minimumLauncherVersion"); + if (!val.isDouble()) + { + QLOG_WARN() << "minimumLauncherVersion is not an int in" << filename + << "(skipping)"; + } + else + { + out.minimumLauncherVersion = val.toDouble(); + } + } + + if (root.contains("tweakers")) + { + 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()); + } + } + if (root.contains("+tweakers")) + { + 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 (root.contains("-tweakers")) + { + 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()); + } + } + + if (root.contains("libraries")) + { + out.shouldOverwriteLibs = !isFTB; + 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 = Library::fromJson(libObj, filename, error); + if (error) + { + QLOG_ERROR() << "Error while reading a library entry in" << filename; + return out; + } + if (isFTB) + { + lib.hint = "local"; + lib.insertType = Library::Prepend; + out.addLibs.prepend(lib); + } + else + { + out.overwriteLibs.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(); + bool error; + Library lib = Library::fromJson(libObj, filename, error); + if (error) + { + QLOG_ERROR() << "Error while reading a library entry in" << filename; + return out; + } + if (!libObj.contains("insert")) + { + 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 == "prepend") + { + lib.insertType = Library::Prepend; + } + else if (insertString == "append") + { + lib.insertType = Library::Prepend; + } + else if (insertString == "replace") + { + lib.insertType = Library::Replace; + } + else + { + QLOG_ERROR() << "A '+' library in" << filename + << "contains an invalid insert type"; + return out; + } + if (libObj.contains("MMC-depend") && libObj.value("MMC-depend").isString()) + { + const QString dependString = libObj.value("MMC-depend").toString(); + if (dependString == "hard") + { + lib.dependType = Library::Hard; + } + else if (dependString == "soft") + { + lib.dependType = Library::Soft; + } + else + { + QLOG_ERROR() << "A '+' library in" << filename + << "contains an invalid depend 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; +} + +std::shared_ptr VersionFile::createLibrary(const VersionFile::Library &lib) +{ + std::shared_ptr out(new OneSixLibrary(lib.name)); + if (!lib.url.isEmpty()) + { + out->setBaseUrl(lib.url); + } + out->setHint(lib.hint); + if (!lib.absoluteUrl.isEmpty()) + { + out->setAbsoluteUrl(lib.absoluteUrl); + } + out->setAbsoluteUrl(lib.absoluteUrl); + out->extract_excludes = lib.excludes; + for (auto native : lib.natives) + { + out->addNative(native.first, native.second); + } + out->setRules(lib.rules); + out->finalize(); + return out; +} + +int VersionFile::findLibrary(QList> haystack, + const QString &needle) +{ + 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; +} + +VersionFile::ApplyError VersionFile::applyTo(VersionFinal *version) +{ + if (minimumLauncherVersion != -1) + { + if (minimumLauncherVersion > CURRENT_MINIMUM_LAUNCHER_VERSION) + { + QLOG_ERROR() << filename << "is for a different launcher version (" + << minimumLauncherVersion << "), current supported is" + << CURRENT_MINIMUM_LAUNCHER_VERSION; + return LauncherVersionError; + } + } + + if (!version->id.isNull() && !mcVersion.isNull()) + { + if (QRegExp(mcVersion, Qt::CaseInsensitive, QRegExp::Wildcard).indexIn(version->id) == + -1) + { + QLOG_ERROR() << filename << "is for a different version of Minecraft"; + return OtherError; + } + } + + if (!id.isNull()) + { + 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; + } + if (!removeMinecraftArguments.isNull()) + { + 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: + case Library::Prepend: + { + + const int startOfVersion = lib.name.lastIndexOf(':') + 1; + const int index = findLibrary( + version->libraries, QString(lib.name).replace(startOfVersion, INT_MAX, '*')); + if (index < 0) + { + if (lib.insertType == Library::Append) + { + version->libraries.append(createLibrary(lib)); + } + else + { + version->libraries.prepend(createLibrary(lib)); + } + } + else + { + auto otherLib = version->libraries.at(index); + const Util::Version ourVersion = lib.name.mid(startOfVersion, INT_MAX); + const Util::Version otherVersion = otherLib->version(); + // if the existing version is a hard dependency we can either use it or + // fail, but we can't change it + if (otherLib->dependType == OneSixLibrary::Hard) + { + // we need a higher version, or we're hard to and the versions aren't + // equal + if (ourVersion > otherVersion || + (lib.dependType == Library::Hard && ourVersion != otherVersion)) + { + QLOG_ERROR() << "Error resolving library dependencies between" + << otherLib->rawName() << "and" << lib.name << "in" + << filename; + return OtherError; + } + else + { + // the library is already existing, so we don't have to do anything + } + } + else if (otherLib->dependType == OneSixLibrary::Soft) + { + // if we are higher it means we should update + if (ourVersion > otherVersion) + { + auto library = createLibrary(lib); + if (Util::Version(otherLib->minVersion) < ourVersion) + { + library->minVersion = ourVersion.toString(); + } + version->libraries.replace(index, library); + } + else + { + // our version is smaller than the existing version, but we require + // it: fail + if (lib.dependType == Library::Hard) + { + QLOG_ERROR() << "Error resolving library dependencies between" + << otherLib->rawName() << "and" << lib.name << "in" + << filename; + return OtherError; + } + } + } + } + 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)"; + } + } + + VersionFinal::VersionFile versionFile; + versionFile.name = name; + versionFile.id = fileId; + versionFile.version = this->version; + versionFile.mcVersion = mcVersion; + versionFile.filename = filename; + versionFile.order = order; + version->versionFiles.append(versionFile); + + return NoApplyError; +} diff --git a/logic/VersionFile.h b/logic/VersionFile.h new file mode 100644 index 00000000..04694999 --- /dev/null +++ b/logic/VersionFile.h @@ -0,0 +1,90 @@ +#pragma once + +#include +#include +#include +#include "logic/OpSys.h" +#include "logic/OneSixRule.h" +class VersionFinal; + +struct VersionFile +{ + int order; + QString name; + QString fileId; + QString version; + // TODO use the mcVersion to determine if a version file should be removed on update + QString mcVersion; + QString filename; + // TODO requirements + // QMap requirements; + 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> natives; + bool applyRules = false; + QList> rules; + + // user for '+' libraries + enum InsertType + { + Apply, + Append, + Prepend, + Replace + }; + InsertType insertType = Append; + QString insertData; + enum DependType + { + Soft, + Hard + }; + DependType dependType = Soft; + + static Library fromJson(const QJsonObject &libObj, const QString &filename, + bool &isError); + }; + bool shouldOverwriteLibs = false; + QList overwriteLibs; + QList addLibs; + QList removeLibs; + + enum ApplyError + { + LauncherVersionError, + OtherError, + NoApplyError + }; + + static VersionFile fromJson(const QJsonDocument &doc, const QString &filename, + const bool requireOrder, bool &isError, + const bool isFTB = false); + + static std::shared_ptr createLibrary(const Library &lib); + int findLibrary(QList> haystack, const QString &needle); + ApplyError applyTo(VersionFinal *version); +}; \ No newline at end of file diff --git a/logic/VersionFinal.cpp b/logic/VersionFinal.cpp new file mode 100644 index 00000000..ce78e8e3 --- /dev/null +++ b/logic/VersionFinal.cpp @@ -0,0 +1,221 @@ +/* Copyright 2013 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 "VersionFinal.h" + +#include +#include + +#include "OneSixVersionBuilder.h" + +VersionFinal::VersionFinal(OneSixInstance *instance, QObject *parent) + : QAbstractListModel(parent), m_instance(instance) +{ + clear(); +} + +bool VersionFinal::reload(QWidget *widgetParent, const bool onlyVanilla, const QStringList &external) +{ + beginResetModel(); + bool ret = OneSixVersionBuilder::build(this, m_instance, widgetParent, onlyVanilla, external); + endResetModel(); + return ret; +} + +void VersionFinal::clear() +{ + beginResetModel(); + id.clear(); + time.clear(); + releaseTime.clear(); + type.clear(); + assets.clear(); + processArguments.clear(); + minecraftArguments.clear(); + minimumLauncherVersion = 0xDEADBEAF; + mainClass.clear(); + libraries.clear(); + tweakers.clear(); + versionFiles.clear(); + endResetModel(); +} + +void VersionFinal::dump() const +{ + qDebug().nospace() << "VersionFinal(" + << "\n\tid=" << id + << "\n\ttime=" << time + << "\n\treleaseTime=" << releaseTime + << "\n\ttype=" << type + << "\n\tassets=" << assets + << "\n\tprocessArguments=" << processArguments + << "\n\tminecraftArguments=" << minecraftArguments + << "\n\tminimumLauncherVersion=" << minimumLauncherVersion + << "\n\tmainClass=" << mainClass + << "\n\tlibraries="; + for (auto lib : libraries) + { + qDebug().nospace() << "\n\t\t" << lib.get(); + } + qDebug().nospace() << "\n)"; +} + +bool VersionFinal::canRemove(const int index) const +{ + if (index < versionFiles.size()) + { + return versionFiles.at(index).id != "org.multimc.version.json"; + } + return false; +} + +QString VersionFinal::versionFileId(const int index) const +{ + if (index < 0 || index >= versionFiles.size()) + { + return QString(); + } + return versionFiles.at(index).id; +} + +bool VersionFinal::remove(const int index) +{ + if (canRemove(index)) + { + return QFile::remove(versionFiles.at(index).filename); + } + return false; +} + +QList > VersionFinal::getActiveNormalLibs() +{ + QList > output; + for (auto lib : libraries) + { + if (lib->isActive() && !lib->isNative()) + { + output.append(lib); + } + } + return output; +} + +QList > VersionFinal::getActiveNativeLibs() +{ + QList > output; + for (auto lib : libraries) + { + if (lib->isActive() && lib->isNative()) + { + output.append(lib); + } + } + return output; +} + +std::shared_ptr VersionFinal::fromJson(const QJsonObject &obj) +{ + std::shared_ptr version(new VersionFinal(0)); + if (OneSixVersionBuilder::readJsonAndApplyToVersion(version.get(), obj)) + { + return version; + } + return 0; +} + +QVariant VersionFinal::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + int row = index.row(); + int column = index.column(); + + if (row < 0 || row >= versionFiles.size()) + return QVariant(); + + if (role == Qt::DisplayRole) + { + switch (column) + { + case 0: + return versionFiles.at(row).name; + case 1: + return versionFiles.at(row).version; + default: + return QVariant(); + } + } + return QVariant(); +} + +QVariant VersionFinal::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Horizontal) + { + if (role == Qt::DisplayRole) + { + switch (section) + { + case 0: + return tr("Name"); + case 1: + return tr("Version"); + default: + return QVariant(); + } + } + } + return QVariant(); +} + +Qt::ItemFlags VersionFinal::flags(const QModelIndex &index) const +{ + if (!index.isValid()) + return Qt::NoItemFlags; + return Qt::ItemIsSelectable | Qt::ItemIsEnabled; +} + +int VersionFinal::rowCount(const QModelIndex &parent) const +{ + return versionFiles.size(); +} + +int VersionFinal::columnCount(const QModelIndex &parent) const +{ + return 2; +} + +QDebug operator<<(QDebug &dbg, const VersionFinal *version) +{ + version->dump(); + return dbg.maybeSpace(); +} +QDebug operator<<(QDebug &dbg, const OneSixLibrary *library) +{ + dbg.nospace() << "OneSixLibrary(" + << "\n\t\t\trawName=" << library->rawName() + << "\n\t\t\tname=" << library->name() + << "\n\t\t\tversion=" << library->version() + << "\n\t\t\ttype=" << library->type() + << "\n\t\t\tisActive=" << library->isActive() + << "\n\t\t\tisNative=" << library->isNative() + << "\n\t\t\tdownloadUrl=" << library->downloadUrl() + << "\n\t\t\tstoragePath=" << library->storagePath() + << "\n\t\t\tabsolutePath=" << library->absoluteUrl() + << "\n\t\t\thint=" << library->hint(); + dbg.nospace() << "\n\t\t)"; + return dbg.maybeSpace(); +} diff --git a/logic/VersionFinal.h b/logic/VersionFinal.h new file mode 100644 index 00000000..c9a5f469 --- /dev/null +++ b/logic/VersionFinal.h @@ -0,0 +1,137 @@ +/* Copyright 2013 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. + */ + +#pragma once + +#include + +#include +#include +#include + +#include "OneSixLibrary.h" + +class OneSixInstance; + +class VersionFinal : public QAbstractListModel +{ + Q_OBJECT +public: + explicit VersionFinal(OneSixInstance *instance, QObject *parent = 0); + + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const; + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; + virtual int columnCount(const QModelIndex &parent) const; + virtual Qt::ItemFlags flags(const QModelIndex &index) const; + + bool reload(QWidget *widgetParent, const bool onlyVanilla = false, const QStringList &external = QStringList()); + void clear(); + + void dump() const; + + bool canRemove(const int index) const; + + QString versionFileId(const int index) const; + +public +slots: + bool remove(const int index); + +public: + QList> getActiveNormalLibs(); + QList> getActiveNativeLibs(); + + static std::shared_ptr fromJson(const QJsonObject &obj); + + // data members +public: + /// the ID - determines which jar to use! ACTUALLY IMPORTANT! + QString id; + /// Last updated time - as a string + QString time; + /// Release time - as a string + QString releaseTime; + /// Release type - "release" or "snapshot" + QString type; + /// Assets type - "legacy" or a version ID + QString assets; + /** + * DEPRECATED: Old versions of the new vanilla launcher used this + * ex: "username_session_version" + */ + QString processArguments; + /** + * arguments that should be used for launching minecraft + * + * ex: "--username ${auth_player_name} --session ${auth_session} + * --version ${version_name} --gameDir ${game_directory} --assetsDir ${game_assets}" + */ + QString minecraftArguments; + /** + * the minimum launcher version required by this version ... current is 4 (at point of + * writing) + */ + int minimumLauncherVersion = 0xDEADBEEF; + /** + * A list of all tweaker classes + */ + QStringList tweakers; + /** + * The main class to load first + */ + QString mainClass; + + /// the list of libs - both active and inactive, native and java + QList> libraries; + + /* + FIXME: add support for those rules here? Looks like a pile of quick hacks to me though. + + "rules": [ + { + "action": "allow" + }, + { + "action": "disallow", + "os": { + "name": "osx", + "version": "^10\\.5\\.\\d$" + } + } + ], + "incompatibilityReason": "There is a bug in LWJGL which makes it incompatible with OSX + 10.5.8. Please go to New Profile and use 1.5.2 for now. Sorry!" + } + */ + // QList rules; + + struct VersionFile + { + QString name; + QString id; + QString version; + QString mcVersion; + QString filename; + int order; + }; + QList versionFiles; + +private: + OneSixInstance *m_instance; +}; + +QDebug operator<<(QDebug &dbg, const VersionFinal *version); +QDebug operator<<(QDebug &dbg, const OneSixLibrary *library); -- cgit v1.2.3