summaryrefslogtreecommitdiffstats
path: root/logic
diff options
context:
space:
mode:
authorPetr Mrázek <peterix@gmail.com>2015-09-09 23:53:33 +0200
committerPetr Mrázek <peterix@gmail.com>2015-09-09 23:53:33 +0200
commita1fd50e920eba0f198b898e5df4ff5f60424d355 (patch)
tree6b2a3e0ec704d5edfec221c84ca1571376df9c1e /logic
parent51070a13f711e53835f9aadc220c8177440590d9 (diff)
downloadMultiMC-a1fd50e920eba0f198b898e5df4ff5f60424d355.tar
MultiMC-a1fd50e920eba0f198b898e5df4ff5f60424d355.tar.gz
MultiMC-a1fd50e920eba0f198b898e5df4ff5f60424d355.tar.lz
MultiMC-a1fd50e920eba0f198b898e5df4ff5f60424d355.tar.xz
MultiMC-a1fd50e920eba0f198b898e5df4ff5f60424d355.zip
GH-1227: World import using drag and drop - zip files and folders
Diffstat (limited to 'logic')
-rw-r--r--logic/minecraft/ModList.cpp1
-rw-r--r--logic/minecraft/World.cpp188
-rw-r--r--logic/minecraft/World.h10
-rw-r--r--logic/minecraft/WorldList.cpp116
-rw-r--r--logic/minecraft/WorldList.h15
5 files changed, 247 insertions, 83 deletions
diff --git a/logic/minecraft/ModList.cpp b/logic/minecraft/ModList.cpp
index a77c5890..8f1bc041 100644
--- a/logic/minecraft/ModList.cpp
+++ b/logic/minecraft/ModList.cpp
@@ -539,6 +539,7 @@ QMimeData *ModList::mimeData(const QModelIndexList &indexes) const
data->setText(params.join('|'));
return data;
}
+
bool ModList::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column,
const QModelIndex &parent)
{
diff --git a/logic/minecraft/World.cpp b/logic/minecraft/World.cpp
index 977a03cf..3b66f113 100644
--- a/logic/minecraft/World.cpp
+++ b/logic/minecraft/World.cpp
@@ -20,10 +20,13 @@
#include <pathutils.h>
#include "GZip.h"
+#include <MMCZip.h>
#include <sstream>
#include <io/stream_reader.h>
#include <tag_string.h>
#include <tag_primitive.h>
+#include <quazip.h>
+#include <quazipfile.h>
World::World(const QFileInfo &file)
{
@@ -32,8 +35,20 @@ World::World(const QFileInfo &file)
void World::repath(const QFileInfo &file)
{
- m_file = file;
+ m_containerFile = file;
m_folderName = file.fileName();
+ if(file.isFile() && file.suffix() == "zip")
+ {
+ readFromZip(file);
+ }
+ else if(file.isDir())
+ {
+ readFromFS(file);
+ }
+}
+
+void World::readFromFS(const QFileInfo &file)
+{
QDir worldDir(file.filePath());
is_valid = file.isDir() && worldDir.exists("level.dat");
if(!is_valid)
@@ -48,66 +63,123 @@ void World::repath(const QFileInfo &file)
{
return;
}
+ QFileInfo finfo(fullFilePath);
+ levelDatTime = finfo.lastModified();
+ parseLevelDat(f.readAll());
+}
- QByteArray output;
- is_valid = GZip::inflate(f.readAll(), output);
- if(!is_valid)
+void World::readFromZip(const QFileInfo &file)
+{
+ QuaZip zip(file.absoluteFilePath());
+ is_valid = zip.open(QuaZip::mdUnzip);
+ if (!is_valid)
+ {
+ return;
+ }
+ QuaZipFile zippedFile(&zip);
+ // read the install profile
+ is_valid = zip.setCurrentFile("level.dat");
+ if (!is_valid)
{
return;
}
- f.close();
+ is_valid = zippedFile.open(QIODevice::ReadOnly);
+ QuaZipFileInfo64 levelDatInfo;
+ zippedFile.getFileInfo(&levelDatInfo);
+ auto modTime = levelDatInfo.getNTFSmTime();
+ if(!modTime.isValid())
+ {
+ modTime = levelDatInfo.dateTime;
+ }
+ levelDatTime = modTime;
+ if (!is_valid)
+ {
+ return;
+ }
+ parseLevelDat(zippedFile.readAll());
+ zippedFile.close();
+}
- auto read_string = [](nbt::value& parent, const char * name, const QString & fallback = QString()) -> QString
+bool World::install(QString to)
+{
+ auto finalPath = PathCombine(to, DirNameFromString(m_actualName, to));
+ if(!ensureFolderPathExists(finalPath))
{
- try
- {
- auto &namedValue = parent.at(name);
- if(namedValue.get_type() != nbt::tag_type::String)
- {
- return fallback;
- }
- auto & tag_str = namedValue.as<nbt::tag_string>();
- return QString::fromStdString(tag_str.get());
- }
- catch(std::out_of_range e)
- {
- // fallback for old world formats
- qWarning() << "String NBT tag" << name << "could not be found. Defaulting to" << fallback;
- return fallback;
- }
- catch(std::bad_cast e)
- {
- // type mismatch
- qWarning() << "NBT tag" << name << "could not be converted to string. Defaulting to" << fallback;
- return fallback;
- }
- };
+ return false;
+ }
+ if(m_containerFile.isFile())
+ {
+ // FIXME: check if this is OK.
+ return !MMCZip::extractDir(m_containerFile.absoluteFilePath(), finalPath).isEmpty();
+ }
+ else if(m_containerFile.isDir())
+ {
+ QString from = m_containerFile.filePath();
+ return copyPath(from, finalPath);
+ }
+ return false;
+}
- auto read_long = [](nbt::value& parent, const char * name, const int64_t & fallback = 0) -> int64_t
+static QString read_string (nbt::value& parent, const char * name, const QString & fallback = QString())
+{
+ try
{
- try
- {
- auto &namedValue = parent.at(name);
- if(namedValue.get_type() != nbt::tag_type::Long)
- {
- return fallback;
- }
- auto & tag_str = namedValue.as<nbt::tag_long>();
- return tag_str.get();
- }
- catch(std::out_of_range e)
+ auto &namedValue = parent.at(name);
+ if(namedValue.get_type() != nbt::tag_type::String)
{
- // fallback for old world formats
- qWarning() << "Long NBT tag" << name << "could not be found. Defaulting to" << fallback;
return fallback;
}
- catch(std::bad_cast e)
+ auto & tag_str = namedValue.as<nbt::tag_string>();
+ return QString::fromStdString(tag_str.get());
+ }
+ catch(std::out_of_range e)
+ {
+ // fallback for old world formats
+ qWarning() << "String NBT tag" << name << "could not be found. Defaulting to" << fallback;
+ return fallback;
+ }
+ catch(std::bad_cast e)
+ {
+ // type mismatch
+ qWarning() << "NBT tag" << name << "could not be converted to string. Defaulting to" << fallback;
+ return fallback;
+ }
+};
+
+static int64_t read_long (nbt::value& parent, const char * name, const int64_t & fallback = 0)
+{
+ try
+ {
+ auto &namedValue = parent.at(name);
+ if(namedValue.get_type() != nbt::tag_type::Long)
{
- // type mismatch
- qWarning() << "NBT tag" << name << "could not be converted to long. Defaulting to" << fallback;
return fallback;
}
- };
+ auto & tag_str = namedValue.as<nbt::tag_long>();
+ return tag_str.get();
+ }
+ catch(std::out_of_range e)
+ {
+ // fallback for old world formats
+ qWarning() << "Long NBT tag" << name << "could not be found. Defaulting to" << fallback;
+ return fallback;
+ }
+ catch(std::bad_cast e)
+ {
+ // type mismatch
+ qWarning() << "NBT tag" << name << "could not be converted to long. Defaulting to" << fallback;
+ return fallback;
+ }
+};
+
+void World::parseLevelDat(QByteArray data)
+{
+ QByteArray output;
+ is_valid = GZip::inflate(data, output);
+ if(!is_valid)
+ {
+ return;
+ }
try
{
@@ -138,8 +210,7 @@ void World::repath(const QFileInfo &file)
int64_t temp = read_long(val, "LastPlayed", 0);
if(temp == 0)
{
- QFileInfo finfo(fullFilePath);
- m_lastPlayed = finfo.lastModified();
+ m_lastPlayed = levelDatTime;
}
else
{
@@ -164,11 +235,11 @@ bool World::replace(World &with)
{
if (!destroy())
return false;
- bool success = copyPath(with.m_file.filePath(), m_file.path());
+ bool success = copyPath(with.m_containerFile.filePath(), m_containerFile.path());
if (success)
{
m_folderName = with.m_folderName;
- m_file.refresh();
+ m_containerFile.refresh();
}
return success;
}
@@ -176,18 +247,15 @@ bool World::replace(World &with)
bool World::destroy()
{
if(!is_valid) return false;
- if (m_file.isDir())
+ if (m_containerFile.isDir())
{
- QDir d(m_file.filePath());
- if (d.removeRecursively())
- {
- return true;
- }
- return false;
+ QDir d(m_containerFile.filePath());
+ return d.removeRecursively();
}
- else
+ else if(m_containerFile.isFile())
{
- return false;
+ QFile file(m_containerFile.absoluteFilePath());
+ return file.remove();
}
return true;
}
diff --git a/logic/minecraft/World.h b/logic/minecraft/World.h
index 91cb2a83..27184e05 100644
--- a/logic/minecraft/World.h
+++ b/logic/minecraft/World.h
@@ -48,15 +48,23 @@ public:
// change the world's filesystem path (used by world lists for *MAGIC* purposes)
void repath(const QFileInfo &file);
+ bool install(QString to);
+
// WEAK compare operator - used for replacing worlds
bool operator==(const World &other) const;
bool strongCompare(const World &other) const;
+private:
+ void readFromZip(const QFileInfo &file);
+ void readFromFS(const QFileInfo &file);
+ void parseLevelDat(QByteArray data);
+
protected:
- QFileInfo m_file;
+ QFileInfo m_containerFile;
QString m_folderName;
QString m_actualName;
+ QDateTime levelDatTime;
QDateTime m_lastPlayed;
int64_t m_randomSeed = 0;
bool is_valid = false;
diff --git a/logic/minecraft/WorldList.cpp b/logic/minecraft/WorldList.cpp
index 7066093c..1c25f3b1 100644
--- a/logic/minecraft/WorldList.cpp
+++ b/logic/minecraft/WorldList.cpp
@@ -62,40 +62,28 @@ void WorldList::stopWatching()
}
}
-void WorldList::internalSort(QList<World> &what)
-{
- auto predicate = [](const World &left, const World &right)
- {
- return left.folderName().localeAwareCompare(right.folderName()) < 0;
- };
- std::sort(what.begin(), what.end(), predicate);
-}
-
bool WorldList::update()
{
if (!isValid())
return false;
- QList<World> orderedWorlds;
QList<World> newWorlds;
m_dir.refresh();
auto folderContents = m_dir.entryInfoList();
// if there are any untracked files...
- if (folderContents.size())
+ for (QFileInfo entry : folderContents)
{
- // the order surely changed!
- for (auto entry : folderContents)
+ if(!entry.isDir())
+ continue;
+
+ World w(entry);
+ if(w.isValid())
{
- World w(entry);
- if(w.isValid()) {
- newWorlds.append(w);
- }
+ newWorlds.append(w);
}
- internalSort(newWorlds);
- orderedWorlds.append(newWorlds);
}
beginResetModel();
- worlds.swap(orderedWorlds);
+ worlds.swap(newWorlds);
endResetModel();
return true;
}
@@ -232,6 +220,7 @@ QStringList WorldList::mimeTypes() const
{
QStringList types;
types << "text/plain";
+ types << "text/uri-list";
return types;
}
@@ -250,3 +239,90 @@ QMimeData *WorldList::mimeData(const QModelIndexList &indexes) const
data->setText(QString::number(row));
return data;
}
+
+Qt::ItemFlags WorldList::flags(const QModelIndex &index) const
+{
+ Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index);
+ if (index.isValid())
+ return Qt::ItemIsUserCheckable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled |
+ defaultFlags;
+ else
+ return Qt::ItemIsDropEnabled | defaultFlags;
+}
+
+Qt::DropActions WorldList::supportedDragActions() const
+{
+ // move to other mod lists or VOID
+ return Qt::MoveAction;
+}
+
+Qt::DropActions WorldList::supportedDropActions() const
+{
+ // copy from outside, move from within and other mod lists
+ return Qt::CopyAction | Qt::MoveAction;
+}
+
+void WorldList::installWorld(QFileInfo filename)
+{
+ qDebug() << "installing: " << filename.absoluteFilePath();
+ World w(filename);
+ if(!w.isValid())
+ {
+ return;
+ }
+ w.install(m_dir.absolutePath());
+}
+
+bool WorldList::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column,
+ const QModelIndex &parent)
+{
+ if (action == Qt::IgnoreAction)
+ return true;
+ // check if the action is supported
+ if (!data || !(action & supportedDropActions()))
+ return false;
+ // files dropped from outside?
+ if (data->hasUrls())
+ {
+ bool was_watching = is_watching;
+ if (was_watching)
+ stopWatching();
+ auto urls = data->urls();
+ for (auto url : urls)
+ {
+ // only local files may be dropped...
+ if (!url.isLocalFile())
+ continue;
+ QString filename = url.toLocalFile();
+
+ QFileInfo worldInfo(filename);
+ installWorld(worldInfo);
+ }
+ if (was_watching)
+ 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;
+
+}
diff --git a/logic/minecraft/WorldList.h b/logic/minecraft/WorldList.h
index 7f119e81..90c7c6ed 100644
--- a/logic/minecraft/WorldList.h
+++ b/logic/minecraft/WorldList.h
@@ -19,6 +19,7 @@
#include <QString>
#include <QDir>
#include <QAbstractListModel>
+#include <QMimeData>
#include "minecraft/World.h"
#include "multimc_logic_export.h"
@@ -71,16 +72,28 @@ public:
/// Reloads the mod list and returns true if the list changed.
virtual bool update();
+ /// Install a world from location
+ void installWorld(QFileInfo filename);
+
/// Deletes the mod at the given index.
virtual bool deleteWorld(int index);
/// Deletes all the selected mods
virtual bool deleteWorlds(int first, int last);
+ /// flags, mostly to support drag&drop
+ virtual Qt::ItemFlags flags(const QModelIndex &index) const;
/// get data for drag action
virtual QMimeData *mimeData(const QModelIndexList &indexes) const;
/// get the supported mime types
virtual QStringList mimeTypes() const;
+ /// process data from drop action
+ virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent);
+ /// what drag actions do we support?
+ virtual Qt::DropActions supportedDragActions() const;
+
+ /// what drop actions do we support?
+ virtual Qt::DropActions supportedDropActions() const;
void startWatching();
void stopWatching();
@@ -97,8 +110,6 @@ public:
return worlds;
}
-private:
- void internalSort(QList<World> &what);
private slots:
void directoryChanged(QString path);