summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/directoryrefresher.cpp26
-rw-r--r--src/directoryrefresher.h1
-rw-r--r--src/mainwindow.cpp100
-rw-r--r--src/mainwindow.h3
-rw-r--r--src/mainwindow.ui57
-rw-r--r--src/modflagicondelegate.cpp54
-rw-r--r--src/modflagicondelegate.h2
-rw-r--r--src/modinfo.h26
-rw-r--r--src/modinfodialog.cpp23
-rw-r--r--src/modinfodialog.h3
-rw-r--r--src/modinfowithconflictinfo.cpp118
-rw-r--r--src/modinfowithconflictinfo.h28
-rw-r--r--src/modlist.cpp53
-rw-r--r--src/modlist.h7
-rw-r--r--src/modlistsortproxy.cpp11
-rw-r--r--src/organizercore.cpp24
-rw-r--r--src/organizercore.h3
-rw-r--r--src/pluginlist.cpp4
-rw-r--r--src/resources.qrc7
-rw-r--r--src/resources/conflict-mixed-blue.pngbin0 -> 692 bytes
-rw-r--r--src/resources/conflict-overwrite-blue.pngbin0 -> 964 bytes
-rw-r--r--src/resources/conflict-overwritten-blue.pngbin0 -> 577 bytes
-rw-r--r--src/resources/red-archive-conflict-loser.pngbin0 -> 746 bytes
-rw-r--r--src/settings.cpp26
-rw-r--r--src/settings.h12
-rw-r--r--src/settingsdialog.cpp26
-rw-r--r--src/settingsdialog.h10
-rw-r--r--src/settingsdialog.ui81
-rw-r--r--src/shared/directoryentry.cpp141
-rw-r--r--src/shared/directoryentry.h26
-rw-r--r--src/syncoverwritedialog.cpp4
-rw-r--r--src/version.rc4
32 files changed, 723 insertions, 157 deletions
diff --git a/src/directoryrefresher.cpp b/src/directoryrefresher.cpp
index cc745cc8..c48b77d9 100644
--- a/src/directoryrefresher.cpp
+++ b/src/directoryrefresher.cpp
@@ -27,6 +27,8 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <QApplication>
#include <QDir>
#include <QString>
+#include <QTextCodec>
+#include <gameplugins.h>
using namespace MOBase;
@@ -83,13 +85,29 @@ void DirectoryRefresher::addModBSAToStructure(DirectoryEntry *directoryStructure
int priority, const QString &directory, const QStringList &archives)
{
std::wstring directoryW = ToWString(QDir::toNativeSeparators(directory));
+ IPluginGame *game = qApp->property("managed_game").value<IPluginGame*>();
+
+ GamePlugins *gamePlugins = game->feature<GamePlugins>();
+ QStringList loadOrder = QStringList();
+ gamePlugins->getLoadOrder(loadOrder);
for (const QString &archive : archives) {
QFileInfo fileInfo(archive);
if (m_EnabledArchives.find(fileInfo.fileName()) != m_EnabledArchives.end()) {
+
+ int order = -1;
+
+ for (auto plugin : loadOrder)
+ {
+ QString name = plugin.left(plugin.size() - 4);
+ if (fileInfo.fileName().startsWith(name + " - ", Qt::CaseInsensitive) || fileInfo.fileName().startsWith(name + ".", Qt::CaseInsensitive)) {
+ order = loadOrder.indexOf(plugin);
+ }
+ }
+
try {
- //directoryStructure->addFromBSA(ToWString(modName), directoryW,
- // ToWString(QDir::toNativeSeparators(fileInfo.absoluteFilePath())), priority);
+ IPluginGame *game = qApp->property("managed_game").value<IPluginGame*>();
+ directoryStructure->addFromBSA(ToWString(modName), directoryW, ToWString(QDir::toNativeSeparators(fileInfo.absoluteFilePath())), priority, order);
} catch (const std::exception &e) {
throw MyException(tr("failed to parse bsa %1: %2").arg(archive, e.what()));
}
@@ -115,7 +133,7 @@ void DirectoryRefresher::addModFilesToStructure(DirectoryEntry *directoryStructu
file->removeOrigin(0);
}
origin.addFile(file->getIndex());
- file->addOrigin(origin.getID(), file->getFileTime(), L"");
+ file->addOrigin(origin.getID(), file->getFileTime(), L"", -1);
} else {
qWarning("%s not found", qUtf8Printable(fileInfo.fileName()));
}
@@ -162,6 +180,8 @@ void DirectoryRefresher::refresh()
emit progress((i * 100) / static_cast<int>(m_Mods.size()) + 1);
}
+ m_DirectoryStructure->getFileRegister()->sortOrigins();
+
emit progress(100);
cleanStructure(m_DirectoryStructure);
diff --git a/src/directoryrefresher.h b/src/directoryrefresher.h
index 53709e1e..a4fc5dbc 100644
--- a/src/directoryrefresher.h
+++ b/src/directoryrefresher.h
@@ -27,6 +27,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <vector>
#include <set>
#include <tuple>
+#include "profile.h"
/**
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index ae37968c..f2ad0470 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -317,7 +317,7 @@ MainWindow::MainWindow(QSettings &initSettings
ui->espList->setItemDelegateForColumn(PluginList::COL_FLAGS, new GenericIconDelegate(ui->espList));
ui->espList->installEventFilter(m_OrganizerCore.pluginList());
- //ui->bsaList->setLocalMoveOnly(true);
+ ui->bsaList->setLocalMoveOnly(true);
initDownloadView();
bool pluginListAdjusted = registerWidgetState(ui->espList->objectName(), ui->espList->header(), "plugin_list_state");
@@ -452,6 +452,19 @@ MainWindow::MainWindow(QSettings &initSettings
ui->profileBox->setCurrentText(m_OrganizerCore.currentProfile()->name());
+ if (m_OrganizerCore.getArchiveParsing())
+ {
+ ui->showArchiveDataCheckBox->setCheckState(Qt::Checked);
+ ui->showArchiveDataCheckBox->setEnabled(true);
+ m_showArchiveData = true;
+ }
+ else
+ {
+ ui->showArchiveDataCheckBox->setCheckState(Qt::Unchecked);
+ ui->showArchiveDataCheckBox->setEnabled(false);
+ m_showArchiveData = false;
+ }
+
refreshExecutablesList();
updateToolBar();
@@ -1262,7 +1275,7 @@ void MainWindow::updateTo(QTreeWidgetItem *subTree, const std::wstring &director
directoryChild->setData(0, Qt::DecorationRole, *folderIcon);
directoryChild->setData(0, Qt::UserRole + 3, isDirectory);
- if (conflictsOnly) {
+ if (conflictsOnly || !m_showArchiveData) {
updateTo(directoryChild, temp.str(), **current, conflictsOnly, fileIcon, folderIcon);
if (directoryChild->childCount() != 0) {
subTree->addChild(directoryChild);
@@ -1297,10 +1310,14 @@ void MainWindow::updateTo(QTreeWidgetItem *subTree, const std::wstring &director
continue;
}
- QString fileName = ToQString(current->getName());
- QStringList columns(fileName);
bool isArchive = false;
int originID = current->getOrigin(isArchive);
+ if (!m_showArchiveData && isArchive) {
+ continue;
+ }
+
+ QString fileName = ToQString(current->getName());
+ QStringList columns(fileName);
FilesOrigin origin = m_OrganizerCore.directoryStructure()->getOriginByID(originID);
QString source("data");
unsigned int modIndex = ModInfo::getIndex(ToQString(origin.getName()));
@@ -1309,9 +1326,9 @@ void MainWindow::updateTo(QTreeWidgetItem *subTree, const std::wstring &director
source = modInfo->name();
}
- std::wstring archive = current->getArchive();
- if (archive.length() != 0) {
- source.append(" (").append(ToQString(archive)).append(")");
+ std::pair<std::wstring, int> archive = current->getArchive();
+ if (archive.first.length() != 0) {
+ source.append(" (").append(ToQString(archive.first)).append(")");
}
columns.append(source);
QTreeWidgetItem *fileChild = new QTreeWidgetItem(columns);
@@ -1333,12 +1350,12 @@ void MainWindow::updateTo(QTreeWidgetItem *subTree, const std::wstring &director
fileChild->setData(1, Qt::UserRole, source);
fileChild->setData(1, Qt::UserRole + 1, originID);
- std::vector<std::pair<int, std::wstring>> alternatives = current->getAlternatives();
+ std::vector<std::pair<int, std::pair<std::wstring, int>>> alternatives = current->getAlternatives();
if (!alternatives.empty()) {
std::wostringstream altString;
altString << ToWString(tr("Also in: <br>"));
- for (std::vector<std::pair<int, std::wstring>>::iterator altIter = alternatives.begin();
+ for (std::vector<std::pair<int, std::pair<std::wstring, int>>>::iterator altIter = alternatives.begin();
altIter != alternatives.end(); ++altIter) {
if (altIter != alternatives.begin()) {
altString << " , ";
@@ -2252,9 +2269,23 @@ void MainWindow::modorder_changed()
for (int i : modInfo->getModOverwritten()) {
ModInfo::getByIndex(i)->clearCaches();
}
+ for (int i : modInfo->getModArchiveOverwrite()) {
+ ModInfo::getByIndex(i)->clearCaches();
+ }
+ for (int i : modInfo->getModArchiveOverwritten()) {
+ ModInfo::getByIndex(i)->clearCaches();
+ }
+ for (int i : modInfo->getModArchiveLooseOverwrite()) {
+ ModInfo::getByIndex(i)->clearCaches();
+ }
+ for (int i : modInfo->getModArchiveLooseOverwritten()) {
+ ModInfo::getByIndex(i)->clearCaches();
+ }
// update conflict check on the moved mod
modInfo->doConflictCheck();
m_OrganizerCore.modList()->setOverwriteMarkers(modInfo->getModOverwrite(), modInfo->getModOverwritten());
+ m_OrganizerCore.modList()->setArchiveOverwriteMarkers(modInfo->getModArchiveOverwrite(), modInfo->getModArchiveOverwritten());
+ m_OrganizerCore.modList()->setArchiveLooseOverwriteMarkers(modInfo->getModArchiveLooseOverwrite(), modInfo->getModArchiveLooseOverwritten());
if (m_ModListSortProxy != nullptr) {
m_ModListSortProxy->invalidate();
}
@@ -2328,10 +2359,10 @@ void MainWindow::fileMoved(const QString &filePath, const QString &oldOriginName
QString fullNewPath = ToQString(newOrigin.getPath()) + "\\" + filePath;
WIN32_FIND_DATAW findData;
- HANDLE hFind;
- hFind = ::FindFirstFileW(ToWString(fullNewPath).c_str(), &findData);
- filePtr->addOrigin(newOrigin.getID(), findData.ftCreationTime, L"");
- FindClose(hFind);
+ HANDLE hFind;
+ hFind = ::FindFirstFileW(ToWString(fullNewPath).c_str(), &findData);
+ filePtr->addOrigin(newOrigin.getID(), findData.ftCreationTime, L"", -1);
+ FindClose(hFind);
}
if (m_OrganizerCore.directoryStructure()->originExists(ToWString(oldOriginName))) {
FilesOrigin &oldOrigin = m_OrganizerCore.directoryStructure()->getOriginByName(ToWString(oldOriginName));
@@ -2491,8 +2522,12 @@ void MainWindow::modlistSelectionChanged(const QModelIndex &current, const QMode
if (current.isValid()) {
ModInfo::Ptr selectedMod = ModInfo::getByIndex(current.data(Qt::UserRole + 1).toInt());
m_OrganizerCore.modList()->setOverwriteMarkers(selectedMod->getModOverwrite(), selectedMod->getModOverwritten());
+ m_OrganizerCore.modList()->setArchiveOverwriteMarkers(selectedMod->getModArchiveOverwrite(), selectedMod->getModArchiveOverwritten());
+ m_OrganizerCore.modList()->setArchiveLooseOverwriteMarkers(selectedMod->getModArchiveLooseOverwrite(), selectedMod->getModArchiveLooseOverwritten());
} else {
m_OrganizerCore.modList()->setOverwriteMarkers(std::set<unsigned int>(), std::set<unsigned int>());
+ m_OrganizerCore.modList()->setArchiveOverwriteMarkers(std::set<unsigned int>(), std::set<unsigned int>());
+ m_OrganizerCore.modList()->setArchiveLooseOverwriteMarkers(std::set<unsigned int>(), std::set<unsigned int>());
}
/* if ((m_ModListSortProxy != nullptr)
&& !m_ModListSortProxy->beingInvalidated()) {
@@ -2785,7 +2820,7 @@ void MainWindow::displayModInformation(ModInfo::Ptr modInfo, unsigned int index,
}
} else {
modInfo->saveMeta();
- ModInfoDialog dialog(modInfo, m_OrganizerCore.directoryStructure(), modInfo->hasFlag(ModInfo::FLAG_FOREIGN), &m_OrganizerCore, &m_PluginContainer,this);
+ ModInfoDialog dialog(modInfo, m_OrganizerCore.directoryStructure(), modInfo->hasFlag(ModInfo::FLAG_FOREIGN), &m_OrganizerCore, &m_PluginContainer, this);
connect(&dialog, SIGNAL(linkActivated(QString)), this, SLOT(linkClicked(QString)));
connect(&dialog, SIGNAL(downloadRequest(QString)), &m_OrganizerCore, SLOT(downloadRequestedNXM(QString)));
connect(&dialog, SIGNAL(modOpen(QString, int)), this, SLOT(displayModInformation(QString, int)), Qt::QueuedConnection);
@@ -2820,7 +2855,6 @@ void MainWindow::displayModInformation(ModInfo::Ptr modInfo, unsigned int index,
m_OrganizerCore.settings().directInterface().setValue("mod_info_tabs", dialog.saveTabState());
settings.setValue(key, dialog.saveGeometry());
-
modInfo->saveMeta();
emit modInfoDisplayed();
m_OrganizerCore.modList()->modInfoChanged(modInfo);
@@ -2842,6 +2876,7 @@ void MainWindow::displayModInformation(ModInfo::Ptr modInfo, unsigned int index,
, modInfo->stealFiles()
, modInfo->archives());
DirectoryRefresher::cleanStructure(m_OrganizerCore.directoryStructure());
+ m_OrganizerCore.directoryStructure()->getFileRegister()->sortOrigins();
m_OrganizerCore.refreshLists();
}
}
@@ -4658,6 +4693,27 @@ void MainWindow::on_actionSettings_triggered()
m_OrganizerCore.profileRefresh();
}
+ const auto state = settings.archiveParsing();
+ if (state != m_OrganizerCore.getArchiveParsing())
+ {
+ m_OrganizerCore.setArchiveParsing(state);
+ if (!state)
+ {
+ ui->showArchiveDataCheckBox->setCheckState(Qt::Unchecked);
+ ui->showArchiveDataCheckBox->setEnabled(false);
+ m_showArchiveData = false;
+ }
+ else
+ {
+ ui->showArchiveDataCheckBox->setCheckState(Qt::Checked);
+ ui->showArchiveDataCheckBox->setEnabled(true);
+ m_showArchiveData = true;
+ }
+ m_OrganizerCore.refreshModList();
+ m_OrganizerCore.refreshDirectoryStructure();
+ m_OrganizerCore.refreshLists();
+ }
+
if (settings.getCacheDirectory() != oldCacheDirectory) {
NexusInterface::instance(&m_PluginContainer)->setCacheDirectory(settings.getCacheDirectory());
}
@@ -6316,3 +6372,17 @@ void MainWindow::sendSelectedModsToSeparator_clicked()
}
settings.setValue(key, dialog.saveGeometry());
}
+
+void MainWindow::on_showArchiveDataCheckBox_toggled(const bool checked)
+{
+ if (m_OrganizerCore.getArchiveParsing() && checked)
+ {
+ m_showArchiveData = checked;
+ }
+ else
+ {
+ m_showArchiveData = false;
+ }
+ refreshDataTree();
+}
+
diff --git a/src/mainwindow.h b/src/mainwindow.h
index a419b797..8b774c93 100644
--- a/src/mainwindow.h
+++ b/src/mainwindow.h
@@ -376,6 +376,8 @@ private:
bool m_closing{ false };
+ bool m_showArchiveData{ true };
+
std::vector<std::pair<QString, QHeaderView*>> m_PersistedGeometry;
MOBase::DelayedFileWriter m_ArchiveListWriter;
@@ -610,6 +612,7 @@ private slots: // ui slots
void on_btnRefreshDownloads_clicked();
void on_categoriesList_customContextMenuRequested(const QPoint &pos);
void on_conflictsCheckBox_toggled(bool checked);
+ void on_showArchiveDataCheckBox_toggled(bool checked);
void on_dataTree_customContextMenuRequested(const QPoint &pos);
void on_executablesListBox_currentIndexChanged(int index);
void on_modList_customContextMenuRequested(const QPoint &pos);
diff --git a/src/mainwindow.ui b/src/mainwindow.ui
index 547faaa9..d287b8bb 100644
--- a/src/mainwindow.ui
+++ b/src/mainwindow.ui
@@ -860,6 +860,9 @@ p, li { white-space: pre-wrap; }
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QPushButton" name="bossButton">
+ <property name="visible">
+ <bool>true</bool>
+ </property>
<property name="text">
<string>Sort</string>
</property>
@@ -1103,7 +1106,7 @@ p, li { white-space: pre-wrap; }
</layout>
</item>
<item>
- <widget class="QTreeWidget" name="bsaList">
+ <widget class="MOBase::SortableTreeWidget" name="bsaList">
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
@@ -1119,19 +1122,19 @@ p, li { white-space: pre-wrap; }
<property name="showDropIndicator" stdset="0">
<bool>false</bool>
</property>
- <property name="dragEnabled">
+ <property name="dragEnabled" stdset="0">
<bool>false</bool>
</property>
- <property name="dragDropOverwriteMode">
+ <property name="dragDropOverwriteMode" stdset="0">
<bool>false</bool>
</property>
- <property name="indentation">
+ <property name="indentation" stdset="0">
<number>20</number>
</property>
- <property name="itemsExpandable">
+ <property name="itemsExpandable" stdset="0">
<bool>true</bool>
</property>
- <property name="columnCount">
+ <property name="columnCount" stdset="0">
<number>1</number>
</property>
<column>
@@ -1211,17 +1214,37 @@ p, li { white-space: pre-wrap; }
</layout>
</item>
<item>
- <widget class="QCheckBox" name="conflictsCheckBox">
- <property name="toolTip">
- <string>Filter the above list so that only conflicts are displayed.</string>
- </property>
- <property name="whatsThis">
- <string>Filter the above list so that only conflicts are displayed.</string>
- </property>
- <property name="text">
- <string>Show only conflicts</string>
- </property>
- </widget>
+ <layout class="QHBoxLayout" name="horizontalLayout_12">
+ <item>
+ <widget class="QCheckBox" name="conflictsCheckBox">
+ <property name="toolTip">
+ <string>Filters the above list so that only conflicts are displayed.</string>
+ </property>
+ <property name="whatsThis">
+ <string>Filters the above list so that only conflicts are displayed.</string>
+ </property>
+ <property name="text">
+ <string>Show only conflicts</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="showArchiveDataCheckBox">
+ <property name="toolTip">
+ <string>Filters the above list so that files from archives are not shown</string>
+ </property>
+ <property name="statusTip">
+ <string/>
+ </property>
+ <property name="whatsThis">
+ <string>Filters the above list so that files from archives are not shown</string>
+ </property>
+ <property name="text">
+ <string>Show files from Archives</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
</item>
</layout>
</widget>
diff --git a/src/modflagicondelegate.cpp b/src/modflagicondelegate.cpp
index 1995964b..eb6945c4 100644
--- a/src/modflagicondelegate.cpp
+++ b/src/modflagicondelegate.cpp
@@ -1,4 +1,4 @@
- #include "modflagicondelegate.h"
+#include "modflagicondelegate.h"
#include <QList>
@@ -7,6 +7,13 @@ ModInfo::EFlag ModFlagIconDelegate::m_ConflictFlags[4] = { ModInfo::FLAG_CONFLIC
, ModInfo::FLAG_CONFLICT_OVERWRITTEN
, ModInfo::FLAG_CONFLICT_REDUNDANT };
+ModInfo::EFlag ModFlagIconDelegate::m_ArchiveLooseConflictFlags[2] = { ModInfo::FLAG_ARCHIVE_LOOSE_CONFLICT_OVERWRITE
+ , ModInfo::FLAG_ARCHIVE_LOOSE_CONFLICT_OVERWRITTEN };
+
+ModInfo::EFlag ModFlagIconDelegate::m_ArchiveConflictFlags[3] = { ModInfo::FLAG_ARCHIVE_CONFLICT_MIXED
+ , ModInfo::FLAG_ARCHIVE_CONFLICT_OVERWRITE
+ , ModInfo::FLAG_ARCHIVE_CONFLICT_OVERWRITTEN };
+
ModFlagIconDelegate::ModFlagIconDelegate(QObject *parent)
: IconDelegate(parent)
{
@@ -19,7 +26,8 @@ QList<QString> ModFlagIconDelegate::getIcons(const QModelIndex &index) const {
ModInfo::Ptr info = ModInfo::getByIndex(modid.toInt());
std::vector<ModInfo::EFlag> flags = info->getFlags();
- { // insert conflict icon first to provide nicer alignment
+ // insert conflict icons to provide nicer alignment
+ { // insert loose file conflicts first
auto iter = std::find_first_of(flags.begin(), flags.end(),
m_ConflictFlags, m_ConflictFlags + 4);
if (iter != flags.end()) {
@@ -30,6 +38,41 @@ QList<QString> ModFlagIconDelegate::getIcons(const QModelIndex &index) const {
}
}
+ { // insert loose vs archive overwrite second
+ auto iter = std::find(flags.begin(), flags.end(),
+ ModInfo::FLAG_ARCHIVE_LOOSE_CONFLICT_OVERWRITE);
+ if (iter != flags.end()) {
+ result.append(getFlagIcon(*iter));
+ flags.erase(iter);
+ }
+ else {
+ result.append(QString());
+ }
+ }
+
+ { // insert loose vs archive overwritten third
+ auto iter = std::find_first_of(flags.begin(), flags.end(),
+ m_ArchiveLooseConflictFlags + 1, m_ArchiveLooseConflictFlags + 2);
+ if (iter != flags.end()) {
+ result.append(getFlagIcon(*iter));
+ flags.erase(iter);
+ }
+ else {
+ result.append(QString());
+ }
+ }
+
+ { // insert archive conflicts last
+ auto iter = std::find_first_of(flags.begin(), flags.end(),
+ m_ArchiveConflictFlags, m_ArchiveConflictFlags + 3);
+ if (iter != flags.end()) {
+ result.append(getFlagIcon(*iter));
+ flags.erase(iter);
+ } else {
+ result.append(QString());
+ }
+ }
+
for (auto iter = flags.begin(); iter != flags.end(); ++iter) {
result.append(getFlagIcon(*iter));
}
@@ -44,10 +87,15 @@ QString ModFlagIconDelegate::getFlagIcon(ModInfo::EFlag flag) const
case ModInfo::FLAG_INVALID: return ":/MO/gui/problem";
case ModInfo::FLAG_NOTENDORSED: return ":/MO/gui/emblem_notendorsed";
case ModInfo::FLAG_NOTES: return ":/MO/gui/emblem_notes";
+ case ModInfo::FLAG_CONFLICT_MIXED: return ":/MO/gui/emblem_conflict_mixed";
case ModInfo::FLAG_CONFLICT_OVERWRITE: return ":/MO/gui/emblem_conflict_overwrite";
case ModInfo::FLAG_CONFLICT_OVERWRITTEN: return ":/MO/gui/emblem_conflict_overwritten";
- case ModInfo::FLAG_CONFLICT_MIXED: return ":/MO/gui/emblem_conflict_mixed";
case ModInfo::FLAG_CONFLICT_REDUNDANT: return ":MO/gui/emblem_conflict_redundant";
+ case ModInfo::FLAG_ARCHIVE_LOOSE_CONFLICT_OVERWRITE: return ":/MO/gui/archive_loose_conflict_overwrite";
+ case ModInfo::FLAG_ARCHIVE_LOOSE_CONFLICT_OVERWRITTEN: return ":/MO/gui/archive_loose_conflict_overwritten";
+ case ModInfo::FLAG_ARCHIVE_CONFLICT_MIXED: return ":/MO/gui/archive_conflict_mixed";
+ case ModInfo::FLAG_ARCHIVE_CONFLICT_OVERWRITE: return ":/MO/gui/archive_conflict_winner";
+ case ModInfo::FLAG_ARCHIVE_CONFLICT_OVERWRITTEN: return ":/MO/gui/archive_conflict_loser";
case ModInfo::FLAG_ALTERNATE_GAME: return ":MO/gui/alternate_game";
default: return QString();
}
diff --git a/src/modflagicondelegate.h b/src/modflagicondelegate.h
index b31a250b..0f257c7d 100644
--- a/src/modflagicondelegate.h
+++ b/src/modflagicondelegate.h
@@ -15,6 +15,8 @@ private:
QString getFlagIcon(ModInfo::EFlag flag) const;
private:
static ModInfo::EFlag m_ConflictFlags[4];
+ static ModInfo::EFlag m_ArchiveLooseConflictFlags[2];
+ static ModInfo::EFlag m_ArchiveConflictFlags[3];
};
#endif // MODFLAGICONDELEGATE_H
diff --git a/src/modinfo.h b/src/modinfo.h
index 5a69185d..350b3a0d 100644
--- a/src/modinfo.h
+++ b/src/modinfo.h
@@ -72,6 +72,11 @@ public:
FLAG_CONFLICT_OVERWRITTEN,
FLAG_CONFLICT_MIXED,
FLAG_CONFLICT_REDUNDANT,
+ FLAG_ARCHIVE_LOOSE_CONFLICT_OVERWRITE,
+ FLAG_ARCHIVE_LOOSE_CONFLICT_OVERWRITTEN,
+ FLAG_ARCHIVE_CONFLICT_OVERWRITE,
+ FLAG_ARCHIVE_CONFLICT_OVERWRITTEN,
+ FLAG_ARCHIVE_CONFLICT_MIXED,
FLAG_PLUGIN_SELECTED,
FLAG_ALTERNATE_GAME
};
@@ -90,7 +95,6 @@ public:
CONTENT_MCM,
CONTENT_INI,
CONTENT_MODGROUP
-
};
static const int NUM_CONTENT_TYPES = CONTENT_MODGROUP + 1;
@@ -626,6 +630,26 @@ public:
virtual std::set<unsigned int> getModOverwritten() { return std::set<unsigned int>(); }
/**
+ * @return retrieve list of mods (as mod index) with archives that are overwritten by this one. Updates may be delayed
+ */
+ virtual std::set<unsigned int> getModArchiveOverwrite() { return std::set<unsigned int>(); }
+
+ /**
+ * @return list of mods (as mod index) with archives that overwrite this one. Updates may be delayed
+ */
+ virtual std::set<unsigned int> getModArchiveOverwritten() { return std::set<unsigned int>(); }
+
+ /**
+ * @return retrieve list of mods (as mod index) with archives that are overwritten by thos mod's loose files. Updates may be delayed
+ */
+ virtual std::set<unsigned int> getModArchiveLooseOverwrite() { return std::set<unsigned int>(); }
+
+ /**
+ * @return list of mods (as mod index) with loose files that overwrite this one's archive files. Updates may be delayed
+ */
+ virtual std::set<unsigned int> getModArchiveLooseOverwritten() { return std::set<unsigned int>(); }
+
+ /**
* @brief update conflict information
*/
virtual void doConflictCheck() const {}
diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp
index c8e0fb9e..02ace33f 100644
--- a/src/modinfodialog.cpp
+++ b/src/modinfodialog.cpp
@@ -314,10 +314,10 @@ void ModInfoDialog::refreshLists()
QString fileName = relativeName.mid(0).prepend(m_RootPath);
bool archive;
if ((*iter)->getOrigin(archive) == m_Origin->getID()) {
- std::vector<std::pair<int, std::wstring>> alternatives = (*iter)->getAlternatives();
+ std::vector<std::pair<int, std::pair<std::wstring, int>>> alternatives = (*iter)->getAlternatives();
if (!alternatives.empty()) {
std::wostringstream altString;
- for (std::vector<std::pair<int, std::wstring>>::iterator altIter = alternatives.begin();
+ for (std::vector<std::pair<int, std::pair<std::wstring, int>>>::iterator altIter = alternatives.begin();
altIter != alternatives.end(); ++altIter) {
if (altIter != alternatives.begin()) {
altString << ", ";
@@ -326,11 +326,18 @@ void ModInfoDialog::refreshLists()
}
QStringList fields(relativeName.prepend("..."));
fields.append(ToQString(altString.str()));
- QTreeWidgetItem *item = new QTreeWidgetItem(fields);
+
+ QTreeWidgetItem *item = new QTreeWidgetItem(fields);
item->setData(0, Qt::UserRole, fileName);
- item->setData(1, Qt::UserRole, ToQString(m_Directory->getOriginByID(alternatives.begin()->first).getName()));
- item->setData(1, Qt::UserRole + 1, alternatives.begin()->first);
+ item->setData(1, Qt::UserRole, ToQString(m_Directory->getOriginByID(alternatives.back().first).getName()));
+ item->setData(1, Qt::UserRole + 1, alternatives.back().first);
item->setData(1, Qt::UserRole + 2, archive);
+ if (archive) {
+ QFont font = item->font(0);
+ font.setItalic(true);
+ item->setFont(0, font);
+ item->setFont(1, font);
+ }
ui->overwriteTree->addTopLevelItem(item);
++numOverwrite;
} else {// otherwise don't display the file
@@ -344,6 +351,12 @@ void ModInfoDialog::refreshLists()
item->setData(0, Qt::UserRole, fileName);
item->setData(1, Qt::UserRole, ToQString(realOrigin.getName()));
item->setData(1, Qt::UserRole + 2, archive);
+ if (archive) {
+ QFont font = item->font(0);
+ font.setItalic(true);
+ item->setFont(0, font);
+ item->setFont(1, font);
+ }
ui->overwrittenTree->addTopLevelItem(item);
++numOverwritten;
}
diff --git a/src/modinfodialog.h b/src/modinfodialog.h
index f3bc48b4..52768649 100644
--- a/src/modinfodialog.h
+++ b/src/modinfodialog.h
@@ -78,7 +78,8 @@ public:
* @param modInfo info structure about the mod to display
* @param parent parend widget
**/
- explicit ModInfoDialog(ModInfo::Ptr modInfo, const MOShared::DirectoryEntry *directory, bool unmanaged, OrganizerCore *organizerCore, PluginContainer *pluginContainer,QWidget *parent = 0);
+ explicit ModInfoDialog(ModInfo::Ptr modInfo, const MOShared::DirectoryEntry *directory, bool unmanaged, OrganizerCore *organizerCore, PluginContainer *pluginContainer, QWidget *parent = 0);
+
~ModInfoDialog();
/**
diff --git a/src/modinfowithconflictinfo.cpp b/src/modinfowithconflictinfo.cpp
index e88ba29d..12cbf416 100644
--- a/src/modinfowithconflictinfo.cpp
+++ b/src/modinfowithconflictinfo.cpp
@@ -7,7 +7,7 @@ using namespace MOBase;
using namespace MOShared;
ModInfoWithConflictInfo::ModInfoWithConflictInfo(PluginContainer *pluginContainer, DirectoryEntry **directoryStructure)
- : ModInfo(pluginContainer), m_DirectoryStructure(directoryStructure) {}
+ : ModInfo(pluginContainer), m_DirectoryStructure(directoryStructure), m_HasLooseOverwrite(false) {}
void ModInfoWithConflictInfo::clearCaches()
{
@@ -32,6 +32,31 @@ std::vector<ModInfo::EFlag> ModInfoWithConflictInfo::getFlags() const
} break;
default: { /* NOP */ }
}
+ switch (isLooseArchiveConflicted()) {
+ case CONFLICT_MIXED: {
+ result.push_back(ModInfo::FLAG_ARCHIVE_LOOSE_CONFLICT_OVERWRITE);
+ result.push_back(ModInfo::FLAG_ARCHIVE_LOOSE_CONFLICT_OVERWRITTEN);
+ } break;
+ case CONFLICT_OVERWRITE: {
+ result.push_back(ModInfo::FLAG_ARCHIVE_LOOSE_CONFLICT_OVERWRITE);
+ } break;
+ case CONFLICT_OVERWRITTEN: {
+ result.push_back(ModInfo::FLAG_ARCHIVE_LOOSE_CONFLICT_OVERWRITTEN);
+ } break;
+ default: { /* NOP */ }
+ }
+ switch (isArchiveConflicted()) {
+ case CONFLICT_MIXED: {
+ result.push_back(ModInfo::FLAG_ARCHIVE_CONFLICT_MIXED);
+ } break;
+ case CONFLICT_OVERWRITE: {
+ result.push_back(ModInfo::FLAG_ARCHIVE_CONFLICT_OVERWRITE);
+ } break;
+ case CONFLICT_OVERWRITTEN: {
+ result.push_back(ModInfo::FLAG_ARCHIVE_CONFLICT_OVERWRITTEN);
+ } break;
+ default: { /* NOP */ }
+ }
return result;
}
@@ -40,6 +65,10 @@ void ModInfoWithConflictInfo::doConflictCheck() const
{
m_OverwriteList.clear();
m_OverwrittenList.clear();
+ m_ArchiveOverwriteList.clear();
+ m_ArchiveOverwrittenList.clear();
+ m_ArchiveLooseOverwriteList.clear();
+ m_ArchiveLooseOverwrittenList.clear();
bool providesAnything = false;
@@ -51,34 +80,73 @@ void ModInfoWithConflictInfo::doConflictCheck() const
std::wstring name = ToWString(this->name());
m_CurrentConflictState = CONFLICT_NONE;
+ m_ArchiveConflictState = CONFLICT_NONE;
+ m_ArchiveConflictLooseState = CONFLICT_NONE;
if ((*m_DirectoryStructure)->originExists(name)) {
FilesOrigin &origin = (*m_DirectoryStructure)->getOriginByName(name);
std::vector<FileEntry::Ptr> files = origin.getFiles();
// for all files in this origin
for (FileEntry::Ptr file : files) {
- const std::vector<std::pair<int, std::wstring>> &alternatives = file->getAlternatives();
- if ((alternatives.size() == 0) || (alternatives.begin()->first == dataID)) {
+ auto alternatives = file->getAlternatives();
+ if ((alternatives.size() == 0) || (alternatives.back().first == dataID)) {
// no alternatives -> no conflict
providesAnything = true;
} else {
+ // Get the archive data for the current mod
+ bool found = file->getOrigin() == origin.getID();
+ std::pair<std::wstring, int> archiveData;
+ if (found)
+ archiveData = file->getArchive();
+ else {
+ for (auto alts : alternatives) {
+ if (alts.first == origin.getID()) {
+ archiveData = alts.second;
+ break;
+ }
+ }
+ }
+
+ // If this is not the origin then determine the correct overwrite
if (file->getOrigin() != origin.getID()) {
FilesOrigin &altOrigin = (*m_DirectoryStructure)->getOriginByID(file->getOrigin());
unsigned int altIndex = ModInfo::getIndex(ToQString(altOrigin.getName()));
- m_OverwrittenList.insert(altIndex);
+ if (file->getArchive().first.size() == 0)
+ if (archiveData.first.size() == 0)
+ m_OverwrittenList.insert(altIndex);
+ else
+ m_ArchiveLooseOverwrittenList.insert(altIndex);
+ else
+ m_ArchiveOverwrittenList.insert(altIndex);
} else {
providesAnything = true;
}
- // for all non-providing alternative origins
+ // Sort out the alternatives
for (auto altInfo : alternatives) {
if ((altInfo.first != dataID) && (altInfo.first != origin.getID())) {
FilesOrigin &altOrigin = (*m_DirectoryStructure)->getOriginByID(altInfo.first);
unsigned int altIndex = ModInfo::getIndex(ToQString(altOrigin.getName()));
- if (origin.getPriority() > altOrigin.getPriority()) {
- m_OverwriteList.insert(altIndex);
+ if (altInfo.second.first.size() == 0) {
+ if (archiveData.first.size() == 0) {
+ if (origin.getPriority() > altOrigin.getPriority()) {
+ m_OverwriteList.insert(altIndex);
+ } else {
+ m_OverwrittenList.insert(altIndex);
+ }
+ } else {
+ m_ArchiveLooseOverwrittenList.insert(altIndex);
+ }
} else {
- m_OverwrittenList.insert(altIndex);
+ if (archiveData.first.size() == 0) {
+ m_ArchiveLooseOverwriteList.insert(altIndex);
+ } else {
+ if (archiveData.second > altInfo.second.second) {
+ m_ArchiveOverwriteList.insert(altIndex);
+ } else if (archiveData.second < altInfo.second.second) {
+ m_ArchiveOverwrittenList.insert(altIndex);
+ }
+ }
}
}
}
@@ -95,6 +163,20 @@ void ModInfoWithConflictInfo::doConflictCheck() const
m_CurrentConflictState = CONFLICT_OVERWRITE;
else if (!m_OverwrittenList.empty())
m_CurrentConflictState = CONFLICT_OVERWRITTEN;
+
+ if (!m_ArchiveOverwriteList.empty() && !m_ArchiveOverwrittenList.empty())
+ m_ArchiveConflictState = CONFLICT_MIXED;
+ else if (!m_ArchiveOverwriteList.empty())
+ m_ArchiveConflictState = CONFLICT_OVERWRITE;
+ else if (!m_ArchiveOverwrittenList.empty())
+ m_ArchiveConflictState = CONFLICT_OVERWRITTEN;
+
+ if (!m_ArchiveLooseOverwrittenList.empty() && !m_ArchiveLooseOverwriteList.empty())
+ m_ArchiveConflictLooseState = CONFLICT_MIXED;
+ else if (!m_ArchiveLooseOverwrittenList.empty())
+ m_ArchiveConflictLooseState = CONFLICT_OVERWRITTEN;
+ else if (!m_ArchiveLooseOverwriteList.empty())
+ m_ArchiveConflictLooseState = CONFLICT_OVERWRITE;
}
}
}
@@ -110,6 +192,26 @@ ModInfoWithConflictInfo::EConflictType ModInfoWithConflictInfo::isConflicted() c
return m_CurrentConflictState;
}
+ModInfoWithConflictInfo::EConflictType ModInfoWithConflictInfo::isArchiveConflicted() const
+{
+ QTime now = QTime::currentTime();
+ if (m_LastConflictCheck.isNull() || (m_LastConflictCheck.secsTo(now) > 10)) {
+ doConflictCheck();
+ }
+
+ return m_ArchiveConflictState;
+}
+
+ModInfoWithConflictInfo::EConflictType ModInfoWithConflictInfo::isLooseArchiveConflicted() const
+{
+ QTime now = QTime::currentTime();
+ if (m_LastConflictCheck.isNull() || (m_LastConflictCheck.secsTo(now) > 10)) {
+ doConflictCheck();
+ }
+
+ return m_ArchiveConflictLooseState;
+}
+
bool ModInfoWithConflictInfo::isRedundant() const
{
diff --git a/src/modinfowithconflictinfo.h b/src/modinfowithconflictinfo.h
index 6c1591c5..13d87d25 100644
--- a/src/modinfowithconflictinfo.h
+++ b/src/modinfowithconflictinfo.h
@@ -23,6 +23,14 @@ public:
virtual std::set<unsigned int> getModOverwritten() { return m_OverwrittenList; }
+ virtual std::set<unsigned int> getModArchiveOverwrite() { return m_ArchiveOverwriteList; }
+
+ virtual std::set<unsigned int> getModArchiveOverwritten() { return m_ArchiveOverwrittenList; }
+
+ virtual std::set<unsigned int> getModArchiveLooseOverwrite() { return m_ArchiveLooseOverwriteList; }
+
+ virtual std::set<unsigned int> getModArchiveLooseOverwritten() { return m_ArchiveLooseOverwrittenList; }
+
virtual void doConflictCheck() const;
private:
@@ -32,7 +40,8 @@ private:
CONFLICT_OVERWRITE,
CONFLICT_OVERWRITTEN,
CONFLICT_MIXED,
- CONFLICT_REDUNDANT
+ CONFLICT_REDUNDANT,
+ CONFLICT_CROSS
};
private:
@@ -43,6 +52,16 @@ private:
EConflictType isConflicted() const;
/**
+ * @return true if there are archive conflicts for files in this mod
+ */
+ EConflictType isArchiveConflicted() const;
+
+ /**
+ * @return true if there are archive conflicts with loose files in this mod
+ */
+ EConflictType isLooseArchiveConflicted() const;
+
+ /**
* @return true if this mod is completely replaced by others
*/
bool isRedundant() const;
@@ -52,10 +71,17 @@ private:
MOShared::DirectoryEntry **m_DirectoryStructure;
mutable EConflictType m_CurrentConflictState;
+ mutable EConflictType m_ArchiveConflictState;
+ mutable EConflictType m_ArchiveConflictLooseState;
+ mutable bool m_HasLooseOverwrite;
mutable QTime m_LastConflictCheck;
mutable std::set<unsigned int> m_OverwriteList; // indices of mods overritten by this mod
mutable std::set<unsigned int> m_OverwrittenList; // indices of mods overwriting this mod
+ mutable std::set<unsigned int> m_ArchiveOverwriteList; // indices of mods with archive files overritten by this mod
+ mutable std::set<unsigned int> m_ArchiveOverwrittenList; // indices of mods with archive files overwriting this mod
+ mutable std::set<unsigned int> m_ArchiveLooseOverwriteList; // indices of mods with archives being overwritten by this mod's loose files
+ mutable std::set<unsigned int> m_ArchiveLooseOverwrittenList; // indices of mods with loose files overwriting this mod's archive files
};
diff --git a/src/modlist.cpp b/src/modlist.cpp
index c1954b39..8f6bd47e 100644
--- a/src/modlist.cpp
+++ b/src/modlist.cpp
@@ -158,10 +158,15 @@ QString ModList::getFlagText(ModInfo::EFlag flag, ModInfo::Ptr modInfo) const
output << QString("<i>%1</i>").arg(modInfo->notes()).replace("\n", "<br>");
return output.join("<br>");
}
- case ModInfo::FLAG_CONFLICT_OVERWRITE: return tr("Overwrites files");
- case ModInfo::FLAG_CONFLICT_OVERWRITTEN: return tr("Overwritten files");
- case ModInfo::FLAG_CONFLICT_MIXED: return tr("Overwrites & Overwritten");
+ case ModInfo::FLAG_CONFLICT_OVERWRITE: return tr("Overwrites loose files");
+ case ModInfo::FLAG_CONFLICT_OVERWRITTEN: return tr("Overwritten loose files");
+ case ModInfo::FLAG_CONFLICT_MIXED: return tr("Loose files Overwrites & Overwritten");
case ModInfo::FLAG_CONFLICT_REDUNDANT: return tr("Redundant");
+ case ModInfo::FLAG_ARCHIVE_LOOSE_CONFLICT_OVERWRITE: return tr("Overwrites an archive with loose files");
+ case ModInfo::FLAG_ARCHIVE_LOOSE_CONFLICT_OVERWRITTEN: return tr("Archive is overwritten by loose files");
+ case ModInfo::FLAG_ARCHIVE_CONFLICT_OVERWRITE: return tr("Overwrites another archive file");
+ case ModInfo::FLAG_ARCHIVE_CONFLICT_OVERWRITTEN: return tr("Overwritten by another archive file");
+ case ModInfo::FLAG_ARCHIVE_CONFLICT_MIXED: return tr("Archive files overwrites & overwritten");
case ModInfo::FLAG_ALTERNATE_GAME: return tr("This mod targets a different game");
default: return "";
}
@@ -399,13 +404,22 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const
return QVariant();
} else if ((role == Qt::BackgroundRole)
|| (role == ViewMarkingScrollBar::DEFAULT_ROLE)) {
+ bool overwrite = m_Overwrite.find(modIndex) != m_Overwrite.end();
+ bool archiveOverwrite = m_ArchiveOverwrite.find(modIndex) != m_ArchiveOverwrite.end();
+ bool archiveLooseOverwrite = m_ArchiveLooseOverwrite.find(modIndex) != m_ArchiveLooseOverwrite.end();
+ bool overwritten = m_Overwritten.find(modIndex) != m_Overwritten.end();
+ bool archiveOverwritten = m_ArchiveOverwritten.find(modIndex) != m_ArchiveOverwritten.end();
+ bool archiveLooseOverwritten = m_ArchiveLooseOverwritten.find(modIndex) != m_ArchiveLooseOverwritten.end();
if (modInfo->getHighlight() & ModInfo::HIGHLIGHT_PLUGIN) {
return Settings::instance().modlistContainsPluginColor();
- } else if (m_Overwrite.find(modIndex) != m_Overwrite.end()) {
- return Settings::instance().modlistOverwrittenLooseColor();
- }
- else if (m_Overwritten.find(modIndex) != m_Overwritten.end()) {
+ } else if (overwritten || archiveLooseOverwritten) {
return Settings::instance().modlistOverwritingLooseColor();
+ } else if (overwrite || archiveLooseOverwrite) {
+ return Settings::instance().modlistOverwrittenLooseColor();
+ } else if (archiveOverwritten) {
+ return Settings::instance().modlistOverwritingArchiveColor();
+ } else if (archiveOverwrite) {
+ return Settings::instance().modlistOverwrittenArchiveColor();
} else if (modInfo->hasFlag(ModInfo::FLAG_SEPARATOR)
&& modInfo->getColor().isValid()
&& ((role != ViewMarkingScrollBar::DEFAULT_ROLE)
@@ -516,7 +530,6 @@ bool ModList::setData(const QModelIndex &index, const QVariant &value, int role)
ModInfo::Ptr info = ModInfo::getByIndex(modID);
IModList::ModStates oldState = state(modID);
-
bool result = false;
emit aboutToChangeData();
@@ -754,6 +767,20 @@ void ModList::setOverwriteMarkers(const std::set<unsigned int> &overwrite, const
notifyChange(0, rowCount() - 1);
}
+void ModList::setArchiveOverwriteMarkers(const std::set<unsigned int> &overwrite, const std::set<unsigned int> &overwritten)
+{
+ m_ArchiveOverwrite = overwrite;
+ m_ArchiveOverwritten = overwritten;
+ notifyChange(0, rowCount() - 1);
+}
+
+void ModList::setArchiveLooseOverwriteMarkers(const std::set<unsigned int> &overwrite, const std::set<unsigned int> &overwritten)
+{
+ m_ArchiveLooseOverwrite = overwrite;
+ m_ArchiveLooseOverwritten = overwritten;
+ notifyChange(0, rowCount() - 1);
+}
+
void ModList::setPluginContainer(PluginContainer *pluginContianer)
{
m_PluginContainer = pluginContianer;
@@ -810,10 +837,10 @@ void ModList::highlightMods(const QItemSelectionModel *selection, const MOShared
const MOShared::FileEntry::Ptr fileEntry = directoryEntry.findFile(modName.toStdWString());
if (fileEntry.get() != nullptr) {
bool archive = false;
- std::vector<std::pair<int, std::wstring>> origins;
+ std::vector<std::pair<int, std::pair<std::wstring, int>>> origins;
{
- std::vector<std::pair<int, std::wstring>> alternatives = fileEntry->getAlternatives();
- origins.insert(origins.end(), std::pair<int, std::wstring>(fileEntry->getOrigin(archive), fileEntry->getArchive()));
+ std::vector<std::pair<int, std::pair<std::wstring, int>>> alternatives = fileEntry->getAlternatives();
+ origins.insert(origins.end(), std::pair<int, std::pair<std::wstring, int>>(fileEntry->getOrigin(archive), fileEntry->getArchive()));
}
for (auto originInfo : origins) {
MOShared::FilesOrigin &origin = directoryEntry.getOriginByID(originInfo.first);
@@ -1121,6 +1148,10 @@ void ModList::notifyChange(int rowStart, int rowEnd)
if (rowStart < 0) {
m_Overwrite.clear();
m_Overwritten.clear();
+ m_ArchiveOverwrite.clear();
+ m_ArchiveOverwritten.clear();
+ m_ArchiveLooseOverwrite.clear();
+ m_ArchiveLooseOverwritten.clear();
beginResetModel();
endResetModel();
} else {
diff --git a/src/modlist.h b/src/modlist.h
index 42269386..49627d68 100644
--- a/src/modlist.h
+++ b/src/modlist.h
@@ -112,6 +112,9 @@ public:
void setOverwriteMarkers(const std::set<unsigned int> &overwrite, const std::set<unsigned int> &overwritten);
void setPluginContainer(PluginContainer *pluginContainer);
+ void setArchiveOverwriteMarkers(const std::set<unsigned int> &overwrite, const std::set<unsigned int> &overwritten);
+ void setArchiveLooseOverwriteMarkers(const std::set<unsigned int> &overwrite, const std::set<unsigned int> &overwritten);
+
bool modInfoAboutToChange(ModInfo::Ptr info);
void modInfoChanged(ModInfo::Ptr info);
@@ -331,6 +334,10 @@ private:
std::set<unsigned int> m_Overwrite;
std::set<unsigned int> m_Overwritten;
+ std::set<unsigned int> m_ArchiveOverwrite;
+ std::set<unsigned int> m_ArchiveOverwritten;
+ std::set<unsigned int> m_ArchiveLooseOverwrite;
+ std::set<unsigned int> m_ArchiveLooseOverwritten;
TModInfoChange m_ChangeInfo;
diff --git a/src/modlistsortproxy.cpp b/src/modlistsortproxy.cpp
index 1a00d764..5797d5af 100644
--- a/src/modlistsortproxy.cpp
+++ b/src/modlistsortproxy.cpp
@@ -254,9 +254,14 @@ bool ModListSortProxy::hasConflictFlag(const std::vector<ModInfo::EFlag> &flags)
{
for (ModInfo::EFlag flag : flags) {
if ((flag == ModInfo::FLAG_CONFLICT_MIXED) ||
- (flag == ModInfo::FLAG_CONFLICT_OVERWRITE) ||
- (flag == ModInfo::FLAG_CONFLICT_OVERWRITTEN) ||
- (flag == ModInfo::FLAG_CONFLICT_REDUNDANT)) {
+ (flag == ModInfo::FLAG_CONFLICT_OVERWRITE) ||
+ (flag == ModInfo::FLAG_CONFLICT_OVERWRITTEN) ||
+ (flag == ModInfo::FLAG_CONFLICT_REDUNDANT) ||
+ (flag == ModInfo::FLAG_ARCHIVE_CONFLICT_OVERWRITE) ||
+ (flag == ModInfo::FLAG_ARCHIVE_CONFLICT_OVERWRITTEN) ||
+ (flag == ModInfo::FLAG_ARCHIVE_CONFLICT_MIXED) ||
+ (flag == ModInfo::FLAG_ARCHIVE_LOOSE_CONFLICT_OVERWRITE) ||
+ (flag == ModInfo::FLAG_ARCHIVE_LOOSE_CONFLICT_OVERWRITTEN)) {
return true;
}
}
diff --git a/src/organizercore.cpp b/src/organizercore.cpp
index c4029a6d..4d5bf48b 100644
--- a/src/organizercore.cpp
+++ b/src/organizercore.cpp
@@ -790,6 +790,16 @@ std::wstring OrganizerCore::crashDumpsPath() {
).toStdWString();
}
+bool OrganizerCore::getArchiveParsing() const
+{
+ return m_ArchiveParsing;
+}
+
+void OrganizerCore::setArchiveParsing(const bool archiveParsing)
+{
+ m_ArchiveParsing = archiveParsing;
+}
+
void OrganizerCore::setCurrentProfile(const QString &profileName)
{
if ((m_CurrentProfile != nullptr)
@@ -1158,7 +1168,7 @@ QList<MOBase::IOrganizer::FileInfo> OrganizerCore::findFileInfos(
info.origins.append(ToQString(
m_DirectoryStructure->getOriginByID(file->getOrigin(fromArchive))
.getName()));
- info.archive = fromArchive ? ToQString(file->getArchive()) : "";
+ info.archive = fromArchive ? ToQString(file->getArchive().first) : "";
foreach (auto idx, file->getAlternatives()) {
info.origins.append(
ToQString(m_DirectoryStructure->getOriginByID(idx.first).getName()));
@@ -1940,12 +1950,14 @@ IPluginGame const *OrganizerCore::managedGame() const
std::vector<QString> OrganizerCore::enabledArchives()
{
std::vector<QString> result;
- QFile archiveFile(m_CurrentProfile->getArchivesFileName());
- if (archiveFile.open(QIODevice::ReadOnly)) {
- while (!archiveFile.atEnd()) {
- result.push_back(QString::fromUtf8(archiveFile.readLine()).trimmed());
+ if (m_ArchiveParsing) {
+ QFile archiveFile(m_CurrentProfile->getArchivesFileName());
+ if (archiveFile.open(QIODevice::ReadOnly)) {
+ while (!archiveFile.atEnd()) {
+ result.push_back(QString::fromUtf8(archiveFile.readLine()).trimmed());
+ }
+ archiveFile.close();
}
- archiveFile.close();
}
return result;
}
diff --git a/src/organizercore.h b/src/organizercore.h
index 086fa11d..d0964ed5 100644
--- a/src/organizercore.h
+++ b/src/organizercore.h
@@ -211,6 +211,8 @@ public:
bool onFinishedRun(const std::function<void (const QString &, unsigned int)> &func);
void refreshModList(bool saveChanges = true);
QStringList modsSortedByProfilePriority() const;
+ bool getArchiveParsing() const;
+ void setArchiveParsing(bool archiveParsing);
public: // IPluginDiagnose interface
@@ -335,6 +337,7 @@ private:
bool m_AskForNexusPW;
bool m_DirectoryUpdate;
bool m_ArchivesInit;
+ bool m_ArchiveParsing{ m_Settings.archiveParsing() };
MOBase::DelayedFileWriter m_PluginListsWriter;
UsvfsConnector m_USVFS;
diff --git a/src/pluginlist.cpp b/src/pluginlist.cpp
index 6f417331..3f7830c4 100644
--- a/src/pluginlist.cpp
+++ b/src/pluginlist.cpp
@@ -141,8 +141,8 @@ void PluginList::highlightPlugins(const QItemSelectionModel *selection, const MO
for (auto plugin : plugins) {
MOShared::FileEntry::Ptr file = directoryEntry.findFile(plugin.toStdWString());
if (file->getOrigin() != origin.getID()) {
- const std::vector<std::pair<int, std::wstring>> alternatives = file->getAlternatives();
- if (std::find_if(alternatives.begin(), alternatives.end(), [&](const std::pair<int, std::wstring>& element) { return element.first == origin.getID(); }) == alternatives.end())
+ const std::vector<std::pair<int, std::pair<std::wstring, int>>> alternatives = file->getAlternatives();
+ if (std::find_if(alternatives.begin(), alternatives.end(), [&](const std::pair<int, std::pair<std::wstring, int>>& element) { return element.first == origin.getID(); }) == alternatives.end())
continue;
}
std::map<QString, int>::iterator iter = m_ESPsByName.find(plugin.toLower());
diff --git a/src/resources.qrc b/src/resources.qrc
index 1dccce0b..5ed1780e 100644
--- a/src/resources.qrc
+++ b/src/resources.qrc
@@ -48,6 +48,9 @@
<file alias="emblem_conflict_overwrite">resources/conflict-overwrite.png</file>
<file alias="emblem_conflict_overwritten">resources/conflict-overwritten.png</file>
<file alias="emblem_conflict_redundant">resources/conflict-redundant.png</file>
+ <file alias="archive_loose_conflict_mixed">resources/conflict-mixed-blue.png</file>
+ <file alias="archive_loose_conflict_overwrite">resources/conflict-overwrite-blue.png</file>
+ <file alias="archive_loose_conflict_overwritten">resources/red-archive-conflict-loser.png</file>
<file alias="emblem_notes">resources/accessories-text-editor.png</file>
<file alias="version_date">resources/x-office-calendar.png</file>
<file alias="warning_16">resources/dialog-warning_16.png</file>
@@ -93,8 +96,8 @@
<file alias="string">resources/contents/conversation.png</file>
<file alias="bsa">resources/contents/locked-chest.png</file>
<file alias="menu">resources/contents/config.png</file>
- <file alias="inifile">resources/contents/feather-and-scroll.png</file>
- <file alias="modgroup">resources/contents/xedit.png</file>
+ <file alias="inifile">resources/contents/feather-and-scroll.png</file>
+ <file alias="modgroup">resources/contents/xedit.png</file>
</qresource>
<qresource prefix="/qt/etc">
<file>qt.conf</file>
diff --git a/src/resources/conflict-mixed-blue.png b/src/resources/conflict-mixed-blue.png
new file mode 100644
index 00000000..2afbb304
--- /dev/null
+++ b/src/resources/conflict-mixed-blue.png
Binary files differ
diff --git a/src/resources/conflict-overwrite-blue.png b/src/resources/conflict-overwrite-blue.png
new file mode 100644
index 00000000..3247e7b7
--- /dev/null
+++ b/src/resources/conflict-overwrite-blue.png
Binary files differ
diff --git a/src/resources/conflict-overwritten-blue.png b/src/resources/conflict-overwritten-blue.png
new file mode 100644
index 00000000..3a032a55
--- /dev/null
+++ b/src/resources/conflict-overwritten-blue.png
Binary files differ
diff --git a/src/resources/red-archive-conflict-loser.png b/src/resources/red-archive-conflict-loser.png
new file mode 100644
index 00000000..18284ee2
--- /dev/null
+++ b/src/resources/red-archive-conflict-loser.png
Binary files differ
diff --git a/src/settings.cpp b/src/settings.cpp
index 5cfe427b..605c257a 100644
--- a/src/settings.cpp
+++ b/src/settings.cpp
@@ -394,6 +394,16 @@ QColor Settings::modlistOverwritingLooseColor() const
return m_Settings.value("Settings/overwritingLooseFilesColor", QColor(255, 0, 0, 64)).value<QColor>();
}
+QColor Settings::modlistOverwrittenArchiveColor() const
+{
+ return m_Settings.value("Settings/overwrittenArchiveFilesColor", QColor(0, 255, 255, 64)).value<QColor>();
+}
+
+QColor Settings::modlistOverwritingArchiveColor() const
+{
+ return m_Settings.value("Settings/overwritingArchiveFilesColor", QColor(255, 0, 255, 64)).value<QColor>();
+}
+
QColor Settings::modlistContainsPluginColor() const
{
return m_Settings.value("Settings/containsPluginColor", QColor(0, 0, 255, 64)).value<QColor>();
@@ -482,6 +492,11 @@ uint Settings::getMotDHash() const
return m_Settings.value("motd_hash", 0).toUInt();
}
+bool Settings::archiveParsing() const
+{
+ return m_Settings.value("Settings/archive_parsing", true).toBool();
+}
+
QVariant Settings::pluginSetting(const QString &pluginName, const QString &key) const
{
auto iterPlugin = m_PluginSettings.find(pluginName);
@@ -727,6 +742,8 @@ Settings::GeneralTab::GeneralTab(Settings *m_parent, SettingsDialog &m_dialog)
, m_usePrereleaseBox(m_dialog.findChild<QCheckBox *>("usePrereleaseBox"))
, m_overwritingBtn(m_dialog.findChild<QPushButton *>("overwritingBtn"))
, m_overwrittenBtn(m_dialog.findChild<QPushButton *>("overwrittenBtn"))
+ , m_overwritingArchiveBtn(m_dialog.findChild<QPushButton *>("overwritingArchiveBtn"))
+ , m_overwrittenArchiveBtn(m_dialog.findChild<QPushButton *>("overwrittenArchiveBtn"))
, m_containsBtn(m_dialog.findChild<QPushButton *>("containsBtn"))
, m_containedBtn(m_dialog.findChild<QPushButton *>("containedBtn"))
, m_colorSeparatorsBox(m_dialog.findChild<QCheckBox *>("colorSeparatorsBox"))
@@ -783,11 +800,15 @@ Settings::GeneralTab::GeneralTab(Settings *m_parent, SettingsDialog &m_dialog)
//version with stylesheet
m_dialog.setButtonColor(m_overwritingBtn, m_parent->modlistOverwritingLooseColor());
m_dialog.setButtonColor(m_overwrittenBtn, m_parent->modlistOverwrittenLooseColor());
+ m_dialog.setButtonColor(m_overwritingArchiveBtn, m_parent->modlistOverwritingArchiveColor());
+ m_dialog.setButtonColor(m_overwrittenArchiveBtn, m_parent->modlistOverwrittenArchiveColor());
m_dialog.setButtonColor(m_containsBtn, m_parent->modlistContainsPluginColor());
m_dialog.setButtonColor(m_containedBtn, m_parent->pluginListContainedColor());
m_dialog.setOverwritingColor(m_parent->modlistOverwritingLooseColor());
m_dialog.setOverwrittenColor(m_parent->modlistOverwrittenLooseColor());
+ m_dialog.setOverwritingArchiveColor(m_parent->modlistOverwritingArchiveColor());
+ m_dialog.setOverwrittenArchiveColor(m_parent->modlistOverwrittenArchiveColor());
m_dialog.setContainsColor(m_parent->modlistContainsPluginColor());
m_dialog.setContainedColor(m_parent->pluginListContainedColor());
@@ -815,6 +836,8 @@ void Settings::GeneralTab::update()
m_Settings.setValue("Settings/overwritingLooseFilesColor", m_dialog.getOverwritingColor());
m_Settings.setValue("Settings/overwrittenLooseFilesColor", m_dialog.getOverwrittenColor());
+ m_Settings.setValue("Settings/overwritingArchiveFilesColor", m_dialog.getOverwritingArchiveColor());
+ m_Settings.setValue("Settings/overwrittenArchiveFilesColor", m_dialog.getOverwrittenArchiveColor());
m_Settings.setValue("Settings/containsPluginColor", m_dialog.getContainsColor());
m_Settings.setValue("Settings/containedColor", m_dialog.getContainedColor());
m_Settings.setValue("Settings/compact_downloads", m_compactBox->isChecked());
@@ -1088,6 +1111,7 @@ Settings::WorkaroundsTab::WorkaroundsTab(Settings *m_parent,
, m_forceEnableBox(m_dialog.findChild<QCheckBox *>("forceEnableBox"))
, m_displayForeignBox(m_dialog.findChild<QCheckBox *>("displayForeignBox"))
, m_lockGUIBox(m_dialog.findChild<QCheckBox *>("lockGUIBox"))
+ , m_enableArchiveParsingBox(m_dialog.findChild<QCheckBox *>("enableArchiveParsingBox"))
{
m_appIDEdit->setText(m_parent->getSteamAppID());
@@ -1122,6 +1146,7 @@ Settings::WorkaroundsTab::WorkaroundsTab(Settings *m_parent,
m_forceEnableBox->setChecked(m_parent->forceEnableCoreFiles());
m_displayForeignBox->setChecked(m_parent->displayForeign());
m_lockGUIBox->setChecked(m_parent->lockGUI());
+ m_enableArchiveParsingBox->setChecked(m_parent->archiveParsing());
m_dialog.setExecutableBlacklist(m_parent->executablesBlacklist());
@@ -1140,6 +1165,7 @@ void Settings::WorkaroundsTab::update()
m_Settings.setValue("Settings/force_enable_core_files", m_forceEnableBox->isChecked());
m_Settings.setValue("Settings/display_foreign", m_displayForeignBox->isChecked());
m_Settings.setValue("Settings/lock_gui", m_lockGUIBox->isChecked());
+ m_Settings.setValue("Settings/archive_parsing", m_enableArchiveParsingBox->isChecked());
m_Settings.setValue("Settings/executable_blacklist", m_dialog.getExecutableBlacklist());
diff --git a/src/settings.h b/src/settings.h
index 25f22911..f88f9652 100644
--- a/src/settings.h
+++ b/src/settings.h
@@ -233,6 +233,10 @@ public:
QColor modlistOverwritingLooseColor() const;
+ QColor modlistOverwrittenArchiveColor() const;
+
+ QColor modlistOverwritingArchiveColor() const;
+
QColor modlistContainsPluginColor() const;
QColor pluginListContainedColor() const;
@@ -286,6 +290,11 @@ public:
void setMotDHash(uint hash);
/**
+ * @return true if the user wants to have archives being parsed to show conflicts and contents
+ */
+ bool archiveParsing() const;
+
+ /**
* @return hash of the last displayed message of the day
**/
uint getMotDHash() const;
@@ -423,6 +432,8 @@ private:
QCheckBox *m_usePrereleaseBox;
QPushButton *m_overwritingBtn;
QPushButton *m_overwrittenBtn;
+ QPushButton *m_overwritingArchiveBtn;
+ QPushButton *m_overwrittenArchiveBtn;
QPushButton *m_containsBtn;
QPushButton *m_containedBtn;
QCheckBox *m_colorSeparatorsBox;
@@ -520,6 +531,7 @@ private:
QCheckBox *m_forceEnableBox;
QCheckBox *m_displayForeignBox;
QCheckBox *m_lockGUIBox;
+ QCheckBox *m_enableArchiveParsingBox;
};
private slots:
diff --git a/src/settingsdialog.cpp b/src/settingsdialog.cpp
index b975fa75..bbd9a22c 100644
--- a/src/settingsdialog.cpp
+++ b/src/settingsdialog.cpp
@@ -266,7 +266,7 @@ void SettingsDialog::on_containedBtn_clicked()
void SettingsDialog::on_overwrittenBtn_clicked()
{
- QColor result = QColorDialog::getColor(m_OverwrittenColor, this, "ColorPicker: Is overwritten", QColorDialog::ShowAlphaChannel);
+ QColor result = QColorDialog::getColor(m_OverwrittenColor, this, "ColorPicker: Is overwritten (loose files)", QColorDialog::ShowAlphaChannel);
if (result.isValid()) {
m_OverwrittenColor = result;
setButtonColor(ui->overwrittenBtn, result);
@@ -275,22 +275,44 @@ void SettingsDialog::on_overwrittenBtn_clicked()
void SettingsDialog::on_overwritingBtn_clicked()
{
- QColor result = QColorDialog::getColor(m_OverwritingColor, this, "ColorPicker: Is overwriting", QColorDialog::ShowAlphaChannel);
+ QColor result = QColorDialog::getColor(m_OverwritingColor, this, "ColorPicker: Is overwriting (loose files)", QColorDialog::ShowAlphaChannel);
if (result.isValid()) {
m_OverwritingColor = result;
setButtonColor(ui->overwritingBtn, result);
}
}
+void SettingsDialog::on_overwrittenArchiveBtn_clicked()
+{
+ QColor result = QColorDialog::getColor(m_OverwrittenArchiveColor, this, "ColorPicker: Is overwritten (archive files)", QColorDialog::ShowAlphaChannel);
+ if (result.isValid()) {
+ m_OverwrittenArchiveColor = result;
+ setButtonColor(ui->overwrittenArchiveBtn, result);
+ }
+}
+
+void SettingsDialog::on_overwritingArchiveBtn_clicked()
+{
+ QColor result = QColorDialog::getColor(m_OverwritingArchiveColor, this, "ColorPicker: Is overwriting (archive files)", QColorDialog::ShowAlphaChannel);
+ if (result.isValid()) {
+ m_OverwritingArchiveColor = result;
+ setButtonColor(ui->overwritingArchiveBtn, result);
+ }
+}
+
void SettingsDialog::on_resetColorsBtn_clicked()
{
m_OverwritingColor = QColor(255, 0, 0, 64);
m_OverwrittenColor = QColor(0, 255, 0, 64);
+ m_OverwritingArchiveColor = QColor(255, 0, 255, 64);
+ m_OverwrittenArchiveColor = QColor(0, 255, 255, 64);
m_ContainsColor = QColor(0, 0, 255, 64);
m_ContainedColor = QColor(0, 0, 255, 64);
setButtonColor(ui->overwritingBtn, m_OverwritingColor);
setButtonColor(ui->overwrittenBtn, m_OverwrittenColor);
+ setButtonColor(ui->overwritingArchiveBtn, m_OverwritingArchiveColor);
+ setButtonColor(ui->overwrittenArchiveBtn, m_OverwrittenArchiveColor);
setButtonColor(ui->containsBtn, m_ContainsColor);
setButtonColor(ui->containedBtn, m_ContainedColor);
}
diff --git a/src/settingsdialog.h b/src/settingsdialog.h
index b005f928..6357f064 100644
--- a/src/settingsdialog.h
+++ b/src/settingsdialog.h
@@ -69,6 +69,8 @@ public:
QColor getOverwritingColor() { return m_OverwritingColor; }
QColor getOverwrittenColor() { return m_OverwrittenColor; }
+ QColor getOverwritingArchiveColor() { return m_OverwritingArchiveColor; }
+ QColor getOverwrittenArchiveColor() { return m_OverwrittenArchiveColor; }
QColor getContainsColor() { return m_ContainsColor; }
QColor getContainedColor() { return m_ContainedColor; }
QString getExecutableBlacklist() { return m_ExecutableBlacklist; }
@@ -76,6 +78,8 @@ public:
void setOverwritingColor(QColor col) { m_OverwritingColor = col; }
void setOverwrittenColor(QColor col) { m_OverwrittenColor = col; }
+ void setOverwritingArchiveColor(QColor col) { m_OverwritingArchiveColor = col; }
+ void setOverwrittenArchiveColor(QColor col) { m_OverwrittenArchiveColor = col; }
void setContainsColor(QColor col) { m_ContainsColor = col; }
void setContainedColor(QColor col) { m_ContainedColor = col; }
void setExecutableBlacklist(QString blacklist) { m_ExecutableBlacklist = blacklist; }
@@ -118,6 +122,10 @@ private slots:
void on_overwrittenBtn_clicked();
+ void on_overwritingArchiveBtn_clicked();
+
+ void on_overwrittenArchiveBtn_clicked();
+
void on_containsBtn_clicked();
void on_containedBtn_clicked();
@@ -142,6 +150,8 @@ private:
QColor m_OverwritingColor;
QColor m_OverwrittenColor;
+ QColor m_OverwritingArchiveColor;
+ QColor m_OverwrittenArchiveColor;
QColor m_ContainsColor;
QColor m_ContainedColor;
diff --git a/src/settingsdialog.ui b/src/settingsdialog.ui
index d9fa92d8..58b2b48f 100644
--- a/src/settingsdialog.ui
+++ b/src/settingsdialog.ui
@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>586</width>
- <height>462</height>
+ <height>486</height>
</rect>
</property>
<property name="windowTitle">
@@ -99,10 +99,26 @@ If you use pre-releases, never contact me directly by e-mail or via private mess
<string>Colors</string>
</property>
<layout class="QGridLayout" name="gridLayout_5">
+ <item row="6" column="0">
+ <widget class="QCheckBox" name="colorSeparatorsBox">
+ <property name="toolTip">
+ <string>When this is enabled, the color defined for a separator will be shown on the mod list scrollbar at the location of the separator. This can be useful for quick navigation between separator sections or to a specific separator section.</string>
+ </property>
+ <property name="whatsThis">
+ <string>When this is enabled, the color defined for a separator will be shown on the mod list scrollbar at the location of the separator. This can be useful for quick navigation between separator sections or to a specific separator section.</string>
+ </property>
+ <property name="text">
+ <string>Show mod list separator colors on the scrollbar</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
<item row="4" column="0" colspan="2">
- <widget class="QPushButton" name="resetColorsBtn">
+ <widget class="QPushButton" name="containedBtn">
<property name="text">
- <string>Reset Colors</string>
+ <string>Plugin is Contained in selected Mod</string>
</property>
</widget>
</item>
@@ -113,40 +129,38 @@ If you use pre-releases, never contact me directly by e-mail or via private mess
</property>
</widget>
</item>
- <item row="2" column="0" colspan="2">
- <widget class="QPushButton" name="containsBtn">
+ <item row="1" column="1">
+ <widget class="QPushButton" name="overwritingBtn">
<property name="text">
- <string>Mod Contains selected Plugin</string>
+ <string>Is overwriting (loose files)</string>
</property>
</widget>
</item>
- <item row="3" column="0" colspan="2">
- <widget class="QPushButton" name="containedBtn">
+ <item row="5" column="0" colspan="2">
+ <widget class="QPushButton" name="resetColorsBtn">
<property name="text">
- <string>Plugin is Contained in selected Mod</string>
+ <string>Reset Colors</string>
</property>
</widget>
</item>
- <item row="1" column="1">
- <widget class="QPushButton" name="overwritingBtn">
+ <item row="3" column="0" colspan="2">
+ <widget class="QPushButton" name="containsBtn">
<property name="text">
- <string>Is overwriting (loose files)</string>
+ <string>Mod Contains selected Plugin</string>
</property>
</widget>
</item>
- <item row="5" column="0">
- <widget class="QCheckBox" name="colorSeparatorsBox">
- <property name="toolTip">
- <string>When this is enabled, the color defined for a separator will be shown on the mod list scrollbar at the location of the separator. This can be useful for quick navigation between separator sections or to a specific separator section.</string>
- </property>
- <property name="whatsThis">
- <string>When this is enabled, the color defined for a separator will be shown on the mod list scrollbar at the location of the separator. This can be useful for quick navigation between separator sections or to a specific separator section.</string>
- </property>
+ <item row="2" column="0">
+ <widget class="QPushButton" name="overwrittenArchiveBtn">
<property name="text">
- <string>Show mod list separator colors on the scrollbar</string>
+ <string>Is overwritten (archive files)</string>
</property>
- <property name="checked">
- <bool>true</bool>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QPushButton" name="overwritingArchiveBtn">
+ <property name="text">
+ <string>Is overwriting (archive files)</string>
</property>
</widget>
</item>
@@ -1115,6 +1129,27 @@ programs you are intentionally running.</string>
</widget>
</item>
<item>
+ <widget class="QCheckBox" name="enableArchiveParsingBox">
+ <property name="toolTip">
+ <string>Enable parsing of Archives Archives, has effects on perfromance.</string>
+ </property>
+ <property name="whatsThis">
+ <string>
+ By default Mod Organizer will parse Archive files to calculate conflicts between themselves and loose files, this process has a noticeable cost in performance.
+ This feature should not be confused from the one offered by ModOrganizer 1, ModOrganizer 2 will only show conflicts with archives NOT load them into the game or program.
+
+ If you disable this feature, MO will only display conflicts with Loose files.
+ </string>
+ </property>
+ <property name="text">
+ <string>Enable parsing of Archives</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
<widget class="QPushButton" name="resetGeometryBtn">
<property name="toolTip">
<string>Resets the window geometries for all windows. This can be useful if a window becomes too small or too large, if a column becomes too thin or too wide, and in similar situations.</string>
diff --git a/src/shared/directoryentry.cpp b/src/shared/directoryentry.cpp
index cebf270e..9f54e526 100644
--- a/src/shared/directoryentry.cpp
+++ b/src/shared/directoryentry.cpp
@@ -220,12 +220,19 @@ std::vector<FileEntry::Ptr> FilesOrigin::getFiles() const
return result;
}
+bool FilesOrigin::containsArchive(std::wstring archiveName)
+{
+ for (FileEntry::Index fileIdx : m_Files)
+ if (FileEntry::Ptr p = m_FileRegister.lock()->getFile(fileIdx))
+ if (p->isFromArchive(archiveName)) return true;
+ return false;
+}
//
// FileEntry
//
-void FileEntry::addOrigin(int origin, FILETIME fileTime, const std::wstring &archive)
+void FileEntry::addOrigin(int origin, FILETIME fileTime, const std::wstring &archive, int order)
{
m_LastAccessed = time(nullptr);
if (m_Parent != nullptr) {
@@ -234,36 +241,36 @@ void FileEntry::addOrigin(int origin, FILETIME fileTime, const std::wstring &arc
if (m_Origin == -1) {
m_Origin = origin;
m_FileTime = fileTime;
- m_Archive = archive;
+ m_Archive = std::pair<std::wstring, int>(archive, order);
} else if ((m_Parent != nullptr)
&& (m_Parent->getOriginByID(origin).getPriority() > m_Parent->getOriginByID(m_Origin).getPriority())
- && (archive.size() == 0 || m_Archive.size() > 0 )) {
- if (std::find_if(m_Alternatives.begin(), m_Alternatives.end(), [&](const std::pair<int, std::wstring> &i) -> bool { return i.first == m_Origin; }) == m_Alternatives.end()) {
- m_Alternatives.push_back(std::pair<int, std::wstring>(m_Origin, m_Archive));
+ && (archive.size() == 0 || m_Archive.first.size() > 0 )) {
+ if (std::find_if(m_Alternatives.begin(), m_Alternatives.end(), [&](const std::pair<int, std::pair<std::wstring, int>> &i) -> bool { return i.first == m_Origin; }) == m_Alternatives.end()) {
+ m_Alternatives.push_back(std::pair<int, std::pair<std::wstring, int>>(m_Origin, m_Archive));
}
m_Origin = origin;
m_FileTime = fileTime;
- m_Archive = archive;
+ m_Archive = std::pair<std::wstring, int>(archive, order);
} else {
bool found = false;
if (m_Origin == origin) {
// already an origin
return;
}
- for (std::vector<std::pair<int, std::wstring>>::iterator iter = m_Alternatives.begin(); iter != m_Alternatives.end(); ++iter) {
+ for (std::vector<std::pair<int, std::pair<std::wstring, int>>>::iterator iter = m_Alternatives.begin(); iter != m_Alternatives.end(); ++iter) {
if (iter->first == origin) {
// already an origin
return;
}
if ((m_Parent != nullptr)
&& (m_Parent->getOriginByID(iter->first).getPriority() < m_Parent->getOriginByID(origin).getPriority())) {
- m_Alternatives.insert(iter, std::pair<int, std::wstring>(origin, archive));
+ m_Alternatives.insert(iter, std::pair<int, std::pair<std::wstring, int>>(origin, std::pair<std::wstring, int>(archive, order)));
found = true;
break;
}
}
if (!found) {
- m_Alternatives.push_back(std::pair<int, std::wstring>(origin, archive));
+ m_Alternatives.push_back(std::pair<int, std::pair<std::wstring, int>>(origin, std::pair<std::wstring, int>(archive, order)));
}
}
}
@@ -273,14 +280,34 @@ bool FileEntry::removeOrigin(int origin)
if (m_Origin == origin) {
if (!m_Alternatives.empty()) {
// find alternative with the highest priority
- std::vector<std::pair<int, std::wstring>>::iterator currentIter = m_Alternatives.begin();
- for (std::vector<std::pair<int, std::wstring>>::iterator iter = m_Alternatives.begin(); iter != m_Alternatives.end(); ++iter) {
- if ((m_Parent->getOriginByID(iter->first).getPriority() > m_Parent->getOriginByID(currentIter->first).getPriority()) &&
- (iter->first != origin)) {
- currentIter = iter;
+ std::vector<std::pair<int, std::pair<std::wstring, int>>>::iterator currentIter = m_Alternatives.begin();
+ for (std::vector<std::pair<int, std::pair<std::wstring, int>>>::iterator iter = m_Alternatives.begin(); iter != m_Alternatives.end(); ++iter) {
+ if (iter->first != origin) {
+
+ //Both files are not from archives.
+ if (!iter->second.first.size() && !currentIter->second.first.size()) {
+ if ((m_Parent->getOriginByID(iter->first).getPriority() > m_Parent->getOriginByID(currentIter->first).getPriority())) {
+ currentIter = iter;
+ }
+ }
+ else {
+ //Both files are from archives
+ if (iter->second.first.size() && currentIter->second.first.size()) {
+ if (iter->second.second > currentIter->second.second) {
+ currentIter = iter;
+ }
+ }
+ else {
+ //Only one of the two is an archive, so we change currentIter only if he is the archive one.
+ if (currentIter->second.first.size()) {
+ currentIter = iter;
+ }
+ }
+ }
}
}
int currentID = currentIter->first;
+ m_Archive = currentIter->second;
m_Alternatives.erase(currentIter);
m_Origin = currentID;
@@ -292,19 +319,20 @@ bool FileEntry::removeOrigin(int origin)
if (!::GetFileTime(file, nullptr, nullptr, &m_FileTime)) {
// maybe this file is in a bsa, but there is no easy way to find out which. User should refresh
// the view to find out
- m_Archive = L"bsa?";
+ //m_Archive = std::pair<std::wstring, int>(L"bsa?", -1);
} else {
- m_Archive = L"";
+ //m_Archive = std::pair<std::wstring, int>(L"", -1);
}
::CloseHandle(file);
} else {
m_Origin = -1;
+ m_Archive = std::pair<std::wstring, int>(L"", -1);
return true;
}
} else {
- std::vector<std::pair<int, std::wstring>>::iterator newEnd = std::find_if(m_Alternatives.begin(), m_Alternatives.end(), [&](const std::pair<int, std::wstring> &i) -> bool { return i.first == origin; });
+ auto newEnd = std::remove_if(m_Alternatives.begin(), m_Alternatives.end(), [&](auto &i) -> bool { return i.first == origin; });
if (newEnd != m_Alternatives.end())
m_Alternatives.erase(newEnd, m_Alternatives.end());
}
@@ -318,7 +346,7 @@ FileEntry::FileEntry()
}
FileEntry::FileEntry(Index index, const std::wstring &name, DirectoryEntry *parent)
- : m_Index(index), m_Name(name), m_Origin(-1), m_Archive(L""), m_Parent(parent), m_LastAccessed(time(nullptr))
+ : m_Index(index), m_Name(name), m_Origin(-1), m_Archive(L"", -1), m_Parent(parent), m_LastAccessed(time(nullptr))
{
LEAK_TRACE;
}
@@ -330,16 +358,23 @@ FileEntry::~FileEntry()
void FileEntry::sortOrigins()
{
- m_Alternatives.push_back(std::pair<int, std::wstring>(m_Origin, m_Archive));
- std::sort(m_Alternatives.begin(), m_Alternatives.end(), [&](const std::pair<int, std::wstring> &LHS, const std::pair<int, std::wstring> &RHS) -> bool {
- if ((!LHS.second.size() && !RHS.second.size()) || (LHS.second.size() && RHS.second.size())) {
+ m_Alternatives.push_back(std::pair<int, std::pair<std::wstring, int>>(m_Origin, m_Archive));
+ std::sort(m_Alternatives.begin(), m_Alternatives.end(), [&](const std::pair<int, std::pair<std::wstring, int>> &LHS, const std::pair<int, std::pair<std::wstring, int>> &RHS) -> bool {
+ if (!LHS.second.first.size() && !RHS.second.first.size()) {
int l = m_Parent->getOriginByID(LHS.first).getPriority(); if (l < 0) l = INT_MAX;
int r = m_Parent->getOriginByID(RHS.first).getPriority(); if (r < 0) r = INT_MAX;
return l < r;
}
- if (RHS.second.size()) return false;
+ if (LHS.second.first.size() && RHS.second.first.size()) {
+ int l = LHS.second.second; if (l < 0) l = INT_MAX;
+ int r = RHS.second.second; if (r < 0) r = INT_MAX;
+
+ return l < r;
+ }
+
+ if (RHS.second.first.size()) return false;
return true;
});
if (!m_Alternatives.empty()) {
@@ -379,6 +414,16 @@ std::wstring FileEntry::getRelativePath() const
return result + L"\\" + m_Name;
}
+bool FileEntry::isFromArchive(std::wstring archiveName)
+{
+ if (archiveName.length() == 0) return m_Archive.first.length() != 0;
+ if (m_Archive.first.compare(archiveName) == 0) return true;
+ for (auto alternative : m_Alternatives) {
+ if (alternative.second.first.compare(archiveName) == 0) return true;
+ }
+ return false;
+}
+
//
// DirectoryEntry
@@ -451,7 +496,7 @@ void DirectoryEntry::addFromOrigin(const std::wstring &originName, const std::ws
}
-void DirectoryEntry::addFromBSA(const std::wstring &originName, std::wstring &directory, const std::wstring &fileName, int priority)
+void DirectoryEntry::addFromBSA(const std::wstring &originName, std::wstring &directory, const std::wstring &fileName, int priority, int order)
{
FilesOrigin &origin = createOrigin(originName, directory, priority);
@@ -459,23 +504,33 @@ void DirectoryEntry::addFromBSA(const std::wstring &originName, std::wstring &di
if (::GetFileAttributesExW(fileName.c_str(), GetFileExInfoStandard, &fileData) == 0) {
throw windows_error("failed to determine file time");
}
+ FILETIME now;
+ ::GetSystemTimeAsFileTime(&now);
+
+ const double clfSecondsPer100ns = 100. * 1.E-9;
+
+ ((ULARGE_INTEGER *)&now)->QuadPart -= ((double)5) / clfSecondsPer100ns;
- BSA::Archive archive;
- BSA::EErrorCode res = archive.read(ToString(fileName, false).c_str(), false);
- if ((res != BSA::ERROR_NONE) && (res != BSA::ERROR_INVALIDHASHES)) {
- std::ostringstream stream;
- stream << "invalid bsa file: " << ToString(fileName, false) << " errorcode " << res << " - " << ::GetLastError();
- throw std::runtime_error(stream.str());
- }
size_t namePos = fileName.find_last_of(L"\\/");
if (namePos == std::wstring::npos) {
namePos = 0;
- } else {
+ }
+ else {
++namePos;
}
- addFiles(origin, archive.getRoot(), fileData.ftLastWriteTime, fileName.substr(namePos));
- m_Populated = true;
+ if (!containsArchive(fileName.substr(namePos)) || ::CompareFileTime(&fileData.ftLastWriteTime, &now) > 0) {
+ BSA::Archive archive;
+ BSA::EErrorCode res = archive.read(ToString(fileName, false).c_str(), false);
+ if ((res != BSA::ERROR_NONE) && (res != BSA::ERROR_INVALIDHASHES)) {
+ std::ostringstream stream;
+ stream << "invalid bsa file: " << ToString(fileName, false) << " errorcode " << res << " - " << ::GetLastError();
+ throw std::runtime_error(stream.str());
+ }
+
+ addFiles(origin, archive.getRoot(), fileData.ftLastWriteTime, fileName.substr(namePos), order);
+ m_Populated = true;
+ }
}
void DirectoryEntry::propagateOrigin(int origin)
@@ -536,7 +591,7 @@ void DirectoryEntry::addFiles(FilesOrigin &origin, wchar_t *buffer, int bufferOf
getSubDirectory(findData.cFileName, true, origin.getID())->addFiles(origin, buffer, bufferOffset + offset);
}
} else {
- insert(findData.cFileName, origin, findData.ftLastWriteTime, L"");
+ insert(findData.cFileName, origin, findData.ftLastWriteTime, L"", -1);
}
result = ::FindNextFileW(searchHandle, &findData);
}
@@ -546,12 +601,12 @@ void DirectoryEntry::addFiles(FilesOrigin &origin, wchar_t *buffer, int bufferOf
}
-void DirectoryEntry::addFiles(FilesOrigin &origin, BSA::Folder::Ptr archiveFolder, FILETIME &fileTime, const std::wstring &archiveName)
+void DirectoryEntry::addFiles(FilesOrigin &origin, BSA::Folder::Ptr archiveFolder, FILETIME &fileTime, const std::wstring &archiveName, int order)
{
// add files
for (unsigned int fileIdx = 0; fileIdx < archiveFolder->getNumFiles(); ++fileIdx) {
BSA::File::Ptr file = archiveFolder->getFile(fileIdx);
- insert(ToWString(file->getName(), true), origin, fileTime, archiveName);
+ insert(ToWString(file->getName(), true), origin, fileTime, archiveName, order);
}
// recurse into subdirectories
@@ -559,7 +614,7 @@ void DirectoryEntry::addFiles(FilesOrigin &origin, BSA::Folder::Ptr archiveFolde
BSA::Folder::Ptr folder = archiveFolder->getSubFolder(folderIdx);
DirectoryEntry *folderEntry = getSubDirectoryRecursive(ToWString(folder->getName(), true), true, origin.getID());
- folderEntry->addFiles(origin, folder, fileTime, archiveName);
+ folderEntry->addFiles(origin, folder, fileTime, archiveName, order);
}
}
@@ -626,7 +681,7 @@ void DirectoryEntry::insertFile(const std::wstring &filePath, FilesOrigin &origi
{
size_t pos = filePath.find_first_of(L"\\/");
if (pos == std::string::npos) {
- this->insert(filePath, origin, fileTime, std::wstring());
+ this->insert(filePath, origin, fileTime, std::wstring(), -1);
} else {
std::wstring dirName = filePath.substr(0, pos);
std::wstring rest = filePath.substr(pos + 1);
@@ -667,6 +722,14 @@ void DirectoryEntry::removeFiles(const std::set<FileEntry::Index> &indices)
}
}
+bool DirectoryEntry::containsArchive(std::wstring archiveName)
+{
+ for (auto iter = m_Files.begin(); iter != m_Files.end(); ++iter) {
+ FileEntry::Ptr entry = m_FileRegister->getFile(iter->second);
+ if (entry->isFromArchive(archiveName)) return true;
+ }
+ return false;
+}
int DirectoryEntry::anyOrigin() const
{
@@ -890,7 +953,7 @@ void FileRegister::unregisterFile(FileEntry::Ptr file)
// unregister from origin
int originID = file->getOrigin(ignore);
m_OriginConnection->getByID(originID).removeFile(file->getIndex());
- const std::vector<std::pair<int, std::wstring>> &alternatives = file->getAlternatives();
+ const std::vector<std::pair<int, std::pair<std::wstring, int>>> &alternatives = file->getAlternatives();
for (auto iter = alternatives.begin(); iter != alternatives.end(); ++iter) {
m_OriginConnection->getByID(iter->first).removeFile(file->getIndex());
}
diff --git a/src/shared/directoryentry.h b/src/shared/directoryentry.h
index 8dbedaf4..12cef11d 100644
--- a/src/shared/directoryentry.h
+++ b/src/shared/directoryentry.h
@@ -64,7 +64,7 @@ public:
time_t lastAccessed() const { return m_LastAccessed; }
- void addOrigin(int origin, FILETIME fileTime, const std::wstring &archive);
+ void addOrigin(int origin, FILETIME fileTime, const std::wstring &archive, int order);
// remove the specified origin from the list of origins that contain this file. if no origin is left,
// the file is effectively deleted and true is returned. otherwise, false is returned
bool removeOrigin(int origin);
@@ -72,13 +72,13 @@ public:
// gets the list of alternative origins (origins with lower priority than the primary one).
// if sortOrigins has been called, it is sorted by priority (ascending)
- const std::vector<std::pair<int, std::wstring>> &getAlternatives() const { return m_Alternatives; }
+ const std::vector<std::pair<int, std::pair<std::wstring, int>>> &getAlternatives() const { return m_Alternatives; }
const std::wstring &getName() const { return m_Name; }
int getOrigin() const { return m_Origin; }
- int getOrigin(bool &archive) const { archive = (m_Archive.length() != 0); return m_Origin; }
- const std::wstring &getArchive() const { return m_Archive; }
- bool isFromArchive() const { return m_Archive.length() != 0; }
+ int getOrigin(bool &archive) const { archive = (m_Archive.first.length() != 0); return m_Origin; }
+ const std::pair<std::wstring, int> &getArchive() const { return m_Archive; }
+ bool isFromArchive(std::wstring archiveName = L"");
std::wstring getFullPath() const;
std::wstring getRelativePath() const;
DirectoryEntry *getParent() { return m_Parent; }
@@ -97,8 +97,8 @@ private:
Index m_Index;
std::wstring m_Name;
int m_Origin = -1;
- std::wstring m_Archive;
- std::vector<std::pair<int, std::wstring>> m_Alternatives;
+ std::pair<std::wstring, int> m_Archive;
+ std::vector<std::pair<int, std::pair<std::wstring, int>>> m_Alternatives;
DirectoryEntry *m_Parent;
mutable FILETIME m_FileTime;
@@ -142,6 +142,8 @@ public:
void addFile(FileEntry::Index index) { m_Files.insert(index); }
void removeFile(FileEntry::Index index);
+ bool containsArchive(std::wstring archiveName);
+
private:
FilesOrigin(int ID, const std::wstring &name, const std::wstring &path, int priority,
@@ -221,7 +223,7 @@ public:
// add files to this directory (and subdirectories) from the specified origin. That origin may exist or not
void addFromOrigin(const std::wstring &originName, const std::wstring &directory, int priority);
- void addFromBSA(const std::wstring &originName, std::wstring &directory, const std::wstring &fileName, int priority);
+ void addFromBSA(const std::wstring &originName, std::wstring &directory, const std::wstring &fileName, int priority, int order);
void propagateOrigin(int origin);
@@ -253,6 +255,8 @@ public:
*/
const FileEntry::Ptr findFile(const std::wstring &name) const;
+ bool containsArchive(std::wstring archiveName);
+
/** search through this directory and all subdirectories for a file by the specified name (relative path).
if directory is not nullptr, the referenced variable will be set to the path containing the file */
const FileEntry::Ptr searchFile(const std::wstring &path, const DirectoryEntry **directory) const;
@@ -297,7 +301,7 @@ private:
DirectoryEntry(const DirectoryEntry &reference);
DirectoryEntry &operator=(const DirectoryEntry &reference);
- void insert(const std::wstring &fileName, FilesOrigin &origin, FILETIME fileTime, const std::wstring &archive) {
+ void insert(const std::wstring &fileName, FilesOrigin &origin, FILETIME fileTime, const std::wstring &archive, int order) {
std::wstring fileNameLower = ToLower(fileName);
auto iter = m_Files.find(fileNameLower);
FileEntry::Ptr file;
@@ -308,12 +312,12 @@ private:
// TODO this has been observed to cause a crash, no clue why
m_Files[fileNameLower] = file->getIndex();
}
- file->addOrigin(origin.getID(), fileTime, archive);
+ file->addOrigin(origin.getID(), fileTime, archive, order);
origin.addFile(file->getIndex());
}
void addFiles(FilesOrigin &origin, wchar_t *buffer, int bufferOffset);
- void addFiles(FilesOrigin &origin, BSA::Folder::Ptr archiveFolder, FILETIME &fileTime, const std::wstring &archiveName);
+ void addFiles(FilesOrigin &origin, BSA::Folder::Ptr archiveFolder, FILETIME &fileTime, const std::wstring &archiveName, int order);
DirectoryEntry *getSubDirectory(const std::wstring &name, bool create, int originID = -1);
diff --git a/src/syncoverwritedialog.cpp b/src/syncoverwritedialog.cpp
index f03e29c5..7c341567 100644
--- a/src/syncoverwritedialog.cpp
+++ b/src/syncoverwritedialog.cpp
@@ -98,8 +98,8 @@ void SyncOverwriteDialog::readTree(const QString &path, DirectoryEntry *director
bool ignore;
int origin = entry->getOrigin(ignore);
addToComboBox(combo, ToQString(m_DirectoryStructure->getOriginByID(origin).getName()), origin);
- const std::vector<std::pair<int, std::wstring>> &alternatives = entry->getAlternatives();
- for (std::vector<std::pair<int, std::wstring>>::const_iterator iter = alternatives.begin(); iter != alternatives.end(); ++iter) {
+ const std::vector<std::pair<int, std::pair<std::wstring, int>>> &alternatives = entry->getAlternatives();
+ for (std::vector<std::pair<int, std::pair<std::wstring, int>>>::const_iterator iter = alternatives.begin(); iter != alternatives.end(); ++iter) {
addToComboBox(combo, ToQString(m_DirectoryStructure->getOriginByID(iter->first).getName()), iter->first);
}
combo->setCurrentIndex(combo->count() - 1);
diff --git a/src/version.rc b/src/version.rc
index de5ec82e..e1f80785 100644
--- a/src/version.rc
+++ b/src/version.rc
@@ -3,8 +3,8 @@
// If VS_FF_PRERELEASE is not set, MO labels the build as a release and uses VER_FILEVERSION to determine version number.
// Otherwise, if letters are used in VER_FILEVERSION_STR, uses the full MOBase::VersionInfo parser
// Otherwise, uses the numbers from VER_FILEVERSION and sets the release type as pre-alpha
-#define VER_FILEVERSION 2,1,7
-#define VER_FILEVERSION_STR "2.1.7alpha4\0"
+#define VER_FILEVERSION 2,2,0
+#define VER_FILEVERSION_STR "2.2.0\0"
VS_VERSION_INFO VERSIONINFO
FILEVERSION VER_FILEVERSION