From d587720010036e3335e321f192449808a75e958b Mon Sep 17 00:00:00 2001
From: Petr Mrázek <peterix@gmail.com>
Date: Mon, 21 Mar 2016 02:19:23 +0100
Subject: NOISSUE use new mojang assets locations

---
 logic/minecraft/AssetsUtils.cpp           | 52 +++++++++++++++++++++++++++++--
 logic/minecraft/AssetsUtils.h             | 12 ++++++-
 logic/minecraft/MinecraftProfile.cpp      | 22 ++++++-------
 logic/minecraft/MinecraftProfile.h        |  9 +++---
 logic/minecraft/MojangVersionFormat.cpp   |  8 +++--
 logic/minecraft/VersionFile.cpp           |  2 +-
 logic/minecraft/onesix/OneSixInstance.cpp |  5 +--
 logic/minecraft/onesix/OneSixUpdate.cpp   | 35 +++++++--------------
 logic/net/NetJob.h                        | 19 ++++++-----
 9 files changed, 104 insertions(+), 60 deletions(-)

diff --git a/logic/minecraft/AssetsUtils.cpp b/logic/minecraft/AssetsUtils.cpp
index b3589cb8..7a525abe 100644
--- a/logic/minecraft/AssetsUtils.cpp
+++ b/logic/minecraft/AssetsUtils.cpp
@@ -13,6 +13,7 @@
  * limitations under the License.
  */
 
+#include <QFileInfo>
 #include <QDir>
 #include <QDirIterator>
 #include <QCryptographicHash>
@@ -23,7 +24,8 @@
 #include <QDebug>
 
 #include "AssetsUtils.h"
-#include <FileSystem.h>
+#include "FileSystem.h"
+#include "net/MD5EtagDownload.h"
 
 namespace AssetsUtils
 {
@@ -32,7 +34,7 @@ namespace AssetsUtils
  * Returns true on success, with index populated
  * index is undefined otherwise
  */
-bool loadAssetsIndexJson(QString path, AssetsIndex *index)
+bool loadAssetsIndexJson(QString assetsId, QString path, AssetsIndex *index)
 {
 	/*
 	{
@@ -56,6 +58,7 @@ bool loadAssetsIndexJson(QString path, AssetsIndex *index)
 		qCritical() << "Failed to read assets index file" << path;
 		return false;
 	}
+	index->id = assetsId;
 
 	// Read the file and close it.
 	QByteArray jsonData = file.readAll();
@@ -143,7 +146,7 @@ QDir reconstructAssets(QString assetsId)
 				 << objectDir.path() << virtualDir.path() << virtualRoot.path();
 
 	AssetsIndex index;
-	bool loadAssetsIndex = AssetsUtils::loadAssetsIndexJson(indexPath, &index);
+	bool loadAssetsIndex = AssetsUtils::loadAssetsIndexJson(assetsId, indexPath, &index);
 
 	if (loadAssetsIndex && index.isVirtual)
 	{
@@ -182,3 +185,46 @@ QDir reconstructAssets(QString assetsId)
 }
 
 }
+
+NetActionPtr AssetObject::getDownloadAction()
+{
+	QFileInfo objectFile(getLocalPath());
+	if ((!objectFile.isFile()) || (objectFile.size() != size))
+	{
+		auto objectDL = MD5EtagDownload::make(getUrl(), objectFile.filePath());
+		objectDL->m_total_progress = size;
+		return objectDL;
+	}
+	return nullptr;
+}
+
+QString AssetObject::getLocalPath()
+{
+	return "assets/objects/" + getRelPath();
+}
+
+QUrl AssetObject::getUrl()
+{
+	return QUrl("http://resources.download.minecraft.net/" + getRelPath());
+}
+
+QString AssetObject::getRelPath()
+{
+	return hash.left(2) + "/" + hash;
+}
+
+NetJobPtr AssetsIndex::getDownloadJob()
+{
+	auto job = new NetJob(QObject::tr("Assets for %1").arg(id));
+	for (auto &object : objects.values())
+	{
+		auto dl = object.getDownloadAction();
+		if(dl)
+		{
+			job->addNetAction(dl);
+		}
+	}
+	if(job->size())
+		return job;
+	return nullptr;
+}
diff --git a/logic/minecraft/AssetsUtils.h b/logic/minecraft/AssetsUtils.h
index 2aecd4c5..90251c2d 100644
--- a/logic/minecraft/AssetsUtils.h
+++ b/logic/minecraft/AssetsUtils.h
@@ -17,22 +17,32 @@
 
 #include <QString>
 #include <QMap>
+#include "net/NetAction.h"
+#include "net/NetJob.h"
 
 struct AssetObject
 {
+	QString getRelPath();
+	QUrl getUrl();
+	QString getLocalPath();
+	NetActionPtr getDownloadAction();
+
 	QString hash;
 	qint64 size;
 };
 
 struct AssetsIndex
 {
+	NetJobPtr getDownloadJob();
+
+	QString id;
 	QMap<QString, AssetObject> objects;
 	bool isVirtual = false;
 };
 
 namespace AssetsUtils
 {
-bool loadAssetsIndexJson(QString file, AssetsIndex* index);
+bool loadAssetsIndexJson(QString id, QString file, AssetsIndex* index);
 /// Reconstruct a virtual assets folder for the given assets ID and return the folder
 QDir reconstructAssets(QString assetsId);
 }
diff --git a/logic/minecraft/MinecraftProfile.cpp b/logic/minecraft/MinecraftProfile.cpp
index 5fd700f2..71ece012 100644
--- a/logic/minecraft/MinecraftProfile.cpp
+++ b/logic/minecraft/MinecraftProfile.cpp
@@ -62,7 +62,7 @@ void MinecraftProfile::clear()
 {
 	m_minecraftVersion.clear();
 	m_minecraftVersionType.clear();
-	m_minecraftAssets.clear();
+	m_minecraftAssets.reset();
 	m_minecraftArguments.clear();
 	m_tweakers.clear();
 	m_mainClass.clear();
@@ -420,9 +420,12 @@ void MinecraftProfile::applyMinecraftVersionType(const QString& type)
 	applyString(type, this->m_minecraftVersionType);
 }
 
-void MinecraftProfile::applyMinecraftAssets(const QString& assets)
+void MinecraftProfile::applyMinecraftAssets(MojangAssetIndexInfo::Ptr assets)
 {
-	applyString(assets, this->m_minecraftAssets);
+	if(assets)
+	{
+		m_minecraftAssets = assets;
+	}
 }
 
 void MinecraftProfile::applyTraits(const QSet<QString>& traits)
@@ -544,18 +547,11 @@ QString MinecraftProfile::getMinecraftVersionType() const
 	return m_minecraftVersionType;
 }
 
-QString MinecraftProfile::getMinecraftAssets() const
+std::shared_ptr<MojangAssetIndexInfo> MinecraftProfile::getMinecraftAssets() const
 {
-	// HACK: deny april fools. my head hurts enough already.
-	QDate now = QDate::currentDate();
-	bool isAprilFools = now.month() == 4 && now.day() == 1;
-	if (m_minecraftAssets.endsWith("_af") && !isAprilFools)
-	{
-		return m_minecraftAssets.left(m_minecraftAssets.length() - 3);
-	}
-	if (m_minecraftAssets.isEmpty())
+	if(!m_minecraftAssets)
 	{
-		return QLatin1Literal("legacy");
+		return std::make_shared<MojangAssetIndexInfo>("legacy");
 	}
 	return m_minecraftAssets;
 }
diff --git a/logic/minecraft/MinecraftProfile.h b/logic/minecraft/MinecraftProfile.h
index 6e7e8f74..ce0ff3cf 100644
--- a/logic/minecraft/MinecraftProfile.h
+++ b/logic/minecraft/MinecraftProfile.h
@@ -24,6 +24,7 @@
 #include "Library.h"
 #include "VersionFile.h"
 #include "JarMod.h"
+#include "MojangDownloadInfo.h"
 
 #include "multimc_logic_export.h"
 
@@ -90,19 +91,19 @@ public: /* application of profile variables from patches */
 	void applyAppletClass(const QString& appletClass);
 	void applyMinecraftArguments(const QString& minecraftArguments);
 	void applyMinecraftVersionType(const QString& type);
-	void applyMinecraftAssets(const QString& assets);
+	void applyMinecraftAssets(MojangAssetIndexInfo::Ptr assets);
 	void applyTraits(const QSet<QString> &traits);
 	void applyTweakers(const QStringList &tweakers);
 	void applyJarMods(const QList<JarmodPtr> &jarMods);
 	void applyLibrary(LibraryPtr library);
 	void applyProblemSeverity(ProblemSeverity severity);
 
-public: /* getters for proifile variables */
+public: /* getters for profile variables */
 	QString getMinecraftVersion() const;
 	QString getMainClass() const;
 	QString getAppletClass() const;
 	QString getMinecraftVersionType() const;
-	QString getMinecraftAssets() const;
+	MojangAssetIndexInfo::Ptr getMinecraftAssets() const;
 	QString getMinecraftArguments() const;
 	const QSet<QString> & getTraits() const;
 	const QStringList & getTweakers() const;
@@ -136,7 +137,7 @@ private: /* data */
 	QString m_minecraftVersionType;
 
 	/// Assets type - "legacy" or a version ID
-	QString m_minecraftAssets;
+	MojangAssetIndexInfo::Ptr m_minecraftAssets;
 
 	/**
 	 * arguments that should be used for launching minecraft
diff --git a/logic/minecraft/MojangVersionFormat.cpp b/logic/minecraft/MojangVersionFormat.cpp
index 779a2b7d..41723493 100644
--- a/logic/minecraft/MojangVersionFormat.cpp
+++ b/logic/minecraft/MojangVersionFormat.cpp
@@ -157,11 +157,15 @@ void MojangVersionFormat::readVersionProperties(const QJsonObject &in, VersionFi
 	}
 	Bits::readString(in, "type", out->type);
 
+	Bits::readString(in, "assets", out->assets);
 	if(in.contains("assetIndex"))
 	{
 		out->mojangAssetIndex = assetIndexFromJson(requireObject(in, "assetIndex"));
 	}
-	Bits::readString(in, "assets", out->assets);
+	else if (!out->assets.isNull())
+	{
+		out->mojangAssetIndex = std::make_shared<MojangAssetIndexInfo>(out->assets);
+	}
 
 	out->m_releaseTime = timeFromS3Time(in.value("releaseTime").toString(""));
 	out->m_updateTime = timeFromS3Time(in.value("time").toString(""));
@@ -231,7 +235,6 @@ void MojangVersionFormat::writeVersionProperties(const VersionFile* in, QJsonObj
 	writeString(out, "mainClass", in->mainClass);
 	writeString(out, "minecraftArguments", in->minecraftArguments);
 	writeString(out, "type", in->type);
-	writeString(out, "assets", in->assets);
 	if(!in->m_releaseTime.isNull())
 	{
 		writeString(out, "releaseTime", timeToS3Time(in->m_releaseTime));
@@ -244,6 +247,7 @@ void MojangVersionFormat::writeVersionProperties(const VersionFile* in, QJsonObj
 	{
 		out.insert("minimumLauncherVersion", in->minimumLauncherVersion);
 	}
+	writeString(out, "assets", in->assets);
 	if(in->mojangAssetIndex && in->mojangAssetIndex->known)
 	{
 		out.insert("assetIndex", assetIndexToJson(in->mojangAssetIndex));
diff --git a/logic/minecraft/VersionFile.cpp b/logic/minecraft/VersionFile.cpp
index 9cda717e..451c3c8b 100644
--- a/logic/minecraft/VersionFile.cpp
+++ b/logic/minecraft/VersionFile.cpp
@@ -40,7 +40,7 @@ void VersionFile::applyTo(MinecraftProfile *profile)
 	{
 		profile->applyMinecraftVersionType(type);
 	}
-	profile->applyMinecraftAssets(assets);
+	profile->applyMinecraftAssets(mojangAssetIndex);
 	profile->applyTweakers(addTweakers);
 
 	profile->applyJarMods(jarMods);
diff --git a/logic/minecraft/onesix/OneSixInstance.cpp b/logic/minecraft/onesix/OneSixInstance.cpp
index f8b274ff..328c3b38 100644
--- a/logic/minecraft/onesix/OneSixInstance.cpp
+++ b/logic/minecraft/onesix/OneSixInstance.cpp
@@ -125,14 +125,15 @@ QStringList OneSixInstance::processMinecraftArgs(AuthSessionPtr session)
 	QString absRootDir = QDir(minecraftRoot()).absolutePath();
 	token_mapping["game_directory"] = absRootDir;
 	QString absAssetsDir = QDir("assets/").absolutePath();
-	token_mapping["game_assets"] = AssetsUtils::reconstructAssets(m_profile->getMinecraftAssets()).absolutePath();
+	auto assets = m_profile->getMinecraftAssets();
+	token_mapping["game_assets"] = AssetsUtils::reconstructAssets(assets->id).absolutePath();
 
 	token_mapping["user_properties"] = session->serializeUserProperties();
 	token_mapping["user_type"] = session->user_type;
 
 	// 1.7.3+ assets tokens
 	token_mapping["assets_root"] = absAssetsDir;
-	token_mapping["assets_index_name"] = m_profile->getMinecraftAssets();
+	token_mapping["assets_index_name"] = assets->id;
 
 	QStringList parts = args_pattern.split(' ', QString::SkipEmptyParts);
 	for (int i = 0; i < parts.length(); i++)
diff --git a/logic/minecraft/onesix/OneSixUpdate.cpp b/logic/minecraft/onesix/OneSixUpdate.cpp
index fe1d3a6e..10d1e294 100644
--- a/logic/minecraft/onesix/OneSixUpdate.cpp
+++ b/logic/minecraft/onesix/OneSixUpdate.cpp
@@ -88,9 +88,9 @@ void OneSixUpdate::assetIndexStart()
 	setStatus(tr("Updating assets index..."));
 	OneSixInstance *inst = (OneSixInstance *)m_inst;
 	auto profile = inst->getMinecraftProfile();
-	QString assetName = profile->getMinecraftAssets();
-	QUrl indexUrl = "http://" + URLConstants::AWS_DOWNLOAD_INDEXES + assetName + ".json";
-	QString localPath = assetName + ".json";
+	auto assets = profile->getMinecraftAssets();
+	QUrl indexUrl = assets->url;
+	QString localPath = assets->id + ".json";
 	auto job = new NetJob(tr("Asset index for %1").arg(inst->name()));
 
 	auto metacache = ENV.metacache();
@@ -114,36 +114,23 @@ void OneSixUpdate::assetIndexFinished()
 
 	OneSixInstance *inst = (OneSixInstance *)m_inst;
 	auto profile = inst->getMinecraftProfile();
-	QString assetName = profile->getMinecraftAssets();
+	auto assets = profile->getMinecraftAssets();
 
-	QString asset_fname = "assets/indexes/" + assetName + ".json";
-	if (!AssetsUtils::loadAssetsIndexJson(asset_fname, &index))
+	QString asset_fname = "assets/indexes/" + assets->id + ".json";
+	// FIXME: this looks like a job for a generic validator based on json schema?
+	if (!AssetsUtils::loadAssetsIndexJson(assets->id, asset_fname, &index))
 	{
 		auto metacache = ENV.metacache();
-		auto entry = metacache->resolveEntry("asset_indexes", assetName + ".json");
+		auto entry = metacache->resolveEntry("asset_indexes", assets->id + ".json");
 		metacache->evictEntry(entry);
 		emitFailed(tr("Failed to read the assets index!"));
 	}
 
-	QList<Md5EtagDownloadPtr> dls;
-	for (auto object : index.objects.values())
-	{
-		QString objectName = object.hash.left(2) + "/" + object.hash;
-		QFileInfo objectFile("assets/objects/" + objectName);
-		if ((!objectFile.isFile()) || (objectFile.size() != object.size))
-		{
-			auto objectDL = MD5EtagDownload::make(QUrl("http://" + URLConstants::RESOURCE_BASE + objectName), objectFile.filePath());
-			objectDL->m_total_progress = object.size;
-			dls.append(objectDL);
-		}
-	}
-	if (dls.size())
+	auto job = index.getDownloadJob();
+	if(job)
 	{
 		setStatus(tr("Getting the assets files from Mojang..."));
-		auto job = new NetJob(tr("Assets for %1").arg(inst->name()));
-		for (auto dl : dls)
-			job->addNetAction(dl);
-		jarlibDownloadJob.reset(job);
+		jarlibDownloadJob = job;
 		connect(jarlibDownloadJob.get(), SIGNAL(succeeded()), SLOT(assetsFinished()));
 		connect(jarlibDownloadJob.get(), &NetJob::failed, this, &OneSixUpdate::assetsFailed);
 		connect(jarlibDownloadJob.get(), SIGNAL(progress(qint64, qint64)), SIGNAL(progress(qint64, qint64)));
diff --git a/logic/net/NetJob.h b/logic/net/NetJob.h
index 45f6dacf..afbe9ff3 100644
--- a/logic/net/NetJob.h
+++ b/logic/net/NetJob.h
@@ -35,16 +35,15 @@ class MULTIMC_LOGIC_EXPORT NetJob : public Task
 public:
 	explicit NetJob(QString job_name) : Task(), m_job_name(job_name) {}
 	virtual ~NetJob() {}
-	template <typename T> bool addNetAction(T action)
+	bool addNetAction(NetActionPtr action)
 	{
-		NetActionPtr base = std::static_pointer_cast<NetAction>(action);
-		base->m_index_within_job = downloads.size();
+		action->m_index_within_job = downloads.size();
 		downloads.append(action);
 		part_info pi;
 		{
-			pi.current_progress = base->currentProgress();
-			pi.total_progress = base->totalProgress();
-			pi.failures = base->numberOfFailures();
+			pi.current_progress = action->currentProgress();
+			pi.total_progress = action->totalProgress();
+			pi.failures = action->numberOfFailures();
 		}
 		parts_progress.append(pi);
 		total_progress += pi.total_progress;
@@ -52,11 +51,11 @@ public:
 		if (isRunning())
 		{
 			setProgress(current_progress, total_progress);
-			connect(base.get(), SIGNAL(succeeded(int)), SLOT(partSucceeded(int)));
-			connect(base.get(), SIGNAL(failed(int)), SLOT(partFailed(int)));
-			connect(base.get(), SIGNAL(netActionProgress(int, qint64, qint64)),
+			connect(action.get(), SIGNAL(succeeded(int)), SLOT(partSucceeded(int)));
+			connect(action.get(), SIGNAL(failed(int)), SLOT(partFailed(int)));
+			connect(action.get(), SIGNAL(netActionProgress(int, qint64, qint64)),
 					SLOT(partProgress(int, qint64, qint64)));
-			base->start();
+			action->start();
 		}
 		return true;
 	}
-- 
cgit