diff options
Diffstat (limited to 'src')
32 files changed, 771 insertions, 210 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 a9dd3e51..44e63997 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();
@@ -1271,7 +1284,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);
@@ -1306,10 +1319,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()));
@@ -1318,9 +1335,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);
@@ -1342,12 +1359,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 << " , ";
@@ -2275,9 +2292,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();
}
@@ -2351,10 +2382,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));
@@ -2520,8 +2551,12 @@ void MainWindow::modlistSelectionChanged(const QModelIndex ¤t, 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()) {
@@ -2814,7 +2849,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);
@@ -2849,7 +2884,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);
@@ -2871,6 +2905,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();
}
}
@@ -4729,6 +4764,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());
}
@@ -6393,3 +6449,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 e0311046..24d726ca 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;
@@ -612,6 +614,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 83b99486..e30466ac 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 b6d7209c..6b7c714e 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 40fae0e4..d2e5e84f 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -318,10 +318,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 << ", ";
@@ -330,11 +330,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
@@ -348,6 +355,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 61517100..4c731c00 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 cc15a111..2e6959d5 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);
@@ -1125,6 +1152,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 443d583b..402d662f 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);
@@ -340,6 +343,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 5afcc26a..99f0efef 100644 --- a/src/modlistsortproxy.cpp +++ b/src/modlistsortproxy.cpp @@ -259,9 +259,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 f4aa5beb..b5a1cb8a 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -792,6 +792,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)
@@ -1161,7 +1171,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()));
@@ -1986,12 +1996,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 7a62d2c8..68d4e7e4 100644 --- a/src/organizercore.h +++ b/src/organizercore.h @@ -216,6 +216,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
@@ -342,6 +344,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 ab8bb9f7..b2fabbe0 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 Binary files differnew file mode 100644 index 00000000..2afbb304 --- /dev/null +++ b/src/resources/conflict-mixed-blue.png diff --git a/src/resources/conflict-overwrite-blue.png b/src/resources/conflict-overwrite-blue.png Binary files differnew file mode 100644 index 00000000..3247e7b7 --- /dev/null +++ b/src/resources/conflict-overwrite-blue.png diff --git a/src/resources/conflict-overwritten-blue.png b/src/resources/conflict-overwritten-blue.png Binary files differnew file mode 100644 index 00000000..3a032a55 --- /dev/null +++ b/src/resources/conflict-overwritten-blue.png diff --git a/src/resources/red-archive-conflict-loser.png b/src/resources/red-archive-conflict-loser.png Binary files differnew file mode 100644 index 00000000..18284ee2 --- /dev/null +++ b/src/resources/red-archive-conflict-loser.png diff --git a/src/settings.cpp b/src/settings.cpp index c4228f65..95d7af49 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); @@ -728,6 +743,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")) @@ -784,11 +801,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()); @@ -816,6 +837,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()); @@ -1089,6 +1112,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()); @@ -1123,6 +1147,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()); @@ -1141,6 +1166,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 a47c255c..0286d4db 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 3032a6c6..1bbc256f 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..686a3028 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>
@@ -1004,20 +1018,20 @@ tl;dr-version: If Nexus-features don't work, insert the current version number o </item>
<item>
<layout class="QGridLayout" name="gridLayout_6">
- <item row="1" column="1">
- <widget class="QCheckBox" name="forceEnableBox">
+ <item row="1" column="0">
+ <widget class="QCheckBox" name="hideUncheckedBox">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
<property name="toolTip">
- <string>If checked, files (i.e. esps, esms and bsas) belonging to the core game can not be disabled in the UI. (default: on)</string>
+ <string>Enforces that inactive ESPs and ESMs are never loaded.</string>
</property>
<property name="whatsThis">
- <string>If checked, files (i.e. esps, esms and bsas) belonging to the core game can not be disabled in the UI. (default: on)
-Uncheck this if you want to use Mod Organizer with total conversions (like Nehrim) but be aware that the game will crash if required files are not enabled.</string>
+ <string>It seems that the Games occasionally load ESP or ESM files even if they haven't been activated as plugins.
+I don't yet know what the circumstances are, but user reports imply it is in some cases unwanted. If this is checked, ESPs and ESMs not checked in the List are invisible to the game and can not be loaded.</string>
</property>
<property name="text">
- <string>Force-enable game files</string>
- </property>
- <property name="checked">
- <bool>true</bool>
+ <string>Hide inactive ESPs/ESMs</string>
</property>
</widget>
</item>
@@ -1040,20 +1054,20 @@ If you disable this feature, MO will only display official DLCs this way. Please </property>
</widget>
</item>
- <item row="1" column="0">
- <widget class="QCheckBox" name="hideUncheckedBox">
- <property name="enabled">
- <bool>false</bool>
- </property>
+ <item row="1" column="1">
+ <widget class="QCheckBox" name="forceEnableBox">
<property name="toolTip">
- <string>Enforces that inactive ESPs and ESMs are never loaded.</string>
+ <string>If checked, files (i.e. esps, esms and bsas) belonging to the core game can not be disabled in the UI. (default: on)</string>
</property>
<property name="whatsThis">
- <string>It seems that the Games occasionally load ESP or ESM files even if they haven't been activated as plugins.
-I don't yet know what the circumstances are, but user reports imply it is in some cases unwanted. If this is checked, ESPs and ESMs not checked in the List are invisible to the game and can not be loaded.</string>
+ <string>If checked, files (i.e. esps, esms and bsas) belonging to the core game can not be disabled in the UI. (default: on)
+Uncheck this if you want to use Mod Organizer with total conversions (like Nehrim) but be aware that the game will crash if required files are not enabled.</string>
</property>
<property name="text">
- <string>Hide inactive ESPs/ESMs</string>
+ <string>Force-enable game files</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
</property>
</widget>
</item>
@@ -1073,6 +1087,22 @@ I don't yet know what the circumstances are, but user reports imply it is in som </property>
</widget>
</item>
+ <item row="3" column="0">
+ <widget class="QCheckBox" name="enableArchiveParsingBox">
+ <property name="toolTip">
+ <string>Enable parsing of Archives. Has negative effects on performance.</string>
+ </property>
+ <property name="whatsThis">
+ <string><html><head/><body><p>By default, MO will parse archive files (BSA, BA2) to calculate conflicts between the contents of the archive files and other loose files. This process has a noticeable cost in performance.</p><p>This feature should not be confused with the archive management feature offered by MO1. MO2 will only show conflicts with archives and will NOT load them into the game or program.</p><p>If you disable this feature, MO will only display conflicts between loose files.</p></body></html></string>
+ </property>
+ <property name="text">
+ <string>Enable parsing of Archives</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
</layout>
</item>
<item>
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 83a2eb5a..3e03e22a 100644 --- a/src/version.rc +++ b/src/version.rc @@ -1,37 +1,37 @@ -#include "Winver.h"
-
-// 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.7alpha6\0"
-
-VS_VERSION_INFO VERSIONINFO
-FILEVERSION VER_FILEVERSION
-PRODUCTVERSION VER_FILEVERSION
-FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
-FILEFLAGS VS_FF_PRERELEASE
-FILEOS VOS__WINDOWS32
-FILETYPE VFT_APP
-FILESUBTYPE (0)
-BEGIN
- BLOCK "StringFileInfo"
- BEGIN
- BLOCK "040904B0"
- BEGIN
- VALUE "FileVersion", VER_FILEVERSION_STR
- VALUE "CompanyName", "Mod Organizer 2 Team\0"
- VALUE "FileDescription", "Mod Organizer 2 GUI\0"
- VALUE "OriginalFilename", "ModOrganizer.exe\0"
- VALUE "InternalName", "ModOrganizer2\0"
- VALUE "LegalCopyright", "Copyright 2011-2016 Sebastian Herbord\r\nCopyright 2016-2018 Mod Organizer 2 contributors\0"
- VALUE "ProductName", "Mod Organizer 2\0"
- VALUE "ProductVersion", VER_FILEVERSION_STR
- END
- END
-
- BLOCK "VarFileInfo"
- BEGIN
- VALUE "Translation", 0x0409L, 1200
- END
-END
+#include "Winver.h" + +// 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,2,0 +#define VER_FILEVERSION_STR "2.2.0dev\0" + +VS_VERSION_INFO VERSIONINFO +FILEVERSION VER_FILEVERSION +PRODUCTVERSION VER_FILEVERSION +FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +FILEFLAGS VS_FF_PRERELEASE +FILEOS VOS__WINDOWS32 +FILETYPE VFT_APP +FILESUBTYPE (0) +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904B0" + BEGIN + VALUE "FileVersion", VER_FILEVERSION_STR + VALUE "CompanyName", "Mod Organizer 2 Team\0" + VALUE "FileDescription", "Mod Organizer 2 GUI\0" + VALUE "OriginalFilename", "ModOrganizer.exe\0" + VALUE "InternalName", "ModOrganizer2\0" + VALUE "LegalCopyright", "Copyright 2011-2016 Sebastian Herbord\r\nCopyright 2016-2018 Mod Organizer 2 contributors\0" + VALUE "ProductName", "Mod Organizer 2\0" + VALUE "ProductVersion", VER_FILEVERSION_STR + END + END + + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0409L, 1200 + END +END |
