diff options
| author | Tannin <devnull@localhost> | 2013-08-15 22:13:18 +0200 |
|---|---|---|
| committer | Tannin <devnull@localhost> | 2013-08-15 22:13:18 +0200 |
| commit | 7d79e3ad1e8a710e836e1cd44106a610e6d2a049 (patch) | |
| tree | 5193b33f6a943fd599832aaba46a653f7d471021 /src | |
| parent | d0f2c4fcf79222d5c6f3c17188a811b0a47833c6 (diff) | |
- bugfix: nameprefix-flag incorrectly interpreted on oblivion-style bsas
- bugfix: optimization for missing inis fixed
- bugfix: fixmods didn't search for esps in overwrite
- bugfix: configurator didn't work for fallout new vegas because of a missing cast
- bugfix: configurator attempted parameter formatting on ini files instead of using the raw data
- bugfix: searching for a path in the directory structure could cause a crash when not using the directory output
- optimization to findfirstfile for cases where a single file is searched
- display of bsas changed. hopefully it is more understandable now
- cache for the test whether a mod is in conflict. Should speed ab scrolling in mod list
- mod list has now an additional column for the installation time
- nmm importer no longer cancels in cases where a data file references an undeclared mod.
- integrated improved settings.json for the configurator contributed by "delta534"
Diffstat (limited to 'src')
| -rw-r--r-- | src/mainwindow.cpp | 83 | ||||
| -rw-r--r-- | src/mainwindow.h | 2 | ||||
| -rw-r--r-- | src/mainwindow.ui | 86 | ||||
| -rw-r--r-- | src/modinfo.cpp | 93 | ||||
| -rw-r--r-- | src/modinfo.h | 17 | ||||
| -rw-r--r-- | src/modlist.cpp | 4 | ||||
| -rw-r--r-- | src/modlist.h | 1 | ||||
| -rw-r--r-- | src/shared/directoryentry.cpp | 16 |
8 files changed, 215 insertions, 87 deletions
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 99b37feb..84e5b094 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -256,6 +256,8 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, QWidget connect(ui->espFilterEdit, SIGNAL(textChanged(QString)), this, SLOT(espFilterChanged(QString))); connect(&m_PluginList, SIGNAL(saveTimer()), this, SLOT(savePluginList())); + connect(ui->bsaWarning, SIGNAL(linkActivated(QString)), this, SLOT(linkClicked(QString))); + connect(&m_DirectoryRefresher, SIGNAL(refreshed()), this, SLOT(directory_refreshed())); connect(&m_DirectoryRefresher, SIGNAL(progress(int)), this, SLOT(refresher_progress(int))); connect(&m_DirectoryRefresher, SIGNAL(error(QString)), this, SLOT(showError(QString))); @@ -1648,7 +1650,8 @@ void MainWindow::refreshBSAList() QTreeWidgetItem *newItem = new QTreeWidgetItem(strings); newItem->setData(0, Qt::UserRole, index); newItem->setData(1, Qt::UserRole, origin); - newItem->setFlags(newItem->flags() & ~Qt::ItemIsDropEnabled | Qt::ItemIsUserCheckable); +// newItem->setFlags(newItem->flags() & ~Qt::ItemIsDropEnabled | Qt::ItemIsUserCheckable); + newItem->setFlags(newItem->flags() | Qt::ItemIsUserCheckable); newItem->setCheckState(0, (index != -1) ? Qt::Checked : Qt::Unchecked); if (m_Settings.forceEnableCoreFiles() && m_DefaultArchives.contains(filename)) { newItem->setCheckState(0, Qt::Checked); @@ -1658,6 +1661,7 @@ void MainWindow::refreshBSAList() } if (index < 0) index = 0; + UINT32 sortValue = ((m_DirectoryStructure->getOriginByID(origin).getPriority() & 0xFFFF) << 16) | (index & 0xFFFF); items.push_back(std::make_pair(sortValue, newItem)); } @@ -1666,7 +1670,18 @@ void MainWindow::refreshBSAList() std::sort(items.begin(), items.end(), BySortValue); for (std::vector<std::pair<UINT32, QTreeWidgetItem*> >::iterator iter = items.begin(); iter != items.end(); ++iter) { - ui->bsaList->addTopLevelItem(iter->second); + int originID = iter->second->data(1, Qt::UserRole).toInt(); + FilesOrigin origin = m_DirectoryStructure->getOriginByID(originID); + QList<QTreeWidgetItem*> items = ui->bsaList->findItems(ToQString(origin.getName()), Qt::MatchFixedString); + QTreeWidgetItem *subItem = NULL; + if (items.length() > 0) { + subItem = items.at(0); + } else { + subItem = new QTreeWidgetItem(QStringList(ToQString(origin.getName()))); + ui->bsaList->addTopLevelItem(subItem); + } + subItem->addChild(iter->second); + //ui->bsaList->addTopLevelItem(iter->second); } checkBSAList(); @@ -1681,27 +1696,35 @@ void MainWindow::checkBSAList() bool warning = false; for (int i = 0; i < ui->bsaList->topLevelItemCount(); ++i) { - QTreeWidgetItem *item = ui->bsaList->topLevelItem(i); - QString filename = item->text(0); - item->setIcon(0, QIcon()); - item->setToolTip(0, QString()); + bool modWarning = false; + QTreeWidgetItem *tlItem = ui->bsaList->topLevelItem(i); + for (int j = 0; j < tlItem->childCount(); ++j) { + QTreeWidgetItem *item = tlItem->child(j); + QString filename = item->text(0); + item->setIcon(0, QIcon()); + item->setToolTip(0, QString()); - if (item->checkState(0) == Qt::Unchecked) { - if (m_DefaultArchives.contains(filename)) { - item->setIcon(0, QIcon(":/MO/gui/resources/dialog-warning.png")); - item->setToolTip(0, tr("This bsa is enabled in the ini file so it may be required!")); - warning = true; - } else { - QString espName = filename.mid(0, filename.length() - 3).append("esp").toLower(); - QString esmName = filename.mid(0, filename.length() - 3).append("esm").toLower(); - if (m_PluginList.isEnabled(espName) || m_PluginList.isEnabled(esmName)) { + if (item->checkState(0) == Qt::Unchecked) { + if (m_DefaultArchives.contains(filename)) { item->setIcon(0, QIcon(":/MO/gui/resources/dialog-warning.png")); - item->setToolTip(0, tr("This archive will still be loaded since there is a plugin of the same name but " - "its files will not follow installation order!")); - warning = true; + item->setToolTip(0, tr("This bsa is enabled in the ini file so it may be required!")); + modWarning = true; + } else { + QString espName = filename.mid(0, filename.length() - 3).append("esp").toLower(); + QString esmName = filename.mid(0, filename.length() - 3).append("esm").toLower(); + if (m_PluginList.isEnabled(espName) || m_PluginList.isEnabled(esmName)) { + item->setIcon(0, QIcon(":/MO/gui/resources/dialog-warning.png")); + item->setToolTip(0, tr("This archive will still be loaded since there is a plugin of the same name but " + "its files will not follow installation order!")); + modWarning = true; + } } } } + if (modWarning) { + ui->bsaList->expandItem(ui->bsaList->topLevelItem(i)); + warning = true; + } } if (warning) { @@ -2072,7 +2095,6 @@ void MainWindow::issueTriggered() ::ShellExecuteW(NULL, L"open", L"http://issue.tannin.eu/tbg", NULL, NULL, SW_SHOWNORMAL); } - void MainWindow::tutorialTriggered() { QAction *tutorialAction = qobject_cast<QAction*>(sender()); @@ -3312,6 +3334,7 @@ void MainWindow::fixMods_clicked() QStringList espFilter("*.esp"); espFilter.append("*.esm"); + // search in data { QDir dataDir(m_GamePath + "/data"); QStringList esps = dataDir.entryList(espFilter); @@ -3323,6 +3346,7 @@ void MainWindow::fixMods_clicked() } } + // search in mods for (unsigned int i = 0; i < m_CurrentProfile->numRegularMods(); ++i) { int modIndex = m_CurrentProfile->modIndexByPriority(i); ModInfo::Ptr modInfo = ModInfo::getByIndex(modIndex); @@ -3336,12 +3360,25 @@ void MainWindow::fixMods_clicked() } } + // search in overwrite + { + QDir overwriteDir(ToQString(GameInfo::instance().getOverwriteDir())); + QStringList esps = overwriteDir.entryList(espFilter); + foreach (const QString &esp, esps) { + std::map<QString, std::vector<QString> >::iterator iter = missingPlugins.find(esp); + if (iter != missingPlugins.end()) { + iter->second.push_back("<overwrite>"); + } + } + } + + ActivateModsDialog dialog(missingPlugins, this); if (dialog.exec() == QDialog::Accepted) { // activate the required mods, then enable all esps std::set<QString> modsToActivate = dialog.getModsToActivate(); for (std::set<QString>::iterator iter = modsToActivate.begin(); iter != modsToActivate.end(); ++iter) { - if (*iter != "<data>") { + if ((*iter != "<data>") && (*iter != "<overwrite>")) { unsigned int modIndex = ModInfo::getIndex(*iter); m_CurrentProfile->setModEnabled(modIndex, true); } @@ -3496,6 +3533,12 @@ void MainWindow::nexusLinkActivated(const QString &link) } +void MainWindow::linkClicked(const QString &url) +{ + ::ShellExecuteW(NULL, L"open", ToWString(url).c_str(), NULL, NULL, SW_SHOWNORMAL); +} + + void MainWindow::downloadRequestedNXM(const QString &url) { QString username, password; diff --git a/src/mainwindow.h b/src/mainwindow.h index 23d5f0da..d7d0a8e7 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -362,6 +362,8 @@ private slots: void checkModsForUpdates(); void nexusLinkActivated(const QString &link); + void linkClicked(const QString &url); + void loginSuccessful(bool necessary); void loginSuccessfulUpdate(bool necessary); void loginFailed(const QString &message); diff --git a/src/mainwindow.ui b/src/mainwindow.ui index c019ccd9..fcdbd63c 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -31,7 +31,16 @@ <property name="spacing">
<number>4</number>
</property>
- <property name="margin">
+ <property name="leftMargin">
+ <number>6</number>
+ </property>
+ <property name="topMargin">
+ <number>6</number>
+ </property>
+ <property name="rightMargin">
+ <number>6</number>
+ </property>
+ <property name="bottomMargin">
<number>6</number>
</property>
<item>
@@ -719,19 +728,18 @@ p, li { white-space: pre-wrap; } <string notr="true">Archives</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_9">
- <property name="margin">
+ <property name="leftMargin">
+ <number>6</number>
+ </property>
+ <property name="topMargin">
+ <number>6</number>
+ </property>
+ <property name="rightMargin">
+ <number>6</number>
+ </property>
+ <property name="bottomMargin">
<number>6</number>
</property>
- <item>
- <widget class="QLabel" name="label">
- <property name="text">
- <string>IMPORTANT: You can change the order of BSAs here but installation order of mods has priority over the order specified here!</string>
- </property>
- <property name="wordWrap">
- <bool>true</bool>
- </property>
- </widget>
- </item>
<item>
<widget class="QTreeWidget" name="bsaList">
<property name="contextMenuPolicy">
@@ -749,14 +757,17 @@ BSAs checked here are loaded in such a way that your installation order is obeye <property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
+ <property name="showDropIndicator" stdset="0">
+ <bool>false</bool>
+ </property>
<property name="dragEnabled">
- <bool>true</bool>
+ <bool>false</bool>
</property>
<property name="dragDropOverwriteMode">
<bool>false</bool>
</property>
<property name="dragDropMode">
- <enum>QAbstractItemView::InternalMove</enum>
+ <enum>QAbstractItemView::NoDragDrop</enum>
</property>
<property name="defaultDropAction">
<enum>Qt::IgnoreAction</enum>
@@ -768,10 +779,10 @@ BSAs checked here are loaded in such a way that your installation order is obeye <enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="indentation">
- <number>0</number>
+ <number>20</number>
</property>
<property name="itemsExpandable">
- <bool>false</bool>
+ <bool>true</bool>
</property>
<attribute name="headerDefaultSectionSize">
<number>200</number>
@@ -788,6 +799,16 @@ BSAs checked here are loaded in such a way that your installation order is obeye </column>
</widget>
</item>
+ <item>
+ <widget class="QLabel" name="bsaWarning">
+ <property name="text">
+ <string><html><head/><body><p>Marked Archives (<img src=":/MO/gui/resources/dialog-warning_16.png"/>) are still loaded on Skyrim but the <a href="http://forums.bethsoft.com/topic/1354395-update-bsas-and-you/"><span style=" text-decoration: underline; color:#0000ff;">regular file override</span></a> mechanism will apply: Loose files override BSAs, no matter the mod/plugin priority.</p></body></html></string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
</layout>
</widget>
<widget class="QWidget" name="dataTab">
@@ -795,7 +816,16 @@ BSAs checked here are loaded in such a way that your installation order is obeye <string>Data</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_5">
- <property name="margin">
+ <property name="leftMargin">
+ <number>6</number>
+ </property>
+ <property name="topMargin">
+ <number>6</number>
+ </property>
+ <property name="rightMargin">
+ <number>6</number>
+ </property>
+ <property name="bottomMargin">
<number>6</number>
</property>
<item>
@@ -865,7 +895,16 @@ BSAs checked here are loaded in such a way that your installation order is obeye <string>Saves</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_3">
- <property name="margin">
+ <property name="leftMargin">
+ <number>6</number>
+ </property>
+ <property name="topMargin">
+ <number>6</number>
+ </property>
+ <property name="rightMargin">
+ <number>6</number>
+ </property>
+ <property name="bottomMargin">
<number>6</number>
</property>
<item>
@@ -894,7 +933,16 @@ p, li { white-space: pre-wrap; } <string>Downloads</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_7">
- <property name="margin">
+ <property name="leftMargin">
+ <number>2</number>
+ </property>
+ <property name="topMargin">
+ <number>2</number>
+ </property>
+ <property name="rightMargin">
+ <number>2</number>
+ </property>
+ <property name="bottomMargin">
<number>2</number>
</property>
<item>
diff --git a/src/modinfo.cpp b/src/modinfo.cpp index 1bb2daad..f6043d97 100644 --- a/src/modinfo.cpp +++ b/src/modinfo.cpp @@ -298,7 +298,7 @@ ModInfoRegular::ModInfoRegular(const QDir &path, DirectoryEntry **directoryStruc m_EndorsedState(ENDORSED_UNKNOWN), m_DirectoryStructure(directoryStructure) { testValid(); - + m_CreationTime = QFileInfo(path.absolutePath()).created(); // read out the meta-file for information QString metaFileName = path.absoluteFilePath("meta.ini"); QSettings metaFile(metaFileName, QSettings::IniFormat); @@ -645,6 +645,11 @@ QString ModInfoRegular::notes() const return m_Notes; } +QDateTime ModInfoRegular::creationTime() const +{ + return m_CreationTime; +} + QString ModInfoRegular::getNexusDescription() const { return m_NexusDescription; @@ -659,55 +664,63 @@ ModInfoRegular::EEndorsedState ModInfoRegular::endorsedState() const ModInfoRegular::EConflictType ModInfoRegular::isConflicted() const { - bool overwrite = false; - bool overwritten = false; - bool regular = false; + // this is costy so cache the result + QTime now = QTime::currentTime(); + if (abs(m_LastConflictCheck.secsTo(now)) > 10) { + bool overwrite = false; + bool overwritten = false; + bool regular = false; - int dataID = 0; - if ((*m_DirectoryStructure)->originExists(L"data")) { - dataID = (*m_DirectoryStructure)->getOriginByName(L"data").getID(); - } + int dataID = 0; + if ((*m_DirectoryStructure)->originExists(L"data")) { + dataID = (*m_DirectoryStructure)->getOriginByName(L"data").getID(); + } - std::wstring name = ToWString(m_Name); - if ((*m_DirectoryStructure)->originExists(name)) { - FilesOrigin &origin = (*m_DirectoryStructure)->getOriginByName(name); - std::vector<FileEntry::Ptr> files = origin.getFiles(); - for (auto iter = files.begin(); iter != files.end() && (!overwrite || !overwritten || !regular); ++iter) { - const std::vector<int> &alternatives = (*iter)->getAlternatives(); - if (alternatives.size() == 0) { - // no alternatives -> no conflict - regular = true; - } else { - for (auto altIter = alternatives.begin(); altIter != alternatives.end(); ++altIter) { - // don't treat files overwritten in data as "conflict" - if (*altIter != dataID) { - bool ignore = false; - if ((*iter)->getOrigin(ignore) == origin.getID()) { - overwrite = true; - break; - } else { - overwritten = true; - break; + std::wstring name = ToWString(m_Name); + if ((*m_DirectoryStructure)->originExists(name)) { + FilesOrigin &origin = (*m_DirectoryStructure)->getOriginByName(name); + std::vector<FileEntry::Ptr> files = origin.getFiles(); + for (auto iter = files.begin(); iter != files.end() && (!overwrite || !overwritten || !regular); ++iter) { + const std::vector<int> &alternatives = (*iter)->getAlternatives(); + if (alternatives.size() == 0) { + // no alternatives -> no conflict + regular = true; + } else { + for (auto altIter = alternatives.begin(); altIter != alternatives.end(); ++altIter) { + // don't treat files overwritten in data as "conflict" + if (*altIter != dataID) { + bool ignore = false; + if ((*iter)->getOrigin(ignore) == origin.getID()) { + overwrite = true; + break; + } else { + overwritten = true; + break; + } + } else if (alternatives.size() == 1) { + // only alternative is data -> no conflict + regular = true; } - } else if (alternatives.size() == 1) { - // only alternative is data -> no conflict - regular = true; } } } } - } - if (overwrite && overwritten) return CONFLICT_MIXED; - else if (overwrite) return CONFLICT_OVERWRITE; - else if (overwritten) { - if (!regular) { - return CONFLICT_REDUNDANT; - } else { - return CONFLICT_OVERWRITTEN; + m_LastConflictCheck = QTime::currentTime(); + + if (overwrite && overwritten) m_CurrentConflictState = CONFLICT_MIXED; + else if (overwrite) m_CurrentConflictState = CONFLICT_OVERWRITE; + else if (overwritten) { + if (!regular) { + m_CurrentConflictState = CONFLICT_REDUNDANT; + } else { + m_CurrentConflictState = CONFLICT_OVERWRITTEN; + } } + else m_CurrentConflictState = CONFLICT_NONE; } - else return CONFLICT_NONE; + + return m_CurrentConflictState; } diff --git a/src/modinfo.h b/src/modinfo.h index 74a77cd2..e6c6e6ac 100644 --- a/src/modinfo.h +++ b/src/modinfo.h @@ -354,11 +354,16 @@ public: virtual QString getDescription() const = 0; /** - * @return manually set notes for this mod + * @return notes for this mod */ virtual QString notes() const = 0; /** + * @return creation time of this mod + */ + virtual QDateTime creationTime() const = 0; + + /** * @return nexus description of the mod (html) */ virtual QString getNexusDescription() const = 0; @@ -670,6 +675,11 @@ public: virtual QString notes() const; /** + * @return time this mod was created (file time of the directory) + */ + virtual QDateTime creationTime() const; + + /** * @return nexus description of the mod (html) */ QString getNexusDescription() const; @@ -729,6 +739,7 @@ private: QString m_Notes; QString m_NexusDescription; + QDateTime m_CreationTime; QDateTime m_LastNexusQuery; int m_NexusID; @@ -742,6 +753,9 @@ private: MOShared::DirectoryEntry **m_DirectoryStructure; + mutable EConflictType m_CurrentConflictState; + mutable QTime m_LastConflictCheck; + }; @@ -797,6 +811,7 @@ public: virtual void endorse(bool) {} virtual QString name() const { return tr("Overwrite"); } virtual QString notes() const { return ""; } + virtual QDateTime creationTime() const { return QDateTime::currentDateTime(); } virtual QString absolutePath() const; virtual MOBase::VersionInfo getNewestVersion() const { return ""; } virtual QString getInstallationFile() const { return ""; } diff --git a/src/modlist.cpp b/src/modlist.cpp index 45bed00a..9c537300 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -178,6 +178,8 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const //return tr("None"); return QVariant(); } + } else if (column == COL_INSTALLTIME) { + return modInfo->creationTime(); } else { return tr("invalid"); } @@ -718,6 +720,7 @@ QString ModList::getColumnName(int column) case COL_PRIORITY: return tr("Priority"); case COL_CATEGORY: return tr("Category"); case COL_MODID: return tr("Nexus ID"); + case COL_INSTALLTIME: return tr("Installation"); default: return tr("unknown"); } } @@ -733,6 +736,7 @@ QString ModList::getColumnToolTip(int column) case COL_CATEGORY: return tr("Category of the mod."); case COL_MODID: return tr("Id of the mod as used on Nexus."); case COL_FLAGS: return tr("Emblemes to highlight things that might require attention."); + case COL_INSTALLTIME: return tr("Time this mod was installed"); default: return tr("unknown"); } } diff --git a/src/modlist.h b/src/modlist.h index 4f1bf85e..069de40b 100644 --- a/src/modlist.h +++ b/src/modlist.h @@ -53,6 +53,7 @@ public: COL_CATEGORY, COL_MODID, COL_VERSION, + COL_INSTALLTIME, COL_PRIORITY, COL_LASTCOLUMN = COL_PRIORITY diff --git a/src/shared/directoryentry.cpp b/src/shared/directoryentry.cpp index ceba113d..7897b6aa 100644 --- a/src/shared/directoryentry.cpp +++ b/src/shared/directoryentry.cpp @@ -699,11 +699,12 @@ const FileEntry::Ptr DirectoryEntry::searchFile(const std::wstring &path, const *directory = NULL;
}
- if ((path.length() == 0) ||
- (path == L"*")) {
+ if ((path.length() == 0) || (path == L"*")) {
// no file name -> the path ended on a (back-)slash
- *directory = this;
- return NULL;
+ if (directory != NULL) {
+ *directory = this;
+ }
+ return FileEntry::Ptr();
}
size_t len = path.find_first_of(L"\\/");
@@ -727,9 +728,10 @@ const FileEntry::Ptr DirectoryEntry::searchFile(const std::wstring &path, const return temp->searchFile(path.substr(len + 1), directory);
}
}
- return NULL;
+ return FileEntry::Ptr();
}
+
DirectoryEntry *DirectoryEntry::findSubDirectory(const std::wstring &name) const
{
for (std::vector<DirectoryEntry*>::const_iterator iter = m_SubDirectories.begin(); iter != m_SubDirectories.end(); ++iter) {
@@ -747,7 +749,7 @@ const FileEntry::Ptr DirectoryEntry::findFile(const std::wstring &name) if (iter != m_Files.end()) {
return m_FileRegister->getFile(iter->second);
} else {
- return NULL;
+ return FileEntry::Ptr();
}
}
@@ -828,7 +830,7 @@ FileEntry::Ptr FileRegister::getFile(FileEntry::Index index) if (iter != m_Files.end()) {
return iter->second;
}
- return NULL;
+ return FileEntry::Ptr();
}
|
