GH-1227 allow structured world zip import and drag and drop out of MultiMC
This commit is contained in:
parent
2315f463a8
commit
8ef07ec634
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"
|
||||||
|
Loading…
Reference in New Issue
Block a user