GH-1227 allow structured world zip import and drag and drop out of MultiMC

This commit is contained in:
Petr Mrázek 2015-09-13 04:21:26 +02:00
parent 2315f463a8
commit 8ef07ec634
5 changed files with 256 additions and 38 deletions

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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())
{ {

View File

@ -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;

View File

@ -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"