Legacy jar reassembly, base of proper custom jar support
This commit is contained in:
		@@ -1,7 +1,7 @@
 | 
			
		||||
#include "JlCompress.h"
 | 
			
		||||
#include <QDebug>
 | 
			
		||||
 | 
			
		||||
static bool copyData(QIODevice &inFile, QIODevice &outFile)
 | 
			
		||||
bool JlCompress::copyData(QIODevice &inFile, QIODevice &outFile)
 | 
			
		||||
{
 | 
			
		||||
    while (!inFile.atEnd()) {
 | 
			
		||||
        char buf[4096];
 | 
			
		||||
 
 | 
			
		||||
@@ -51,6 +51,8 @@ private:
 | 
			
		||||
    static bool removeFile(QStringList listFile);
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    /// copy data from inFile to outFile
 | 
			
		||||
    static bool copyData(QIODevice &inFile, QIODevice &outFile);
 | 
			
		||||
    /// Compress a single file.
 | 
			
		||||
    /**
 | 
			
		||||
      \param fileCompressed The name of the archive.
 | 
			
		||||
 
 | 
			
		||||
@@ -29,7 +29,17 @@ LIBUTIL_EXPORT QString RemoveInvalidFilenameChars(QString string, QChar replaceW
 | 
			
		||||
 | 
			
		||||
LIBUTIL_EXPORT QString DirNameFromString(QString string, QString inDir = ".");
 | 
			
		||||
 | 
			
		||||
LIBUTIL_EXPORT bool ensurePathExists(QString filenamepath);
 | 
			
		||||
/**
 | 
			
		||||
 * Creates all the folders in a path for the specified path
 | 
			
		||||
 * last segment of the path is treated as a file name and is ignored!
 | 
			
		||||
 */
 | 
			
		||||
LIBUTIL_EXPORT bool ensureFilePathExists(QString filenamepath);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Creates all the folders in a path for the specified path
 | 
			
		||||
 * last segment of the path is treated as a folder name and is created!
 | 
			
		||||
 */
 | 
			
		||||
LIBUTIL_EXPORT bool ensureFolderPathExists(QString filenamepath);
 | 
			
		||||
 | 
			
		||||
LIBUTIL_EXPORT bool copyPath(QString src, QString dst);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -19,6 +19,7 @@
 | 
			
		||||
#include <QDir>
 | 
			
		||||
#include <QDesktopServices>
 | 
			
		||||
#include <QUrl>
 | 
			
		||||
#include <QDebug>
 | 
			
		||||
 | 
			
		||||
QString PathCombine(QString path1, QString path2)
 | 
			
		||||
{
 | 
			
		||||
@@ -68,24 +69,38 @@ QString DirNameFromString(QString string, QString inDir)
 | 
			
		||||
	return dirName;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ensurePathExists(QString filenamepath)
 | 
			
		||||
bool ensureFilePathExists(QString filenamepath)
 | 
			
		||||
{
 | 
			
		||||
	QFileInfo a ( filenamepath );
 | 
			
		||||
	QDir dir;
 | 
			
		||||
	return (dir.mkpath ( a.filePath() ));
 | 
			
		||||
	QString ensuredPath = a.path();
 | 
			
		||||
	bool success = dir.mkpath ( ensuredPath );
 | 
			
		||||
	qDebug() << "ensureFilePathExists:" << success << ensuredPath << filenamepath;
 | 
			
		||||
	return success;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ensureFolderPathExists(QString foldernamepath)
 | 
			
		||||
{
 | 
			
		||||
	QFileInfo a ( foldernamepath );
 | 
			
		||||
	QDir dir;
 | 
			
		||||
	QString ensuredPath = a.filePath();
 | 
			
		||||
	bool success = dir.mkpath ( ensuredPath );
 | 
			
		||||
	qDebug() << "ensureFolderPathExists:" << success << ensuredPath << foldernamepath;
 | 
			
		||||
	return success;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
bool copyPath(QString src, QString dst)
 | 
			
		||||
{
 | 
			
		||||
	QDir dir(src);
 | 
			
		||||
	if (!dir.exists())
 | 
			
		||||
		return false;
 | 
			
		||||
	if(!ensurePathExists(dst))
 | 
			
		||||
	if(!ensureFolderPathExists(dst))
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	foreach (QString d, dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot))
 | 
			
		||||
	{
 | 
			
		||||
		QString inner_src = src+ QDir::separator() + d;
 | 
			
		||||
		QString inner_src = src + QDir::separator() + d;
 | 
			
		||||
		QString inner_dst = dst + QDir::separator() + d;
 | 
			
		||||
		copyPath(inner_src, inner_dst);
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -28,9 +28,9 @@ LegacyModEditDialog::LegacyModEditDialog( LegacyInstance* inst, QWidget* parent
 | 
			
		||||
	ui(new Ui::LegacyModEditDialog)
 | 
			
		||||
{
 | 
			
		||||
	ui->setupUi(this);
 | 
			
		||||
	ensurePathExists(m_inst->coreModsDir());
 | 
			
		||||
	ensurePathExists(m_inst->mlModsDir());
 | 
			
		||||
	ensurePathExists(m_inst->jarModsDir());
 | 
			
		||||
	ensureFolderPathExists(m_inst->coreModsDir());
 | 
			
		||||
	ensureFolderPathExists(m_inst->mlModsDir());
 | 
			
		||||
	ensureFolderPathExists(m_inst->jarModsDir());
 | 
			
		||||
	
 | 
			
		||||
	m_mods = m_inst->loaderModList();
 | 
			
		||||
	m_coremods = m_inst->coreModList();
 | 
			
		||||
 
 | 
			
		||||
@@ -42,6 +42,15 @@ BaseInstance::BaseInstance( BaseInstancePrivate* d_in,
 | 
			
		||||
	settings().registerSetting(new Setting("notes", ""));
 | 
			
		||||
	settings().registerSetting(new Setting("lastLaunchTime", 0));
 | 
			
		||||
	
 | 
			
		||||
	/*
 | 
			
		||||
	 * custom base jar has no default. it is determined in code... see the accessor methods for it
 | 
			
		||||
	 * 
 | 
			
		||||
	 * for instances that DO NOT have the CustomBaseJar setting (legacy instances),
 | 
			
		||||
	 * [.]minecraft/bin/mcbackup.jar is the default base jar
 | 
			
		||||
	 */
 | 
			
		||||
	settings().registerSetting(new Setting("UseCustomBaseJar", true));
 | 
			
		||||
	settings().registerSetting(new Setting("CustomBaseJar", ""));
 | 
			
		||||
	
 | 
			
		||||
	// Java Settings
 | 
			
		||||
	settings().registerSetting(new Setting("OverrideJava", false));
 | 
			
		||||
	settings().registerSetting(new OverrideSetting("JavaPath", globalSettings->getSetting("JavaPath")));
 | 
			
		||||
@@ -121,6 +130,51 @@ SettingsObject &BaseInstance::settings() const
 | 
			
		||||
	return *d->m_settings;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QString BaseInstance::baseJar() const
 | 
			
		||||
{
 | 
			
		||||
	I_D(BaseInstance);
 | 
			
		||||
	bool customJar = d->m_settings->get("UseCustomBaseJar").toBool();
 | 
			
		||||
	if(customJar)
 | 
			
		||||
	{
 | 
			
		||||
		return customBaseJar();
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
		return defaultBaseJar();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QString BaseInstance::customBaseJar() const
 | 
			
		||||
{
 | 
			
		||||
	I_D(BaseInstance);
 | 
			
		||||
	QString value = d->m_settings->get ( "CustomBaseJar" ).toString();
 | 
			
		||||
	if(value.isNull() || value.isEmpty())
 | 
			
		||||
	{
 | 
			
		||||
		return defaultCustomBaseJar();
 | 
			
		||||
	}
 | 
			
		||||
	return value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BaseInstance::setCustomBaseJar ( QString val )
 | 
			
		||||
{
 | 
			
		||||
	I_D(BaseInstance);
 | 
			
		||||
	if(val.isNull() || val.isEmpty() || val == defaultCustomBaseJar())
 | 
			
		||||
		d->m_settings->reset ( "CustomBaseJar" );
 | 
			
		||||
	else
 | 
			
		||||
		d->m_settings->set ( "CustomBaseJar", val );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BaseInstance::setShouldUseCustomBaseJar ( bool val )
 | 
			
		||||
{
 | 
			
		||||
	I_D(BaseInstance);
 | 
			
		||||
	d->m_settings->set ( "UseCustomBaseJar", val );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool BaseInstance::shouldUseCustomBaseJar() const
 | 
			
		||||
{
 | 
			
		||||
	I_D(BaseInstance);
 | 
			
		||||
	return d->m_settings->get ( "UseCustomBaseJar" ).toBool();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
qint64 BaseInstance::lastLaunch() const
 | 
			
		||||
{
 | 
			
		||||
	I_D(BaseInstance);
 | 
			
		||||
 
 | 
			
		||||
@@ -90,6 +90,25 @@ public:
 | 
			
		||||
	virtual bool shouldUpdate() const = 0;
 | 
			
		||||
	virtual void setShouldUpdate(bool val) = 0;
 | 
			
		||||
 | 
			
		||||
	/// Get the curent base jar of this instance. By default, it's the versions/$version/$version.jar
 | 
			
		||||
	QString baseJar() const;
 | 
			
		||||
 | 
			
		||||
	/// the default base jar of this instance
 | 
			
		||||
	virtual QString defaultBaseJar() const = 0;
 | 
			
		||||
	/// the default custom base jar of this instance
 | 
			
		||||
	virtual QString defaultCustomBaseJar() const = 0;
 | 
			
		||||
	
 | 
			
		||||
	/*!
 | 
			
		||||
	 * Whether or not custom base jar is used
 | 
			
		||||
	 */
 | 
			
		||||
	bool shouldUseCustomBaseJar() const;
 | 
			
		||||
	void setShouldUseCustomBaseJar(bool val);
 | 
			
		||||
	/*!
 | 
			
		||||
	 * The value of the custom base jar
 | 
			
		||||
	 */
 | 
			
		||||
	QString customBaseJar() const;
 | 
			
		||||
	void setCustomBaseJar(QString val);
 | 
			
		||||
	
 | 
			
		||||
	/**
 | 
			
		||||
	 * Gets the time that the instance was last launched.
 | 
			
		||||
	 * Stored in milliseconds since epoch.
 | 
			
		||||
 
 | 
			
		||||
@@ -39,8 +39,8 @@ IconList::IconList() : QAbstractListModel(), d(new Private())
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	// FIXME: get from settings
 | 
			
		||||
	ensurePathExists("icons/");
 | 
			
		||||
	QDir user_icons("icons/");
 | 
			
		||||
	ensureFolderPathExists("icons");
 | 
			
		||||
	QDir user_icons("icons");
 | 
			
		||||
	file_info_list = user_icons.entryInfoList(QDir::Files, QDir::Name);
 | 
			
		||||
	for(auto file_info: file_info_list)
 | 
			
		||||
	{
 | 
			
		||||
 
 | 
			
		||||
@@ -90,16 +90,19 @@ InstanceFactory::InstCreateError InstanceFactory::createInstance( BaseInstance*&
 | 
			
		||||
			m_settings->set("InstanceType", "Legacy");
 | 
			
		||||
			inst = new LegacyInstance(instDir, m_settings, this);
 | 
			
		||||
			inst->setIntendedVersionId(version->descriptor);
 | 
			
		||||
			inst->setShouldUseCustomBaseJar(false);
 | 
			
		||||
			break;
 | 
			
		||||
		case MinecraftVersion::OneSix:
 | 
			
		||||
			m_settings->set("InstanceType", "OneSix");
 | 
			
		||||
			inst = new OneSixInstance(instDir, m_settings, this);
 | 
			
		||||
			inst->setIntendedVersionId(version->descriptor);
 | 
			
		||||
			inst->setShouldUseCustomBaseJar(false);
 | 
			
		||||
			break;
 | 
			
		||||
		case MinecraftVersion::Nostalgia:
 | 
			
		||||
			m_settings->set("InstanceType", "Nostalgia");
 | 
			
		||||
			inst = new NostalgiaInstance(instDir, m_settings, this);
 | 
			
		||||
			inst->setIntendedVersionId(version->descriptor);
 | 
			
		||||
			inst->setShouldUseCustomBaseJar(false);
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
		{
 | 
			
		||||
 
 | 
			
		||||
@@ -169,30 +169,25 @@ QString LegacyInstance::resourceDir() const
 | 
			
		||||
	return PathCombine(minecraftRoot(), "resources");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QString LegacyInstance::mcJar() const
 | 
			
		||||
QString LegacyInstance::runnableJar() const
 | 
			
		||||
{
 | 
			
		||||
	return PathCombine(binDir(), "minecraft.jar");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QString LegacyInstance::mcBackup() const
 | 
			
		||||
{
 | 
			
		||||
	return PathCombine(binDir(), "mcbackup.jar");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QString LegacyInstance::modListFile() const
 | 
			
		||||
{
 | 
			
		||||
	return PathCombine(instanceRoot(), "modlist");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
bool LegacyInstance::shouldUpdateCurrentVersion() const
 | 
			
		||||
{
 | 
			
		||||
	QFileInfo jar(mcJar());
 | 
			
		||||
	QFileInfo jar(runnableJar());
 | 
			
		||||
	return jar.lastModified().toUTC().toMSecsSinceEpoch() != lastCurrentVersionUpdate();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void LegacyInstance::updateCurrentVersion(bool keepCurrent)
 | 
			
		||||
{
 | 
			
		||||
	QFileInfo jar(mcJar());
 | 
			
		||||
	QFileInfo jar(runnableJar());
 | 
			
		||||
	
 | 
			
		||||
	if(!jar.exists())
 | 
			
		||||
	{
 | 
			
		||||
@@ -221,6 +216,7 @@ void LegacyInstance::setLastCurrentVersionUpdate ( qint64 val )
 | 
			
		||||
	I_D(LegacyInstance);
 | 
			
		||||
	d->m_settings->set ( "lastVersionUpdate", val );
 | 
			
		||||
}
 | 
			
		||||
*/
 | 
			
		||||
bool LegacyInstance::shouldRebuild() const
 | 
			
		||||
{
 | 
			
		||||
	I_D(LegacyInstance);
 | 
			
		||||
@@ -278,3 +274,14 @@ void LegacyInstance::setShouldUpdate ( bool val )
 | 
			
		||||
{
 | 
			
		||||
	settings().set ( "ShouldUpdate", val );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QString LegacyInstance::defaultBaseJar() const
 | 
			
		||||
{
 | 
			
		||||
	return "versions/" + intendedVersionId() + "/" + intendedVersionId() + ".jar";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QString LegacyInstance::defaultCustomBaseJar() const
 | 
			
		||||
{
 | 
			
		||||
	return PathCombine(binDir(), "mcbackup.jar");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -13,10 +13,7 @@ public:
 | 
			
		||||
	explicit LegacyInstance(const QString &rootDir, SettingsObject * settings, QObject *parent = 0);
 | 
			
		||||
	
 | 
			
		||||
	/// Path to the instance's minecraft.jar
 | 
			
		||||
	QString mcJar() const;
 | 
			
		||||
	
 | 
			
		||||
	//! Path to the instance's mcbackup.jar
 | 
			
		||||
	QString mcBackup() const;
 | 
			
		||||
	QString runnableJar() const;
 | 
			
		||||
	
 | 
			
		||||
	//! Path to the instance's modlist file.
 | 
			
		||||
	QString modListFile() const;
 | 
			
		||||
@@ -34,35 +31,6 @@ public:
 | 
			
		||||
	QString coreModsDir() const;
 | 
			
		||||
	QString resourceDir() const;
 | 
			
		||||
	
 | 
			
		||||
	/*!
 | 
			
		||||
	 * \brief Checks whether or not the currentVersion of the instance needs to be updated. 
 | 
			
		||||
	 *        If this returns true, updateCurrentVersion is called. In the 
 | 
			
		||||
	 *        standard instance, this is determined by checking a timestamp 
 | 
			
		||||
	 *        stored in the instance config file against the last modified time of Minecraft.jar.
 | 
			
		||||
	 * \return True if updateCurrentVersion() should be called.
 | 
			
		||||
	 */
 | 
			
		||||
	bool shouldUpdateCurrentVersion() const;
 | 
			
		||||
	
 | 
			
		||||
	/*!
 | 
			
		||||
	 * \brief Updates the current version. 
 | 
			
		||||
	 *        This function should first set the current version timestamp 
 | 
			
		||||
	 *        (setCurrentVersionTimestamp()) to the current time. Next, if 
 | 
			
		||||
	 *        keepCurrent is false, this function should check what the 
 | 
			
		||||
	 *        instance's current version is and call setCurrentVersion() to 
 | 
			
		||||
	 *        update it. This function will automatically be called when the 
 | 
			
		||||
	 *        instance is loaded if shouldUpdateCurrentVersion returns true.
 | 
			
		||||
	 * \param keepCurrent If true, only the version timestamp will be updated.
 | 
			
		||||
	 */
 | 
			
		||||
	void updateCurrentVersion(bool keepCurrent = false); 
 | 
			
		||||
	
 | 
			
		||||
	/*!
 | 
			
		||||
	 * Gets the last time that the current version was checked.
 | 
			
		||||
	 * This is checked against the last modified time on the jar file to see if
 | 
			
		||||
	 * the current version needs to be checked again.
 | 
			
		||||
	 */
 | 
			
		||||
	qint64 lastCurrentVersionUpdate() const;
 | 
			
		||||
	void setLastCurrentVersionUpdate(qint64 val);
 | 
			
		||||
	
 | 
			
		||||
	/*!
 | 
			
		||||
	 * Whether or not the instance's minecraft.jar needs to be rebuilt.
 | 
			
		||||
	 * If this is true, when the instance launches, its jar mods will be 
 | 
			
		||||
@@ -90,6 +58,9 @@ public:
 | 
			
		||||
	virtual void cleanupAfterRun();
 | 
			
		||||
	virtual QDialog * createModEditDialog ( QWidget* parent );
 | 
			
		||||
	
 | 
			
		||||
	virtual QString defaultBaseJar() const;
 | 
			
		||||
	virtual QString defaultCustomBaseJar() const;
 | 
			
		||||
	
 | 
			
		||||
protected slots:
 | 
			
		||||
	virtual void jarModsChanged();
 | 
			
		||||
};
 | 
			
		||||
@@ -4,9 +4,11 @@
 | 
			
		||||
#include "BaseInstance.h"
 | 
			
		||||
#include "LegacyInstance.h"
 | 
			
		||||
#include "net/NetWorker.h"
 | 
			
		||||
#include "ModList.h"
 | 
			
		||||
#include <pathutils.h>
 | 
			
		||||
#include <quazip.h>
 | 
			
		||||
#include <quazipfile.h>
 | 
			
		||||
#include <JlCompress.h>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
LegacyUpdate::LegacyUpdate ( BaseInstance* inst, QObject* parent ) : BaseUpdate ( inst, parent ) {}
 | 
			
		||||
@@ -22,7 +24,7 @@ void LegacyUpdate::lwjglStart()
 | 
			
		||||
 | 
			
		||||
	lwjglVersion =  inst->lwjglVersion();
 | 
			
		||||
	lwjglTargetPath = PathCombine("lwjgl", lwjglVersion );
 | 
			
		||||
	lwjglNativesPath = PathCombine( lwjglTargetPath, "natives/");
 | 
			
		||||
	lwjglNativesPath = PathCombine( lwjglTargetPath, "natives");
 | 
			
		||||
	
 | 
			
		||||
	// if the 'done' file exists, we don't have to download this again
 | 
			
		||||
	QFileInfo doneFile(PathCombine(lwjglTargetPath, "done"));
 | 
			
		||||
@@ -112,7 +114,7 @@ void LegacyUpdate::extractLwjgl()
 | 
			
		||||
{
 | 
			
		||||
	// make sure the directories are there
 | 
			
		||||
 | 
			
		||||
	bool success = ensurePathExists(lwjglNativesPath);
 | 
			
		||||
	bool success = ensureFolderPathExists(lwjglNativesPath);
 | 
			
		||||
	
 | 
			
		||||
	if(!success)
 | 
			
		||||
	{
 | 
			
		||||
@@ -201,33 +203,14 @@ void LegacyUpdate::lwjglFailed()
 | 
			
		||||
 | 
			
		||||
void LegacyUpdate::jarStart()
 | 
			
		||||
{
 | 
			
		||||
	setStatus("Checking ...");
 | 
			
		||||
	LegacyInstance * inst = (LegacyInstance *) m_inst;
 | 
			
		||||
	QString current_version_id = inst->currentVersionId();
 | 
			
		||||
	QString intended_version_id = inst->intendedVersionId();
 | 
			
		||||
	bool shouldUpdate = inst->shouldUpdate();
 | 
			
		||||
	if(!shouldUpdate)
 | 
			
		||||
	if(!inst->shouldUpdate() || inst->shouldUseCustomBaseJar())
 | 
			
		||||
	{
 | 
			
		||||
		emitSucceeded();
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	// nuke the backup file, we are replacing the base jar anyway
 | 
			
		||||
	QFile mc_backup(inst->mcBackup());
 | 
			
		||||
	if (mc_backup.exists())
 | 
			
		||||
	{
 | 
			
		||||
		mc_backup.remove();
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	// Get a pointer to the version object that corresponds to the instance's version.
 | 
			
		||||
	auto targetVersion = MinecraftVersionList::getMainList().findVersion(intended_version_id);
 | 
			
		||||
	
 | 
			
		||||
	if(!targetVersion)
 | 
			
		||||
	{
 | 
			
		||||
		emitFailed("Not a valid version:" + intended_version_id);
 | 
			
		||||
		ModTheJar();
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	setStatus("Checking for jar updates...");
 | 
			
		||||
	// Make directories
 | 
			
		||||
	QDir binDir(inst->binDir());
 | 
			
		||||
	if (!binDir.exists() && !binDir.mkpath("."))
 | 
			
		||||
@@ -239,14 +222,11 @@ void LegacyUpdate::jarStart()
 | 
			
		||||
	// Build a list of URLs that will need to be downloaded.
 | 
			
		||||
	setStatus("Downloading new minecraft.jar");
 | 
			
		||||
 | 
			
		||||
	// This will be either 'minecraft' or the version number, depending on where
 | 
			
		||||
	// we're downloading from.
 | 
			
		||||
	QString jarFilename = "minecraft";
 | 
			
		||||
	QString download_path = PathCombine(inst->minecraftRoot(), "bin/minecraft.jar");
 | 
			
		||||
 | 
			
		||||
	QString urlstr("http://s3.amazonaws.com/Minecraft.Download/versions/");
 | 
			
		||||
	urlstr += targetVersion->descriptor + "/" + targetVersion->descriptor + ".jar";
 | 
			
		||||
	auto dljob = DownloadJob::create(QUrl(urlstr), download_path);
 | 
			
		||||
	QString intended_version_id = inst->intendedVersionId();
 | 
			
		||||
	urlstr += intended_version_id + "/" + intended_version_id + ".jar";
 | 
			
		||||
	
 | 
			
		||||
	auto dljob = DownloadJob::create(QUrl(urlstr), inst->defaultBaseJar());
 | 
			
		||||
	
 | 
			
		||||
	legacyDownloadJob.reset(new JobList());
 | 
			
		||||
	legacyDownloadJob->add(dljob);
 | 
			
		||||
@@ -259,7 +239,7 @@ void LegacyUpdate::jarStart()
 | 
			
		||||
void LegacyUpdate::jarFinished()
 | 
			
		||||
{
 | 
			
		||||
	// process the jar
 | 
			
		||||
	emitSucceeded();
 | 
			
		||||
	ModTheJar();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void LegacyUpdate::jarFailed()
 | 
			
		||||
@@ -268,74 +248,132 @@ void LegacyUpdate::jarFailed()
 | 
			
		||||
	emitFailed("Failed to download the minecraft jar. Try again later.");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool LegacyUpdate::MergeZipFiles( QuaZip* into, QFileInfo from, QSet< QString >& contained )
 | 
			
		||||
{
 | 
			
		||||
	setStatus("Installing mods - Adding " + from.fileName());
 | 
			
		||||
	
 | 
			
		||||
	QuaZip modZip(from.filePath());
 | 
			
		||||
	modZip.open(QuaZip::mdUnzip);
 | 
			
		||||
	
 | 
			
		||||
	QuaZipFile fileInsideMod(&modZip);
 | 
			
		||||
	QuaZipFile zipOutFile( into );
 | 
			
		||||
	for(bool more=modZip.goToFirstFile(); more; more=modZip.goToNextFile())
 | 
			
		||||
	{
 | 
			
		||||
		QString filename = modZip.getCurrentFileName();
 | 
			
		||||
		if(filename.contains("META-INF"))
 | 
			
		||||
			continue;
 | 
			
		||||
		if(contained.contains(filename))
 | 
			
		||||
			continue;
 | 
			
		||||
		contained.insert(filename);
 | 
			
		||||
		qDebug() << "Adding file " << filename << " from " << from.fileName();
 | 
			
		||||
		
 | 
			
		||||
		if(!fileInsideMod.open(QIODevice::ReadOnly))
 | 
			
		||||
		{
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
		if(!zipOutFile.open(QIODevice::WriteOnly, QuaZipNewInfo(fileInsideMod.getActualFileName())))
 | 
			
		||||
		{
 | 
			
		||||
			fileInsideMod.close();
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
		if(!JlCompress::copyData(fileInsideMod, zipOutFile))
 | 
			
		||||
		{
 | 
			
		||||
			zipOutFile.close();
 | 
			
		||||
			fileInsideMod.close();
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
		zipOutFile.close();
 | 
			
		||||
		fileInsideMod.close();
 | 
			
		||||
	}
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void LegacyUpdate::ModTheJar()
 | 
			
		||||
{
 | 
			
		||||
	/*
 | 
			
		||||
	LegacyInstance * inst = (LegacyInstance *) m_inst;
 | 
			
		||||
	// Get the mod list
 | 
			
		||||
	auto modList = inst->getJarModList();
 | 
			
		||||
	
 | 
			
		||||
	QFileInfo mcBin(inst->binDir());
 | 
			
		||||
	QFileInfo mcJar(inst->mcJar());
 | 
			
		||||
	QFileInfo mcBackup(inst->mcBackup());
 | 
			
		||||
	
 | 
			
		||||
	// Nothing to do if there are no jar mods to install, no backup and just the mc jar
 | 
			
		||||
	if(mcJar.isFile() && !mcBackup.exists() && modList->empty())
 | 
			
		||||
	if(!inst->shouldRebuild())
 | 
			
		||||
	{
 | 
			
		||||
		inst->setShouldRebuild(false);
 | 
			
		||||
		emitSucceeded();
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	setStatus("Installing mods - backing up minecraft.jar...");
 | 
			
		||||
	if (!mcBackup.exists() && !QFile::copy(mcJar.absoluteFilePath(), mcBackup.absoluteFilePath()) )
 | 
			
		||||
	// Get the mod list
 | 
			
		||||
	auto modList = inst->jarModList();
 | 
			
		||||
	
 | 
			
		||||
	QFileInfo runnableJar (inst->runnableJar());
 | 
			
		||||
	QFileInfo baseJar (inst->baseJar());
 | 
			
		||||
	bool base_is_custom = inst->shouldUseCustomBaseJar();
 | 
			
		||||
	
 | 
			
		||||
	// Nothing to do if there are no jar mods to install, no backup and just the mc jar
 | 
			
		||||
	if(base_is_custom)
 | 
			
		||||
	{
 | 
			
		||||
		emitFailed("Failed to back up minecraft.jar");
 | 
			
		||||
		// yes, this can happen if the instance only has the runnable jar and not the base jar
 | 
			
		||||
		// it *could* be assumed that such an instance is vanilla, but that wouldn't be safe
 | 
			
		||||
		// because that's not something mmc4 guarantees
 | 
			
		||||
		if(runnableJar.isFile() && !baseJar.exists() && modList->empty())
 | 
			
		||||
		{
 | 
			
		||||
			inst->setShouldRebuild(false);
 | 
			
		||||
			emitSucceeded();
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		setStatus("Installing mods - backing up minecraft.jar...");
 | 
			
		||||
		if (!baseJar.exists() && !QFile::copy(runnableJar.filePath(), baseJar.filePath()))
 | 
			
		||||
		{
 | 
			
		||||
			emitFailed("Failed to back up minecraft.jar");
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	if (!baseJar.exists())
 | 
			
		||||
	{
 | 
			
		||||
		emitFailed("The base jar " + baseJar.filePath() + " does not exist");
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	if (mcJar.isFile() && !QFile::remove(mcJar.absoluteFilePath()))
 | 
			
		||||
	if (runnableJar.exists() && !QFile::remove(runnableJar.filePath()))
 | 
			
		||||
	{
 | 
			
		||||
		emitFailed("Failed to delete old minecraft.jar");
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	//TaskStep(); // STEP 1
 | 
			
		||||
	setStatus("Installing mods - Opening minecraft.jar");
 | 
			
		||||
 | 
			
		||||
	wxFFileOutputStream jarStream(mcJar.absoluteFilePath());
 | 
			
		||||
	wxZipOutputStream zipOut(jarStream);
 | 
			
		||||
 | 
			
		||||
	QuaZip zipOut(runnableJar.filePath());
 | 
			
		||||
	if(!zipOut.open(QuaZip::mdCreate))
 | 
			
		||||
	{
 | 
			
		||||
		QFile::remove(runnableJar.filePath());
 | 
			
		||||
		emitFailed("Failed to open the minecraft.jar for modding");
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	// Files already added to the jar.
 | 
			
		||||
	// These files will be skipped.
 | 
			
		||||
	QSet<QString> addedFiles;
 | 
			
		||||
 | 
			
		||||
	// Modify the jar
 | 
			
		||||
	setStatus("Installing mods - Adding mod files...");
 | 
			
		||||
	for (ModList::const_reverse_iterator iter = modList->rbegin(); iter != modList->rend(); iter++)
 | 
			
		||||
	for (int i = modList->size() - 1; i >= 0; i--)
 | 
			
		||||
	{
 | 
			
		||||
		wxFileName modFileName = iter->GetFileName();
 | 
			
		||||
		setStatus("Installing mods - Adding " + modFileName.GetFullName());
 | 
			
		||||
		if (iter->GetModType() == Mod::ModType::MOD_ZIPFILE)
 | 
			
		||||
		auto &mod = modList->operator[](i);
 | 
			
		||||
		if (mod.type() == Mod::MOD_ZIPFILE)
 | 
			
		||||
		{
 | 
			
		||||
			wxFFileInputStream modStream(modFileName.GetFullPath());
 | 
			
		||||
			wxZipInputStream zipStream(modStream);
 | 
			
		||||
			std::unique_ptr<wxZipEntry> entry;
 | 
			
		||||
			while (entry.reset(zipStream.GetNextEntry()), entry.get() != NULL)
 | 
			
		||||
			if(!MergeZipFiles(&zipOut, mod.filename(), addedFiles))
 | 
			
		||||
			{
 | 
			
		||||
				if (entry->IsDir())
 | 
			
		||||
					continue;
 | 
			
		||||
 | 
			
		||||
				wxString name = entry->GetName();
 | 
			
		||||
				if (addedFiles.count(name) == 0)
 | 
			
		||||
				{
 | 
			
		||||
					if (!zipOut.CopyEntry(entry.release(), zipStream))
 | 
			
		||||
						break;
 | 
			
		||||
					addedFiles.insert(name);
 | 
			
		||||
				}
 | 
			
		||||
				zipOut.close();
 | 
			
		||||
				QFile::remove(runnableJar.filePath());
 | 
			
		||||
				emitFailed("Failed to add " + mod.filename().fileName() + " to the jar.");
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
		else if (mod.type() == Mod::MOD_SINGLEFILE)
 | 
			
		||||
		{
 | 
			
		||||
			zipOut.close();
 | 
			
		||||
			QFile::remove(runnableJar.filePath());
 | 
			
		||||
			emitFailed("Loose files are NOT supported as jar mods.");
 | 
			
		||||
			return;
 | 
			
		||||
			/*
 | 
			
		||||
			wxFileName destFileName = modFileName;
 | 
			
		||||
			destFileName.MakeRelativeTo(m_inst->GetInstModsDir().GetFullPath());
 | 
			
		||||
			wxString destFile = destFileName.GetFullPath();
 | 
			
		||||
@@ -348,34 +386,35 @@ void LegacyUpdate::ModTheJar()
 | 
			
		||||
 | 
			
		||||
				addedFiles.insert(destFile);
 | 
			
		||||
			}
 | 
			
		||||
			*/
 | 
			
		||||
		}
 | 
			
		||||
		else if (mod.type() == Mod::MOD_FOLDER)
 | 
			
		||||
		{
 | 
			
		||||
			zipOut.close();
 | 
			
		||||
			QFile::remove(runnableJar.filePath());
 | 
			
		||||
			emitFailed("Folders are NOT supported as jar mods.");
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	if(!MergeZipFiles(&zipOut, baseJar, addedFiles))
 | 
			
		||||
	{
 | 
			
		||||
		wxFFileInputStream inStream(mcBackup.GetFullPath());
 | 
			
		||||
		wxZipInputStream zipIn(inStream);
 | 
			
		||||
 | 
			
		||||
		std::auto_ptr<wxZipEntry> entry;
 | 
			
		||||
		while (entry.reset(zipIn.GetNextEntry()), entry.get() != NULL)
 | 
			
		||||
		{
 | 
			
		||||
			wxString name = entry->GetName();
 | 
			
		||||
 | 
			
		||||
			if (!name.Matches("META-INF*") &&
 | 
			
		||||
				addedFiles.count(name) == 0)
 | 
			
		||||
			{
 | 
			
		||||
				if (!zipOut.CopyEntry(entry.release(), zipIn))
 | 
			
		||||
					break;
 | 
			
		||||
				addedFiles.insert(name);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		zipOut.close();
 | 
			
		||||
		QFile::remove(runnableJar.filePath());
 | 
			
		||||
		emitFailed("Failed to insert minecraft.jar contents.");
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	// Recompress the jar
 | 
			
		||||
	TaskStep(); // STEP 3
 | 
			
		||||
	SetStatus(_("Installing mods - Recompressing jar..."));
 | 
			
		||||
 | 
			
		||||
	inst->SetNeedsRebuild(false);
 | 
			
		||||
	inst->UpdateVersion(true);
 | 
			
		||||
	return (ExitCode)1;
 | 
			
		||||
	*/
 | 
			
		||||
	zipOut.close();
 | 
			
		||||
    if(zipOut.getZipError()!=0)
 | 
			
		||||
	{
 | 
			
		||||
		QFile::remove(runnableJar.filePath());
 | 
			
		||||
		emitFailed("Failed to finalize minecraft.jar!");
 | 
			
		||||
		return;
 | 
			
		||||
    }
 | 
			
		||||
	inst->setShouldRebuild(false);
 | 
			
		||||
	//inst->UpdateVersion(true);
 | 
			
		||||
	emitSucceeded();
 | 
			
		||||
	return;
 | 
			
		||||
}
 | 
			
		||||
@@ -25,6 +25,8 @@
 | 
			
		||||
 | 
			
		||||
class MinecraftVersion;
 | 
			
		||||
class BaseInstance;
 | 
			
		||||
class QuaZip;
 | 
			
		||||
class Mod;
 | 
			
		||||
 | 
			
		||||
class LegacyUpdate : public BaseUpdate
 | 
			
		||||
{
 | 
			
		||||
@@ -46,6 +48,8 @@ private slots:
 | 
			
		||||
	
 | 
			
		||||
	void ModTheJar();
 | 
			
		||||
private:
 | 
			
		||||
	bool MergeZipFiles(QuaZip *into, QFileInfo from, QSet<QString>& contained);
 | 
			
		||||
private:
 | 
			
		||||
	
 | 
			
		||||
	QSharedPointer<QNetworkReply> m_reply;
 | 
			
		||||
	
 | 
			
		||||
@@ -59,9 +63,6 @@ private:
 | 
			
		||||
private:
 | 
			
		||||
	JobListPtr legacyDownloadJob;
 | 
			
		||||
	JobListQueue download_queue;
 | 
			
		||||
	
 | 
			
		||||
	// target version, determined during this task
 | 
			
		||||
	QSharedPointer<MinecraftVersion> targetVersion;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -37,6 +37,7 @@ public:
 | 
			
		||||
	virtual int columnCount ( const QModelIndex& parent ) const;
 | 
			
		||||
	
 | 
			
		||||
	size_t size() const { return mods.size(); };
 | 
			
		||||
	bool empty() const { return size() == 0; }
 | 
			
		||||
	Mod& operator[](size_t index) { return mods[index]; };
 | 
			
		||||
	
 | 
			
		||||
	/// Reloads the mod list and returns true if the list changed.
 | 
			
		||||
 
 | 
			
		||||
@@ -92,7 +92,7 @@ MinecraftProcess* OneSixInstance::prepareForLaunch ( QString user, QString sessi
 | 
			
		||||
		return nullptr;
 | 
			
		||||
	auto libs_to_extract = version->getActiveNativeLibs();
 | 
			
		||||
	QString natives_dir_raw = PathCombine(instanceRoot(), "natives/");
 | 
			
		||||
	bool success = ensurePathExists(natives_dir_raw);
 | 
			
		||||
	bool success = ensureFolderPathExists(natives_dir_raw);
 | 
			
		||||
	if(!success)
 | 
			
		||||
	{
 | 
			
		||||
		// FIXME: handle errors
 | 
			
		||||
@@ -216,3 +216,13 @@ QSharedPointer< FullVersion > OneSixInstance::getFullVersion()
 | 
			
		||||
	I_D(OneSixInstance);
 | 
			
		||||
	return d->version;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QString OneSixInstance::defaultBaseJar() const
 | 
			
		||||
{
 | 
			
		||||
	return "versions/" + intendedVersionId() + "/" + intendedVersionId() + ".jar";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QString OneSixInstance::defaultCustomBaseJar() const
 | 
			
		||||
{
 | 
			
		||||
	return PathCombine(instanceRoot(), "custom.jar");
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -29,6 +29,9 @@ public:
 | 
			
		||||
	bool reloadFullVersion();
 | 
			
		||||
	/// get the current full version info
 | 
			
		||||
	QSharedPointer<FullVersion> getFullVersion();
 | 
			
		||||
	
 | 
			
		||||
	virtual QString defaultBaseJar() const;
 | 
			
		||||
	virtual QString defaultCustomBaseJar() const;
 | 
			
		||||
private:
 | 
			
		||||
	QStringList processMinecraftArgs( QString user, QString session );
 | 
			
		||||
};
 | 
			
		||||
@@ -91,7 +91,7 @@ void OneSixUpdate::versionFileFinished()
 | 
			
		||||
	// save the version file in $instanceId/version.json
 | 
			
		||||
	{
 | 
			
		||||
		QString version1 =  PathCombine(inst_dir, "/version.json");
 | 
			
		||||
		ensurePathExists(version1);
 | 
			
		||||
		ensureFilePathExists(version1);
 | 
			
		||||
		// FIXME: detect errors here, download to a temp file, swap
 | 
			
		||||
		QFile  vfile1 (version1);
 | 
			
		||||
		vfile1.open(QIODevice::Truncate | QIODevice::WriteOnly );
 | 
			
		||||
 
 | 
			
		||||
@@ -48,7 +48,7 @@ void DownloadJob::start()
 | 
			
		||||
				m_expected_md5 = hash;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if(!ensurePathExists(filename))
 | 
			
		||||
		if(!ensureFilePathExists(filename))
 | 
			
		||||
		{
 | 
			
		||||
			emit fail();
 | 
			
		||||
			return;
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user