summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPetr Mrázek <peterix@gmail.com>2015-09-13 04:21:26 +0200
committerPetr Mrázek <peterix@gmail.com>2015-09-13 04:21:26 +0200
commit8ef07ec6346a4f9c078184c9497e7c12f5b8c33d (patch)
treeddb81e3591825bd1cd5e080e1fcc4aede553d911
parent2315f463a8e7713fc62027bb9540a22b240a0f78 (diff)
downloadMultiMC-8ef07ec6346a4f9c078184c9497e7c12f5b8c33d.tar
MultiMC-8ef07ec6346a4f9c078184c9497e7c12f5b8c33d.tar.gz
MultiMC-8ef07ec6346a4f9c078184c9497e7c12f5b8c33d.tar.lz
MultiMC-8ef07ec6346a4f9c078184c9497e7c12f5b8c33d.tar.xz
MultiMC-8ef07ec6346a4f9c078184c9497e7c12f5b8c33d.zip
GH-1227 allow structured world zip import and drag and drop out of MultiMC
-rw-r--r--logic/MMCZip.cpp151
-rw-r--r--logic/MMCZip.h31
-rw-r--r--logic/minecraft/World.cpp18
-rw-r--r--logic/minecraft/World.h9
-rw-r--r--logic/minecraft/WorldList.cpp85
5 files changed, 256 insertions, 38 deletions
diff --git a/logic/MMCZip.cpp b/logic/MMCZip.cpp
index 75f49ced..62904fa5 100644
--- a/logic/MMCZip.cpp
+++ b/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 <quazip.h>
#include <JlCompress.h>
+#include <quazipdir.h>
#include "MMCZip.h"
#include <QDebug>
@@ -338,3 +339,153 @@ bool MMCZip::compressDir(QString zipFile, QString dir, QString prefix, const Sep
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;
+}
diff --git a/logic/MMCZip.h b/logic/MMCZip.h
index a3167079..f350e668 100644
--- a/logic/MMCZip.h
+++ b/logic/MMCZip.h
@@ -57,5 +57,32 @@ namespace MMCZip
* left empty.
* \return The list of the full paths of the files extracted, empty on failure.
*/
- QStringList MULTIMC_LOGIC_EXPORT extractDir(QString fileCompressed, QString dir = QString());
-} \ No newline at end of file
+ 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);
+}
diff --git a/logic/minecraft/World.cpp b/logic/minecraft/World.cpp
index 142839d2..bb99aa96 100644
--- a/logic/minecraft/World.cpp
+++ b/logic/minecraft/World.cpp
@@ -27,6 +27,7 @@
#include <tag_primitive.h>
#include <quazip.h>
#include <quazipfile.h>
+#include <quazipdir.h>
World::World(const QFileInfo &file)
{
@@ -76,9 +77,16 @@ void World::readFromZip(const QFileInfo &file)
{
return;
}
+ auto location = MMCZip::findFileInZip(&zip, "level.dat");
+ is_valid = !location.isEmpty();
+ if (!is_valid)
+ {
+ return;
+ }
+ m_containerOffsetPath = location;
QuaZipFile zippedFile(&zip);
// read the install profile
- is_valid = zip.setCurrentFile("level.dat");
+ is_valid = zip.setCurrentFile(location + "level.dat");
if (!is_valid)
{
return;
@@ -109,8 +117,12 @@ bool World::install(QString to)
}
if(m_containerFile.isFile())
{
- // FIXME: check if this is OK.
- return !MMCZip::extractDir(m_containerFile.absoluteFilePath(), finalPath).isEmpty();
+ QuaZip zip(m_containerFile.absoluteFilePath());
+ if (!zip.open(QuaZip::mdUnzip))
+ {
+ return false;
+ }
+ return !MMCZip::extractSubDir(&zip, m_containerOffsetPath, finalPath).isEmpty();
}
else if(m_containerFile.isDir())
{
diff --git a/logic/minecraft/World.h b/logic/minecraft/World.h
index 27184e05..bed8f967 100644
--- a/logic/minecraft/World.h
+++ b/logic/minecraft/World.h
@@ -41,6 +41,14 @@ public:
{
return is_valid;
}
+ bool isOnFS() const
+ {
+ return m_containerFile.isDir();
+ }
+ QFileInfo container() const
+ {
+ return m_containerFile;
+ }
// delete all the files of this world
bool destroy();
// replace this world with a copy of the other
@@ -62,6 +70,7 @@ private:
protected:
QFileInfo m_containerFile;
+ QString m_containerOffsetPath;
QString m_folderName;
QString m_actualName;
QDateTime levelDatTime;
diff --git a/logic/minecraft/WorldList.cpp b/logic/minecraft/WorldList.cpp
index 1c25f3b1..e58ca064 100644
--- a/logic/minecraft/WorldList.cpp
+++ b/logic/minecraft/WorldList.cpp
@@ -219,25 +219,64 @@ QVariant WorldList::headerData(int section, Qt::Orientation orientation, int rol
QStringList WorldList::mimeTypes() const
{
QStringList types;
- types << "text/plain";
types << "text/uri-list";
return types;
}
-QMimeData *WorldList::mimeData(const QModelIndexList &indexes) const
+class WorldMimeData : public QMimeData
{
- QMimeData *data = new QMimeData();
+Q_OBJECT
- if (indexes.size() == 0)
- return data;
+public:
+ WorldMimeData(QList<World> worlds)
+ {
+ m_worlds = worlds;
- auto idx = indexes[0];
- int row = idx.row();
- if (row < 0 || row >= worlds.size())
- return data;
+ }
+ 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
+{
+ if (indexes.size() == 0)
+ return new QMimeData();
- data->setText(QString::number(row));
- return data;
+ QList<World> worlds;
+ for(auto idx : indexes)
+ {
+ if(idx.column() != 0)
+ continue;
+ int row = idx.row();
+ 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
@@ -302,27 +341,7 @@ bool WorldList::dropMimeData(const QMimeData *data, Qt::DropAction action, int r
startWatching();
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;
-
}
+
+#include "WorldList.moc"