diff options
author | Petr Mrázek <peterix@gmail.com> | 2015-09-13 04:21:26 +0200 |
---|---|---|
committer | Petr Mrázek <peterix@gmail.com> | 2015-09-13 04:21:26 +0200 |
commit | 8ef07ec6346a4f9c078184c9497e7c12f5b8c33d (patch) | |
tree | ddb81e3591825bd1cd5e080e1fcc4aede553d911 | |
parent | 2315f463a8e7713fc62027bb9540a22b240a0f78 (diff) | |
download | MultiMC-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.cpp | 151 | ||||
-rw-r--r-- | logic/MMCZip.h | 31 | ||||
-rw-r--r-- | logic/minecraft/World.cpp | 18 | ||||
-rw-r--r-- | logic/minecraft/World.h | 9 | ||||
-rw-r--r-- | logic/minecraft/WorldList.cpp | 85 |
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" |