From acf25d8a33ef67b79d8e8a8859f5559e011373a5 Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Thu, 26 Dec 2013 05:14:32 +0100 Subject: Disable/enable mods with checkboxes. Needs testing. A lot of testing! --- logic/ModList.cpp | 210 ++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 156 insertions(+), 54 deletions(-) (limited to 'logic/ModList.cpp') diff --git a/logic/ModList.cpp b/logic/ModList.cpp index d5235fe9..dbc85320 100644 --- a/logic/ModList.cpp +++ b/logic/ModList.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include "logger/QsLog.h" @@ -27,7 +28,7 @@ ModList::ModList(const QString &dir, const QString &list_file) { m_dir.setFilter(QDir::Readable | QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs | QDir::NoSymLinks); - m_dir.setSorting(QDir::Name); + m_dir.setSorting(QDir::Name | QDir::IgnoreCase | QDir::LocaleAware); m_list_id = QUuid::createUuid().toString(); m_watcher = new QFileSystemWatcher(this); is_watching = false; @@ -66,52 +67,79 @@ bool ModList::update() if (!isValid()) return false; + QList orderedMods; QList newMods; m_dir.refresh(); auto folderContents = m_dir.entryInfoList(); - bool orderWasInvalid = false; + bool orderOrStateChanged = false; // first, process the ordered items (if any) - QStringList listOrder = readListFile(); + OrderList listOrder = readListFile(); for (auto item : listOrder) { - QFileInfo info(m_dir.filePath(item)); - int idx = folderContents.indexOf(info); + QFileInfo infoEnabled(m_dir.filePath(item.id)); + QFileInfo infoDisabled(m_dir.filePath(item.id + ".disabled")); + int idxEnabled = folderContents.indexOf(infoEnabled); + int idxDisabled = folderContents.indexOf(infoDisabled); + // if both enabled and disabled versions are present, PANIC! + if (idxEnabled >= 0 && idxDisabled >= 0) + { + return false; + } + bool isEnabled = idxEnabled >= 0; + int idx = isEnabled ? idxEnabled : idxDisabled; + QFileInfo info = isEnabled ? infoEnabled : infoDisabled; // if the file from the index file exists if (idx != -1) { // remove from the actual folder contents list folderContents.takeAt(idx); // append the new mod - newMods.append(Mod(info)); + orderedMods.append(Mod(info)); + if (isEnabled != item.enabled) + orderOrStateChanged = true; } else { - orderWasInvalid = true; + orderOrStateChanged = true; } } - for (auto entry : folderContents) + // if there are any untracked files... + if (folderContents.size()) { - newMods.append(Mod(entry)); + // the order surely changed! + for (auto entry : folderContents) + { + newMods.append(Mod(entry)); + } + std::sort(newMods.begin(), newMods.end(), [](const Mod & left, const Mod & right) + { return left.name().localeAwareCompare(right.name()) <= 0; }); + orderedMods.append(newMods); + orderOrStateChanged = true; } - if (mods.size() != newMods.size()) + // otherwise, if we were already tracking some mods + else if (mods.size()) { - orderWasInvalid = true; - } - else - for (int i = 0; i < mods.size(); i++) - { - if (!mods[i].strongCompare(newMods[i])) + // if the number doesn't match, order changed. + if (mods.size() != orderedMods.size()) + orderOrStateChanged = true; + // if it does match, compare the mods themselves + else + for (int i = 0; i < mods.size(); i++) { - orderWasInvalid = true; - break; + if (!mods[i].strongCompare(orderedMods[i])) + { + orderOrStateChanged = true; + break; + } } - } + } beginResetModel(); - mods.swap(newMods); + mods.swap(orderedMods); endResetModel(); - if (orderWasInvalid) + if (orderOrStateChanged && !m_list_file.isEmpty()) { + QLOG_INFO() << "Mod list " << m_list_file << " changed!"; saveListFile(); emit changed(); } @@ -123,17 +151,19 @@ void ModList::directoryChanged(QString path) update(); } -QStringList ModList::readListFile() +ModList::OrderList ModList::readListFile() { - QStringList stringList; + OrderList itemList; if (m_list_file.isNull() || m_list_file.isEmpty()) - return stringList; + return itemList; QFile textFile(m_list_file); if (!textFile.open(QIODevice::ReadOnly | QIODevice::Text)) - return QStringList(); + return OrderList(); - QTextStream textStream(&textFile); + QTextStream textStream; + textStream.setAutoDetectUnicode(true); + textStream.setDevice(&textFile); while (true) { QString line = textStream.readLine(); @@ -141,11 +171,18 @@ QStringList ModList::readListFile() break; else { - stringList.append(line); + OrderItem it; + it.enabled = !line.endsWith(".disabled"); + if (!it.enabled) + { + line.chop(9); + } + it.id = line; + itemList.append(it); } } textFile.close(); - return stringList; + return itemList; } bool ModList::saveListFile() @@ -155,12 +192,16 @@ bool ModList::saveListFile() QFile textFile(m_list_file); if (!textFile.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) return false; - QTextStream textStream(&textFile); + QTextStream textStream; + textStream.setGenerateByteOrderMark(true); + textStream.setCodec("UTF-8"); + textStream.setDevice(&textFile); for (auto mod : mods) { - auto pathname = mod.filename(); - QString filename = pathname.fileName(); - textStream << filename << endl; + textStream << mod.mmc_id(); + if (!mod.enabled()) + textStream << ".disabled"; + textStream << endl; } textFile.close(); return false; @@ -327,7 +368,7 @@ bool ModList::moveModsDown(int first, int last) int ModList::columnCount(const QModelIndex &parent) const { - return 2; + return 3; } QVariant ModList::data(const QModelIndex &index, int role) const @@ -341,43 +382,96 @@ QVariant ModList::data(const QModelIndex &index, int role) const if (row < 0 || row >= mods.size()) return QVariant(); - if (role != Qt::DisplayRole) - return QVariant(); - - switch (column) + switch (role) { - case 0: - return mods[row].name(); - case 1: - return mods[row].version(); - case 2: - return mods[row].mcversion(); + case Qt::DisplayRole: + switch (index.column()) + { + case NameColumn: + return mods[row].name(); + case VersionColumn: + return mods[row].version(); + + default: + return QVariant(); + } + + case Qt::ToolTipRole: + return mods[row].mmc_id(); + + case Qt::CheckStateRole: + switch (index.column()) + { + case ActiveColumn: + return mods[row].enabled(); + default: + return QVariant(); + } default: return QVariant(); } } +bool ModList::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (index.row() < 0 || index.row() >= rowCount(index) || !index.isValid()) + { + return false; + } + + if (role == Qt::CheckStateRole) + { + auto &mod = mods[index.row()]; + if (mod.enable(!mod.enabled())) + { + emit dataChanged(index, index); + return true; + } + } + return false; +} + QVariant ModList::headerData(int section, Qt::Orientation orientation, int role) const { - if (role != Qt::DisplayRole || orientation != Qt::Horizontal) - return QVariant(); - switch (section) + switch (role) { - case 0: - return QString("Name"); - case 1: - return QString("Version"); - case 2: - return QString("Minecraft"); + case Qt::DisplayRole: + switch (section) + { + case ActiveColumn: + return QString(); + case NameColumn: + return QString("Name"); + case VersionColumn: + return QString("Version"); + default: + return QVariant(); + } + + case Qt::ToolTipRole: + switch (section) + { + case ActiveColumn: + return "Is the mod enabled?"; + case NameColumn: + return "The name of the mod."; + case VersionColumn: + return "The version of the mod."; + default: + return QVariant(); + } + default: + return QVariant(); } - return QString(); + return QVariant(); } Qt::ItemFlags ModList::flags(const QModelIndex &index) const { Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index); if (index.isValid()) - return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags; + return Qt::ItemIsUserCheckable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | + defaultFlags; else return Qt::ItemIsDropEnabled | defaultFlags; } @@ -456,6 +550,14 @@ bool ModList::dropMimeData(const QMimeData *data, Qt::DropAction action, int row QString filename = url.toLocalFile(); installMod(filename, row); QLOG_INFO() << "installing: " << filename; + // if there is no ordering, re-sort the list + if (m_list_file.isEmpty()) + { + beginResetModel(); + std::sort(mods.begin(), mods.end(), [](const Mod & left, const Mod & right) + { return left.name().localeAwareCompare(right.name()) <= 0; }); + endResetModel(); + } } if (was_watching) startWatching(); -- cgit From 4bf1cac8d89809106819c86543ef8efbf78f163f Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Fri, 27 Dec 2013 02:18:40 +0100 Subject: Handle the foo + foo.disabled jar mod corner case better. --- logic/ModList.cpp | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) (limited to 'logic/ModList.cpp') diff --git a/logic/ModList.cpp b/logic/ModList.cpp index dbc85320..eb7f3128 100644 --- a/logic/ModList.cpp +++ b/logic/ModList.cpp @@ -81,14 +81,24 @@ bool ModList::update() QFileInfo infoDisabled(m_dir.filePath(item.id + ".disabled")); int idxEnabled = folderContents.indexOf(infoEnabled); int idxDisabled = folderContents.indexOf(infoDisabled); - // if both enabled and disabled versions are present, PANIC! + bool isEnabled; + // if both enabled and disabled versions are present, it's a special case... if (idxEnabled >= 0 && idxDisabled >= 0) { - return false; + // we only process the one we actually have in the order file. + // and exactly as we have it. + // THIS IS A CORNER CASE + isEnabled = item.enabled; + } + else + { + // only one is present. + // we pick the one that we found. + // we assume the mod was enabled/disabled by external means + isEnabled = idxEnabled >= 0; } - bool isEnabled = idxEnabled >= 0; int idx = isEnabled ? idxEnabled : idxDisabled; - QFileInfo info = isEnabled ? infoEnabled : infoDisabled; + QFileInfo & info = isEnabled ? infoEnabled : infoDisabled; // if the file from the index file exists if (idx != -1) { @@ -226,6 +236,9 @@ bool ModList::installMod(const QFileInfo &filename, int index) int idx = mods.indexOf(m); if (idx != -1) { + int idx2 = mods.indexOf(m,idx+1); + if(idx2 != -1) + return false; if (mods[idx].replace(m)) { -- cgit From 8549e5317f708e71c2dead9d84cc27d6185b523e Mon Sep 17 00:00:00 2001 From: robotbrainify Date: Sat, 28 Dec 2013 20:28:24 -0500 Subject: Litemod version info. --- logic/Mod.cpp | 67 ++++++++++++++++++++++++++++++++++++++++++++----------- logic/Mod.h | 2 ++ logic/ModList.cpp | 2 +- 3 files changed, 57 insertions(+), 14 deletions(-) (limited to 'logic/ModList.cpp') diff --git a/logic/Mod.cpp b/logic/Mod.cpp index 8e880be1..009a32a4 100644 --- a/logic/Mod.cpp +++ b/logic/Mod.cpp @@ -47,7 +47,7 @@ void Mod::repath(const QFileInfo &file) } else if (m_file.isFile()) { - if(name_base.endsWith(".disabled")) + if (name_base.endsWith(".disabled")) { m_enabled = false; name_base.chop(9); @@ -62,6 +62,11 @@ void Mod::repath(const QFileInfo &file) m_type = MOD_ZIPFILE; name_base.chop(4); } + else if (name_base.endsWith(".litemod")) + { + m_type = MOD_LITEMOD; + name_base.chop(8); + } else { m_type = MOD_SINGLEFILE; @@ -79,7 +84,7 @@ void Mod::repath(const QFileInfo &file) if (zip.setCurrentFile("mcmod.info")) { - if(!file.open(QIODevice::ReadOnly)) + if (!file.open(QIODevice::ReadOnly)) { zip.close(); return; @@ -120,6 +125,27 @@ void Mod::repath(const QFileInfo &file) ReadMCModInfo(data); } } + else if (m_type == MOD_LITEMOD) + { + QuaZip zip(m_file.filePath()); + if (!zip.open(QuaZip::mdUnzip)) + return; + + QuaZipFile file(&zip); + + if (zip.setCurrentFile("litemod.json")) + { + if (!file.open(QIODevice::ReadOnly)) + { + zip.close(); + return; + } + + ReadLiteModInfo(file.readAll()); + file.close(); + } + zip.close(); + } } // NEW format @@ -152,8 +178,7 @@ void Mod::ReadMCModInfo(QByteArray contents) } m_credits = firstObj.value("credits").toString(); return; - } - ; + }; QJsonParseError jsonError; QJsonDocument jsonDoc = QJsonDocument::fromJson(contents, &jsonError); // this is the very old format that had just the array @@ -197,13 +222,29 @@ void Mod::ReadForgeInfo(QByteArray contents) m_version = major + "." + minor + "." + revision + "." + build; } +void Mod::ReadLiteModInfo(QByteArray contents) +{ + QJsonParseError jsonError; + QJsonDocument jsonDoc = QJsonDocument::fromJson(contents, &jsonError); + auto object = jsonDoc.object(); + m_mod_id = object.value("name").toString(); + if(object.contains("version")) + m_version=object.value("version").toString(""); + else + m_version=object.value("revision").toString(""); + m_mcversion = object.value("mcversion").toString(); + m_authors = object.value("author").toString(); + m_description = object.value("description").toString(); + m_homeurl = object.value("url").toString(); +} + bool Mod::replace(Mod &with) { if (!destroy()) return false; bool success = false; auto t = with.type(); - + if (t == MOD_ZIPFILE || t == MOD_SINGLEFILE) { QLOG_DEBUG() << "Copy: " << with.m_file.filePath() << " to " << m_file.filePath(); @@ -260,6 +301,7 @@ QString Mod::version() const switch (type()) { case MOD_ZIPFILE: + case MOD_LITEMOD: return m_version; case MOD_FOLDER: return "Folder"; @@ -272,27 +314,27 @@ QString Mod::version() const bool Mod::enable(bool value) { - if(m_type == Mod::MOD_UNKNOWN || m_type == Mod::MOD_FOLDER) + if (m_type == Mod::MOD_UNKNOWN || m_type == Mod::MOD_FOLDER) return false; - if(m_enabled == value) + if (m_enabled == value) return false; QString path = m_file.absoluteFilePath(); - if(value) + if (value) { QFile foo(path); - if(!path.endsWith(".disabled")) + if (!path.endsWith(".disabled")) return false; path.chop(9); - if(!foo.rename(path)) + if (!foo.rename(path)) return false; } else { QFile foo(path); path += ".disabled"; - if(!foo.rename(path)) + if (!foo.rename(path)) return false; } m_file = QFileInfo(path); @@ -305,6 +347,5 @@ bool Mod::operator==(const Mod &other) const } bool Mod::strongCompare(const Mod &other) const { - return mmc_id() == other.mmc_id() && - version() == other.version() && type() == other.type(); + return mmc_id() == other.mmc_id() && version() == other.version() && type() == other.type(); } diff --git a/logic/Mod.h b/logic/Mod.h index 05d3cea2..2eb2b97a 100644 --- a/logic/Mod.h +++ b/logic/Mod.h @@ -25,6 +25,7 @@ public: MOD_ZIPFILE, //!< The mod is a zip file containing the mod's class files. MOD_SINGLEFILE, //!< The mod is a single file (not a zip file). MOD_FOLDER, //!< The mod is in a folder on the filesystem. + MOD_LITEMOD, //!< The mod is a litemod }; Mod(const QFileInfo &file); @@ -102,6 +103,7 @@ public: private: void ReadMCModInfo(QByteArray contents); void ReadForgeInfo(QByteArray contents); + void ReadLiteModInfo(QByteArray contents); protected: diff --git a/logic/ModList.cpp b/logic/ModList.cpp index eb7f3128..fd41bcf7 100644 --- a/logic/ModList.cpp +++ b/logic/ModList.cpp @@ -255,7 +255,7 @@ bool ModList::installMod(const QFileInfo &filename, int index) auto type = m.type(); if (type == Mod::MOD_UNKNOWN) return false; - if (type == Mod::MOD_SINGLEFILE || type == Mod::MOD_ZIPFILE) + if (type == Mod::MOD_SINGLEFILE || type == Mod::MOD_ZIPFILE || type == Mod::MOD_LITEMOD) { QString newpath = PathCombine(m_dir.path(), filename.fileName()); if (!QFile::copy(filename.filePath(), newpath)) -- cgit