GH-1227 allow structured world zip import and drag and drop out of MultiMC
This commit is contained in:
		
							
								
								
									
										151
									
								
								logic/MMCZip.cpp
									
									
									
									
									
								
							
							
						
						
									
										151
									
								
								logic/MMCZip.cpp
									
									
									
									
									
								
							| @@ -26,6 +26,7 @@ see quazip/(un)MMCZip.h files for details. Basically it's the zlib license. | |||||||
| #include <pathutils.h> | #include <pathutils.h> | ||||||
| #include <quazip.h> | #include <quazip.h> | ||||||
| #include <JlCompress.h> | #include <JlCompress.h> | ||||||
|  | #include <quazipdir.h> | ||||||
| #include "MMCZip.h" | #include "MMCZip.h" | ||||||
|  |  | ||||||
| #include <QDebug> | #include <QDebug> | ||||||
| @@ -338,3 +339,153 @@ bool MMCZip::compressDir(QString zipFile, QString dir, QString prefix, const Sep | |||||||
| 	return true; | 	return true; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | QString MMCZip::findFileInZip(QuaZip * zip, const QString & what, const QString &root) | ||||||
|  | { | ||||||
|  | 	QuaZipDir rootDir(zip, root); | ||||||
|  | 	for(auto fileName: rootDir.entryList(QDir::Files)) | ||||||
|  | 	{ | ||||||
|  | 		if(fileName == what) | ||||||
|  | 			return root; | ||||||
|  | 	} | ||||||
|  | 	for(auto fileName: rootDir.entryList(QDir::Dirs)) | ||||||
|  | 	{ | ||||||
|  | 		QString result = findFileInZip(zip, what, root + fileName); | ||||||
|  | 		if(!result.isEmpty()) | ||||||
|  | 		{ | ||||||
|  | 			return result; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return QString(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool MMCZip::findFilesInZip(QuaZip * zip, const QString & what, QStringList & result, const QString &root) | ||||||
|  | { | ||||||
|  | 	QuaZipDir rootDir(zip, root); | ||||||
|  | 	for(auto fileName: rootDir.entryList(QDir::Files)) | ||||||
|  | 	{ | ||||||
|  | 		if(fileName == what) | ||||||
|  | 		{ | ||||||
|  | 			result.append(root); | ||||||
|  | 			return true; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	for(auto fileName: rootDir.entryList(QDir::Dirs)) | ||||||
|  | 	{ | ||||||
|  | 		findFilesInZip(zip, what, result, root + fileName); | ||||||
|  | 	} | ||||||
|  | 	return !result.isEmpty(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool removeFile(QStringList listFile) | ||||||
|  | { | ||||||
|  | 	bool ret = true; | ||||||
|  | 	for (int i = 0; i < listFile.count(); i++) | ||||||
|  | 	{ | ||||||
|  | 		ret &= QFile::remove(listFile.at(i)); | ||||||
|  | 	} | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool MMCZip::extractFile(QuaZip *zip, const QString &fileName, const QString &fileDest) | ||||||
|  | { | ||||||
|  | 	if(!zip) | ||||||
|  | 		return false; | ||||||
|  |  | ||||||
|  | 	if (zip->getMode() != QuaZip::mdUnzip) | ||||||
|  | 		return false; | ||||||
|  |  | ||||||
|  | 	if (!fileName.isEmpty()) | ||||||
|  | 		zip->setCurrentFile(fileName); | ||||||
|  |  | ||||||
|  | 	QuaZipFile inFile(zip); | ||||||
|  | 	if (!inFile.open(QIODevice::ReadOnly) || inFile.getZipError() != UNZ_OK) | ||||||
|  | 		return false; | ||||||
|  |  | ||||||
|  | 	// Controllo esistenza cartella file risultato | ||||||
|  | 	QDir curDir; | ||||||
|  | 	if (fileDest.endsWith('/')) | ||||||
|  | 	{ | ||||||
|  | 		if (!curDir.mkpath(fileDest)) | ||||||
|  | 		{ | ||||||
|  | 			return false; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	else | ||||||
|  | 	{ | ||||||
|  | 		if (!curDir.mkpath(QFileInfo(fileDest).absolutePath())) | ||||||
|  | 		{ | ||||||
|  | 			return false; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	QuaZipFileInfo64 info; | ||||||
|  | 	if (!zip->getCurrentFileInfo(&info)) | ||||||
|  | 		return false; | ||||||
|  |  | ||||||
|  | 	QFile::Permissions srcPerm = info.getPermissions(); | ||||||
|  | 	if (fileDest.endsWith('/') && QFileInfo(fileDest).isDir()) | ||||||
|  | 	{ | ||||||
|  | 		if (srcPerm != 0) | ||||||
|  | 		{ | ||||||
|  | 			QFile(fileDest).setPermissions(srcPerm); | ||||||
|  | 		} | ||||||
|  | 		return true; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	QFile outFile; | ||||||
|  | 	outFile.setFileName(fileDest); | ||||||
|  | 	if (!outFile.open(QIODevice::WriteOnly)) | ||||||
|  | 		return false; | ||||||
|  |  | ||||||
|  | 	if (!copyData(inFile, outFile) || inFile.getZipError() != UNZ_OK) | ||||||
|  | 	{ | ||||||
|  | 		outFile.close(); | ||||||
|  | 		removeFile(QStringList(fileDest)); | ||||||
|  | 		return false; | ||||||
|  | 	} | ||||||
|  | 	outFile.close(); | ||||||
|  |  | ||||||
|  | 	inFile.close(); | ||||||
|  | 	if (inFile.getZipError() != UNZ_OK) | ||||||
|  | 	{ | ||||||
|  | 		removeFile(QStringList(fileDest)); | ||||||
|  | 		return false; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (srcPerm != 0) | ||||||
|  | 	{ | ||||||
|  | 		outFile.setPermissions(srcPerm); | ||||||
|  | 	} | ||||||
|  | 	return true; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | QStringList MMCZip::extractSubDir(QuaZip *zip, const QString & subdir, const QString &target) | ||||||
|  | { | ||||||
|  | 	QDir directory(target); | ||||||
|  | 	QStringList extracted; | ||||||
|  | 	if (!zip->goToFirstFile()) | ||||||
|  | 	{ | ||||||
|  | 		return QStringList(); | ||||||
|  | 	} | ||||||
|  | 	do | ||||||
|  | 	{ | ||||||
|  | 		QString name = zip->getCurrentFileName(); | ||||||
|  | 		if(!name.startsWith(subdir)) | ||||||
|  | 		{ | ||||||
|  | 			continue; | ||||||
|  | 		} | ||||||
|  | 		name.remove(0, subdir.size()); | ||||||
|  | 		QString absFilePath = directory.absoluteFilePath(name); | ||||||
|  | 		if(name.isEmpty()) | ||||||
|  | 		{ | ||||||
|  | 			absFilePath += "/"; | ||||||
|  | 		} | ||||||
|  | 		if (!extractFile(zip, "", absFilePath)) | ||||||
|  | 		{ | ||||||
|  | 			removeFile(extracted); | ||||||
|  | 			return QStringList(); | ||||||
|  | 		} | ||||||
|  | 		extracted.append(absFilePath); | ||||||
|  | 	} while (zip->goToNextFile()); | ||||||
|  | 	return extracted; | ||||||
|  | } | ||||||
|   | |||||||
| @@ -57,5 +57,32 @@ namespace MMCZip | |||||||
| 	 * left empty. | 	 * left empty. | ||||||
| 	 * \return The list of the full paths of the files extracted, empty on failure. | 	 * \return The list of the full paths of the files extracted, empty on failure. | ||||||
| 	 */ | 	 */ | ||||||
|     QStringList MULTIMC_LOGIC_EXPORT extractDir(QString fileCompressed, QString dir = QString()); | 	QStringList MULTIMC_LOGIC_EXPORT extractDir(QString fileCompressed, QString dir = QString()); | ||||||
| } |  | ||||||
|  | 	/** | ||||||
|  | 	 * Find a single file in archive by file name (not path) | ||||||
|  | 	 * | ||||||
|  | 	 * \return the path prefix where the file is | ||||||
|  | 	 */ | ||||||
|  | 	QString MULTIMC_LOGIC_EXPORT findFileInZip(QuaZip * zip, const QString & what, const QString &root = QString()); | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * Find a multiple files of the same name in archive by file name | ||||||
|  | 	 * If a file is found in a path, no deeper paths are searched | ||||||
|  | 	 * | ||||||
|  | 	 * \return true if anything was found | ||||||
|  | 	 */ | ||||||
|  | 	bool MULTIMC_LOGIC_EXPORT findFilesInZip(QuaZip * zip, const QString & what, QStringList & result, const QString &root = QString()); | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * Extract a single file to a destination | ||||||
|  | 	 * | ||||||
|  | 	 * \return true if it succeeds | ||||||
|  | 	 */ | ||||||
|  | 	bool MULTIMC_LOGIC_EXPORT extractFile(QuaZip *zip, const QString &fileName, const QString &fileDest); | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * Extract a subdirectory from an archive | ||||||
|  | 	 */ | ||||||
|  | 	QStringList MULTIMC_LOGIC_EXPORT extractSubDir(QuaZip *zip, const QString & subdir, const QString &target); | ||||||
|  | } | ||||||
|   | |||||||
| @@ -27,6 +27,7 @@ | |||||||
| #include <tag_primitive.h> | #include <tag_primitive.h> | ||||||
| #include <quazip.h> | #include <quazip.h> | ||||||
| #include <quazipfile.h> | #include <quazipfile.h> | ||||||
|  | #include <quazipdir.h> | ||||||
|  |  | ||||||
| World::World(const QFileInfo &file) | World::World(const QFileInfo &file) | ||||||
| { | { | ||||||
| @@ -76,9 +77,16 @@ void World::readFromZip(const QFileInfo &file) | |||||||
| 	{ | 	{ | ||||||
| 		return; | 		return; | ||||||
| 	} | 	} | ||||||
|  | 	auto location = MMCZip::findFileInZip(&zip, "level.dat"); | ||||||
|  | 	is_valid = !location.isEmpty(); | ||||||
|  | 	if (!is_valid) | ||||||
|  | 	{ | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 	m_containerOffsetPath = location; | ||||||
| 	QuaZipFile zippedFile(&zip); | 	QuaZipFile zippedFile(&zip); | ||||||
| 	// read the install profile | 	// read the install profile | ||||||
| 	is_valid = zip.setCurrentFile("level.dat"); | 	is_valid = zip.setCurrentFile(location + "level.dat"); | ||||||
| 	if (!is_valid) | 	if (!is_valid) | ||||||
| 	{ | 	{ | ||||||
| 		return; | 		return; | ||||||
| @@ -109,8 +117,12 @@ bool World::install(QString to) | |||||||
| 	} | 	} | ||||||
| 	if(m_containerFile.isFile()) | 	if(m_containerFile.isFile()) | ||||||
| 	{ | 	{ | ||||||
| 		// FIXME: check if this is OK. | 		QuaZip zip(m_containerFile.absoluteFilePath()); | ||||||
| 		return !MMCZip::extractDir(m_containerFile.absoluteFilePath(), finalPath).isEmpty(); | 		if (!zip.open(QuaZip::mdUnzip)) | ||||||
|  | 		{ | ||||||
|  | 			return false; | ||||||
|  | 		} | ||||||
|  | 		return !MMCZip::extractSubDir(&zip, m_containerOffsetPath, finalPath).isEmpty(); | ||||||
| 	} | 	} | ||||||
| 	else if(m_containerFile.isDir()) | 	else if(m_containerFile.isDir()) | ||||||
| 	{ | 	{ | ||||||
|   | |||||||
| @@ -41,6 +41,14 @@ public: | |||||||
| 	{ | 	{ | ||||||
| 		return is_valid; | 		return is_valid; | ||||||
| 	} | 	} | ||||||
|  | 	bool isOnFS() const | ||||||
|  | 	{ | ||||||
|  | 		return m_containerFile.isDir(); | ||||||
|  | 	} | ||||||
|  | 	QFileInfo container() const | ||||||
|  | 	{ | ||||||
|  | 		return m_containerFile; | ||||||
|  | 	} | ||||||
| 	// delete all the files of this world | 	// delete all the files of this world | ||||||
| 	bool destroy(); | 	bool destroy(); | ||||||
| 	// replace this world with a copy of the other | 	// replace this world with a copy of the other | ||||||
| @@ -62,6 +70,7 @@ private: | |||||||
| protected: | protected: | ||||||
|  |  | ||||||
| 	QFileInfo m_containerFile; | 	QFileInfo m_containerFile; | ||||||
|  | 	QString m_containerOffsetPath; | ||||||
| 	QString m_folderName; | 	QString m_folderName; | ||||||
| 	QString m_actualName; | 	QString m_actualName; | ||||||
| 	QDateTime levelDatTime; | 	QDateTime levelDatTime; | ||||||
|   | |||||||
| @@ -219,25 +219,64 @@ QVariant WorldList::headerData(int section, Qt::Orientation orientation, int rol | |||||||
| QStringList WorldList::mimeTypes() const | QStringList WorldList::mimeTypes() const | ||||||
| { | { | ||||||
| 	QStringList types; | 	QStringList types; | ||||||
| 	types << "text/plain"; |  | ||||||
| 	types << "text/uri-list"; | 	types << "text/uri-list"; | ||||||
| 	return types; | 	return types; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | class WorldMimeData : public QMimeData | ||||||
|  | { | ||||||
|  | Q_OBJECT | ||||||
|  |  | ||||||
|  | public: | ||||||
|  | 	WorldMimeData(QList<World> worlds) | ||||||
|  | 	{ | ||||||
|  | 		m_worlds = worlds; | ||||||
|  |  | ||||||
|  | 	} | ||||||
|  | 	QStringList formats() const | ||||||
|  | 	{ | ||||||
|  | 		return QMimeData::formats() << "text/uri-list"; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | protected: | ||||||
|  | 	QVariant retrieveData(const QString &mimetype, QVariant::Type type) const | ||||||
|  | 	{ | ||||||
|  | 		QList<QUrl> urls; | ||||||
|  | 		for(auto &world: m_worlds) | ||||||
|  | 		{ | ||||||
|  | 			if(!world.isValid() || !world.isOnFS()) | ||||||
|  | 				continue; | ||||||
|  | 			QString worldPath = world.container().absoluteFilePath(); | ||||||
|  | 			qDebug() << worldPath; | ||||||
|  | 			urls.append(QUrl::fromLocalFile(worldPath)); | ||||||
|  | 		} | ||||||
|  | 		const_cast<WorldMimeData*>(this)->setUrls(urls); | ||||||
|  | 		return QMimeData::retrieveData(mimetype, type); | ||||||
|  | 	} | ||||||
|  | private: | ||||||
|  | 	QList<World> m_worlds; | ||||||
|  | }; | ||||||
|  |  | ||||||
| QMimeData *WorldList::mimeData(const QModelIndexList &indexes) const | QMimeData *WorldList::mimeData(const QModelIndexList &indexes) const | ||||||
| { | { | ||||||
| 	QMimeData *data = new QMimeData(); |  | ||||||
|  |  | ||||||
| 	if (indexes.size() == 0) | 	if (indexes.size() == 0) | ||||||
| 		return data; | 		return new QMimeData(); | ||||||
|  |  | ||||||
| 	auto idx = indexes[0]; | 	QList<World> worlds; | ||||||
| 	int row = idx.row(); | 	for(auto idx : indexes) | ||||||
| 	if (row < 0 || row >= worlds.size()) | 	{ | ||||||
| 		return data; | 		if(idx.column() != 0) | ||||||
|  | 			continue; | ||||||
| 	data->setText(QString::number(row)); | 		int row = idx.row(); | ||||||
| 	return data; | 		if (row < 0 || row >= this->worlds.size()) | ||||||
|  | 			continue; | ||||||
|  | 		worlds.append(this->worlds[row]); | ||||||
|  | 	} | ||||||
|  | 	if(!worlds.size()) | ||||||
|  | 	{ | ||||||
|  | 		return new QMimeData(); | ||||||
|  | 	} | ||||||
|  | 	return new WorldMimeData(worlds); | ||||||
| } | } | ||||||
|  |  | ||||||
| Qt::ItemFlags WorldList::flags(const QModelIndex &index) const | Qt::ItemFlags WorldList::flags(const QModelIndex &index) const | ||||||
| @@ -302,27 +341,7 @@ bool WorldList::dropMimeData(const QMimeData *data, Qt::DropAction action, int r | |||||||
| 			startWatching(); | 			startWatching(); | ||||||
| 		return true; | 		return true; | ||||||
| 	} | 	} | ||||||
| 	/* |  | ||||||
| 	else if (data->hasText()) |  | ||||||
| 	{ |  | ||||||
| 		QString sourcestr = data->text(); |  | ||||||
| 		auto list = sourcestr.split('|'); |  | ||||||
| 		if (list.size() != 2) |  | ||||||
| 			return false; |  | ||||||
| 		QString remoteId = list[0]; |  | ||||||
| 		int remoteIndex = list[1].toInt(); |  | ||||||
| 		qDebug() << "move: " << sourcestr; |  | ||||||
| 		// no moving of things between two lists |  | ||||||
| 		if (remoteId != m_list_id) |  | ||||||
| 			return false; |  | ||||||
| 		// no point moving to the same place... |  | ||||||
| 		if (row == remoteIndex) |  | ||||||
| 			return false; |  | ||||||
| 		// otherwise, move the mod :D |  | ||||||
| 		moveModTo(remoteIndex, row); |  | ||||||
| 		return true; |  | ||||||
| 	} |  | ||||||
| 	*/ |  | ||||||
| 	return false; | 	return false; | ||||||
|  |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | #include "WorldList.moc" | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user