summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt46
-rw-r--r--MMCError.h25
-rw-r--r--MultiMC.cpp1
-rw-r--r--depends/settings/inisettingsobject.cpp5
-rw-r--r--depends/settings/inisettingsobject.h2
-rw-r--r--depends/settings/settingsobject.cpp9
-rw-r--r--depends/settings/settingsobject.h6
-rw-r--r--gui/dialogs/OneSixModEditDialog.cpp174
-rw-r--r--gui/dialogs/OneSixModEditDialog.h6
-rw-r--r--gui/dialogs/OneSixModEditDialog.ui50
-rw-r--r--logic/BaseInstaller.cpp2
-rw-r--r--logic/BaseInstance.cpp5
-rw-r--r--logic/BaseInstance.h2
-rw-r--r--logic/ForgeInstaller.cpp6
-rw-r--r--logic/ForgeInstaller.h4
-rw-r--r--logic/LiteLoaderInstaller.cpp4
-rw-r--r--logic/MMCJson.cpp60
-rw-r--r--logic/MMCJson.h46
-rw-r--r--logic/MinecraftProcess.cpp200
-rw-r--r--logic/MinecraftProcess.h7
-rw-r--r--logic/OneSixFTBInstance.cpp81
-rw-r--r--logic/OneSixInstance.cpp59
-rw-r--r--logic/OneSixInstance.h18
-rw-r--r--logic/OneSixInstance_p.h6
-rw-r--r--logic/OneSixLibrary.h3
-rw-r--r--logic/OneSixUpdate.cpp48
-rw-r--r--logic/OneSixVersion.cpp221
-rw-r--r--logic/OneSixVersionBuilder.cpp1111
-rw-r--r--logic/OneSixVersionBuilder.h31
-rw-r--r--logic/VersionFile.cpp535
-rw-r--r--logic/VersionFile.h127
-rw-r--r--logic/VersionFinal.cpp183
-rw-r--r--logic/VersionFinal.h (renamed from logic/OneSixVersion.h)23
-rw-r--r--logic/lists/LiteLoaderVersionList.cpp55
-rw-r--r--logic/lists/LiteLoaderVersionList.h11
-rw-r--r--translations/CMakeLists.txt20
-rw-r--r--translations/mmc_de.ts1205
37 files changed, 2653 insertions, 1744 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 54a4be19..d9279bcb 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -257,6 +257,7 @@ SET(MULTIMC_SOURCES
MultiMC.h
MultiMC.cpp
MultiMCVersion.h
+MMCError.h
# Logging
logger/QsDebugOutput.cpp
@@ -353,6 +354,10 @@ logic/ModList.cpp
logic/InstanceLauncher.h
logic/InstanceLauncher.cpp
+# JSON parsing helpers
+logic/MMCJson.h
+logic/MMCJson.cpp
+
# network stuffs
logic/net/NetAction.h
logic/net/MD5EtagDownload.h
@@ -414,31 +419,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
@@ -758,24 +770,8 @@ INCLUDE(CPack)
include_directories(${PROJECT_BINARY_DIR}/include)
-### translation stuff
-
-file (GLOB TRANSLATIONS_FILES translations/*.ts)
-
-option (UPDATE_TRANSLATIONS "Update source translation translations/*.ts files (WARNING: make clean will delete the source .ts files! Danger!)")
-IF(UPDATE_TRANSLATIONS)
- qt5_create_translation(QM_FILES ${FILES_TO_TRANSLATE} ${TRANSLATIONS_FILES})
-ELSE()
- qt5_add_translation(QM_FILES ${TRANSLATIONS_FILES})
-ENDIF()
-
-add_custom_target (translations DEPENDS ${QM_FILES})
-IF(APPLE AND UNIX) ## OSX
- install(FILES ${QM_FILES} DESTINATION MultiMC.app/Contents/MacOS/translations)
-ELSE()
- install(FILES ${QM_FILES} DESTINATION translations)
-ENDIF()
-
+# Translations
+add_subdirectory(translations)
# Tests
add_subdirectory(tests)
diff --git a/MMCError.h b/MMCError.h
new file mode 100644
index 00000000..7b2bd0c4
--- /dev/null
+++ b/MMCError.h
@@ -0,0 +1,25 @@
+#pragma once
+#include <exception>
+#include <QString>
+#include <logger/QsLog.h>
+
+class MMCError : public std::exception
+{
+public:
+ MMCError(QString cause)
+ {
+ exceptionCause = cause;
+ QLOG_ERROR() << "Exception: " + cause;
+ };
+ virtual ~MMCError(){};
+ virtual const char *what() const noexcept
+ {
+ return exceptionCause.toLocal8Bit();
+ };
+ virtual QString cause() const
+ {
+ return exceptionCause;
+ }
+private:
+ QString exceptionCause;
+};
diff --git a/MultiMC.cpp b/MultiMC.cpp
index a0745a87..ddb264d1 100644
--- a/MultiMC.cpp
+++ b/MultiMC.cpp
@@ -457,6 +457,7 @@ void MultiMC::initHttpMetaCache()
m_metacache->addBase("versions", QDir("versions").absolutePath());
m_metacache->addBase("libraries", QDir("libraries").absolutePath());
m_metacache->addBase("minecraftforge", QDir("mods/minecraftforge").absolutePath());
+ m_metacache->addBase("liteloader", QDir("mods/liteloader").absolutePath());
m_metacache->addBase("skins", QDir("accounts/skins").absolutePath());
m_metacache->addBase("root", QDir(root()).absolutePath());
m_metacache->Load();
diff --git a/depends/settings/inisettingsobject.cpp b/depends/settings/inisettingsobject.cpp
index 5e52a56f..2cee8e3c 100644
--- a/depends/settings/inisettingsobject.cpp
+++ b/depends/settings/inisettingsobject.cpp
@@ -28,6 +28,11 @@ void INISettingsObject::setFilePath(const QString &filePath)
m_filePath = filePath;
}
+bool INISettingsObject::reload()
+{
+ return m_ini.loadFile(m_filePath) && SettingsObject::reload();
+}
+
void INISettingsObject::changeSetting(const Setting &setting, QVariant value)
{
if (contains(setting.id()))
diff --git a/depends/settings/inisettingsobject.h b/depends/settings/inisettingsobject.h
index 8badc0c6..12370896 100644
--- a/depends/settings/inisettingsobject.h
+++ b/depends/settings/inisettingsobject.h
@@ -47,6 +47,8 @@ public:
*/
virtual void setFilePath(const QString &filePath);
+ bool reload() override;
+
protected
slots:
virtual void changeSetting(const Setting &setting, QVariant value);
diff --git a/depends/settings/settingsobject.cpp b/depends/settings/settingsobject.cpp
index 43fc989a..0e3030df 100644
--- a/depends/settings/settingsobject.cpp
+++ b/depends/settings/settingsobject.cpp
@@ -126,6 +126,15 @@ bool SettingsObject::contains(const QString &id)
return m_settings.contains(id);
}
+bool SettingsObject::reload()
+{
+ for (auto setting : m_settings.values())
+ {
+ setting->set(setting->get());
+ }
+ return true;
+}
+
void SettingsObject::connectSignals(const Setting &setting)
{
connect(&setting, SIGNAL(settingChanged(const Setting &, QVariant)),
diff --git a/depends/settings/settingsobject.h b/depends/settings/settingsobject.h
index 27746f2d..b1b26b09 100644
--- a/depends/settings/settingsobject.h
+++ b/depends/settings/settingsobject.h
@@ -113,6 +113,12 @@ public:
*/
bool contains(const QString &id);
+ /*!
+ * \brief Reloads the settings and emit signals for changed settings
+ * \return True if reloading was successful
+ */
+ virtual bool reload();
+
signals:
/*!
* \brief Signal emitted when one of this SettingsObject object's settings changes.
diff --git a/gui/dialogs/OneSixModEditDialog.cpp b/gui/dialogs/OneSixModEditDialog.cpp
index fe621a9a..78585a05 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"
@@ -42,8 +42,7 @@
#include "logic/LiteLoaderInstaller.h"
#include "logic/OneSixVersionBuilder.h"
-template<typename A, typename B>
-QMap<A, B> invert(const QMap<B, A> &in)
+template <typename A, typename B> QMap<A, B> invert(const QMap<B, A> &in)
{
QMap<A, B> out;
for (auto it = in.begin(); it != in.end(); ++it)
@@ -96,7 +95,8 @@ OneSixModEditDialog::OneSixModEditDialog(OneSixInstance *inst, QWidget *parent)
m_resourcepacks->startWatching();
}
- connect(m_inst, &OneSixInstance::versionReloaded, this, &OneSixModEditDialog::updateVersionControls);
+ connect(m_inst, &OneSixInstance::versionReloaded, this,
+ &OneSixModEditDialog::updateVersionControls);
}
OneSixModEditDialog::~OneSixModEditDialog()
@@ -110,7 +110,6 @@ void OneSixModEditDialog::updateVersionControls()
{
ui->forgeBtn->setEnabled(true);
ui->liteloaderBtn->setEnabled(true);
- ui->mainClassEdit->setText(m_version->mainClass);
}
void OneSixModEditDialog::disableVersionControls()
@@ -119,125 +118,86 @@ void OneSixModEditDialog::disableVersionControls()
ui->liteloaderBtn->setEnabled(false);
ui->reloadLibrariesBtn->setEnabled(false);
ui->removeLibraryBtn->setEnabled(false);
- ui->mainClassEdit->setText("");
+}
+
+bool OneSixModEditDialog::reloadInstanceVersion()
+{
+ try
+ {
+ m_inst->reloadVersion();
+ return true;
+ }
+ catch (MMCError &e)
+ {
+ QMessageBox::critical(this, tr("Error"), e.cause());
+ return false;
+ }
+ catch (...)
+ {
+ QMessageBox::critical(
+ this, tr("Error"),
+ tr("Failed to load the version description file for reasons unknown."));
+ return false;
+ }
}
void OneSixModEditDialog::on_reloadLibrariesBtn_clicked()
{
- m_inst->reloadVersion(this);
+ reloadInstanceVersion();
}
void OneSixModEditDialog::on_removeLibraryBtn_clicked()
{
if (ui->libraryTreeView->currentIndex().isValid())
{
+ // FIXME: use actual model, not reloading.
if (!m_version->remove(ui->libraryTreeView->currentIndex().row()))
{
QMessageBox::critical(this, tr("Error"), tr("Couldn't remove file"));
}
else
{
- m_inst->reloadVersion(this);
+ reloadInstanceVersion();
}
}
}
void OneSixModEditDialog::on_resetLibraryOrderBtn_clicked()
{
- QDir(m_inst->instanceRoot()).remove("order.json");
- m_inst->reloadVersion(this);
+ // FIXME: IMPLEMENT LOGIC IN MODEL. SEE LEGACY DIALOG FOR EXAMPLE(S).
}
+
void OneSixModEditDialog::on_moveLibraryUpBtn_clicked()
{
-
- QMap<QString, int> order = getExistingOrder();
- if (order.size() < 2 || ui->libraryTreeView->selectionModel()->selectedIndexes().isEmpty())
- {
- return;
- }
- const int ourRow = ui->libraryTreeView->selectionModel()->selectedIndexes().first().row();
- const QString ourId = m_version->versionFileId(ourRow);
- const int ourOrder = order[ourId];
- if (ourId.isNull() || ourId.startsWith("org.multimc."))
- {
- return;
- }
-
- QMap<int, QString> sortedOrder = invert(order);
-
- QList<int> sortedOrders = sortedOrder.keys();
- const int ourIndex = sortedOrders.indexOf(ourOrder);
- if (ourIndex <= 0)
- {
- return;
- }
- const int ourNewOrder = sortedOrders.at(ourIndex - 1);
- order[ourId] = ourNewOrder;
- order[sortedOrder[sortedOrders[ourIndex - 1]]] = ourOrder;
-
- if (!OneSixVersionBuilder::writeOverrideOrders(order, m_inst))
- {
- QMessageBox::critical(this, tr("Error"), tr("Couldn't save the new order"));
- }
- else
- {
- m_inst->reloadVersion(this);
- ui->libraryTreeView->selectionModel()->select(m_version->index(ourRow - 1), QItemSelectionModel::SelectCurrent);
- }
+ // FIXME: IMPLEMENT LOGIC IN MODEL. SEE LEGACY DIALOG FOR EXAMPLE(S).
}
+
void OneSixModEditDialog::on_moveLibraryDownBtn_clicked()
{
- QMap<QString, int> order = getExistingOrder();
- if (order.size() < 2 || ui->libraryTreeView->selectionModel()->selectedIndexes().isEmpty())
- {
- return;
- }
- const int ourRow = ui->libraryTreeView->selectionModel()->selectedIndexes().first().row();
- const QString ourId = m_version->versionFileId(ourRow);
- const int ourOrder = order[ourId];
- if (ourId.isNull() || ourId.startsWith("org.multimc."))
- {
- return;
- }
-
- QMap<int, QString> sortedOrder = invert(order);
-
- QList<int> sortedOrders = sortedOrder.keys();
- const int ourIndex = sortedOrders.indexOf(ourOrder);
- if ((ourIndex + 1) >= sortedOrders.size())
- {
- return;
- }
- const int ourNewOrder = sortedOrders.at(ourIndex + 1);
- order[ourId] = ourNewOrder;
- order[sortedOrder[sortedOrders[ourIndex + 1]]] = ourOrder;
-
- if (!OneSixVersionBuilder::writeOverrideOrders(order, m_inst))
- {
- QMessageBox::critical(this, tr("Error"), tr("Couldn't save the new order"));
- }
- else
- {
- m_inst->reloadVersion(this);
- ui->libraryTreeView->selectionModel()->select(m_version->index(ourRow + 1), QItemSelectionModel::SelectCurrent);
- }
+ // FIXME: IMPLEMENT LOGIC IN MODEL. SEE LEGACY DIALOG FOR EXAMPLE(S).
}
void OneSixModEditDialog::on_forgeBtn_clicked()
{
+ // FIXME: use actual model, not reloading. Move logic to model.
+
+ // FIXME: model::isCustom();
if (QDir(m_inst->instanceRoot()).exists("custom.json"))
{
- if (QMessageBox::question(this, tr("Revert?"), tr("This action will remove your custom.json. Continue?")) != QMessageBox::Yes)
+ if (QMessageBox::question(this, tr("Revert?"),
+ tr("This action will remove your custom.json. Continue?")) !=
+ QMessageBox::Yes)
{
return;
}
+ // FIXME: model::revertToBase();
QDir(m_inst->instanceRoot()).remove("custom.json");
- m_inst->reloadVersion(this);
+ reloadInstanceVersion();
}
VersionSelectDialog vselect(MMC->forgelist().get(), tr("Select Forge version"), this);
vselect.setFilter(1, m_inst->currentVersionId());
vselect.setEmptyString(tr("No Forge versions are currently available for Minecraft ") +
- m_inst->currentVersionId());
+ m_inst->currentVersionId());
if (vselect.exec() && vselect.selectedVersion())
{
ForgeVersionPtr forgeVersion =
@@ -277,28 +237,32 @@ void OneSixModEditDialog::on_forgeBtn_clicked()
}
}
}
- m_inst->reloadVersion(this);
+ reloadInstanceVersion();
}
void OneSixModEditDialog::on_liteloaderBtn_clicked()
{
+ // FIXME: model...
if (QDir(m_inst->instanceRoot()).exists("custom.json"))
{
- if (QMessageBox::question(this, tr("Revert?"), tr("This action will remove your custom.json. Continue?")) != QMessageBox::Yes)
+ if (QMessageBox::question(this, tr("Revert?"),
+ tr("This action will remove your custom.json. Continue?")) !=
+ QMessageBox::Yes)
{
return;
}
QDir(m_inst->instanceRoot()).remove("custom.json");
- m_inst->reloadVersion(this);
+ reloadInstanceVersion();
}
- VersionSelectDialog vselect(MMC->liteloaderlist().get(), tr("Select LiteLoader version"), this);
+ VersionSelectDialog vselect(MMC->liteloaderlist().get(), tr("Select LiteLoader version"),
+ this);
vselect.setFilter(1, m_inst->currentVersionId());
vselect.setEmptyString(tr("No LiteLoader versions are currently available for Minecraft ") +
- m_inst->currentVersionId());
+ m_inst->currentVersionId());
if (vselect.exec() && vselect.selectedVersion())
{
LiteLoaderVersionPtr liteloaderVersion =
- std::dynamic_pointer_cast<LiteLoaderVersion>(vselect.selectedVersion());
+ std::dynamic_pointer_cast<LiteLoaderVersion>(vselect.selectedVersion());
if (!liteloaderVersion)
return;
LiteLoaderInstaller liteloader(liteloaderVersion);
@@ -310,7 +274,7 @@ void OneSixModEditDialog::on_liteloaderBtn_clicked()
}
else
{
- m_inst->reloadVersion(this);
+ reloadInstanceVersion();
}
}
}
@@ -347,35 +311,6 @@ bool OneSixModEditDialog::resourcePackListFilter(QKeyEvent *keyEvent)
return QDialog::eventFilter(ui->resPackTreeView, keyEvent);
}
-QMap<QString, int> OneSixModEditDialog::getExistingOrder() const
-{
-
- QMap<QString, int> order;
- // default
- {
- for (OneSixVersion::VersionFile file : m_version->versionFiles)
- {
- if (file.id.startsWith("org.multimc."))
- {
- continue;
- }
- order.insert(file.id, file.order);
- }
- }
- // overriden
- {
- QMap<QString, int> overridenOrder = OneSixVersionBuilder::readOverrideOrders(m_inst);
- for (auto id : order.keys())
- {
- if (overridenOrder.contains(id))
- {
- order[id] = overridenOrder[id];
- }
- }
- }
- return order;
-}
-
bool OneSixModEditDialog::eventFilter(QObject *obj, QEvent *ev)
{
if (ev->type() != QEvent::KeyPress)
@@ -461,7 +396,8 @@ void OneSixModEditDialog::loaderCurrent(QModelIndex current, QModelIndex previou
ui->frame->updateWithMod(m);
}
-void OneSixModEditDialog::versionCurrent(const QModelIndex &current, const QModelIndex &previous)
+void OneSixModEditDialog::versionCurrent(const QModelIndex &current,
+ const QModelIndex &previous)
{
if (!current.isValid())
{
diff --git a/gui/dialogs/OneSixModEditDialog.h b/gui/dialogs/OneSixModEditDialog.h
index f44b336b..e106c6fe 100644
--- a/gui/dialogs/OneSixModEditDialog.h
+++ b/gui/dialogs/OneSixModEditDialog.h
@@ -57,17 +57,17 @@ protected:
bool eventFilter(QObject *obj, QEvent *ev);
bool loaderListFilter(QKeyEvent *ev);
bool resourcePackListFilter(QKeyEvent *ev);
+ /// FIXME: this shouldn't be necessary!
+ bool reloadInstanceVersion();
private:
Ui::OneSixModEditDialog *ui;
- std::shared_ptr<OneSixVersion> m_version;
+ std::shared_ptr<VersionFinal> m_version;
std::shared_ptr<ModList> m_mods;
std::shared_ptr<ModList> m_resourcepacks;
EnabledItemFilter *main_model;
OneSixInstance *m_inst;
- QMap<QString, int> getExistingOrder() const;
-
public
slots:
void loaderCurrent(QModelIndex current, QModelIndex previous);
diff --git a/gui/dialogs/OneSixModEditDialog.ui b/gui/dialogs/OneSixModEditDialog.ui
index eaf8f7fd..b606dcd2 100644
--- a/gui/dialogs/OneSixModEditDialog.ui
+++ b/gui/dialogs/OneSixModEditDialog.ui
@@ -48,24 +48,6 @@
</attribute>
</widget>
</item>
- <item>
- <layout class="QHBoxLayout" name="horizontalLayout_7">
- <item>
- <widget class="QLabel" name="label">
- <property name="text">
- <string>Main Class:</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QLineEdit" name="mainClassEdit">
- <property name="enabled">
- <bool>false</bool>
- </property>
- </widget>
- </item>
- </layout>
- </item>
</layout>
</item>
<item>
@@ -109,13 +91,6 @@
</widget>
</item>
<item>
- <widget class="QPushButton" name="resetLibraryOrderBtn">
- <property name="text">
- <string>Reset order</string>
- </property>
- </widget>
- </item>
- <item>
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
@@ -124,6 +99,12 @@
</item>
<item>
<widget class="QPushButton" name="moveLibraryUpBtn">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="toolTip">
+ <string>This isn't implemented yet.</string>
+ </property>
<property name="text">
<string>Move up</string>
</property>
@@ -131,12 +112,31 @@
</item>
<item>
<widget class="QPushButton" name="moveLibraryDownBtn">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="toolTip">
+ <string>This isn't implemented yet.</string>
+ </property>
<property name="text">
<string>Move down</string>
</property>
</widget>
</item>
<item>
+ <widget class="QPushButton" name="resetLibraryOrderBtn">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="toolTip">
+ <string>This isn't implemented yet.</string>
+ </property>
+ <property name="text">
+ <string>Reset order</string>
+ </property>
+ </widget>
+ </item>
+ <item>
<spacer name="verticalSpacer_7">
<property name="orientation">
<enum>Qt::Vertical</enum>
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 <QFile>
-#include "OneSixVersion.h"
+#include "VersionFinal.h"
#include "OneSixLibrary.h"
#include "OneSixInstance.h"
diff --git a/logic/BaseInstance.cpp b/logic/BaseInstance.cpp
index d78f1ea0..c565ab59 100644
--- a/logic/BaseInstance.cpp
+++ b/logic/BaseInstance.cpp
@@ -169,6 +169,11 @@ bool BaseInstance::canLaunch() const
return !(flags() & VersionBrokenFlag);
}
+bool BaseInstance::reload()
+{
+ return settings().reload();
+}
+
QString BaseInstance::baseJar() const
{
I_D(BaseInstance);
diff --git a/logic/BaseInstance.h b/logic/BaseInstance.h
index 27f939bc..195fd339 100644
--- a/logic/BaseInstance.h
+++ b/logic/BaseInstance.h
@@ -190,6 +190,8 @@ public:
bool canLaunch() const;
+ virtual bool reload();
+
signals:
/*!
* \brief Signal emitted when properties relevant to the instance view change
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 <quazip.h>
@@ -33,7 +33,7 @@
ForgeInstaller::ForgeInstaller(QString filename, QString universal_url)
{
- std::shared_ptr<OneSixVersion> newVersion;
+ std::shared_ptr<VersionFinal> 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 <QString>
#include <memory>
-class OneSixVersion;
+class VersionFinal;
class ForgeInstaller : public BaseInstaller
{
@@ -33,7 +33,7 @@ public:
private:
// the version, read from the installer
- std::shared_ptr<OneSixVersion> m_forge_version;
+ std::shared_ptr<VersionFinal> m_forge_version;
QString internalPath;
QString finalPath;
QString realVersionId;
diff --git a/logic/LiteLoaderInstaller.cpp b/logic/LiteLoaderInstaller.cpp
index 126027fb..bb4b07ca 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"
@@ -69,7 +69,7 @@ bool LiteLoaderInstaller::add(OneSixInstance *to)
obj.insert("+libraries", libraries);
obj.insert("name", QString("LiteLoader"));
obj.insert("fileId", id());
- obj.insert("version", to->intendedVersionId());
+ obj.insert("version", m_version->version);
obj.insert("mcVersion", to->intendedVersionId());
QFile file(filename(to->instanceRoot()));
diff --git a/logic/MMCJson.cpp b/logic/MMCJson.cpp
new file mode 100644
index 00000000..80d36204
--- /dev/null
+++ b/logic/MMCJson.cpp
@@ -0,0 +1,60 @@
+#include "MMCJson.h"
+#include <QString>
+
+bool MMCJson::ensureBoolean(const QJsonValue val, const QString what)
+{
+ if (!val.isBool())
+ throw JSONValidationError(what + " is not boolean");
+ return val.isBool();
+}
+
+QJsonValue MMCJson::ensureExists(QJsonValue val, const QString what)
+{
+ if(val.isNull())
+ throw JSONValidationError(what + " does not exist");
+ return val;
+}
+
+QJsonArray MMCJson::ensureArray(const QJsonValue val, const QString what)
+{
+ if (!val.isArray())
+ throw JSONValidationError(what + " is not an array");
+ return val.toArray();
+}
+
+double MMCJson::ensureDouble(const QJsonValue val, const QString what)
+{
+ if (!val.isDouble())
+ throw JSONValidationError(what + " is not a number");
+ double ret = val.toDouble();
+}
+
+int MMCJson::ensureInteger(const QJsonValue val, const QString what)
+{
+ double ret = ensureDouble(val, what);
+ if (fmod(ret, 1) != 0)
+ throw JSONValidationError(what + " is not an integer");
+ return ret;
+}
+
+QJsonObject MMCJson::ensureObject(const QJsonValue val, const QString what)
+{
+ if (!val.isObject())
+ throw JSONValidationError(what + " is not an object");
+ return val.toObject();
+}
+
+QJsonObject MMCJson::ensureObject(const QJsonDocument val, const QString what)
+{
+ if (!val.isObject())
+ throw JSONValidationError(what + " is not an object");
+ return val.object();
+}
+
+QString MMCJson::ensureString(const QJsonValue val, const QString what)
+{
+ if (!val.isString())
+ throw JSONValidationError(what + " is not a string");
+ return val.toString();
+}
+
diff --git a/logic/MMCJson.h b/logic/MMCJson.h
new file mode 100644
index 00000000..f2cc4b31
--- /dev/null
+++ b/logic/MMCJson.h
@@ -0,0 +1,46 @@
+/**
+ * Some de-bullshitting for Qt JSON failures.
+ *
+ * Simple exception-throwing
+ */
+
+#pragma once
+#include <QJsonValue>
+#include <QJsonObject>
+#include <QJsonDocument>
+#include <QJsonArray>
+#include "MMCError.h"
+
+class JSONValidationError : public MMCError
+{
+public:
+ JSONValidationError(QString cause) : MMCError(cause) {};
+ virtual ~JSONValidationError() {};
+};
+
+namespace MMCJson
+{
+/// make sure the value exists. throw otherwise.
+QJsonValue ensureExists(QJsonValue val, const QString what = "value");
+
+/// make sure the value is converted into an object. throw otherwise.
+QJsonObject ensureObject(const QJsonValue val, const QString what = "value");
+
+/// make sure the document is converted into an object. throw otherwise.
+QJsonObject ensureObject(const QJsonDocument val, const QString what = "value");
+
+/// make sure the value is converted into an array. throw otherwise.
+QJsonArray ensureArray(const QJsonValue val, QString what = "value");
+
+/// make sure the value is converted into a string. throw otherwise.
+QString ensureString(const QJsonValue val, QString what = "value");
+
+/// make sure the value is converted into a boolean. throw otherwise.
+bool ensureBoolean(const QJsonValue val, QString what = "value");
+
+/// make sure the value is converted into an integer. throw otherwise.
+int ensureInteger(const QJsonValue val, QString what = "value");
+
+/// make sure the value is converted into a double precision floating number. throw otherwise.
+double ensureDouble(const QJsonValue val, QString what = "value");
+}
diff --git a/logic/MinecraftProcess.cpp b/logic/MinecraftProcess.cpp
index e26459ab..effa1095 100644
--- a/logic/MinecraftProcess.cpp
+++ b/logic/MinecraftProcess.cpp
@@ -49,9 +49,11 @@ MinecraftProcess::MinecraftProcess(BaseInstance *inst) : m_instance(inst)
#endif
// export some infos
- env.insert("INST_NAME", inst->name());
- env.insert("INST_ID", inst->id());
- env.insert("INST_DIR", QDir(inst->instanceRoot()).absolutePath());
+ auto variables = getVariables();
+ for (auto it = variables.begin(); it != variables.end(); ++it)
+ {
+ env.insert(it.key(), it.value());
+ }
this->setProcessEnvironment(env);
m_prepostlaunchprocess.setProcessEnvironment(env);
@@ -63,10 +65,10 @@ MinecraftProcess::MinecraftProcess(BaseInstance *inst) : m_instance(inst)
// Log prepost launch command output (can be disabled.)
if (m_instance->settings().get("LogPrePostOutput").toBool())
{
- connect(&m_prepostlaunchprocess, &QProcess::readyReadStandardError,
- this, &MinecraftProcess::on_prepost_stdErr);
- connect(&m_prepostlaunchprocess, &QProcess::readyReadStandardOutput,
- this, &MinecraftProcess::on_prepost_stdOut);
+ connect(&m_prepostlaunchprocess, &QProcess::readyReadStandardError, this,
+ &MinecraftProcess::on_prepost_stdErr);
+ connect(&m_prepostlaunchprocess, &QProcess::readyReadStandardOutput, this,
+ &MinecraftProcess::on_prepost_stdOut);
}
}
@@ -79,10 +81,10 @@ void MinecraftProcess::setWorkdir(QString path)
QString MinecraftProcess::censorPrivateInfo(QString in)
{
- if(!m_session)
+ if (!m_session)
return in;
- if(m_session->session != "-")
+ if (m_session->session != "-")
in.replace(m_session->session, "<SESSION ID>");
in.replace(m_session->access_token, "<ACCESS TOKEN>");
in.replace(m_session->client_token, "<CLIENT TOKEN>");
@@ -113,7 +115,7 @@ MessageLevel::Enum MinecraftProcess::guessLevel(const QString &line, MessageLeve
level = MessageLevel::Fatal;
if (line.contains("[DEBUG]"))
level = MessageLevel::Debug;
- if(line.contains("overwriting existing"))
+ if (line.contains("overwriting existing"))
level = MessageLevel::Fatal;
return level;
}
@@ -139,17 +141,15 @@ MessageLevel::Enum MinecraftProcess::getLevel(const QString &levelName)
return MessageLevel::Message;
}
-void MinecraftProcess::logOutput(const QStringList &lines,
- MessageLevel::Enum defaultLevel,
+void MinecraftProcess::logOutput(const QStringList &lines, MessageLevel::Enum defaultLevel,
bool guessLevel, bool censor)
{
for (int i = 0; i < lines.size(); ++i)
logOutput(lines[i], defaultLevel, guessLevel, censor);
}
-void MinecraftProcess::logOutput(QString line,
- MessageLevel::Enum defaultLevel,
- bool guessLevel, bool censor)
+void MinecraftProcess::logOutput(QString line, MessageLevel::Enum defaultLevel, bool guessLevel,
+ bool censor)
{
MessageLevel::Enum level = defaultLevel;
@@ -251,33 +251,7 @@ void MinecraftProcess::finish(int code, ExitStatus status)
m_prepostlaunchprocess.processEnvironment().insert("INST_EXITCODE", QString(code));
// run post-exit
- QString postlaunch_cmd = m_instance->settings().get("PostExitCommand").toString();
- if (!postlaunch_cmd.isEmpty())
- {
- emit log(tr("Running Post-Launch command: %1").arg(postlaunch_cmd));
- m_prepostlaunchprocess.start(postlaunch_cmd);
- m_prepostlaunchprocess.waitForFinished();
- // Flush console window
- if (!m_err_leftover.isEmpty())
- {
- logOutput(m_err_leftover, MessageLevel::PrePost);
- m_err_leftover.clear();
- }
- if (!m_out_leftover.isEmpty())
- {
- logOutput(m_out_leftover, MessageLevel::PrePost);
- m_out_leftover.clear();
- }
- if (m_prepostlaunchprocess.exitStatus() != NormalExit)
- {
- emit log(tr("Post-Launch command failed with code %1.\n\n").arg(m_prepostlaunchprocess.exitCode()),
- MessageLevel::Error);
- emit postlaunch_failed(m_instance, m_prepostlaunchprocess.exitCode(),
- m_prepostlaunchprocess.exitStatus());
- }
- else
- emit log(tr("Post-Launch command ran successfully.\n\n"));
- }
+ postLaunch();
m_instance->cleanupAfterRun();
emit ended(m_instance, code, status);
}
@@ -288,14 +262,12 @@ void MinecraftProcess::killMinecraft()
kill();
}
-void MinecraftProcess::arm()
+bool MinecraftProcess::preLaunch()
{
- emit log("MultiMC version: " + MMC->version().toString() + "\n\n");
- emit log("Minecraft folder is:\n" + workingDirectory() + "\n\n");
-
QString prelaunch_cmd = m_instance->settings().get("PreLaunchCommand").toString();
if (!prelaunch_cmd.isEmpty())
{
+ prelaunch_cmd = substituteVariables(prelaunch_cmd);
// Launch
emit log(tr("Running Pre-Launch command: %1").arg(prelaunch_cmd));
m_prepostlaunchprocess.start(prelaunch_cmd);
@@ -315,47 +287,129 @@ void MinecraftProcess::arm()
// Process return values
if (m_prepostlaunchprocess.exitStatus() != NormalExit)
{
- emit log(tr("Pre-Launch command failed with code %1.\n\n").arg(m_prepostlaunchprocess.exitCode()),
+ emit log(tr("Pre-Launch command failed with code %1.\n\n")
+ .arg(m_prepostlaunchprocess.exitCode()),
MessageLevel::Fatal);
m_instance->cleanupAfterRun();
emit prelaunch_failed(m_instance, m_prepostlaunchprocess.exitCode(),
m_prepostlaunchprocess.exitStatus());
- return;
+ return false;
}
else
emit log(tr("Pre-Launch command ran successfully.\n\n"));
+
+ return m_instance->reload();
+ }
+ return true;
+}
+bool MinecraftProcess::postLaunch()
+{
+ QString postlaunch_cmd = m_instance->settings().get("PostExitCommand").toString();
+ if (!postlaunch_cmd.isEmpty())
+ {
+ postlaunch_cmd = substituteVariables(postlaunch_cmd);
+ emit log(tr("Running Post-Launch command: %1").arg(postlaunch_cmd));
+ m_prepostlaunchprocess.start(postlaunch_cmd);
+ m_prepostlaunchprocess.waitForFinished();
+ // Flush console window
+ if (!m_err_leftover.isEmpty())
+ {
+ logOutput(m_err_leftover, MessageLevel::PrePost);
+ m_err_leftover.clear();
+ }
+ if (!m_out_leftover.isEmpty())
+ {
+ logOutput(m_out_leftover, MessageLevel::PrePost);
+ m_out_leftover.clear();
+ }
+ if (m_prepostlaunchprocess.exitStatus() != NormalExit)
+ {
+ emit log(tr("Post-Launch command failed with code %1.\n\n")
+ .arg(m_prepostlaunchprocess.exitCode()),
+ MessageLevel::Error);
+ emit postlaunch_failed(m_instance, m_prepostlaunchprocess.exitCode(),
+ m_prepostlaunchprocess.exitStatus());
+ }
+ else
+ emit log(tr("Post-Launch command ran successfully.\n\n"));
+
+ return m_instance->reload();
}
+ return true;
+}
- m_instance->setLastLaunch();
- auto &settings = m_instance->settings();
+QMap<QString, QString> MinecraftProcess::getVariables() const
+{
+ QMap<QString, QString> out;
+ out.insert("INST_NAME", m_instance->name());
+ out.insert("INST_ID", m_instance->id());
+ out.insert("INST_DIR", QDir(m_instance->instanceRoot()).absolutePath());
+ out.insert("INST_MC_DIR", QDir(m_instance->minecraftRoot()).absolutePath());
+ out.insert("INST_JAVA", m_instance->settings().get("JavaPath").toString());
+ out.insert("INST_JAVA_ARGS", javaArguments().join(' '));
+ return out;
+}
+QString MinecraftProcess::substituteVariables(const QString &cmd) const
+{
+ QString out = cmd;
+ auto variables = getVariables();
+ for (auto it = variables.begin(); it != variables.end(); ++it)
+ {
+ out.replace("$" + it.key(), it.value());
+ }
+ auto env = QProcessEnvironment::systemEnvironment();
+ for (auto var : env.keys())
+ {
+ out.replace("$" + var, env.value(var));
+ }
+ return out;
+}
- //////////// java arguments ////////////
+QStringList MinecraftProcess::javaArguments() const
+{
QStringList args;
+
+ // custom args go first. we want to override them if we have our own here.
+ args.append(m_instance->extraArguments());
+
+// OSX dock icon and name
+#ifdef OSX
+ args << "-Xdock:icon=icon.png";
+ args << QString("-Xdock:name=\"%1\"").arg(m_instance->windowTitle());
+#endif
+
+// HACK: Stupid hack for Intel drivers. See: https://mojang.atlassian.net/browse/MCL-767
+#ifdef Q_OS_WIN32
+ args << QString("-XX:HeapDumpPath=MojangTricksIntelDriversForPerformance_javaw.exe_"
+ "minecraft.exe.heapdump");
+#endif
+
+ args << QString("-Xms%1m").arg(m_instance->settings().get("MinMemAlloc").toInt());
+ args << QString("-Xmx%1m").arg(m_instance->settings().get("MaxMemAlloc").toInt());
+ args << QString("-XX:PermSize=%1m").arg(m_instance->settings().get("PermGen").toInt());
+ args << "-Duser.language=en";
+ if (!m_nativeFolder.isEmpty())
+ args << QString("-Djava.library.path=%1").arg(m_nativeFolder);
+ args << "-jar" << PathCombine(MMC->bin(), "jars", "NewLaunch.jar");
+
+ return args;
+}
+
+void MinecraftProcess::arm()
+{
+ emit log("MultiMC version: " + MMC->version().toString() + "\n\n");
+ emit log("Minecraft folder is:\n" + workingDirectory() + "\n\n");
+
+ if (!preLaunch())
{
- // custom args go first. we want to override them if we have our own here.
- args.append(m_instance->extraArguments());
-
- // OSX dock icon and name
- #ifdef OSX
- args << "-Xdock:icon=icon.png";
- args << QString("-Xdock:name=\"%1\"").arg(m_instance->windowTitle());
- #endif
-
- // HACK: Stupid hack for Intel drivers. See: https://mojang.atlassian.net/browse/MCL-767
- #ifdef Q_OS_WIN32
- args << QString("-XX:HeapDumpPath=MojangTricksIntelDriversForPerformance_javaw.exe_"
- "minecraft.exe.heapdump");
- #endif
-
- args << QString("-Xms%1m").arg(settings.get("MinMemAlloc").toInt());
- args << QString("-Xmx%1m").arg(settings.get("MaxMemAlloc").toInt());
- args << QString("-XX:PermSize=%1m").arg(settings.get("PermGen").toInt());
- args << "-Duser.language=en";
- if(!m_nativeFolder.isEmpty())
- args << QString("-Djava.library.path=%1").arg(m_nativeFolder);
- args << "-jar" << PathCombine(MMC->bin(), "jars", "NewLaunch.jar");
+ return;
}
+ m_instance->setLastLaunch();
+ auto &settings = m_instance->settings();
+
+ QStringList args = javaArguments();
+
QString JavaPath = m_instance->settings().get("JavaPath").toString();
emit log("Java path is:\n" + JavaPath + "\n\n");
QString allArgs = args.join(", ");
diff --git a/logic/MinecraftProcess.h b/logic/MinecraftProcess.h
index 56340962..d91dad56 100644
--- a/logic/MinecraftProcess.h
+++ b/logic/MinecraftProcess.h
@@ -131,6 +131,13 @@ protected:
QString launchScript;
QString m_nativeFolder;
+ bool preLaunch();
+ bool postLaunch();
+ QMap<QString, QString> getVariables() const;
+ QString substituteVariables(const QString &cmd) const;
+
+ QStringList javaArguments() const;
+
protected
slots:
void finish(int, QProcess::ExitStatus status);
diff --git a/logic/OneSixFTBInstance.cpp b/logic/OneSixFTBInstance.cpp
index 91efce8e..8f70ed08 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"
@@ -10,76 +10,6 @@
#include "MultiMC.h"
#include "pathutils.h"
-class OneSixFTBInstanceForge : public Task
-{
- Q_OBJECT
-public:
- explicit OneSixFTBInstanceForge(const QString &version, OneSixFTBInstance *inst, QObject *parent = 0) :
- Task(parent), instance(inst), version("Forge " + version)
- {
- }
-
- void executeTask()
- {
- for (int i = 0; i < MMC->forgelist()->count(); ++i)
- {
- if (MMC->forgelist()->at(i)->name() == version)
- {
- forgeVersion = std::dynamic_pointer_cast<ForgeVersion>(MMC->forgelist()->at(i));
- break;
- }
- }
- if (!forgeVersion)
- {
- emitFailed(QString("Couldn't find forge version ") + version );
- return;
- }
- entry = MMC->metacache()->resolveEntry("minecraftforge", forgeVersion->filename);
- if (entry->stale)
- {
- setStatus(tr("Downloading Forge..."));
- fjob = new NetJob("Forge download");
- fjob->addNetAction(CacheDownload::make(forgeVersion->installer_url, entry));
- connect(fjob, &NetJob::failed, [this](){emitFailed(m_failReason);});
- connect(fjob, &NetJob::succeeded, this, &OneSixFTBInstanceForge::installForge);
- connect(fjob, &NetJob::progress, [this](qint64 c, qint64 total){ setProgress(100 * c / total); });
- fjob->start();
- }
- else
- {
- installForge();
- }
- }
-
-private
-slots:
- void installForge()
- {
- setStatus(tr("Installing Forge..."));
- QString forgePath = entry->getFullPath();
- ForgeInstaller forge(forgePath, forgeVersion->universal_url);
- if (!instance->reloadVersion())
- {
- emitFailed(tr("Couldn't load the version config"));
- return;
- }
- auto version = instance->getFullVersion();
- if (!forge.add(instance))
- {
- emitFailed(tr("Couldn't install Forge"));
- return;
- }
- emitSucceeded();
- }
-
-private:
- OneSixFTBInstance *instance;
- QString version;
- ForgeVersionPtr forgeVersion;
- MetaEntryPtr entry;
- NetJob *fjob;
-};
-
OneSixFTBInstance::OneSixFTBInstance(const QString &rootDir, SettingsObject *settings, QObject *parent) :
OneSixInstance(rootDir, settings, parent)
{
@@ -87,7 +17,14 @@ OneSixFTBInstance::OneSixFTBInstance(const QString &rootDir, SettingsObject *set
void OneSixFTBInstance::init()
{
- reloadVersion();
+ try
+ {
+ reloadVersion();
+ }
+ catch(MMCError & e)
+ {
+ // QLOG_ERROR() << "Caught exception on instance init: " << e.cause();
+ }
}
void OneSixFTBInstance::copy(const QDir &newDir)
diff --git a/logic/OneSixInstance.cpp b/logic/OneSixInstance.cpp
index 10411c56..ed7ad993 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"
@@ -27,6 +27,7 @@
#include "icons/IconList.h"
#include "MinecraftProcess.h"
#include "gui/dialogs/OneSixModEditDialog.h"
+#include <MMCError.h>
OneSixInstance::OneSixInstance(const QString &rootDir, SettingsObject *settings, QObject *parent)
: BaseInstance(new OneSixInstancePrivate(), rootDir, settings, parent)
@@ -34,15 +35,23 @@ 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()
{
+ // FIXME: why is this decided here? what does this even mean?
if (QDir(instanceRoot()).exists("version.json"))
{
- reloadVersion();
+ try
+ {
+ reloadVersion();
+ }
+ catch(MMCError & e)
+ {
+ // QLOG_ERROR() << "Caught exception on instance init: " << e.cause();
+ }
}
else
{
@@ -79,7 +88,7 @@ QString replaceTokensIn(QString text, QMap<QString, QString> with)
return result;
}
-QDir OneSixInstance::reconstructAssets(std::shared_ptr<OneSixVersion> version)
+QDir OneSixInstance::reconstructAssets(std::shared_ptr<VersionFinal> version)
{
QDir assetsDir = QDir("assets/");
QDir indexDir = QDir(PathCombine(assetsDir.path(), "indexes"));
@@ -316,25 +325,26 @@ QString OneSixInstance::currentVersionId() const
return intendedVersionId();
}
-bool OneSixInstance::reloadVersion(QWidget *widgetParent)
+void OneSixInstance::reloadVersion()
{
I_D(OneSixInstance);
- bool ret = d->version->reload(widgetParent, false, externalPatches());
- if (ret)
- {
- ret = d->vanillaVersion->reload(widgetParent, true, externalPatches());
- }
- if (ret)
+ try
{
+ d->version->reload(false, externalPatches());
+ d->vanillaVersion->reload(true, externalPatches());
setFlags(flags() & ~VersionBrokenFlag);
emit versionReloaded();
}
- else
+ catch(MMCError & error)
{
+ d->version->clear();
+ d->vanillaVersion->clear();
setFlags(flags() | VersionBrokenFlag);
+ //TODO: rethrow to show some error message(s)?
+ emit versionReloaded();
+ throw;
}
- return ret;
}
void OneSixInstance::clearVersion()
@@ -345,13 +355,13 @@ void OneSixInstance::clearVersion()
emit versionReloaded();
}
-std::shared_ptr<OneSixVersion> OneSixInstance::getFullVersion() const
+std::shared_ptr<VersionFinal> OneSixInstance::getFullVersion() const
{
I_D(const OneSixInstance);
return d->version;
}
-std::shared_ptr<OneSixVersion> OneSixInstance::getVanillaVersion() const
+std::shared_ptr<VersionFinal> OneSixInstance::getVanillaVersion() const
{
I_D(const OneSixInstance);
return d->vanillaVersion;
@@ -413,6 +423,23 @@ bool OneSixInstance::providesVersionFile() const
return false;
}
+bool OneSixInstance::reload()
+{
+ if(BaseInstance::reload())
+ {
+ try
+ {
+ reloadVersion();
+ return true;
+ }
+ catch (...)
+ {
+ return false;
+ }
+ }
+ return false;
+}
+
QString OneSixInstance::loaderModsDir() const
{
return PathCombine(minecraftRoot(), "mods");
diff --git a/logic/OneSixInstance.h b/logic/OneSixInstance.h
index 06fd9de3..7a049922 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
@@ -53,14 +53,18 @@ public:
virtual QDialog *createModEditDialog(QWidget *parent) override;
- /// reload the full version json files. return true on success!
- bool reloadVersion(QWidget *widgetParent = 0);
+ /**
+ * reload the full version json files. return true on success!
+ *
+ * throws various exceptions :3
+ */
+ void reloadVersion();
/// clears all version information in preparation for an update
void clearVersion();
/// get the current full version info
- std::shared_ptr<OneSixVersion> getFullVersion() const;
+ std::shared_ptr<VersionFinal> getFullVersion() const;
/// gets the current version info, but only for version.json
- std::shared_ptr<OneSixVersion> getVanillaVersion() const;
+ std::shared_ptr<VersionFinal> getVanillaVersion() const;
/// is the current version original, or custom?
virtual bool versionIsCustom() override;
@@ -75,10 +79,12 @@ public:
virtual QStringList externalPatches() const;
virtual bool providesVersionFile() const;
+ bool reload() override;
+
signals:
void versionReloaded();
private:
QStringList processMinecraftArgs(AuthSessionPtr account);
- QDir reconstructAssets(std::shared_ptr<OneSixVersion> version);
+ QDir reconstructAssets(std::shared_ptr<VersionFinal> 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<OneSixVersion> version;
- std::shared_ptr<OneSixVersion> vanillaVersion;
+ std::shared_ptr<VersionFinal> version;
+ std::shared_ptr<VersionFinal> vanillaVersion;
std::shared_ptr<ModList> loader_mod_list;
std::shared_ptr<ModList> resource_pack_list;
};
diff --git a/logic/OneSixLibrary.h b/logic/OneSixLibrary.h
index 371ca6f4..3bd21c51 100644
--- a/logic/OneSixLibrary.h
+++ b/logic/OneSixLibrary.h
@@ -26,6 +26,9 @@
class Rule;
+class OneSixLibrary;
+typedef std::shared_ptr<OneSixLibrary> OneSixLibraryPtr;
+
class OneSixLibrary
{
private:
diff --git a/logic/OneSixUpdate.cpp b/logic/OneSixUpdate.cpp
index f87c65e7..65f30cda 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"
@@ -48,7 +48,7 @@ void OneSixUpdate::executeTask()
QDir mcDir(m_inst->minecraftRoot());
if (!mcDir.exists() && !mcDir.mkpath("."))
{
- emitFailed("Failed to create bin folder.");
+ emitFailed(tr("Failed to create folder for minecraft binaries."));
return;
}
@@ -60,7 +60,7 @@ void OneSixUpdate::executeTask()
if (targetVersion == nullptr)
{
// don't do anything if it was invalid
- emitFailed("The specified Minecraft version is invalid. Choose a different one.");
+ emitFailed(tr("The specified Minecraft version is invalid. Choose a different one."));
return;
}
versionFileStart();
@@ -108,20 +108,19 @@ void OneSixUpdate::versionFileFinished()
QSaveFile vfile1(version1);
if (!vfile1.open(QIODevice::Truncate | QIODevice::WriteOnly))
{
- emitFailed("Can't open " + version1 + " for writing.");
+ emitFailed(tr("Can't open %1 for writing.").arg(version1));
return;
}
auto data = std::dynamic_pointer_cast<ByteArrayDownload>(DlJob)->m_data;
qint64 actual = 0;
if ((actual = vfile1.write(data)) != data.size())
{
- emitFailed("Failed to write into " + version1 + ". Written " + actual + " out of " +
- data.size() + '.');
+ emitFailed(tr("Failed to write into %1. Written %2 out of %3.").arg(version1).arg(actual).arg(data.size()));
return;
}
if (!vfile1.commit())
{
- emitFailed("Can't commit changes to " + version1);
+ emitFailed(tr("Can't commit changes to %1").arg(version1));
return;
}
}
@@ -136,21 +135,20 @@ void OneSixUpdate::versionFileFinished()
{
finfo.remove();
}
- inst->reloadVersion();
-
+ // NOTE: Version is reloaded in jarlibStart
jarlibStart();
}
void OneSixUpdate::versionFileFailed()
{
- emitFailed("Failed to download the version description. Try again.");
+ emitFailed(tr("Failed to download the version description. Try again."));
}
void OneSixUpdate::assetIndexStart()
{
setStatus(tr("Updating assets index..."));
OneSixInstance *inst = (OneSixInstance *)m_inst;
- std::shared_ptr<OneSixVersion> version = inst->getFullVersion();
+ std::shared_ptr<VersionFinal> version = inst->getFullVersion();
QString assetName = version->assets;
QUrl indexUrl = "http://" + URLConstants::AWS_DOWNLOAD_INDEXES + assetName + ".json";
QString localPath = assetName + ".json";
@@ -174,13 +172,13 @@ void OneSixUpdate::assetIndexFinished()
AssetsIndex index;
OneSixInstance *inst = (OneSixInstance *)m_inst;
- std::shared_ptr<OneSixVersion> version = inst->getFullVersion();
+ std::shared_ptr<VersionFinal> version = inst->getFullVersion();
QString assetName = version->assets;
QString asset_fname = "assets/indexes/" + assetName + ".json";
if (!AssetsUtils::loadAssetsIndexJson(asset_fname, &index))
{
- emitFailed("Failed to read the assets index!");
+ emitFailed(tr("Failed to read the assets index!"));
}
QList<Md5EtagDownloadPtr> dls;
@@ -216,7 +214,7 @@ void OneSixUpdate::assetIndexFinished()
void OneSixUpdate::assetIndexFailed()
{
- emitFailed("Failed to download the assets index!");
+ emitFailed(tr("Failed to download the assets index!"));
}
void OneSixUpdate::assetsFinished()
@@ -226,7 +224,7 @@ void OneSixUpdate::assetsFinished()
void OneSixUpdate::assetsFailed()
{
- emitFailed("Failed to download assets!");
+ emitFailed(tr("Failed to download assets!"));
}
void OneSixUpdate::jarlibStart()
@@ -234,16 +232,23 @@ 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->reloadVersion();
- if (!successful)
+ try
+ {
+ inst->reloadVersion();
+ }
+ catch(MMCError & e)
+ {
+ emitFailed(e.cause());
+ return;
+ }
+ catch(...)
{
- emitFailed("Failed to load the version description file. It might be "
- "corrupted, missing or simply too new.");
+ emitFailed(tr("Failed to load the version description file for reasons unknown."));
return;
}
// Build a list of URLs that will need to be downloaded.
- std::shared_ptr<OneSixVersion> version = inst->getFullVersion();
+ std::shared_ptr<VersionFinal> version = inst->getFullVersion();
// minecraft.jar for this version
{
QString version_id = version->id;
@@ -326,6 +331,5 @@ void OneSixUpdate::jarlibFailed()
{
QStringList failed = jarlibDownloadJob->getFailedFiles();
QString failed_all = failed.join("\n");
- emitFailed("Failed to download the following files:\n" + failed_all +
- "\n\nPlease try again.");
+ emitFailed(tr("Failed to download the following files:\n%1\n\nPlease try again.").arg(failed_all));
}
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 <QDebug>
-#include <QFile>
-
-#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<std::shared_ptr<OneSixLibrary> > OneSixVersion::getActiveNormalLibs()
-{
- QList<std::shared_ptr<OneSixLibrary> > output;
- for (auto lib : libraries)
- {
- if (lib->isActive() && !lib->isNative())
- {
- output.append(lib);
- }
- }
- return output;
-}
-
-QList<std::shared_ptr<OneSixLibrary> > OneSixVersion::getActiveNativeLibs()
-{
- QList<std::shared_ptr<OneSixLibrary> > output;
- for (auto lib : libraries)
- {
- if (lib->isActive() && lib->isNative())
- {
- output.append(lib);
- }
- }
- return output;
-}
-
-std::shared_ptr<OneSixVersion> OneSixVersion::fromJson(const QJsonObject &obj)
-{
- std::shared_ptr<OneSixVersion> 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/OneSixVersionBuilder.cpp b/logic/OneSixVersionBuilder.cpp
index f6917697..8eacbce4 100644
--- a/logic/OneSixVersionBuilder.cpp
+++ b/logic/OneSixVersionBuilder.cpp
@@ -26,806 +26,37 @@
#include <QDir>
#include <QDebug>
-#include "OneSixVersion.h"
+#include "VersionFinal.h"
#include "OneSixInstance.h"
#include "OneSixRule.h"
+#include "VersionFile.h"
+#include "MMCJson.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<QString, QString> 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<QPair<OpSys, QString>> natives;
- bool applyRules = false;
- QList<std::shared_ptr<Rule>> 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<Library> overwriteLibs;
- QList<Library> addLibs;
- QList<QString> 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<OneSixLibrary> createLibrary(const Library &lib)
- {
- std::shared_ptr<OneSixLibrary> 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<std::shared_ptr<OneSixLibrary>> 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,
- QWidget *widgetParent, const bool onlyVanilla, const QStringList &external)
+void OneSixVersionBuilder::build(VersionFinal *version, OneSixInstance *instance,
+ 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);
+ builder.buildInternal(onlyVanilla, external);
}
-bool OneSixVersionBuilder::read(OneSixVersion *version, const QJsonObject &obj)
+void 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);
+ builder.readJsonAndApply(obj);
}
-bool OneSixVersionBuilder::build(const bool onlyVanilla, const QStringList &external)
+void OneSixVersionBuilder::buildInternal(const bool onlyVanilla, const QStringList &external)
{
m_version->clear();
@@ -833,245 +64,194 @@ bool OneSixVersionBuilder::build(const bool onlyVanilla, const QStringList &exte
QDir patches(root.absoluteFilePath("patches/"));
// if we do external files, do just those.
- if(!external.isEmpty()) for (auto fileName : external)
- {
- QLOG_INFO() << "Reading" << fileName;
- VersionFile file;
- ParseFlags flags = fileName.endsWith("pack.json") ? IsFTBPackJson : NoFlags;
- if (!read(QFileInfo(fileName), false, &file, flags))
- {
- return false;
+ if (!external.isEmpty())
+ for (auto fileName : external)
+ {
+ QLOG_INFO() << "Reading" << fileName;
+ auto file =
+ parseJsonFile(QFileInfo(fileName), false, fileName.endsWith("pack.json"));
+ file->name = QFileInfo(fileName).fileName();
+ file->fileId = "org.multimc.external." + file->name;
+ file->version = QString();
+ file->mcVersion = QString();
+ file->applyTo(m_version);
+ m_version->versionFiles.append(file);
}
- file.name = QFileInfo(fileName).fileName();
- file.fileId = "org.multimc.external." + file.name;
- file.version = QString();
- file.mcVersion = QString();
- bool isError = false;
- auto errorcode = file.applyTo(m_version);
- if(errorcode != VersionFile::NoApplyError)
- return false;
- }
// else, if there's custom json, we just do that.
else if (QFile::exists(root.absoluteFilePath("custom.json")))
{
QLOG_INFO() << "Reading custom.json";
- VersionFile file;
- if (!read(QFileInfo(root.absoluteFilePath("custom.json")), false, &file))
- {
- return false;
- }
- file.name = "custom.json";
- file.filename = "custom.json";
- file.fileId = "org.multimc.custom.json";
- file.version = QString();
- auto errorcode = file.applyTo(m_version);
- if(errorcode != VersionFile::NoApplyError)
- return false;
- // QObject::tr("The version descriptors of this instance are not compatible with the current version of MultiMC"));
+ auto file = parseJsonFile(QFileInfo(root.absoluteFilePath("custom.json")), false);
+ file->name = "custom.json";
+ file->filename = "custom.json";
+ file->fileId = "org.multimc.custom.json";
+ file->version = QString();
+ file->applyTo(m_version);
+ m_version->versionFiles.append(file);
+ // QObject::tr("The version descriptors of this instance are not compatible with the
+ // current version of MultiMC"));
// QObject::tr("Error while applying %1. Please check MultiMC-0.log for more info.")
}
// version.json -> patches/*.json -> user.json
- else do
- {
- // version.json
- QLOG_INFO() << "Reading version.json";
- VersionFile file;
- if (!read(QFileInfo(root.absoluteFilePath("version.json")), false, &file))
- {
- return false;
- }
- file.name = "version.json";
- file.fileId = "org.multimc.version.json";
- file.version = m_instance->intendedVersionId();
- file.mcVersion = m_instance->intendedVersionId();
- auto error = file.applyTo(m_version);
- if (error != VersionFile::NoApplyError)
- {
- 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;
- }
-
- if (onlyVanilla)
- break;
+ else
+ do
+ {
+ // version.json
+ QLOG_INFO() << "Reading version.json";
+ auto file = parseJsonFile(QFileInfo(root.absoluteFilePath("version.json")), false);
+ file->name = "Minecraft";
+ file->fileId = "org.multimc.version.json";
+ file->version = m_instance->intendedVersionId();
+ file->mcVersion = m_instance->intendedVersionId();
+ file->applyTo(m_version);
+ m_version->versionFiles.append(file);
+ // QObject::tr("Error while applying %1. Please check MultiMC-0.log for more
+ // info.").arg(root.absoluteFilePath("version.json")));
+
+ if (onlyVanilla)
+ break;
- // patches/
- // load all, put into map for ordering, apply in the right order
- QMap<QString, int> overrideOrder = readOverrideOrders(m_instance);
+ // patches/
+ // load all, put into map for ordering, apply in the right order
- QMap<int, QPair<QString, VersionFile>> files;
- for (auto info : patches.entryInfoList(QStringList() << "*.json", QDir::Files))
- {
- QLOG_INFO() << "Reading" << info.fileName();
- VersionFile file;
- if (!read(info, true, &file))
- {
- return false;
- }
- if (overrideOrder.contains(file.fileId))
+ QMap<int, QPair<QString, VersionFilePtr>> files;
+ for (auto info : patches.entryInfoList(QStringList() << "*.json", QDir::Files))
{
- file.order = overrideOrder.value(file.fileId);
- }
- if (files.contains(file.order))
- {
- QLOG_ERROR() << file.fileId << "has the same order as" << files[file.order].second.fileId;
- return false;
+ QLOG_INFO() << "Reading" << info.fileName();
+ auto file = parseJsonFile(info, true);
+ if (files.contains(file->order))
+ {
+ throw VersionBuildError(QObject::tr("%1 has the same order as %2").arg(
+ file->fileId, files[file->order].second->fileId));
+ }
+ files.insert(file->order, qMakePair(info.fileName(), file));
}
- files.insert(file.order, qMakePair(info.fileName(), file));
- }
- for (auto order : files.keys())
- {
- QLOG_DEBUG() << "Applying file with order" << order;
- auto filePair = files[order];
- auto error = filePair.second.applyTo(m_version);
- if (error != VersionFile::NoApplyError)
+ for (auto order : files.keys())
{
- 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;
+ QLOG_DEBUG() << "Applying file with order" << order;
+ auto &filePair = files[order];
+ filePair.second->applyTo(m_version);
+ m_version->versionFiles.append(filePair.second);
}
- }
- } while(0);
+ } while (0);
// some final touches
+ finalizeVersion();
+}
+
+void OneSixVersionBuilder::finalizeVersion()
+{
+ if (m_version->assets.isEmpty())
{
- if (m_version->assets.isEmpty())
+ m_version->assets = "legacy";
+ }
+ if (m_version->minecraftArguments.isEmpty())
+ {
+ QString toCompare = m_version->processArguments.toLower();
+ if (toCompare == "legacy")
{
- m_version->assets = "legacy";
+ m_version->minecraftArguments = " ${auth_player_name} ${auth_session}";
}
- if (m_version->minecraftArguments.isEmpty())
+ else if (toCompare == "username_session")
{
- QString toCompare = m_version->processArguments.toLower();
- if (toCompare == "legacy")
- {
- m_version->minecraftArguments = " ${auth_player_name} ${auth_session}";
- }
- else if (toCompare == "username_session")
- {
- 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}";
- }
+ 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}";
}
}
-
- return true;
}
-bool OneSixVersionBuilder::read(const QJsonObject &obj)
+void OneSixVersionBuilder::readJsonAndApply(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;
- }
- VersionFile::ApplyError error = file.applyTo(m_version);
- if (error == VersionFile::OtherError)
- {
- QMessageBox::critical(
- m_widgetParent, QObject::tr("Error"),
- QObject::tr("Error while applying. Please check MultiMC-0.log for more info."));
- return false;
- }
- else if (error == VersionFile::LauncherVersionError)
- {
- QMessageBox::critical(
- m_widgetParent, QObject::tr("Error"),
- QObject::tr("The version descriptors of this instance are not compatible with the current version of MultiMC"));
- return false;
- }
+ auto file = VersionFile::fromJson(QJsonDocument(obj), QString(), false);
+ // QObject::tr("Error while reading. Please check MultiMC-0.log for more info."));
- return true;
+ file->applyTo(m_version);
+ m_version->versionFiles.append(file);
+ // QObject::tr("Error while applying. Please check MultiMC-0.log for more info."));
+ // QObject::tr("The version descriptors of this instance are not compatible with the current
+ // version of MultiMC"));
}
-bool OneSixVersionBuilder::read(const QFileInfo &fileInfo, const bool requireOrder,
- VersionFile *out, const ParseFlags flags)
+VersionFilePtr OneSixVersionBuilder::parseJsonFile(const QFileInfo &fileInfo,
+ const bool requireOrder, bool isFTB)
{
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()));
- return false;
+ throw JSONValidationError(QObject::tr("Unable to open the version file %1: %2.")
+ .arg(fileInfo.fileName(), file.errorString()));
}
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));
- return false;
+ throw JSONValidationError(QObject::tr("Unable to process the version file %1: %2 at %3.")
+ .arg(fileInfo.fileName(), error.errorString())
+ .arg(error.offset));
}
- bool isError = false;
- *out = VersionFile::fromJson(doc, file.fileName(), requireOrder, isError, flags);
- 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;
+ return VersionFile::fromJson(doc, file.fileName(), requireOrder, isFTB);
+ // QObject::tr("Error while reading %1. Please check MultiMC-0.log for more
+ // info.").arg(file.fileName());
}
QMap<QString, int> OneSixVersionBuilder::readOverrideOrders(OneSixInstance *instance)
{
QMap<QString, int> out;
- if (QDir(instance->instanceRoot()).exists("order.json"))
+
+ // make sure the order file exists
+ if (!QDir(instance->instanceRoot()).exists("order.json"))
+ return out;
+
+ // and it can be opened
+ QFile orderFile(instance->instanceRoot() + "/order.json");
+ if (!orderFile.open(QFile::ReadOnly))
{
- QFile orderFile(instance->instanceRoot() + "/order.json");
- if (!orderFile.open(QFile::ReadOnly))
- {
- QLOG_ERROR() << "Couldn't open" << orderFile.fileName()
- << " for reading:" << orderFile.errorString();
- QLOG_WARN() << "Ignoring overriden order";
- }
- else
+ QLOG_ERROR() << "Couldn't open" << orderFile.fileName()
+ << " for reading:" << orderFile.errorString();
+ QLOG_WARN() << "Ignoring overriden order";
+ return out;
+ }
+
+ // and it's valid JSON
+ QJsonParseError error;
+ QJsonDocument doc = QJsonDocument::fromJson(orderFile.readAll(), &error);
+ if (error.error != QJsonParseError::NoError)
+ {
+ QLOG_ERROR() << "Couldn't parse" << orderFile.fileName() << ":" << error.errorString();
+ QLOG_WARN() << "Ignoring overriden order";
+ return out;
+ }
+
+ // and then read it and process it if all above is true.
+ try
+ {
+ auto obj = MMCJson::ensureObject(doc);
+ for (auto it = obj.begin(); it != obj.end(); ++it)
{
- QJsonParseError error;
- QJsonDocument doc = QJsonDocument::fromJson(orderFile.readAll(), &error);
- if (error.error != QJsonParseError::NoError || !doc.isObject())
- {
- QLOG_ERROR() << "Couldn't parse" << orderFile.fileName() << ":"
- << error.errorString();
- QLOG_WARN() << "Ignoring overriden order";
- }
- else
+ if (it.key().startsWith("org.multimc."))
{
- QJsonObject obj = doc.object();
- for (auto it = obj.begin(); it != obj.end(); ++it)
- {
- if (it.key().startsWith("org.multimc."))
- {
- continue;
- }
- out.insert(it.key(), it.value().toDouble());
- }
+ continue;
}
+ out.insert(it.key(), MMCJson::ensureInteger(it.value()));
}
}
+ catch (JSONValidationError &err)
+ {
+ QLOG_ERROR() << "Couldn't parse" << orderFile.fileName() << ": bad file format";
+ QLOG_WARN() << "Ignoring overriden order";
+ return out;
+ }
return out;
}
+
bool OneSixVersionBuilder::writeOverrideOrders(const QMap<QString, int> &order,
OneSixInstance *instance)
{
@@ -1094,4 +274,3 @@ bool OneSixVersionBuilder::writeOverrideOrders(const QMap<QString, int> &order,
orderFile.write(QJsonDocument(obj).toJson(QJsonDocument::Indented));
return true;
}
-
diff --git a/logic/OneSixVersionBuilder.h b/logic/OneSixVersionBuilder.h
index 8cf6f32f..8be3d9d3 100644
--- a/logic/OneSixVersionBuilder.h
+++ b/logic/OneSixVersionBuilder.h
@@ -17,39 +17,32 @@
#include <QString>
#include <QMap>
+#include "VersionFile.h"
-class OneSixVersion;
+class VersionFinal;
class OneSixInstance;
-class QWidget;
class QJsonObject;
class QFileInfo;
-class VersionFile;
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 void build(VersionFinal *version, OneSixInstance *instance, const bool onlyVanilla,
+ const QStringList &external);
+ static void readJsonAndApplyToVersion(VersionFinal *version, const QJsonObject &obj);
+
static QMap<QString, int> readOverrideOrders(OneSixInstance *instance);
static bool writeOverrideOrders(const QMap<QString, int> &order, OneSixInstance *instance);
- enum ParseFlag
- {
- NoFlags = 0x0,
- IsFTBPackJson = 0x1
- };
- 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);
+ void buildInternal(const bool onlyVanilla, const QStringList &external);
+ void readJsonAndApply(const QJsonObject &obj);
+ void finalizeVersion();
- bool read(const QFileInfo &fileInfo, const bool requireOrder, VersionFile *out, const ParseFlags flags = NoFlags);
+ VersionFilePtr parseJsonFile(const QFileInfo &fileInfo, const bool requireOrder,
+ bool isFTB = false);
};
-
-Q_DECLARE_OPERATORS_FOR_FLAGS(OneSixVersionBuilder::ParseFlags)
diff --git a/logic/VersionFile.cpp b/logic/VersionFile.cpp
new file mode 100644
index 00000000..831b086e
--- /dev/null
+++ b/logic/VersionFile.cpp
@@ -0,0 +1,535 @@
+#include <QJsonArray>
+#include <QJsonDocument>
+
+#include <modutils.h>
+
+#include "logger/QsLog.h"
+#include "logic/VersionFile.h"
+#include "logic/OneSixLibrary.h"
+#include "logic/VersionFinal.h"
+#include "MMCJson.h"
+
+using namespace MMCJson;
+
+#define CURRENT_MINIMUM_LAUNCHER_VERSION 14
+
+RawLibraryPtr RawLibrary::fromJson(const QJsonObject &libObj, const QString &filename)
+{
+ RawLibraryPtr out(new RawLibrary());
+ if (!libObj.contains("name"))
+ {
+ throw JSONValidationError(filename +
+ "contains a library that doesn't have a 'name' field");
+ }
+ 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"))
+ {
+ out->applyExcludes = true;
+ auto extractObj = ensureObject(libObj.value("extract"));
+ for (auto excludeVal : ensureArray(extractObj.value("exclude")))
+ {
+ out->excludes.append(ensureString(excludeVal));
+ }
+ }
+ if (libObj.contains("natives"))
+ {
+ out->applyNatives = true;
+ QJsonObject nativesObj = ensureObject(libObj.value("natives"));
+ 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);
+ }
+ return out;
+}
+
+VersionFilePtr VersionFile::fromJson(const QJsonDocument &doc, const QString &filename,
+ const bool requireOrder, const bool isFTB)
+{
+ VersionFilePtr out(new VersionFile());
+ if (doc.isEmpty() || doc.isNull())
+ {
+ throw JSONValidationError(filename + " is empty or null");
+ }
+ if (!doc.isObject())
+ {
+ throw JSONValidationError("The root of " + filename + " is not an object");
+ }
+
+ QJsonObject root = doc.object();
+
+ if (requireOrder)
+ {
+ if (root.contains("order"))
+ {
+ out->order = ensureInteger(root.value("order"));
+ }
+ else
+ {
+ // FIXME: evaluate if we don't want to throw exceptions here instead
+ 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))
+ {
+ variable = ensureString(root.value(key));
+ }
+ };
+
+ // FIXME: This should be ignored when applying.
+ 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"))
+ {
+ out->minimumLauncherVersion = ensureInteger(root.value("minimumLauncherVersion"));
+ }
+
+ if (root.contains("tweakers"))
+ {
+ out->shouldOverwriteTweakers = true;
+ for (auto tweakerVal : ensureArray(root.value("tweakers")))
+ {
+ out->overwriteTweakers.append(ensureString(tweakerVal));
+ }
+ }
+
+ if (root.contains("+tweakers"))
+ {
+ for (auto tweakerVal : ensureArray(root.value("+tweakers")))
+ {
+ out->addTweakers.append(ensureString(tweakerVal));
+ }
+ }
+
+ if (root.contains("-tweakers"))
+ {
+ for (auto tweakerVal : ensureArray(root.value("-tweakers")))
+ {
+ out->removeTweakers.append(ensureString(tweakerVal));
+ }
+ }
+
+ if (root.contains("libraries"))
+ {
+ // FIXME: This should be done when applying.
+ out->shouldOverwriteLibs = !isFTB;
+ for (auto libVal : ensureArray(root.value("libraries")))
+ {
+ auto libObj = ensureObject(libVal);
+
+ auto lib = RawLibrary::fromJson(libObj, filename);
+ // FIXME: This should be done when applying.
+ if (isFTB)
+ {
+ lib->hint = "local";
+ lib->insertType = RawLibrary::Prepend;
+ out->addLibs.prepend(lib);
+ }
+ else
+ {
+ out->overwriteLibs.append(lib);
+ }
+ }
+ }
+
+ if (root.contains("+libraries"))
+ {
+ for (auto libVal : ensureArray(root.value("+libraries")))
+ {
+ QJsonObject libObj = ensureObject(libVal);
+ QJsonValue insertVal = ensureExists(libObj.value("insert"));
+
+ // parse the library
+ auto lib = RawLibrary::fromJson(libObj, filename);
+
+ // TODO: utility functions for handling this case. templates?
+ QString insertString;
+ {
+ if (insertVal.isString())
+ {
+ insertString = insertVal.toString();
+ }
+ else if (insertVal.isObject())
+ {
+ QJsonObject insertObj = insertVal.toObject();
+ if (insertObj.isEmpty())
+ {
+ throw JSONValidationError("One library has an empty insert object in " +
+ filename);
+ }
+ insertString = insertObj.keys().first();
+ lib->insertData = insertObj.value(insertString).toString();
+ }
+ }
+ if (insertString == "apply")
+ {
+ lib->insertType = RawLibrary::Apply;
+ }
+ else if (insertString == "prepend")
+ {
+ lib->insertType = RawLibrary::Prepend;
+ }
+ else if (insertString == "append")
+ {
+ lib->insertType = RawLibrary::Prepend;
+ }
+ else if (insertString == "replace")
+ {
+ lib->insertType = RawLibrary::Replace;
+ }
+ else
+ {
+ throw JSONValidationError("A '+' library in " + filename +
+ " contains an invalid insert type");
+ }
+ if (libObj.contains("MMC-depend"))
+ {
+ const QString dependString = ensureString(libObj.value("MMC-depend"));
+ if (dependString == "hard")
+ {
+ lib->dependType = RawLibrary::Hard;
+ }
+ else if (dependString == "soft")
+ {
+ lib->dependType = RawLibrary::Soft;
+ }
+ else
+ {
+ throw JSONValidationError("A '+' library in " + filename +
+ " contains an invalid depend type");
+ }
+ }
+ out->addLibs.append(lib);
+ }
+ }
+ if (root.contains("-libraries"))
+ {
+ for (auto libVal : ensureArray(root.value("-libraries")))
+ {
+ auto libObj = ensureObject(libVal);
+ out->removeLibs.append(ensureString(libObj.value("name")));
+ }
+ }
+ return out;
+}
+
+OneSixLibraryPtr VersionFile::createLibrary(RawLibraryPtr lib)
+{
+ std::shared_ptr<OneSixLibrary> 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<OneSixLibraryPtr> 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;
+}
+
+void VersionFile::applyTo(VersionFinal *version)
+{
+ if (minimumLauncherVersion != -1)
+ {
+ if (minimumLauncherVersion > CURRENT_MINIMUM_LAUNCHER_VERSION)
+ {
+ throw LauncherVersionError(minimumLauncherVersion, CURRENT_MINIMUM_LAUNCHER_VERSION);
+ }
+ }
+
+ if (!version->id.isNull() && !mcVersion.isNull())
+ {
+ if (QRegExp(mcVersion, Qt::CaseInsensitive, QRegExp::Wildcard).indexIn(version->id) ==
+ -1)
+ {
+ throw MinecraftVersionMismatch(fileId, mcVersion, version->id);
+ }
+ }
+
+ 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 RawLibrary::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->name << "(skipping)";
+ }
+ break;
+ }
+ case RawLibrary::Append:
+ case RawLibrary::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 == RawLibrary::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 == RawLibrary::Hard && ourVersion != otherVersion))
+ {
+ throw VersionBuildError(
+ QObject::tr(
+ "Error resolving library dependencies between %1 and %2 in %3.")
+ .arg(otherLib->rawName(), lib->name, filename));
+ }
+ 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 == RawLibrary::Hard)
+ {
+ throw VersionBuildError(QObject::tr(
+ "Error resolving library dependencies between %1 and %2 in %3.")
+ .arg(otherLib->rawName(), lib->name,
+ filename));
+ }
+ }
+ }
+ }
+ break;
+ }
+ case RawLibrary::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)";
+ }
+ }
+}
diff --git a/logic/VersionFile.h b/logic/VersionFile.h
new file mode 100644
index 00000000..67d22b23
--- /dev/null
+++ b/logic/VersionFile.h
@@ -0,0 +1,127 @@
+#pragma once
+
+#include <QString>
+#include <QStringList>
+#include <memory>
+#include "logic/OpSys.h"
+#include "logic/OneSixRule.h"
+#include "MMCError.h"
+
+class VersionFinal;
+
+class VersionBuildError : public MMCError
+{
+public:
+ VersionBuildError(QString cause) : MMCError(cause) {};
+ virtual ~VersionBuildError() {};
+};
+
+/**
+ * the base version file was meant for a newer version of the vanilla launcher than we support
+ */
+class LauncherVersionError : public VersionBuildError
+{
+public:
+ LauncherVersionError(int actual, int supported)
+ : VersionBuildError(QObject::tr(
+ "The base version file of this instance was meant for a newer (%1) "
+ "version of the vanilla launcher than this version of MultiMC supports (%2).")
+ .arg(actual)
+ .arg(supported)) {};
+ virtual ~LauncherVersionError() {};
+};
+
+/**
+ * some patch was intended for a different version of minecraft
+ */
+class MinecraftVersionMismatch : public VersionBuildError
+{
+public:
+ MinecraftVersionMismatch(QString fileId, QString mcVersion, QString parentMcVersion)
+ : VersionBuildError(QObject::tr("The patch %1 is for a different version of Minecraft "
+ "(%2) than that of the instance (%3).")
+ .arg(fileId)
+ .arg(mcVersion)
+ .arg(parentMcVersion)) {};
+ virtual ~MinecraftVersionMismatch() {};
+};
+
+struct RawLibrary;
+typedef std::shared_ptr<RawLibrary> RawLibraryPtr;
+struct RawLibrary
+{
+ 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;
+
+ // user for '+' libraries
+ enum InsertType
+ {
+ Apply,
+ Append,
+ Prepend,
+ Replace
+ };
+ InsertType insertType = Append;
+ QString insertData;
+ enum DependType
+ {
+ Soft,
+ Hard
+ };
+ DependType dependType = Soft;
+
+ static RawLibraryPtr fromJson(const QJsonObject &libObj, const QString &filename);
+};
+
+struct VersionFile;
+typedef std::shared_ptr<VersionFile> VersionFilePtr;
+struct VersionFile
+{
+public: /* methods */
+ static VersionFilePtr fromJson(const QJsonDocument &doc, const QString &filename,
+ const bool requireOrder, const bool isFTB = false);
+
+ static OneSixLibraryPtr createLibrary(RawLibraryPtr lib);
+ int findLibrary(QList<OneSixLibraryPtr> haystack, const QString &needle);
+ void applyTo(VersionFinal *version);
+
+public: /* data */
+ int order = 0;
+ 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<QString, QString> 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;
+
+ bool shouldOverwriteLibs = false;
+ QList<RawLibraryPtr> overwriteLibs;
+ QList<RawLibraryPtr> addLibs;
+ QList<QString> removeLibs;
+}; \ No newline at end of file
diff --git a/logic/VersionFinal.cpp b/logic/VersionFinal.cpp
new file mode 100644
index 00000000..a057ecdd
--- /dev/null
+++ b/logic/VersionFinal.cpp
@@ -0,0 +1,183 @@
+/* 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 <QDebug>
+#include <QFile>
+
+#include "OneSixVersionBuilder.h"
+
+VersionFinal::VersionFinal(OneSixInstance *instance, QObject *parent)
+ : QAbstractListModel(parent), m_instance(instance)
+{
+ clear();
+}
+
+bool VersionFinal::reload(const bool onlyVanilla, const QStringList &external)
+{
+ //FIXME: source of epic failure.
+ beginResetModel();
+ OneSixVersionBuilder::build(this, m_instance, onlyVanilla, external);
+ endResetModel();
+}
+
+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();
+}
+
+bool VersionFinal::canRemove(const int index) const
+{
+ if (index < versionFiles.size())
+ {
+ return versionFiles.at(index)->fileId != "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)->fileId;
+}
+
+bool VersionFinal::remove(const int index)
+{
+ if (canRemove(index))
+ {
+ return QFile::remove(versionFiles.at(index)->filename);
+ }
+ return false;
+}
+
+QList<std::shared_ptr<OneSixLibrary> > VersionFinal::getActiveNormalLibs()
+{
+ QList<std::shared_ptr<OneSixLibrary> > output;
+ for (auto lib : libraries)
+ {
+ if (lib->isActive() && !lib->isNative())
+ {
+ output.append(lib);
+ }
+ }
+ return output;
+}
+
+QList<std::shared_ptr<OneSixLibrary> > VersionFinal::getActiveNativeLibs()
+{
+ QList<std::shared_ptr<OneSixLibrary> > output;
+ for (auto lib : libraries)
+ {
+ if (lib->isActive() && lib->isNative())
+ {
+ output.append(lib);
+ }
+ }
+ return output;
+}
+
+std::shared_ptr<VersionFinal> VersionFinal::fromJson(const QJsonObject &obj)
+{
+ std::shared_ptr<VersionFinal> version(new VersionFinal(0));
+ try
+ {
+ OneSixVersionBuilder::readJsonAndApplyToVersion(version.get(), obj);
+ }
+ catch(MMCError & err)
+ {
+ return 0;
+ }
+ return version;
+}
+
+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;
+}
diff --git a/logic/OneSixVersion.h b/logic/VersionFinal.h
index fee47fa3..99fd5ff0 100644
--- a/logic/OneSixVersion.h
+++ b/logic/VersionFinal.h
@@ -22,14 +22,15 @@
#include <memory>
#include "OneSixLibrary.h"
+#include "VersionFile.h"
class OneSixInstance;
-class OneSixVersion : public QAbstractListModel
+class VersionFinal : public QAbstractListModel
{
Q_OBJECT
public:
- explicit OneSixVersion(OneSixInstance *instance, QObject *parent = 0);
+ 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;
@@ -37,7 +38,7 @@ public:
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());
+ bool reload(const bool onlyVanilla = false, const QStringList &external = QStringList());
void clear();
void dump() const;
@@ -54,7 +55,7 @@ public:
QList<std::shared_ptr<OneSixLibrary>> getActiveNormalLibs();
QList<std::shared_ptr<OneSixLibrary>> getActiveNativeLibs();
- static std::shared_ptr<OneSixVersion> fromJson(const QJsonObject &obj);
+ static std::shared_ptr<VersionFinal> fromJson(const QJsonObject &obj);
// data members
public:
@@ -118,20 +119,8 @@ public:
*/
// QList<Rule> rules;
- struct VersionFile
- {
- QString name;
- QString id;
- QString version;
- QString mcVersion;
- QString filename;
- int order;
- };
- QList<VersionFile> versionFiles;
+ QList<VersionFilePtr> versionFiles;
private:
OneSixInstance *m_instance;
};
-
-QDebug operator<<(QDebug &dbg, const OneSixVersion *version);
-QDebug operator<<(QDebug &dbg, const OneSixLibrary *library);
diff --git a/logic/lists/LiteLoaderVersionList.cpp b/logic/lists/LiteLoaderVersionList.cpp
index b8cea442..ef95eefd 100644
--- a/logic/lists/LiteLoaderVersionList.cpp
+++ b/logic/lists/LiteLoaderVersionList.cpp
@@ -92,7 +92,6 @@ void LiteLoaderVersionList::updateListData(QList<BaseVersionPtr> versions)
LLListLoadTask::LLListLoadTask(LiteLoaderVersionList *vlist)
{
m_list = vlist;
- vlistReply = nullptr;
}
LLListLoadTask::~LLListLoadTask()
@@ -102,23 +101,49 @@ LLListLoadTask::~LLListLoadTask()
void LLListLoadTask::executeTask()
{
setStatus(tr("Loading LiteLoader version list..."));
- auto worker = MMC->qnam();
- vlistReply = worker->get(QNetworkRequest(QUrl(URLConstants::LITELOADER_URL)));
- connect(vlistReply, SIGNAL(finished()), this, SLOT(listDownloaded()));
+ auto job = new NetJob("Version index");
+ // we do not care if the version is stale or not.
+ auto liteloaderEntry = MMC->metacache()->resolveEntry("liteloader", "versions.json");
+
+ // verify by poking the server.
+ liteloaderEntry->stale = true;
+
+ job->addNetAction(listDownload = CacheDownload::make(QUrl(URLConstants::LITELOADER_URL),
+ liteloaderEntry));
+
+ connect(listDownload.get(), SIGNAL(failed(int)), SLOT(listFailed()));
+
+ listJob.reset(job);
+ connect(listJob.get(), SIGNAL(succeeded()), SLOT(listDownloaded()));
+ connect(listJob.get(), SIGNAL(progress(qint64, qint64)), SIGNAL(progress(qint64, qint64)));
+ listJob->start();
+}
+
+void LLListLoadTask::listFailed()
+{
+ emitFailed("Failed to load LiteLoader version list.");
+ return;
}
void LLListLoadTask::listDownloaded()
{
- if (vlistReply->error() != QNetworkReply::NoError)
+ QByteArray data;
{
- vlistReply->deleteLater();
- emitFailed("Failed to load LiteLoader version list" + vlistReply->errorString());
- return;
+ auto dlJob = listDownload;
+ auto filename = std::dynamic_pointer_cast<CacheDownload>(dlJob)->getTargetFilepath();
+ QFile listFile(filename);
+ if (!listFile.open(QIODevice::ReadOnly))
+ {
+ emitFailed("Failed to open the LiteLoader version list.");
+ return;
+ }
+ data = listFile.readAll();
+ listFile.close();
+ dlJob.reset();
}
QJsonParseError jsonError;
- QJsonDocument jsonDoc = QJsonDocument::fromJson(vlistReply->readAll(), &jsonError);
- vlistReply->deleteLater();
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError);
if (jsonError.error != QJsonParseError::NoError)
{
@@ -140,7 +165,12 @@ void LLListLoadTask::listDownloaded()
emitFailed("Error parsing version list JSON: missing 'versions' object");
return;
}
- const QJsonObject versions = root.value("versions").toObject();
+
+ auto meta = root.value("meta").toObject();
+ QString description = meta.value("description").toString(tr("This is a lightweight loader for mods that don't change game mechanics."));
+ QString defaultUrl = meta.value("url").toString("http://dl.liteloader.com");
+ QString authors = meta.value("authors").toString("Mumfrey");
+ auto versions = root.value("versions").toObject();
QList<BaseVersionPtr> tempList;
for (auto vIt = versions.begin(); vIt != versions.end(); ++vIt)
@@ -170,6 +200,9 @@ void LLListLoadTask::listDownloaded()
version->md5 = artefact.value("md5").toString();
version->timestamp = artefact.value("timestamp").toDouble();
version->tweakClass = artefact.value("tweakClass").toString();
+ version->authors = authors;
+ version->description = description;
+ version->defaultUrl = defaultUrl;
const QJsonArray libs = artefact.value("libraries").toArray();
for (auto lIt = libs.begin(); lIt != libs.end(); ++lIt)
{
diff --git a/logic/lists/LiteLoaderVersionList.h b/logic/lists/LiteLoaderVersionList.h
index 8f761caf..bfc913e5 100644
--- a/logic/lists/LiteLoaderVersionList.h
+++ b/logic/lists/LiteLoaderVersionList.h
@@ -22,6 +22,7 @@
#include "BaseVersionList.h"
#include "logic/tasks/Task.h"
#include "logic/BaseVersion.h"
+#include <logic/net/NetJob.h>
class LLListLoadTask;
class QNetworkReply;
@@ -46,6 +47,7 @@ public:
return version;
}
+ // important info
QString version;
QString file;
QString mcVersion;
@@ -54,6 +56,11 @@ public:
bool isLatest;
QString tweakClass;
QStringList libraries;
+
+ // meta
+ QString defaultUrl;
+ QString description;
+ QString authors;
};
typedef std::shared_ptr<LiteLoaderVersion> LiteLoaderVersionPtr;
@@ -96,8 +103,10 @@ public:
protected
slots:
void listDownloaded();
+ void listFailed();
protected:
- QNetworkReply *vlistReply;
+ NetJobPtr listJob;
+ CacheDownloadPtr listDownload;
LiteLoaderVersionList *m_list;
};
diff --git a/translations/CMakeLists.txt b/translations/CMakeLists.txt
new file mode 100644
index 00000000..7463ae40
--- /dev/null
+++ b/translations/CMakeLists.txt
@@ -0,0 +1,20 @@
+set_directory_properties(PROPERTIES CLEAN_NO_CUSTOM 1)
+
+### translation stuff
+
+file(GLOB TRANSLATION_FILES ${CMAKE_CURRENT_LIST_DIR}/*.ts)
+set(FILES_TO_TRANSLATE_ABSOLUTE)
+foreach(file ${FILES_TO_TRANSLATE})
+ list(APPEND FILES_TO_TRANSLATE_ABSOLUTE "${CMAKE_SOURCE_DIR}/${file}")
+endforeach()
+
+qt5_create_translation(TRANSLATION_MESSAGES ${FILES_TO_TRANSLATE_ABSOLUTE} ${TRANSLATION_FILES})
+qt5_add_translation(TRANSLATION_QM ${TRANSLATION_FILES})
+add_custom_target(translations_update DEPENDS ${TRANSLATION_MESSAGES})
+add_custom_target(translations DEPENDS ${TRANSLATION_QM})
+
+IF(APPLE AND UNIX) ## OSX
+ install(FILES ${TRANSLATION_QM} DESTINATION MultiMC.app/Contents/MacOS/translations)
+ELSE()
+ install(FILES ${TRANSLATION_QM} DESTINATION translations)
+ENDIF()
diff --git a/translations/mmc_de.ts b/translations/mmc_de.ts
index 67ea67f6..6fb26eb5 100644
--- a/translations/mmc_de.ts
+++ b/translations/mmc_de.ts
@@ -8,12 +8,11 @@
<translation type="vanished">Dialog</translation>
</message>
<message>
- <location filename="../gui/dialogs/AboutDialog.ui" line="+89"/>
<source>MultiMC</source>
- <translation>MultiMC</translation>
+ <translation type="vanished">MultiMC</translation>
</message>
<message>
- <location line="+22"/>
+ <location filename="../gui/dialogs/AboutDialog.ui" line="+111"/>
<source>About</source>
<translation>Über</translation>
</message>
@@ -27,7 +26,37 @@
<translation>Über MultiMC</translation>
</message>
<message>
- <location line="+100"/>
+ <location line="+69"/>
+ <source>MultiMC 5</source>
+ <translation type="unfinished">MultiMC 5</translation>
+ </message>
+ <message>
+ <location line="+28"/>
+ <source>Version:</source>
+ <translation type="unfinished">Version:</translation>
+ </message>
+ <message>
+ <location line="+10"/>
+ <source>Version Type:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+10"/>
+ <source>Platform:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+10"/>
+ <source>Build Number:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+10"/>
+ <source>Channel:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+13"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;MultiMC is a custom launcher that makes managing Minecraft easier by allowing you to have multiple instances of Minecraft at once.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;MultiMC ist ein alternativer Launcher, der das Management von Minecraft vereinfacht, indem er es dir erlaubt, mehrere Installationen von Minecraft zu verwalten.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
</message>
@@ -42,7 +71,197 @@
<translation></translation>
</message>
<message>
- <location line="+28"/>
+ <location line="+41"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;Bitstream Vera Sans&apos;; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:10pt; font-weight:600;&quot;&gt;MultiMC&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:10pt;&quot;&gt;Andrew Okin &amp;lt;&lt;/span&gt;&lt;a href=&quot;mailto:forkk@forkk.net&quot;&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;forkk@forkk.net&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:10pt;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:10pt;&quot;&gt;Petr Mrázek &amp;lt;&lt;/span&gt;&lt;a href=&quot;mailto:peterix@gmail.com&quot;&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;peterix@gmail.com&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:10pt;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:10pt;&quot;&gt;Sky &amp;lt;&lt;/span&gt;&lt;a href=&quot;https://www.twitter.com/drayshak&quot;&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;@drayshak&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:10pt;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:&apos;MS Shell Dlg 2&apos;; font-size:10pt; font-weight:600;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;Ubuntu&apos;; font-size:10pt; font-weight:600;&quot;&gt;With thanks to&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:10pt;&quot;&gt;Orochimarufan &amp;lt;&lt;/span&gt;&lt;a href=&quot;mailto:orochimarufan.x3@gmail.com&quot;&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;orochimarufan.x3@gmail.com&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:10pt;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:10pt;&quot;&gt;TakSuyu &amp;lt;&lt;/span&gt;&lt;a href=&quot;mailto:taksuyu@gmail.com&quot;&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;taksuyu@gmail.com&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:10pt;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:10pt;&quot;&gt;Kilobyte &amp;lt;&lt;/span&gt;&lt;a href=&quot;mailto:stiepen22@gmx.de&quot;&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;stiepen22@gmx.de&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:10pt;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:10pt;&quot;&gt;Jan (02JanDal) &amp;lt;&lt;/span&gt;&lt;a href=&quot;mailto:02jandal@gmail.com&quot;&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;02jandal@gmail.com&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:10pt;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:10pt;&quot;&gt;Robotbrain &amp;lt;&lt;/span&gt;&lt;a href=&quot;https://twitter.com/skylordelros&quot;&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;@skylordelros&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:10pt;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:10pt;&quot;&gt;Rootbear75 &amp;lt;&lt;/span&gt;&lt;a href=&quot;https://twitter.com/rootbear75&quot;&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;@rootbear75&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:10pt;&quot;&gt;&amp;gt; (build server)&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+64"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;DejaVu Sans Mono&apos;; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;Bitstream Vera Sans&apos;; font-size:18pt; font-weight:600;&quot;&gt;MultiMC&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Copyright 2012-2014 MultiMC Contributors&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Licensed under the Apache License, Version 2.0 (the &amp;quot;License&amp;quot;);&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;you may not use this file except in compliance with the License.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;You may obtain a copy of the License at&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt; http://www.apache.org/licenses/LICENSE-2.0&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Unless required by applicable law or agreed to in writing, software&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;distributed under the License is distributed on an &amp;quot;AS IS&amp;quot; BASIS,&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;See the License for the specific language governing permissions and&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;limitations under the License.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;Bitstream Vera Sans&apos;; font-size:18pt; font-weight:600;&quot;&gt;QSLog&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;Copyright (c) 2010, Razvan Petru&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;All rights reserved.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;Redistribution and use in source and binary forms, with or without modification,&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;are permitted provided that the following conditions are met:&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;* Redistributions of source code must retain the above copyright notice, this&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; list of conditions and the following disclaimer.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;* Redistributions in binary form must reproduce the above copyright notice, this&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; list of conditions and the following disclaimer in the documentation and/or other&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; materials provided with the distribution.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;* The name of the contributors may not be used to endorse or promote products&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; derived from this software without specific prior written permission.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS &amp;quot;AS IS&amp;quot; AND&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;OF THE POSSIBILITY OF SUCH DAMAGE.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;Bitstream Vera Sans&apos;; font-size:18pt; font-weight:600;&quot;&gt;Group View (instance view)&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; /*&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * Copyright (C) 2007 Rafael Fernández López &amp;lt;ereslibre@kde.org&amp;gt;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * Copyright (C) 2007 John Tapsell &amp;lt;tapsell@kde.org&amp;gt;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; *&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * This library is free software; you can redistribute it and/or&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * modify it under the terms of the GNU Library General Public&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * License as published by the Free Software Foundation; either&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * version 2 of the License, or (at your option) any later version.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; *&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * This library is distributed in the hope that it will be useful,&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * but WITHOUT ANY WARRANTY; without even the implied warranty of&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * Library General Public License for more details.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; *&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * You should have received a copy of the GNU Library General Public License&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * along with this library; see the file COPYING.LIB. If not, write to&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * Boston, MA 02110-1301, USA.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; */&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;Bitstream Vera Sans&apos;; font-size:18pt; font-weight:600;&quot;&gt;Pack200&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;The GNU General Public License (GPL)&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;Version 2, June 1991&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;+ &amp;quot;CLASSPATH&amp;quot; EXCEPTION TO THE GPL&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;Certain source files distributed by Oracle America and/or its affiliates are&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;subject to the following clarification and special exception to the GPL, but&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;only where Oracle has expressly included in the particular source file&apos;s header&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;the words &amp;quot;Oracle designates this particular file as subject to the &amp;quot;Classpath&amp;quot;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;exception as provided by Oracle in the LICENSE file that accompanied this code.&amp;quot;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; Linking this library statically or dynamically with other modules is making&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; a combined work based on this library. Thus, the terms and conditions of&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; the GNU General Public License cover the whole combination.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; As a special exception, the copyright holders of this library give you&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; permission to link this library with independent modules to produce an&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; executable, regardless of the license terms of these independent modules,&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; and to copy and distribute the resulting executable under terms of your&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; choice, provided that you also meet, for each linked independent module,&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; the terms and conditions of the license of that module. An independent&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; module is a module which is not derived from or based on this library. If&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; you modify this library, you may extend this exception to your version of&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; the library, but you are not obligated to do so. If you do not wish to do&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; so, delete this exception statement from your version.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;Bitstream Vera Sans&apos;; font-size:18pt; font-weight:600;&quot;&gt;Quazip&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;Copyright (C) 2005-2011 Sergey A. Tachenov&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;This program is free software; you can redistribute it and/or modify it&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;under the terms of the GNU Lesser General Public License as published by&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;the Free Software Foundation; either version 2 of the License, or (at&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;your option) any later version.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;This program is distributed in the hope that it will be useful, but&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;WITHOUT ANY WARRANTY; without even the implied warranty of&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;General Public License for more details.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;You should have received a copy of the GNU Lesser General Public License&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;along with this program; if not, write to the Free Software Foundation,&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;See COPYING file for the full LGPL text.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;Original ZIP package is copyrighted by Gilles Vollant, see&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;quazip/(un)zip.h files for details, basically it&apos;s zlib license.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;Bitstream Vera Sans&apos;; font-size:18pt; font-weight:600;&quot;&gt;xz-minidec&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;/*&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * XZ decompressor&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; *&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * Authors: Lasse Collin &amp;lt;lasse.collin@tukaani.org&amp;gt;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * Igor Pavlov &amp;lt;http://7-zip.org/&amp;gt;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; *&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * This file has been put into the public domain.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * You can do whatever you want with this file.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; */&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;Bitstream Vera Sans&apos;; font-size:18pt; font-weight:600;&quot;&gt;Java IconLoader class&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;Copyright (c) 2011, Chris Molini&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;All rights reserved.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;Redistribution and use in source and binary forms, with or without&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;modification, are permitted provided that the following conditions are met:&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * Redistributions of source code must retain the above copyright&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; notice, this list of conditions and the following disclaimer.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * Redistributions in binary form must reproduce the above copyright&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; notice, this list of conditions and the following disclaimer in the&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; documentation and/or other materials provided with the distribution.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * Neither the name of the &amp;lt;organization&amp;gt; nor the&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; names of its contributors may be used to endorse or promote products&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; derived from this software without specific prior written permission.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS &amp;quot;AS IS&amp;quot; AND&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;DISCLAIMED. IN NO EVENT SHALL &amp;lt;COPYRIGHT HOLDER&amp;gt; BE LIABLE FOR ANY&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+175"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;Bitstream Vera Sans&apos;; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;We keep MultiMC open source because we think it&apos;s important to be able to see the source code for a project like this, and we do so using the Apache license.&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Part of the reason for using the Apache license is we don&apos;t want people using the &amp;quot;MultiMC&amp;quot; name when redistributing the project. This means people must take the time to go through the source code and remove all references to &amp;quot;MultiMC&amp;quot;, including but not limited to the project icon and the title of windows, (no *MultiMC-fork* in the title).&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;The Apache license covers reasonable use for the name - a mention of the project&apos;s origins in the About dialog and the license is acceptable. However, it should be abundantly clear that the project is a fork &lt;span style=&quot; font-weight:600;&quot;&gt;without&lt;/span&gt; implying that you have our blessing.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
<source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
@@ -59,7 +278,7 @@ p, li { white-space: pre-wrap; }
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Jan (02JanDal) &amp;lt;&lt;/span&gt;&lt;a href=&quot;mailto:02jandal@gmail.com&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;02jandal@gmail.com&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Robotbrain &amp;lt;&lt;/span&gt;&lt;a href=&quot;https://twitter.com/skylordelros&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;@skylordelros&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Rootbear75 &amp;lt;&lt;/span&gt;&lt;a href=&quot;https://twitter.com/rootbear75&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;@rootbear75&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;&amp;gt; (build server)&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
- <translation>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+ <translation type="vanished">&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:7.8pt; font-weight:400; font-style:normal;&quot;&gt;
@@ -77,13 +296,12 @@ p, li { white-space: pre-wrap; }
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Rootbear75 &amp;lt;&lt;/span&gt;&lt;a href=&quot;https://twitter.com/rootbear75&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;@rootbear75&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;&amp;gt; (bau server)&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
</message>
<message>
- <location line="+25"/>
+ <location line="-214"/>
<source>No Language file loaded.</source>
<extracomment>Hey, Translator, feel free to put credit to you here</extracomment>
<translation>Deutsche Sprachdatei von Kilobyte (siehe oben). Aktualisiert von xnrand (nsfw auf IRC), Jan und ACGaming.</translation>
</message>
<message>
- <location line="+39"/>
<source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
@@ -209,7 +427,7 @@ p, li { white-space: pre-wrap; }
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * This file has been put into the public domain.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * You can do whatever you want with this file.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; */&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
- <translation>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+ <translation type="vanished">&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;DejaVu Sans Mono&apos;; font-size:7.8pt; font-weight:400; font-style:normal;&quot;&gt;
@@ -336,12 +554,11 @@ p, li { white-space: pre-wrap; }
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; */&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
</message>
<message>
- <location line="+140"/>
+ <location line="+208"/>
<source>Forking/Redistribution</source>
<translation>Abspaltung/Weiterverbreitung</translation>
</message>
<message>
- <location line="+6"/>
<source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
@@ -351,7 +568,7 @@ p, li { white-space: pre-wrap; }
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;Bitstream Vera Sans&apos;; font-size:11pt;&quot;&gt;Part of the reason for using the Apache license is we don&apos;t want people using the &amp;quot;MultiMC&amp;quot; name when redistributing the project. This means people must take the time to go through the source code and remove all references to &amp;quot;MultiMC&amp;quot;, including but not limited to the project icon and the title of windows, (no *MultiMC-fork* in the title).&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:&apos;Bitstream Vera Sans&apos;; font-size:11pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;Bitstream Vera Sans&apos;; font-size:11pt;&quot;&gt;The Apache license covers reasonable use for the name - a mention of the project&apos;s origins in the About dialog and the license is acceptable. However, it should be abundantly clear that the project is a fork &lt;/span&gt;&lt;span style=&quot; font-family:&apos;Bitstream Vera Sans&apos;; font-size:11pt; font-weight:600;&quot;&gt;without&lt;/span&gt;&lt;span style=&quot; font-family:&apos;Bitstream Vera Sans&apos;; font-size:11pt;&quot;&gt; implying that you have our blessing.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
- <translation>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+ <translation type="vanished">&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:7.8pt; font-weight:400; font-style:normal;&quot;&gt;
@@ -366,7 +583,7 @@ p, li { white-space: pre-wrap; }
<translation type="obsolete">&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;http://github.com/Forkk/MultiMC5&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;http://github.com/Forkk/MultiMC5&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
</message>
<message>
- <location line="-219"/>
+ <location line="-242"/>
<source>Credits</source>
<translation>Dank an</translation>
</message>
@@ -477,7 +694,7 @@ p, li { white-space: pre-wrap; }
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;POSSIBILITY OF SUCH DAMAGE.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
</message>
<message>
- <location line="+193"/>
+ <location line="+222"/>
<source>About Qt</source>
<translation>Über Qt</translation>
</message>
@@ -486,6 +703,31 @@ p, li { white-space: pre-wrap; }
<source>Close</source>
<translation>Schließen</translation>
</message>
+ <message>
+ <location filename="../gui/dialogs/AboutDialog.cpp" line="+32"/>
+ <source>Version</source>
+ <translation type="unfinished">Version</translation>
+ </message>
+ <message>
+ <location line="+1"/>
+ <source>Version Type</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+1"/>
+ <source>Platform</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>Build Number</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+5"/>
+ <source>Channel</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>AccountListDialog</name>
@@ -585,6 +827,27 @@ p, li { white-space: pre-wrap; }
</message>
</context>
<context>
+ <name>BaseExternalTool</name>
+ <message>
+ <location filename="../logic/tools/BaseExternalTool.cpp" line="+48"/>
+ <source>MCEdit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+0"/>
+ <source>Choose which world to open:</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>BaseProfiler</name>
+ <message>
+ <location filename="../logic/tools/BaseProfiler.cpp" line="+29"/>
+ <source>Profiler aborted</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
<name>ConsoleWindow</name>
<message>
<location filename="../gui/ConsoleWindow.ui" line="+14"/>
@@ -597,6 +860,11 @@ p, li { white-space: pre-wrap; }
<translation>Log hochladen</translation>
</message>
<message>
+ <location line="+7"/>
+ <source>Manage Screenshots</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
<location line="+20"/>
<source>&amp;Kill Minecraft</source>
<translation>&amp;Minecraft töten</translation>
@@ -611,11 +879,22 @@ p, li { white-space: pre-wrap; }
<translation type="vanished">Minecraft töten</translation>
</message>
<message>
+ <location filename="../gui/ConsoleWindow.cpp" line="+58"/>
+ <source>Console window for </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+120"/>
<source>Close</source>
- <translation type="vanished">Schließen</translation>
+ <translation>Schließen</translation>
+ </message>
+ <message>
+ <location line="+2"/>
+ <source>Hide</source>
+ <translation type="unfinished"></translation>
</message>
<message>
- <location filename="../gui/ConsoleWindow.cpp" line="+161"/>
+ <location line="+49"/>
<source>Kill Minecraft?</source>
<translation>Minecraft töten?</translation>
</message>
@@ -641,7 +920,7 @@ p, li { white-space: pre-wrap; }
<context>
<name>DownloadUpdateTask</name>
<message>
- <location filename="../logic/updater/DownloadUpdateTask.cpp" line="+80"/>
+ <location filename="../logic/updater/DownloadUpdateTask.cpp" line="+81"/>
<source>Finding information about the current version...</source>
<translation>Finde Informationen zur benutzten Version...</translation>
</message>
@@ -681,12 +960,12 @@ p, li { white-space: pre-wrap; }
<translation>Bearbeite Dateilisten - Rechne aus, wie das Update installiert werden soll...</translation>
</message>
<message>
- <location line="+206"/>
+ <location line="+213"/>
<source>Failed to write update script file.</source>
<translation>Fehler beim Schreiben des Updatescripts.</translation>
</message>
<message>
- <location line="+43"/>
+ <location line="+31"/>
<source>Failed to download update files.</source>
<translation>Fehler beim Herunterladen der Updatedateien.</translation>
</message>
@@ -813,7 +1092,29 @@ p, li { white-space: pre-wrap; }
<translation>Konsole automatisch schließen, nachdem das Spiel beendet wurde?</translation>
</message>
<message>
- <location line="+159"/>
+ <location line="+45"/>
+ <source>The maximum amount of memory Minecraft is allowed to use.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <location line="+36"/>
+ <location line="+22"/>
+ <source> MB</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="-25"/>
+ <source>The amount of memory Minecraft is started with.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+22"/>
+ <source>The amount of memory available to store loaded Java classes.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+74"/>
<source>Browse...</source>
<translation>Durchsuchen...</translation>
</message>
@@ -831,7 +1132,7 @@ p, li { white-space: pre-wrap; }
<translation type="vanished">Automatisch einloggen, wenn das Instanzsymbol doppelt gecklickt wurde?</translation>
</message>
<message>
- <location line="-142"/>
+ <location line="-160"/>
<source>Java</source>
<translation>Java</translation>
</message>
@@ -841,7 +1142,7 @@ p, li { white-space: pre-wrap; }
<translation>Arbeitsspeicher</translation>
</message>
<message>
- <location line="+28"/>
+ <location line="+34"/>
<source>Minimum memory allocation:</source>
<translation>Min. Arbeitsspeicher:</translation>
</message>
@@ -851,7 +1152,7 @@ p, li { white-space: pre-wrap; }
<translation>Max. Arbeitsspeicher:</translation>
</message>
<message>
- <location line="+39"/>
+ <location line="+51"/>
<source>PermGen:</source>
<translation>PermGen:</translation>
</message>
@@ -926,6 +1227,32 @@ p, li { white-space: pre-wrap; }
</message>
</context>
<context>
+ <name>JProfiler</name>
+ <message>
+ <location filename="../logic/tools/JProfiler.cpp" line="+24"/>
+ <source>Listening on port: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+7"/>
+ <source>Profiler aborted</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>JVisualVM</name>
+ <message>
+ <location filename="../logic/tools/JVisualVM.cpp" line="+21"/>
+ <source>JVisualVM started</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+7"/>
+ <source>Profiler aborted</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
<name>JavaListLoadTask</name>
<message>
<location filename="../logic/lists/JavaVersionList.cpp" line="+175"/>
@@ -934,6 +1261,14 @@ p, li { white-space: pre-wrap; }
</message>
</context>
<context>
+ <name>LLListLoadTask</name>
+ <message>
+ <location filename="../logic/lists/LiteLoaderVersionList.cpp" line="+104"/>
+ <source>Loading LiteLoader version list...</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
<name>LWJGLSelectDialog</name>
<message>
<location filename="../gui/dialogs/LwjglSelectDialog.ui" line="+14"/>
@@ -1022,7 +1357,7 @@ p, li { white-space: pre-wrap; }
<translation>Texturenpakete</translation>
</message>
<message>
- <location filename="../gui/dialogs/OneSixModEditDialog.cpp" line="+303"/>
+ <location filename="../gui/dialogs/OneSixModEditDialog.cpp" line="+401"/>
<location filename="../gui/dialogs/LegacyModEditDialog.cpp" line="+258"/>
<source>Select Loader Mods</source>
<extracomment>Title of regular mod selection dialog</extracomment>
@@ -1060,7 +1395,7 @@ p, li { white-space: pre-wrap; }
<context>
<name>LegacyUpdate</name>
<message>
- <location filename="../logic/LegacyUpdate.cpp" line="+79"/>
+ <location filename="../logic/LegacyUpdate.cpp" line="+80"/>
<source>Downloading new LWJGL...</source>
<translation>LWJGL wird heruntergeladen...</translation>
</message>
@@ -1085,7 +1420,7 @@ p, li { white-space: pre-wrap; }
<translation>Neue minecraft.jar wird heruntergeladen...</translation>
</message>
<message>
- <location line="+34"/>
+ <location line="+33"/>
<source>Installing mods: Adding </source>
<translation>Mod-Installation: Hinzufügen </translation>
</message>
@@ -1209,7 +1544,7 @@ p, li { white-space: pre-wrap; }
<context>
<name>MCVListLoadTask</name>
<message>
- <location filename="../logic/lists/MinecraftVersionList.cpp" line="+142"/>
+ <location filename="../logic/lists/MinecraftVersionList.cpp" line="+146"/>
<source>Loading instance version list...</source>
<translation>Lade Liste von Minecraft-Versionen...</translation>
</message>
@@ -1232,24 +1567,24 @@ p, li { white-space: pre-wrap; }
<translation>Instanz-Werkzeugleiste</translation>
</message>
<message>
- <location line="+40"/>
+ <location line="+43"/>
<source>News Toolbar</source>
<translation>Nachrichten-Werkzeugleiste</translation>
</message>
<message>
- <location line="+34"/>
+ <location line="+35"/>
<source>Add Instance</source>
<translation>Instanz hinzufügen</translation>
</message>
<message>
<location line="+3"/>
<location line="+3"/>
- <location line="+332"/>
+ <location line="+342"/>
<source>Add a new instance.</source>
<translation>Neue Instanz erstellen.</translation>
</message>
<message>
- <location line="-323"/>
+ <location line="-332"/>
<source>View Instance Folder</source>
<translation>Instanzordner öffnen</translation>
</message>
@@ -1260,7 +1595,7 @@ p, li { white-space: pre-wrap; }
<translation>Instanzordner im Dateimanager öffnen.</translation>
</message>
<message>
- <location line="+9"/>
+ <location line="+10"/>
<source>Refresh</source>
<translation>Aktualisieren</translation>
</message>
@@ -1271,7 +1606,7 @@ p, li { white-space: pre-wrap; }
<translation>Instanzliste neuladen.</translation>
</message>
<message>
- <location line="+9"/>
+ <location line="+10"/>
<source>View Central Mods Folder</source>
<translation>Zenstralen Mod-Ordner öffnen</translation>
</message>
@@ -1282,7 +1617,7 @@ p, li { white-space: pre-wrap; }
<translation>Zentralen Mod-Ordner in einem Dateimanager öffnen.</translation>
</message>
<message>
- <location line="+9"/>
+ <location line="+10"/>
<source>Check for Updates</source>
<translation>Auf Updates überprüfen</translation>
</message>
@@ -1293,19 +1628,19 @@ p, li { white-space: pre-wrap; }
<translation>Auf Updates für MultiMC prüfen</translation>
</message>
<message>
- <location line="+9"/>
- <location line="+133"/>
+ <location line="+10"/>
+ <location line="+136"/>
<source>Settings</source>
<translation>Einstellungen</translation>
</message>
<message>
- <location line="-130"/>
+ <location line="-133"/>
<location line="+3"/>
<source>Change settings.</source>
<translation>Einstellungen ändern.</translation>
</message>
<message>
- <location line="+12"/>
+ <location line="+13"/>
<source>Report a Bug</source>
<translation>Fehler melden</translation>
</message>
@@ -1324,7 +1659,7 @@ p, li { white-space: pre-wrap; }
<translation type="vanished">Den MultiMC-Entwicklerblog öffnen, um Neuigkeiten über MultiMC zu erhalten.</translation>
</message>
<message>
- <location line="+9"/>
+ <location line="+10"/>
<source>More News</source>
<translation>Mehr Nachrichten</translation>
</message>
@@ -1340,7 +1675,7 @@ p, li { white-space: pre-wrap; }
<translation>Öffne den MultiMC-Entwicklerblog, um weitere Neuigkeiten über MultiMC zu erhalten.</translation>
</message>
<message>
- <location line="+9"/>
+ <location line="+10"/>
<location line="+6"/>
<source>About MultiMC</source>
<translation>Über MultiMC</translation>
@@ -1358,11 +1693,12 @@ p, li { white-space: pre-wrap; }
<message>
<location line="+3"/>
<location line="+3"/>
+ <location line="+213"/>
<source>Launch the selected instance.</source>
<translation>Die ausgewählte Instanz starten.</translation>
</message>
<message>
- <location line="+5"/>
+ <location line="-208"/>
<source>Instance Name</source>
<translation>Instanzname</translation>
</message>
@@ -1499,17 +1835,32 @@ p, li { white-space: pre-wrap; }
<translation>Den Konfigurationsordner im Dateimanager anzeigen</translation>
</message>
<message>
- <location line="+12"/>
+ <location line="+13"/>
<source>Meow</source>
<translation>Miau</translation>
</message>
<message>
<location line="+3"/>
- <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-weight:600; color:#ff0004;&quot;&gt;Catnarok!&lt;/span&gt;&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;Or just a cat with a ball of yarn?&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-style:italic;&quot;&gt;WHO KNOWS?!&lt;/span&gt;&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;&lt;img src=&quot;:/icons/instances/tnt&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
- <translation></translation>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;It&apos;s a fluffy kitty :3&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
</message>
<message>
- <location line="+9"/>
+ <location line="+32"/>
+ <source>Launch the selected instance in offline mode.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+8"/>
+ <source>Manage Screenshots</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;View and upload screenshots for this instance&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="-33"/>
<source>Copy Instance</source>
<translation>Kopiere Instanz</translation>
</message>
@@ -1520,7 +1871,7 @@ p, li { white-space: pre-wrap; }
</message>
<message>
<location line="+8"/>
- <location filename="../gui/MainWindow.cpp" line="+201"/>
+ <location filename="../gui/MainWindow.cpp" line="+239"/>
<source>Manage Accounts</source>
<translation>Verwalte Konten</translation>
</message>
@@ -1534,17 +1885,22 @@ p, li { white-space: pre-wrap; }
<translation type="obsolete">&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-weight:600; color:#ff0004;&quot;&gt;Catnatok!&lt;/span&gt;&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;Or just a cat with a ball of yarn?&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-style:italic;&quot;&gt;WHO KNOWS?!&lt;/span&gt;&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;&lt;img src=&quot;:/icons/instances/tnt&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
</message>
<message>
- <location filename="../gui/MainWindow.cpp" line="-9"/>
+ <location filename="../gui/MainWindow.cpp" line="-31"/>
<source>No instance selected</source>
<translation>Keine Instanz ausgewählt</translation>
</message>
<message>
- <location line="+17"/>
+ <location line="+1"/>
+ <source>No status available</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+38"/>
<source>Accounts</source>
<translation>Konten</translation>
</message>
<message>
- <location line="+67"/>
+ <location line="+69"/>
<source>No update found.</source>
<translation>Keine neue Version gefunden.</translation>
</message>
@@ -1556,7 +1912,38 @@ You are using the latest version.</source>
Du verwendest bereits die neueste Version.</translation>
</message>
<message>
- <location line="+52"/>
+ <location line="+34"/>
+ <source>Rename</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+20"/>
+ <location line="+947"/>
+ <source>Launch</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="-945"/>
+ <source>Profilers</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+8"/>
+ <source>Profiler not setup correctly. Go into settings, &quot;External Tools&quot;.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+7"/>
+ <source>Tools</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+8"/>
+ <source>Tool not setup correctly. Go into settings, &quot;External Tools&quot;.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+28"/>
<source>No accounts added!</source>
<translation>Keine Konten angegeben!</translation>
</message>
@@ -1566,7 +1953,7 @@ Du verwendest bereits die neueste Version.</translation>
<translation>Kein voreingestelltes Konto</translation>
</message>
<message>
- <location line="+95"/>
+ <location line="+94"/>
<source>Loading news...</source>
<translation>Nachrichten werden geladen...</translation>
</message>
@@ -1576,7 +1963,17 @@ Du verwendest bereits die neueste Version.</translation>
<translation>Keine Nachrichten verfügbar.</translation>
</message>
<message>
- <location line="+105"/>
+ <location line="+122"/>
+ <source>Notification</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+2"/>
+ <source>Don&apos;t show again</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+97"/>
<location line="+7"/>
<location line="+7"/>
<location line="+12"/>
@@ -1584,20 +1981,22 @@ Du verwendest bereits die neueste Version.</translation>
<location line="+37"/>
<location line="+7"/>
<location line="+7"/>
- <location line="+436"/>
+ <location line="+423"/>
+ <location line="+30"/>
+ <location line="+80"/>
<source>Error</source>
<translation>Fehler</translation>
</message>
<message>
- <location line="-486"/>
- <location line="+487"/>
+ <location line="-583"/>
+ <location line="+584"/>
<source>MultiMC cannot download Minecraft or update instances unless you have at least one account added.
Please add your Mojang or Minecraft account.</source>
<translation>MultiMC kann Minecraft nicht herunterladen und keine Instanzen aktualisieren, solange du kein Konto erstellt hast.
Bitte füge dein Mojang- oder Minecraft-Konto hinzu.</translation>
</message>
<message>
- <location line="-390"/>
+ <location line="-472"/>
<source>Group name</source>
<translation>Gruppenname</translation>
</message>
@@ -1607,7 +2006,7 @@ Bitte füge dein Mojang- oder Minecraft-Konto hinzu.</translation>
<translation>Neuen Gruppennamen eingeben.</translation>
</message>
<message>
- <location line="+91"/>
+ <location line="+92"/>
<source>CAREFUL</source>
<translation>ACHTUNG</translation>
</message>
@@ -1629,7 +2028,7 @@ Die folgende Instanz löschen:</translation>
<translation>Neuen Instanznamen eingeben.</translation>
</message>
<message>
- <location line="+89"/>
+ <location line="+98"/>
<source>No Accounts</source>
<translation>Keine Konten</translation>
</message>
@@ -1644,17 +2043,65 @@ Die folgende Instanz löschen:</translation>
<translation>Welches Konto möchtest du benutzen?</translation>
</message>
<message>
- <location line="+17"/>
+ <location line="+22"/>
<source>Your account is currently not logged in. Please enter your password to log in again.</source>
<translation>Dein Konto ist momentan nicht angemeldet. Bitte gib dein Passwort an, um dich anzumelden.</translation>
</message>
<message>
+ <location line="+47"/>
+ <source>Player name</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+1"/>
+ <source>Choose your offline mode player name.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+69"/>
+ <source>Couldn&apos;t start profiler: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+9"/>
+ <source>Waiting for profiler...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
<location line="+7"/>
+ <source>The launch of Minecraft itself is delayed until you press the button. This is the right time to setup the profiler, as the profiler server is running now.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>Waiting</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+10"/>
+ <source>Couldn&apos;t start the profiler: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+289"/>
+ <source>Failed to load screenshots!</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+7"/>
+ <source>Done uploading!</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../gui/MainWindow.ui" line="+5"/>
+ <location filename="../gui/MainWindow.cpp" line="-428"/>
<source>Play Offline</source>
<translation>Offline spielen</translation>
</message>
<message>
- <location line="+90"/>
+ <location filename="../gui/MainWindow.cpp" line="+150"/>
<source>Error updating instance</source>
<translation>Fehler beim Aktualisieren der Instanz</translation>
</message>
@@ -1699,12 +2146,12 @@ Die folgende Instanz löschen:</translation>
<translation>Instanzeinstellungen</translation>
</message>
<message>
- <location line="+38"/>
+ <location line="+41"/>
<source>Rename Instance</source>
<translation>Instanz umbenennen</translation>
</message>
<message>
- <location line="+77"/>
+ <location line="+80"/>
<source>Select a Java version</source>
<translation>Wähle eine Java-Version</translation>
</message>
@@ -1722,7 +2169,7 @@ Die folgende Instanz löschen:</translation>
<context>
<name>MinecraftProcess</name>
<message>
- <location filename="../logic/MinecraftProcess.cpp" line="+139"/>
+ <location filename="../logic/MinecraftProcess.cpp" line="+237"/>
<source>Minecraft exited with exitcode %1.</source>
<extracomment>Message displayed on instance exit</extracomment>
<translation>Minecraft wurde mit Status %1 beendet.</translation>
@@ -1740,7 +2187,45 @@ Die folgende Instanz löschen:</translation>
<translation>Minecraft wurde durch den Nutzer getötet.</translation>
</message>
<message>
- <location line="+52"/>
+ <location line="+9"/>
+ <source>Running Post-Launch command: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+16"/>
+ <source>Post-Launch command failed with code %1.
+
+</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+6"/>
+ <source>Post-Launch command ran successfully.
+
+</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+21"/>
+ <source>Running Pre-Launch command: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+18"/>
+ <source>Pre-Launch command failed with code %1.
+
+</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+8"/>
+ <source>Pre-Launch command ran successfully.
+
+</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+42"/>
<source>Could not launch minecraft!</source>
<extracomment>Error message displayed if instace can&apos;t start</extracomment>
<translation>Konnte Minecraft nicht starten!</translation>
@@ -1849,7 +2334,7 @@ Die folgende Instanz löschen:</translation>
<context>
<name>OneSixFTBInstanceForge</name>
<message>
- <location filename="../logic/OneSixFTBInstance.cpp" line="+37"/>
+ <location filename="../logic/OneSixFTBInstance.cpp" line="+40"/>
<source>Downloading Forge...</source>
<translation>Forge wird heruntergeladen...</translation>
</message>
@@ -1864,7 +2349,7 @@ Die folgende Instanz löschen:</translation>
<translation>Fehlschlag beim Laden der Versions-Konfiguration</translation>
</message>
<message>
- <location line="+8"/>
+ <location line="+6"/>
<source>Couldn&apos;t install Forge</source>
<translation>Fehler beim Installieren von Forge</translation>
</message>
@@ -1881,19 +2366,18 @@ Die folgende Instanz löschen:</translation>
<translation type="obsolete">Bibliothek</translation>
</message>
<message>
- <location filename="../gui/dialogs/OneSixModEditDialog.ui" line="+179"/>
+ <location filename="../gui/dialogs/OneSixModEditDialog.ui" line="+158"/>
<source>Loader Mods</source>
<translation>Mods</translation>
</message>
<message>
- <location line="-50"/>
- <location line="+80"/>
+ <location line="+30"/>
<location line="+67"/>
<source>&amp;Add</source>
<translation>&amp;Hinzufügen</translation>
</message>
<message>
- <location line="-262"/>
+ <location line="-241"/>
<source>Manage Mods</source>
<translation>Verwalte Mods</translation>
</message>
@@ -1903,7 +2387,7 @@ Die folgende Instanz löschen:</translation>
<translation>Version</translation>
</message>
<message>
- <location line="+20"/>
+ <location line="+23"/>
<source>Main Class:</source>
<translation>Hauptklasse:</translation>
</message>
@@ -1923,49 +2407,66 @@ Die folgende Instanz löschen:</translation>
<translation>Installiere LiteLoader</translation>
</message>
<message>
+ <location line="+14"/>
+ <source>Reload</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+7"/>
+ <source>Remove</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
<location line="+7"/>
+ <source>Reset order</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+14"/>
+ <source>Move up</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+7"/>
+ <source>Move down</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
<source>Create an customized copy of the base version</source>
- <translation>Eine modifizierte Kopie der Version erstellen</translation>
+ <translation type="vanished">Eine modifizierte Kopie der Version erstellen</translation>
</message>
<message>
- <location line="+3"/>
<source>Customize</source>
- <translation>Benutzerdefiniert</translation>
+ <translation type="vanished">Benutzerdefiniert</translation>
</message>
<message>
- <location line="+10"/>
<source>Revert to original base version</source>
- <translation>Benutzerdefinierte Einstellungen zurücksetzen</translation>
+ <translation type="vanished">Benutzerdefinierte Einstellungen zurücksetzen</translation>
</message>
<message>
- <location line="+3"/>
<source>Revert</source>
- <translation>Zurücksetzen</translation>
+ <translation type="vanished">Zurücksetzen</translation>
</message>
<message>
- <location line="+20"/>
<source>Add new libraries</source>
- <translation>Füge neue Bibliotheken hinzu</translation>
+ <translation type="vanished">Füge neue Bibliotheken hinzu</translation>
</message>
<message>
- <location line="+13"/>
<source>Remove selected libraries</source>
- <translation>Entferne ausgewählte Bibliotheken</translation>
+ <translation type="vanished">Entferne ausgewählte Bibliotheken</translation>
</message>
<message>
- <location line="+3"/>
- <location line="+74"/>
+ <location line="+60"/>
<location line="+67"/>
<source>&amp;Remove</source>
<translation>&amp;Entfernen</translation>
</message>
<message>
- <location line="-127"/>
<source>Open custom.json</source>
- <translation>Öffne custom.json</translation>
+ <translation type="vanished">Öffne custom.json</translation>
</message>
<message>
- <location line="+80"/>
+ <location line="-47"/>
<location line="+67"/>
<source>&amp;View Folder</source>
<translation>&amp;Ordner öffnen</translation>
@@ -1976,49 +2477,78 @@ Die folgende Instanz löschen:</translation>
<translation>Ressourcenpakete</translation>
</message>
<message>
- <location filename="../gui/dialogs/OneSixModEditDialog.cpp" line="-205"/>
- <location line="+34"/>
+ <location filename="../gui/dialogs/OneSixModEditDialog.cpp" line="-292"/>
+ <source>Couldn&apos;t remove file</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+44"/>
+ <location line="+37"/>
+ <source>Couldn&apos;t save the new order</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+13"/>
+ <location line="+57"/>
<source>Revert?</source>
<translation>Zurücksetzen?</translation>
</message>
<message>
- <location line="-34"/>
+ <location line="-57"/>
+ <location line="+57"/>
+ <source>This action will remove your custom.json. Continue?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="-48"/>
+ <source>No Forge versions are currently available for Minecraft </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+55"/>
+ <source>Select LiteLoader version</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+2"/>
+ <source>No LiteLoader versions are currently available for Minecraft </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
<source>Do you want to revert the version of this instance to its original configuration?</source>
- <translation>Möchtest du wirklich die Version dieser Instanz zurücksetzen?</translation>
+ <translation type="vanished">Möchtest du wirklich die Version dieser Instanz zurücksetzen?</translation>
</message>
<message>
- <location line="+20"/>
+ <location line="-160"/>
+ <location line="+44"/>
+ <location line="+37"/>
<source>Error</source>
<translation>Fehler</translation>
</message>
<message>
- <location line="+0"/>
<source>Unable to open custom.json, check the settings</source>
- <translation>Fehler beim Öffnen der custom.json-Datei, überprüfe deine Einstellungen</translation>
+ <translation type="vanished">Fehler beim Öffnen der custom.json-Datei, überprüfe deine Einstellungen</translation>
</message>
<message>
- <location line="+7"/>
+ <location line="+20"/>
<source>Select Forge version</source>
<translation>Wähle Forge-Version</translation>
</message>
<message>
- <location line="+8"/>
<source>This will revert any changes you did to the version up to this point. Is that OK?</source>
- <translation>Dies wird alle Änderungen, die du vorgenommen hast, zurücksetzen. Bist du damit einverstanden?</translation>
+ <translation type="vanished">Dies wird alle Änderungen, die du vorgenommen hast, zurücksetzen. Bist du damit einverstanden?</translation>
</message>
<message>
- <location line="+69"/>
- <location line="+15"/>
+ <location line="+70"/>
<source>LiteLoader</source>
<translation>LiteLoader</translation>
</message>
<message>
- <location line="-14"/>
<source>There is no information available on how to install LiteLoader into this version of Minecraft</source>
- <translation>Es gibt momentan keine Informationen zur Installation von LiteLoader für diese Version von Minecraft</translation>
+ <translation type="vanished">Es gibt momentan keine Informationen zur Installation von LiteLoader für diese Version von Minecraft</translation>
</message>
<message>
- <location line="+15"/>
+ <location line="+1"/>
<source>For reasons unknown, the LiteLoader installation failed. Check your MultiMC log files for details.</source>
<translation>Aus unbekannten Gründen ist die Installation von LiteLoader fehlgeschlagen. Sieh dir die MultiMC-Logdateien an, um weitere Details zu erhalten.</translation>
</message>
@@ -2026,18 +2556,16 @@ Die folgende Instanz löschen:</translation>
<context>
<name>OneSixUpdate</name>
<message>
- <location filename="../logic/OneSixUpdate.cpp" line="+60"/>
- <location line="+32"/>
<source>Testing the Java installation...</source>
- <translation>Java-Installation wird getestet...</translation>
+ <translation type="vanished">Java-Installation wird getestet...</translation>
</message>
<message>
- <location line="+39"/>
+ <location filename="../logic/OneSixUpdate.cpp" line="+82"/>
<source>Getting the version files from Mojang...</source>
<translation>Versionsdateien von Mojang werden heruntergeladen...</translation>
</message>
<message>
- <location line="+68"/>
+ <location line="+69"/>
<source>Updating assets index...</source>
<translation>Datenindex wird aktualisiert...</translation>
</message>
@@ -2047,14 +2575,26 @@ Die folgende Instanz löschen:</translation>
<translation>Daten werden von Mojang geholt...</translation>
</message>
<message>
- <location line="+34"/>
+ <location line="+32"/>
<source>Getting the library files from Mojang...</source>
<translation>Bibliotheken werden von Mojang geholt...</translation>
</message>
<message>
- <location line="+88"/>
<source>Preparing for launch...</source>
- <translation>Start wird vorbereitet...</translation>
+ <translation type="vanished">Start wird vorbereitet...</translation>
+ </message>
+</context>
+<context>
+ <name>OneSixVersion</name>
+ <message>
+ <location filename="../logic/OneSixVersion.cpp" line="+173"/>
+ <source>Name</source>
+ <translation type="unfinished">Name</translation>
+ </message>
+ <message>
+ <location line="+2"/>
+ <source>Version</source>
+ <translation type="unfinished">Version</translation>
</message>
</context>
<context>
@@ -2101,11 +2641,96 @@ Diese Mitteilung wird so lange angezeigt, bis du die Option entfernt hast.</tran
<source>The mod author didn&apos;t provide a website link for this mod.</source>
<translation>Der Autor der Modifikation hat keine URL hinterlegt.</translation>
</message>
+ <message>
+ <location filename="../logic/tools/JVisualVM.cpp" line="+36"/>
+ <location filename="../logic/tools/JProfiler.cpp" line="+32"/>
+ <source>Empty path</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+6"/>
+ <source>Invalid path to JVisualVM</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../logic/tools/JProfiler.cpp" line="+6"/>
+ <location filename="../logic/tools/MCEditTool.cpp" line="+68"/>
+ <source>Path does not exist</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+5"/>
+ <source>Invalid JProfiler install</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../logic/tools/MCEditTool.cpp" line="-6"/>
+ <source>Path is empty</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+11"/>
+ <source>Path does not seem to be a MCEdit path</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../logic/lists/LiteLoaderVersionList.h" line="+36"/>
+ <source>Latest</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../logic/OneSixVersionBuilder.cpp" line="+891"/>
+ <location line="+42"/>
+ <location line="+47"/>
+ <location line="+8"/>
+ <location line="+7"/>
+ <location line="+15"/>
+ <location line="+8"/>
+ <location line="+11"/>
+ <source>Error</source>
+ <translation type="unfinished">Fehler</translation>
+ </message>
+ <message>
+ <location line="-137"/>
+ <location line="+42"/>
+ <source>Error while applying %1. Please check MultiMC-0.log for more info.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+47"/>
+ <source>Error while reading. Please check MultiMC-0.log for more info.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+8"/>
+ <source>Error while applying. Please check MultiMC-0.log for more info.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+7"/>
+ <source>The version descriptors of this instance are not compatible with the current version of MultiMC</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+15"/>
+ <source>Unable to open %1: %2</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+8"/>
+ <source>Unable to parse %1: %2 at %3</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+11"/>
+ <source>Error while reading %1. Please check MultiMC-0.log for more info.</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>RefreshTask</name>
<message>
- <location filename="../logic/auth/flows/RefreshTask.cpp" line="+148"/>
+ <location filename="../logic/auth/flows/RefreshTask.cpp" line="+146"/>
<source>Refreshing login token...</source>
<translation>Erneuerung des Login-Tokens...</translation>
</message>
@@ -2116,6 +2741,63 @@ Diese Mitteilung wird so lange angezeigt, bis du die Option entfernt hast.</tran
</message>
</context>
<context>
+ <name>ScreenshotDialog</name>
+ <message>
+ <location filename="../gui/dialogs/ScreenshotDialog.ui" line="+14"/>
+ <source>Screenshot Manager</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+40"/>
+ <source>Upload</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+7"/>
+ <source>Delete</source>
+ <translation type="unfinished">Löschen</translation>
+ </message>
+ <message>
+ <location line="+20"/>
+ <source>Close</source>
+ <translation type="unfinished">Schließen</translation>
+ </message>
+ <message>
+ <location filename="../gui/dialogs/ScreenshotDialog.cpp" line="+28"/>
+ <source>&lt;a href=&quot;https://imgur.com/a/%1&quot;&gt;Visit album&lt;/a&gt;&lt;br/&gt;Delete hash: %2 (save this if you want to be able to edit/delete the album)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+41"/>
+ <source>Failed to upload screenshots!</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+1"/>
+ <source>Unknown error</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenshotList</name>
+ <message>
+ <location filename="../logic/screenshots/ScreenshotList.cpp" line="+98"/>
+ <location line="+10"/>
+ <source>Error!</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="-9"/>
+ <source>Failed to delete screenshots!</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+10"/>
+ <source>Unable to refresh list: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
<name>SettingsDialog</name>
<message>
<location filename="../gui/dialogs/SettingsDialog.ui" line="+20"/>
@@ -2123,12 +2805,11 @@ Diese Mitteilung wird so lange angezeigt, bis du die Option entfernt hast.</tran
<translation>Einstellungen</translation>
</message>
<message>
- <location line="+20"/>
<source>General</source>
- <translation>Allgemein</translation>
+ <translation type="vanished">Allgemein</translation>
</message>
<message>
- <location line="+9"/>
+ <location line="+273"/>
<source>Sorting Mode</source>
<translation>Sortiermodus</translation>
</message>
@@ -2143,22 +2824,21 @@ Diese Mitteilung wird so lange angezeigt, bis du die Option entfernt hast.</tran
<translation>Nach Namen</translation>
</message>
<message>
- <location line="+13"/>
+ <location line="-263"/>
<source>Update Settings</source>
<translation>Updateeinstellungen</translation>
</message>
<message>
- <location line="+6"/>
<source>Use development builds?</source>
- <translation>Entwicklungsversionen benutzen?</translation>
+ <translation type="vanished">Entwicklungsversionen benutzen?</translation>
</message>
<message>
- <location line="+7"/>
+ <location line="+6"/>
<source>Check for updates when MultiMC starts?</source>
<translation>Beim Start nach Updates suchen?</translation>
</message>
<message>
- <location line="+97"/>
+ <location line="+121"/>
<source>Folders</source>
<translation>Ordner</translation>
</message>
@@ -2174,13 +2854,31 @@ Diese Mitteilung wird so lange angezeigt, bis du die Option entfernt hast.</tran
<location line="+20"/>
<location line="+14"/>
<location line="+17"/>
- <location line="+26"/>
- <location line="+289"/>
+ <location line="+98"/>
+ <location line="+307"/>
+ <location line="+240"/>
+ <location line="+37"/>
+ <location line="+37"/>
<source>...</source>
<translation>...</translation>
</message>
<message>
- <location line="-469"/>
+ <location line="-919"/>
+ <source>Features</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+19"/>
+ <source>Update Channel:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+14"/>
+ <source>No channel selected.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+13"/>
<source>FTB</source>
<translation>FTB</translation>
</message>
@@ -2215,7 +2913,17 @@ Diese Mitteilung wird so lange angezeigt, bis du die Option entfernt hast.</tran
<translation>Symbole:</translation>
</message>
<message>
- <location line="+17"/>
+ <location line="+31"/>
+ <source>User Interface</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+14"/>
+ <source>Language (needs restart):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+44"/>
<source>External Editors (leave empty for system default)</source>
<translation>Externe Editor-Anwendungen (leer lassen, um die System-Voreinstellung zu benutzen)</translation>
</message>
@@ -2265,11 +2973,128 @@ Diese Mitteilung wird so lange angezeigt, bis du die Option entfernt hast.</tran
<translation>Konsole automatisch schließen, nachdem das Spiel beendet wurde?</translation>
</message>
<message>
+ <location line="+265"/>
+ <source>Network settings.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>Network</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+6"/>
+ <source>Proxy</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+6"/>
+ <source>Type</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+6"/>
+ <source>Uses your system&apos;s default proxy settings.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>Default</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+10"/>
+ <source>None</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+10"/>
+ <source>SOCKS5</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+10"/>
+ <source>HTTP</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+13"/>
+ <source>Address and Port</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+6"/>
+ <source>127.0.0.1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+26"/>
+ <source>Authentication</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+9"/>
+ <source>Username:</source>
+ <translation type="unfinished">Nutzername:</translation>
+ </message>
+ <message>
+ <location line="+7"/>
+ <source>Password:</source>
+ <translation type="unfinished">Passwort:</translation>
+ </message>
+ <message>
+ <location line="+14"/>
+ <source>Note: Proxy username and password are stored in plain text inside MultiMC&apos;s configuration file!</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+30"/>
+ <source>External Tools</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+6"/>
+ <source>JProfiler</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+20"/>
+ <location line="+37"/>
+ <location line="+37"/>
+ <source>Check</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="-67"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;http://www.ej-technologies.com/products/jprofiler/overview.html&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;http://www.ej-technologies.com/products/jprofiler/overview.html&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+10"/>
+ <source>JVisualVM</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+27"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;http://visualvm.java.net/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;http://visualvm.java.net/&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+10"/>
+ <source>MCEdit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+27"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;http://www.mcedit.net/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;http://www.mcedit.net/&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
<source>Login automatically when an instance icon is double clicked?</source>
<translation type="vanished">Automatisch einloggen, wenn das Instanzsymbol doppelt geklickt wurde?</translation>
</message>
<message>
- <location line="+24"/>
+ <location line="-507"/>
<source>Java</source>
<translation>Java</translation>
</message>
@@ -2279,7 +3104,19 @@ Diese Mitteilung wird so lange angezeigt, bis du die Option entfernt hast.</tran
<translation>Arbeitsspeicher</translation>
</message>
<message>
- <location line="+22"/>
+ <location line="+6"/>
+ <source>The maximum amount of memory Minecraft is allowed to use.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <location line="+36"/>
+ <location line="+29"/>
+ <source> MB</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="-46"/>
<source>Minimum memory allocation:</source>
<translation>Min. Arbeitspeicher:</translation>
</message>
@@ -2289,12 +3126,22 @@ Diese Mitteilung wird so lange angezeigt, bis du die Option entfernt hast.</tran
<translation>Max. Arbeitspeicher:</translation>
</message>
<message>
- <location line="+23"/>
+ <location line="+7"/>
+ <source>The amount of memory Minecraft is started with.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+22"/>
<source>PermGen:</source>
<translation>PermGen:</translation>
</message>
<message>
- <location line="+26"/>
+ <location line="+7"/>
+ <source>The amount of memory available to store loaded Java classes.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+25"/>
<source>Java Settings</source>
<translation>Java-Einstellungen</translation>
</message>
@@ -2347,17 +3194,17 @@ Diese Mitteilung wird so lange angezeigt, bis du die Option entfernt hast.</tran
<translation>Der Vor-Start-Befehl wird ausgeführt, bevor die Instanz startet, der Nach-Ende-Befehl, nachdem die Instanz beendet wurde. Beide werden im Hauptverzeichnis von MultiMC gestartet. Verfügbare Umgebungsvariablen: INST_ID, INST_DIR, INST_NAME.</translation>
</message>
<message>
- <location filename="../gui/dialogs/SettingsDialog.cpp" line="+77"/>
+ <location filename="../gui/dialogs/SettingsDialog.cpp" line="+102"/>
<source>FTB Launcher Directory</source>
<translation>FTB-Launcher-Ordner</translation>
</message>
<message>
- <location line="+13"/>
+ <location line="+14"/>
<source>FTB Directory</source>
<translation>FTB-Ordner</translation>
</message>
<message>
- <location line="+13"/>
+ <location line="+12"/>
<source>Instance Directory</source>
<translation>Instanz-Ordner</translation>
</message>
@@ -2382,27 +3229,103 @@ Diese Mitteilung wird so lange angezeigt, bis du die Option entfernt hast.</tran
<translation>JSON-Editor</translation>
</message>
<message>
- <location line="+23"/>
+ <location line="+22"/>
<source>Invalid</source>
<translation>Ungültig</translation>
</message>
<message>
- <location line="+0"/>
+ <location line="+1"/>
<source>The file chosen does not seem to be an executable</source>
<translation>Die ausgewählte Datei scheint keine Anwendung zu sein</translation>
</message>
<message>
- <location line="+34"/>
+ <location line="+201"/>
+ <source>English</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+144"/>
+ <source>JProfiler Directory</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+8"/>
+ <location line="+16"/>
+ <location line="+23"/>
+ <location line="+16"/>
+ <location line="+28"/>
+ <location line="+17"/>
+ <source>Error</source>
+ <translation type="unfinished">Fehler</translation>
+ </message>
+ <message>
+ <location line="-99"/>
+ <location line="+16"/>
+ <source>Error while checking JProfiler install:
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+4"/>
+ <location line="+39"/>
+ <location line="+45"/>
+ <source>OK</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="-84"/>
+ <source>JProfiler setup seems to be OK</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+10"/>
+ <source>JVisualVM Executable</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+9"/>
+ <location line="+16"/>
+ <source>Error while checking JVisualVM install:
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+4"/>
+ <source>JVisualVM setup seems to be OK</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+12"/>
+ <source>MCEdit Application</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+2"/>
+ <source>MCEdit Directory</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+10"/>
+ <location line="+17"/>
+ <source>Error while checking MCEdit install:
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+4"/>
+ <source>MCEdit setup seems to be OK</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
<source>Development builds</source>
- <translation>Entwicklungsversionen</translation>
+ <translation type="vanished">Entwicklungsversionen</translation>
</message>
<message>
- <location line="+1"/>
<source>Development builds contain experimental features and may be unstable. Are you sure you want to enable them?</source>
- <translation>Entwicklungsversionen enthalten experimentelle Features und können instabil sein. Möchtest du sie dennoch aktivieren?</translation>
+ <translation type="vanished">Entwicklungsversionen enthalten experimentelle Features und können instabil sein. Möchtest du sie dennoch aktivieren?</translation>
</message>
<message>
- <location line="+132"/>
+ <location line="-170"/>
<source>Select a Java version</source>
<translation>Wähle eine Java-Version</translation>
</message>
@@ -2481,6 +3404,14 @@ Diese Mitteilung wird so lange angezeigt, bis du die Option entfernt hast.</tran
</message>
</context>
<context>
+ <name>VersionListView</name>
+ <message>
+ <location filename="../gui/widgets/VersionListView.cpp" line="+27"/>
+ <source>No versions are currently available.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
<name>VersionSelectDialog</name>
<message>
<source>Dialog</source>