diff options
Diffstat (limited to 'src')
31 files changed, 553 insertions, 301 deletions
diff --git a/src/categories.h b/src/categories.h index 31fccd6d..bfebe1cb 100644 --- a/src/categories.h +++ b/src/categories.h @@ -47,6 +47,8 @@ public: static const int CATEGORY_SPECIAL_NOCATEGORY = 10003; static const int CATEGORY_SPECIAL_CONFLICT = 10004; static const int CATEGORY_SPECIAL_NOTENDORSED = 10005; + static const int CATEGORY_SPECIAL_MANAGED = 10006; + static const int CATEGORY_SPECIAL_UNMANAGED = 10007; public: diff --git a/src/directoryrefresher.cpp b/src/directoryrefresher.cpp index 33bde469..a00907a1 100644 --- a/src/directoryrefresher.cpp +++ b/src/directoryrefresher.cpp @@ -63,7 +63,6 @@ void DirectoryRefresher::setMods(const std::vector<std::tuple<QString, QString, m_EnabledArchives = managedArchives; } - void DirectoryRefresher::cleanStructure(DirectoryEntry *structure) { static wchar_t *files[] = { L"meta.ini", L"readme.txt" }; @@ -77,21 +76,27 @@ void DirectoryRefresher::cleanStructure(DirectoryEntry *structure) } } -void DirectoryRefresher::addModToStructure(DirectoryEntry *directoryStructure - , const QString &modName - , int priority - , const QString &directory - , const QStringList &stealFiles - , const QStringList &archives) +void DirectoryRefresher::addModBSAToStructure(DirectoryEntry *directoryStructure, const QString &modName, int priority, const QString &directory, const QStringList &archives) { std::wstring directoryW = ToWString(QDir::toNativeSeparators(directory)); - std::wstring modNameW = ToWString(modName); + foreach (const QString &archive, archives) { + QFileInfo fileInfo(archive); + if (m_EnabledArchives.find(fileInfo.fileName()) != m_EnabledArchives.end()) { + directoryStructure->addFromBSA(ToWString(modName), directoryW, + ToWString(QDir::toNativeSeparators(fileInfo.absoluteFilePath())), priority); + } + } +} + +void DirectoryRefresher::addModFilesToStructure(DirectoryEntry *directoryStructure, const QString &modName, int priority, const QString &directory, const QStringList &stealFiles) +{ + std::wstring directoryW = ToWString(QDir::toNativeSeparators(directory)); if (stealFiles.length() > 0) { // instead of adding all the files of the target directory, we just change the root of the specified // files to this mod - directoryStructure->createOrigin(modNameW, directoryW, priority); + FilesOrigin origin = directoryStructure->createOrigin(ToWString(modName), directoryW, priority); foreach (const QString &filename, stealFiles) { QFileInfo fileInfo(filename); FileEntry::Ptr file = directoryStructure->findFile(ToWString(fileInfo.fileName())); @@ -99,30 +104,24 @@ void DirectoryRefresher::addModToStructure(DirectoryEntry *directoryStructure if (file->getOrigin() == 0) { // replace data as the origin on this bsa file->removeOrigin(0); - file->addOrigin(directoryStructure->getOriginByName(modNameW).getID(), - file->getFileTime(), L""); + file->addOrigin(origin.getID(), file->getFileTime(), L""); } } } } else { - directoryStructure->addFromOrigin(modNameW, directoryW, priority); + directoryStructure->addFromOrigin(ToWString(modName), directoryW, priority); } -/* QDir dir(directory); - QFileInfoList bsaFiles = dir.entryInfoList(QStringList("*.bsa"), QDir::Files); - foreach (QFileInfo file, bsaFiles) { - if (m_EnabledArchives.find(file.fileName()) != m_EnabledArchives.end()) { - directoryStructure->addFromBSA(ToWString(modName), directoryW, - ToWString(QDir::toNativeSeparators(file.absoluteFilePath())), priority); - } - }*/ +} - foreach (const QString &archive, archives) { - QFileInfo fileInfo(archive); - if (m_EnabledArchives.find(fileInfo.fileName()) != m_EnabledArchives.end()) { - directoryStructure->addFromBSA(modNameW, directoryW, - ToWString(QDir::toNativeSeparators(fileInfo.absoluteFilePath())), priority); - } - } +void DirectoryRefresher::addModToStructure(DirectoryEntry *directoryStructure + , const QString &modName + , int priority + , const QString &directory + , const QStringList &stealFiles + , const QStringList &archives) +{ + addModFilesToStructure(directoryStructure, modName, priority, directory, stealFiles); + addModBSAToStructure(directoryStructure, modName, priority, directory, archives); } void DirectoryRefresher::refresh() diff --git a/src/directoryrefresher.h b/src/directoryrefresher.h index f828d793..66e85465 100644 --- a/src/directoryrefresher.h +++ b/src/directoryrefresher.h @@ -79,13 +79,35 @@ public: /** * @brief add files for a mod to the directory structure, including bsas + * @param directoryStructure * @param modName - * @param priorityBSA + * @param priority * @param directory - * @param priorityDir + * @param stealFiles + * @param archives */ void addModToStructure(MOShared::DirectoryEntry *directoryStructure, const QString &modName, int priority, const QString &directory, const QStringList &stealFiles, const QStringList &archives); + /** + * @brief add only the bsas of a mod to the directory structure + * @param directoryStructure + * @param modName + * @param priority + * @param directory + * @param archives + */ + void addModBSAToStructure(MOShared::DirectoryEntry *directoryStructure, const QString &modName, int priority, const QString &directory, const QStringList &archives); + + /** + * @brief add only regular files ofr a mod to the directory structure + * @param directoryStructure + * @param modName + * @param priority + * @param directory + * @param stealFiles + */ + void addModFilesToStructure(MOShared::DirectoryEntry *directoryStructure, const QString &modName, int priority, const QString &directory, const QStringList &stealFiles); + public slots: /** diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 8fcd4024..c6413fc1 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -164,7 +164,7 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, QWidget m_Updater(NexusInterface::instance(), this), m_CategoryFactory(CategoryFactory::instance()), m_CurrentProfile(NULL), m_AskForNexusPW(false), m_ArchivesInit(false), m_ContextItem(NULL), m_ContextAction(NULL), m_CurrentSaveView(NULL), - m_GameInfo(new GameInfoImpl()), m_AboutToRun(), m_ModInstalled() + m_GameInfo(new GameInfoImpl()), m_AboutToRun(), m_ModInstalled(), m_DidUpdateMasterList(false) { ui->setupUi(this); this->setWindowTitle(ToQString(GameInfo::instance().getGameName()) + " Mod Organizer v" + m_Updater.getVersion().displayString()); @@ -193,7 +193,7 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, QWidget updateToolBar(); - ModInfo::updateFromDisc(m_Settings.getModDirectory(), &m_DirectoryStructure); + ModInfo::updateFromDisc(m_Settings.getModDirectory(), &m_DirectoryStructure, m_Settings.displayForeign()); // set up mod list m_ModListSortProxy = new ModListSortProxy(m_CurrentProfile, this); @@ -320,6 +320,9 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, QWidget m_CheckBSATimer.setSingleShot(true); connect(&m_CheckBSATimer, SIGNAL(timeout()), this, SLOT(checkBSAList())); + m_UpdateProblemsTimer.setSingleShot(true); + connect(&m_UpdateProblemsTimer, SIGNAL(timeout()), this, SLOT(updateProblemsButton())); + m_SaveMetaTimer.setSingleShot(false); connect(&m_SaveMetaTimer, SIGNAL(timeout()), this, SLOT(saveModMetas())); m_SaveMetaTimer.start(5000); @@ -530,6 +533,14 @@ void MainWindow::updateToolBar() } +void MainWindow::scheduleUpdateButton() +{ + if (!m_UpdateProblemsTimer.isActive()) { + m_UpdateProblemsTimer.start(1000); + } +} + + void MainWindow::updateProblemsButton() { int numProblems = checkForProblems(); @@ -661,7 +672,7 @@ void MainWindow::createHelpWidget() } -void MainWindow::saveArchiveList() +bool MainWindow::saveArchiveList() { if (m_ArchivesInit) { SafeWriteFile archiveFile(m_CurrentProfile->getArchivesFileName()); @@ -680,10 +691,12 @@ void MainWindow::saveArchiveList() } if (archiveFile.commitIfDifferent(m_ArchiveListHash)) { qDebug("%s saved", qPrintable(QDir::toNativeSeparators(m_CurrentProfile->getArchivesFileName()))); + return true; } } else { qWarning("archive list not initialised"); } + return false; } void MainWindow::savePluginList() @@ -1034,31 +1047,48 @@ void MainWindow::toolPluginInvoke() void MainWindow::requestDownload(const QUrl &url, QNetworkReply *reply) { QToolButton *browserBtn = qobject_cast<QToolButton*>(ui->toolBar->widgetForAction(ui->actionNexus)); - - QList<QAction*> browserActions = browserBtn->menu()->actions(); - foreach (QAction *action, browserActions) { - // the nexus action doesn't have a plugin connected currently - if (action->data().isValid()) { - IPluginModPage *plugin = qobject_cast<IPluginModPage*>(qvariant_cast<QObject*>(action->data())); - if (plugin == NULL) { - qCritical("invalid mod page. This is a bug"); - continue; - } - ModRepositoryFileInfo *fileInfo = new ModRepositoryFileInfo(); - if (plugin->handlesDownload(url, reply->url(), *fileInfo)) { - fileInfo->repository = plugin->name(); - m_DownloadManager.addDownload(reply, fileInfo); - return; + if (browserBtn->menu() != NULL) { + // go through modpage plugins, find one to handle the download. + QList<QAction*> browserActions = browserBtn->menu()->actions(); + foreach (QAction *action, browserActions) { + // the nexus action doesn't have a plugin connected currently + if (action->data().isValid()) { + IPluginModPage *plugin = qobject_cast<IPluginModPage*>(qvariant_cast<QObject*>(action->data())); + if (plugin == NULL) { + qCritical("invalid mod page. This is a bug"); + continue; + } + ModRepositoryFileInfo *fileInfo = new ModRepositoryFileInfo(); + if (plugin->handlesDownload(url, reply->url(), *fileInfo)) { + fileInfo->repository = plugin->name(); + m_DownloadManager.addDownload(reply, fileInfo); + return; + } } } } - if (QMessageBox::question(this, tr("Download?"), - tr("A download has been started but no installed page plugin recognizes it.\n" - "If you download anyway no information (i.e. version) will be associated with the download.\n" - "Continue?"), - QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { - m_DownloadManager.addDownload(reply, new ModRepositoryFileInfo()); + // no mod found that could handle the download. Is it a nexus mod? + if (url.host() == "www.nexusmods.com") { + int modID = 0; + int fileID = 0; + QRegExp modExp("mods/(\\d+)"); + if (modExp.indexIn(url.toString()) != -1) { + modID = modExp.cap(1).toInt(); + } + QRegExp fileExp("fid=(\\d+)"); + if (fileExp.indexIn(reply->url().toString()) != -1) { + fileID = fileExp.cap(1).toInt(); + } + m_DownloadManager.addDownload(reply, new ModRepositoryFileInfo(modID, fileID)); + } else { + if (QMessageBox::question(this, tr("Download?"), + tr("A download has been started but no installed page plugin recognizes it.\n" + "If you download anyway no information (i.e. version) will be associated with the download.\n" + "Continue?"), + QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { + m_DownloadManager.addDownload(reply, new ModRepositoryFileInfo()); + } } } @@ -1131,7 +1161,7 @@ bool MainWindow::registerPlugin(QObject *plugin, const QString &fileName) IPluginDiagnose *diagnose = qobject_cast<IPluginDiagnose*>(plugin); if (diagnose != NULL) { m_DiagnosisPlugins.push_back(diagnose); - diagnose->onInvalidated([&] () { this->updateProblemsButton(); }); + diagnose->onInvalidated([&] () { this->scheduleUpdateButton(); }); } } { // mod page plugin @@ -1401,10 +1431,8 @@ void MainWindow::setExecutableIndex(int index) } else { executableBox->setCurrentIndex(1); } - } - void MainWindow::activateSelectedProfile() { QString profileName = ui->profileBox->currentText(); @@ -1423,7 +1451,6 @@ void MainWindow::activateSelectedProfile() refreshModList(); } - void MainWindow::on_profileBox_currentIndexChanged(int index) { if (ui->profileBox->isEnabled()) { @@ -1629,8 +1656,6 @@ bool MainWindow::refreshProfiles(bool selectProfile) std::set<QString> MainWindow::enabledArchives() { std::set<QString> result; - - QFile archiveFile(m_CurrentProfile->getArchivesFileName()); if (archiveFile.open(QIODevice::ReadOnly)) { while (!archiveFile.atEnd()) { @@ -1641,7 +1666,6 @@ std::set<QString> MainWindow::enabledArchives() return result; } - void MainWindow::refreshDirectoryStructure() { m_DirectoryUpdate = true; @@ -1655,7 +1679,6 @@ void MainWindow::refreshDirectoryStructure() QTimer::singleShot(0, &m_DirectoryRefresher, SLOT(refresh())); } - #if QT_VERSION >= 0x050000 extern QPixmap qt_pixmapFromWinHICON(HICON icon); #else @@ -1675,7 +1698,6 @@ QIcon MainWindow::iconForExecutable(const QString &filePath) } } - void MainWindow::refreshExecutablesList() { QComboBox* executablesList = findChild<QComboBox*>("executablesListBox"); @@ -1788,7 +1810,8 @@ void MainWindow::refreshModList(bool saveChanges) if (saveChanges) { m_CurrentProfile->writeModlistNow(true); } - ModInfo::updateFromDisc(m_Settings.getModDirectory(), &m_DirectoryStructure); + ModInfo::updateFromDisc(m_Settings.getModDirectory(), &m_DirectoryStructure, m_Settings.displayForeign()); + m_CurrentProfile->refreshModStatus(); m_ModList.notifyChange(-1); @@ -1952,15 +1975,6 @@ void MainWindow::checkBSAList() item->setIcon(0, QIcon(":/MO/gui/warning")); 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/warning")); - 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; - }*/ } } } @@ -2615,7 +2629,6 @@ void MainWindow::directory_refreshed() if (m_CurrentProfile != NULL) { refreshLists(); } -// m_RefreshProgress->setVisible(false); // some problem-reports may rely on the virtual directory tree so they need to be updated // now @@ -2628,7 +2641,6 @@ void MainWindow::directory_refreshed() statusBar()->hide(); } - void MainWindow::externalMessage(const QString &message) { if (message.left(6).toLower() == "nxm://") { @@ -2637,31 +2649,54 @@ void MainWindow::externalMessage(const QString &message) } } +void MainWindow::updateModInDirectoryStructure(unsigned int index, ModInfo::Ptr modInfo) +{ + // add files of the bsa to the directory structure + m_DirectoryRefresher.addModFilesToStructure(m_DirectoryStructure + , modInfo->name() + , m_CurrentProfile->getModPriority(index) + , modInfo->absolutePath() + , modInfo->stealFiles() + ); + DirectoryRefresher::cleanStructure(m_DirectoryStructure); + // need to refresh plugin list now so we can activate esps + refreshESPList(); + // activate all esps of the specified mod so the bsas get activated along with it + updateModActiveState(index, true); + // now we need to refresh the bsa list and save it so there is no confusion about what archives are avaiable and active + refreshBSAList(); + saveArchiveList(); + m_DirectoryRefresher.setMods(m_CurrentProfile->getActiveMods(), enabledArchives()); + + // finally also add files from bsas to the directory structure + m_DirectoryRefresher.addModBSAToStructure(m_DirectoryStructure + , modInfo->name() + , m_CurrentProfile->getModPriority(index) + , modInfo->absolutePath() + , modInfo->archives() + ); +} void MainWindow::modStatusChanged(unsigned int index) { try { ModInfo::Ptr modInfo = ModInfo::getByIndex(index); if (m_CurrentProfile->modEnabled(index)) { - m_DirectoryRefresher.addModToStructure(m_DirectoryStructure - , modInfo->name() - , m_CurrentProfile->getModPriority(index) - , modInfo->absolutePath() - , modInfo->stealFiles() - , modInfo->archives()); - DirectoryRefresher::cleanStructure(m_DirectoryStructure); + updateModInDirectoryStructure(index, modInfo); } else { if (m_DirectoryStructure->originExists(ToWString(modInfo->name()))) { FilesOrigin &origin = m_DirectoryStructure->getOriginByName(ToWString(modInfo->name())); origin.enable(false); } } + modInfo->clearCaches(); for (unsigned int i = 0; i < m_CurrentProfile->numMods(); ++i) { ModInfo::Ptr modInfo = ModInfo::getByIndex(i); int priority = m_CurrentProfile->getModPriority(i); if (m_DirectoryStructure->originExists(ToWString(modInfo->name()))) { - m_DirectoryStructure->getOriginByName(ToWString(modInfo->name())).setPriority(priority); + // priorities in the directory structure are one higher because data is 0 + m_DirectoryStructure->getOriginByName(ToWString(modInfo->name())).setPriority(priority + 1); } } m_DirectoryStructure->getFileRegister()->sortOrigins(); @@ -2687,7 +2722,8 @@ void MainWindow::modorder_changed() int priority = m_CurrentProfile->getModPriority(i); if (m_CurrentProfile->modEnabled(i)) { ModInfo::Ptr modInfo = ModInfo::getByIndex(i); - m_DirectoryStructure->getOriginByName(ToWString(modInfo->name())).setPriority(priority); + // priorities in the directory structure are one higher because data is 0 + m_DirectoryStructure->getOriginByName(ToWString(modInfo->name())).setPriority(priority + 1); } } refreshBSAList(); @@ -2706,33 +2742,30 @@ void MainWindow::procFinished(int, QProcess::ExitStatus) this->sender()->deleteLater(); } - void MainWindow::profileRefresh() { - m_CurrentProfile->writeModlist(); + // have to refresh mods twice (again in refreshModList), otherwise the refresh isn't complete. Not sure why + ModInfo::updateFromDisc(m_Settings.getModDirectory(), &m_DirectoryStructure, m_Settings.displayForeign()); + m_CurrentProfile->refreshModStatus(); refreshModList(); } - void MainWindow::showMessage(const QString &message) { MessageDialog::showMessage(message, this); } - void MainWindow::showError(const QString &message) { reportError(message); } - void MainWindow::installMod_clicked() { installMod(); } - void MainWindow::renameModInList(QFile &modList, const QString &oldName, const QString &newName) { //TODO this code needs to be merged with ModList::readFrom @@ -2894,6 +2927,8 @@ void MainWindow::refreshFilters() addFilterItem(NULL, tr("<Checked>"), CategoryFactory::CATEGORY_SPECIAL_CHECKED); addFilterItem(NULL, tr("<Unchecked>"), CategoryFactory::CATEGORY_SPECIAL_UNCHECKED); addFilterItem(NULL, tr("<Update>"), CategoryFactory::CATEGORY_SPECIAL_UPDATEAVAILABLE); + addFilterItem(NULL, tr("<Managed by MO>"), CategoryFactory::CATEGORY_SPECIAL_MANAGED); + addFilterItem(NULL, tr("<Managed outside MO>"), CategoryFactory::CATEGORY_SPECIAL_UNMANAGED); addFilterItem(NULL, tr("<No category>"), CategoryFactory::CATEGORY_SPECIAL_NOCATEGORY); addFilterItem(NULL, tr("<Conflicted>"), CategoryFactory::CATEGORY_SPECIAL_CONFLICT); addFilterItem(NULL, tr("<Not Endorsed>"), CategoryFactory::CATEGORY_SPECIAL_NOTENDORSED); @@ -2958,37 +2993,37 @@ void MainWindow::restoreBackup_clicked() } } - -void MainWindow::modlistChanged(const QModelIndex &index, int role) +void MainWindow::updateModActiveState(int index, bool active) { - if (role == Qt::CheckStateRole) { - if (index.data(Qt::CheckStateRole).toBool()) { - ModInfo::Ptr modInfo = ModInfo::getByIndex(index.row()); - - QDir dir(modInfo->absolutePath()); - foreach (const QString &esm, dir.entryList(QStringList("*.esm"), QDir::Files)) { - m_PluginList.enableESP(esm); - } + ModInfo::Ptr modInfo = ModInfo::getByIndex(index); - int enabled = 0; - QStringList esps = dir.entryList(QStringList("*.esp"), QDir::Files); - foreach (const QString &esp, esps) { - if (!m_PluginList.isEnabled(esp)) { - m_PluginList.enableESP(esp); - ++enabled; - } - } - if (enabled > 1) { - MessageDialog::showMessage(tr("Multiple esps activated, please check that they don't conflict."), this); - } - m_PluginList.refreshLoadOrder(); - // immediately save affected lists - savePluginList(); - saveArchiveList(); + QDir dir(modInfo->absolutePath()); + foreach (const QString &esm, dir.entryList(QStringList("*.esm"), QDir::Files)) { + m_PluginList.enableESP(esm, active); + } + int enabled = 0; + QStringList esps = dir.entryList(QStringList("*.esp"), QDir::Files); + foreach (const QString &esp, esps) { + if (active && !m_PluginList.isEnabled(esp)) { + m_PluginList.enableESP(esp, active); + ++enabled; } } + if (active && (enabled > 1)) { + MessageDialog::showMessage(tr("Multiple esps activated, please check that they don't conflict."), this); + } + m_PluginList.refreshLoadOrder(); + // immediately save affected lists + savePluginList(); +// refreshBSAList(); } +void MainWindow::modlistChanged(const QModelIndex&, int) +{ +/* if (role == Qt::CheckStateRole) { + updateModActiveState(index.row(), index.data(Qt::CheckStateRole).toBool()); + }*/ +} void MainWindow::removeMod_clicked() { @@ -3145,12 +3180,9 @@ void MainWindow::displayModInformation(ModInfo::Ptr modInfo, unsigned int index, dialog->raise(); dialog->activateWindow(); connect(dialog, SIGNAL(finished(int)), this, SLOT(overwriteClosed(int))); - } else if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_FOREIGN) != flags.end()) { - // nop - no menu for this file type - return; } else { modInfo->saveMeta(); - ModInfoDialog dialog(modInfo, m_DirectoryStructure, this); + ModInfoDialog dialog(modInfo, m_DirectoryStructure, modInfo->hasFlag(ModInfo::FLAG_FOREIGN), this); connect(&dialog, SIGNAL(nexusLinkActivated(QString)), this, SLOT(nexusLinkActivated(QString))); connect(&dialog, SIGNAL(downloadRequest(QString)), this, SLOT(downloadRequestedNXM(QString))); connect(&dialog, SIGNAL(modOpen(QString, int)), this, SLOT(displayModInformation(QString, int)), Qt::QueuedConnection); @@ -3707,7 +3739,7 @@ void addMenuAsPushButton(QMenu *menu, QMenu *subMenu) QMenu *MainWindow::modListContextMenu() { - QMenu *menu = new QMenu(); + QMenu *menu = new QMenu(this); menu->addAction(tr("Install Mod..."), this, SLOT(installMod_clicked())); menu->addAction(tr("Enable all visible"), this, SLOT(enableVisibleMods())); @@ -3734,7 +3766,6 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) if (m_ContextRow == -1) { // no selection menu = allMods; - menu->setParent(this); } else { menu = new QMenu(this); allMods->setTitle(tr("All Mods")); @@ -4057,6 +4088,7 @@ void MainWindow::on_actionSettings_triggered() { QString oldModDirectory(m_Settings.getModDirectory()); QString oldCacheDirectory(m_Settings.getCacheDirectory()); + bool oldDisplayForeign(m_Settings.displayForeign()); bool proxy = m_Settings.useProxy(); m_Settings.query(this); m_InstallationManager.setModsDirectory(m_Settings.getModDirectory()); @@ -4072,9 +4104,9 @@ void MainWindow::on_actionSettings_triggered() } m_DownloadManager.setPreferredServers(m_Settings.getPreferredServers()); - if (m_Settings.getModDirectory() != oldModDirectory) { - refreshModList(); - refreshLists(); + if ((m_Settings.getModDirectory() != oldModDirectory) + || (m_Settings.displayForeign() != oldDisplayForeign)) { + profileRefresh(); } if (m_Settings.getCacheDirectory() != oldCacheDirectory) { @@ -4226,7 +4258,6 @@ void MainWindow::installDownload(int index) MessageDialog::showMessage(tr("Installation successful"), this); refreshModList(); - QModelIndexList posList = m_ModList.match(m_ModList.index(0, 0), Qt::DisplayRole, static_cast<const QString&>(modName)); if (posList.count() == 1) { ui->modList->scrollTo(posList.at(0)); @@ -5254,6 +5285,11 @@ void MainWindow::on_bossButton_clicked() parameters << "--game" << ToQString(GameInfo::instance().getGameShortName()) << "--gamePath" << QString("\"%1\"").arg(ToQString(GameInfo::instance().getGameDirectory())); + if (!m_DidUpdateMasterList) { + parameters << "--updateMasterlist"; + m_DidUpdateMasterList = true; + } + HANDLE stdOutWrite = INVALID_HANDLE_VALUE; HANDLE stdOutRead = INVALID_HANDLE_VALUE; createStdoutPipe(&stdOutRead, &stdOutWrite); diff --git a/src/mainwindow.h b/src/mainwindow.h index f55a7f8f..ddbb7d6a 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -99,7 +99,6 @@ public: void readSettings(); bool addProfile(); - void refreshLists(); void refreshBSAList(); void refreshDataTree(); void refreshSaveList(); @@ -127,7 +126,7 @@ public: void addPrimaryCategoryCandidates(QMenu *primaryCategoryMenu, ModInfo::Ptr info); - void saveArchiveList(); + bool saveArchiveList(); void createStdoutPipe(HANDLE *stdOutRead, HANDLE *stdOutWrite); std::string readFromPipe(HANDLE stdOutRead); @@ -135,8 +134,12 @@ public: HANDLE startApplication(const QString &executable, const QStringList &args = QStringList(), const QString &cwd = "", const QString &profile = ""); + void updateModInDirectoryStructure(unsigned int index, ModInfo::Ptr modInfo); + public slots: + void refreshLists(); + void displayColumnSelection(const QPoint &pos); void externalMessage(const QString &message); @@ -260,8 +263,6 @@ private: void setCategoryListVisible(bool visible); - void updateProblemsButton(); - SaveGameGamebryo *getSaveGame(const QString &name); SaveGameGamebryo *getSaveGame(QListWidgetItem *item); @@ -287,6 +288,10 @@ private: std::set<QString> enabledArchives(); + void scheduleUpdateButton(); + + void updateModActiveState(int index, bool active); + private: static const unsigned int PROBLEM_PLUGINSNOTLOADED = 1; @@ -356,6 +361,7 @@ private: bool m_ArchivesInit; QTimer m_CheckBSATimer; QTimer m_SaveMetaTimer; + QTimer m_UpdateProblemsTimer; QTime m_StartTime; SaveGameInfoWidget *m_CurrentSaveView; @@ -383,6 +389,8 @@ private: uint m_ArchiveListHash; + bool m_DidUpdateMasterList; + private slots: void showMessage(const QString &message); @@ -476,7 +484,6 @@ private slots: void modlistChanged(int row); void nxmUpdatesAvailable(const std::vector<int> &modIDs, QVariant userData, QVariant resultData, int requestID); -// void nxmEndorsementToggled(int, QVariant, QVariant resultData, int); void nxmDownloadURLs(int modID, int fileID, QVariant userData, QVariant resultData, int requestID); void nxmRequestFailed(int modID, int fileID, QVariant userData, int requestID, const QString &errorString); @@ -508,6 +515,9 @@ private slots: void startExeAction(); void checkBSAList(); + + void updateProblemsButton(); + void saveModMetas(); void updateStyle(const QString &style); @@ -549,9 +559,9 @@ private slots: void delayedRemove(); void requestDownload(const QUrl &url, QNetworkReply *reply); - void profileRefresh(); private slots: // ui slots + void profileRefresh(); // actions void on_actionAdd_Profile_triggered(); void on_actionInstallMod_triggered(); diff --git a/src/modinfo.cpp b/src/modinfo.cpp index 87321b69..2dadf2c6 100644 --- a/src/modinfo.cpp +++ b/src/modinfo.cpp @@ -184,7 +184,7 @@ unsigned int ModInfo::findMod(const boost::function<bool (ModInfo::Ptr)> &filter } -void ModInfo::updateFromDisc(const QString &modDirectory, DirectoryEntry **directoryStructure) +void ModInfo::updateFromDisc(const QString &modDirectory, DirectoryEntry **directoryStructure, bool displayForeign) { QMutexLocker lock(&s_Mutex); s_Collection.clear(); @@ -200,10 +200,13 @@ void ModInfo::updateFromDisc(const QString &modDirectory, DirectoryEntry **direc } { // list plugins in the data directory and make a foreign-managed mod out of each + std::vector<std::wstring> dlcPlugins = GameInfo::instance().getDLCPlugins(); QDir dataDir(QDir::fromNativeSeparators(ToQString(GameInfo::instance().getGameDirectory())) + "/data"); foreach (const QFileInfo &file, dataDir.entryInfoList(QStringList() << "*.esp" << "*.esm")) { - if ((file.baseName() != "Update") - && (file.baseName() != ToQString(GameInfo::instance().getGameName()))) { + if ((file.baseName() != "Update") // hide update + && (file.baseName() != ToQString(GameInfo::instance().getGameName())) // hide the game esp + && (displayForeign // show non-dlc bundles only if the user wants them + || std::find(dlcPlugins.begin(), dlcPlugins.end(), ToWString(file.fileName())) != dlcPlugins.end())) { QStringList archives; foreach (const QString archiveName, dataDir.entryList(QStringList() << file.baseName() + "*.bsa")) { archives.append(dataDir.absoluteFilePath(archiveName)); @@ -279,6 +282,12 @@ void ModInfo::setVersion(const VersionInfo &version) m_Version = version; } +bool ModInfo::hasFlag(ModInfo::EFlag flag) const +{ + std::vector<EFlag> flags = getFlags(); + return std::find(flags.begin(), flags.end(), flag) != flags.end(); +} + bool ModInfo::categorySet(int categoryID) const { @@ -747,7 +756,7 @@ void ModInfoRegular::setNeverEndorse() bool ModInfoRegular::remove() { m_MetaInfoChanged = false; - return shellDelete(QStringList(absolutePath())); + return shellDelete(QStringList(absolutePath()), true); } void ModInfoRegular::endorse(bool doEndorse) diff --git a/src/modinfo.h b/src/modinfo.h index 21267068..ef647eab 100644 --- a/src/modinfo.h +++ b/src/modinfo.h @@ -87,7 +87,7 @@ public: /** * @brief read the mod directory and Mod ModInfo objects for all subdirectories **/ - static void updateFromDisc(const QString &modDirectory, MOShared::DirectoryEntry **directoryStructure); + static void updateFromDisc(const QString &modDirectory, MOShared::DirectoryEntry **directoryStructure, bool displayForeign); static void clear() { s_Collection.clear(); s_ModsByName.clear(); s_ModsByModID.clear(); } @@ -369,6 +369,13 @@ public: virtual std::vector<EFlag> getFlags() const = 0; /** + * @brief test if the specified flag is set for this mod + * @param flag the flag to test + * @return true if the flag is set, false otherwise + */ + bool hasFlag(EFlag flag) const; + + /** * @return an indicator if and how this mod should be highlighted by the UI */ virtual int getHighlight() const { return HIGHLIGHT_NONE; } @@ -911,6 +918,7 @@ public: virtual void setNeverEndorse() {} virtual bool remove() { return false; } virtual void endorse(bool) {} + virtual bool alwaysEnabled() const { return true; } virtual bool isEmpty() const; virtual QString name() const { return "Overwrite"; } virtual QString notes() const { return ""; } diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index dae4cc18..fa8a9a77 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -60,7 +60,7 @@ bool operator<(const ModFileListWidget &LHS, const ModFileListWidget &RHS) } -ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directory, QWidget *parent) +ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directory, bool unmanaged, QWidget *parent) : TutorableDialog("ModInfoDialog", parent), ui(new Ui::ModInfoDialog), m_ModInfo(modInfo), m_ThumbnailMapper(this), m_RequestStarted(false), m_DeleteAction(NULL), m_RenameAction(NULL), m_OpenAction(NULL), @@ -69,54 +69,19 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo ui->setupUi(this); this->setWindowTitle(modInfo->name()); this->setWindowModality(Qt::WindowModal); - m_UTF8Codec = QTextCodec::codecForName("utf-8"); - - QListWidget *textFileList = findChild<QListWidget*>("textFileList"); - QListWidget *iniTweaksList = findChild<QListWidget*>("iniTweaksList"); - QListWidget *activeESPList = findChild<QListWidget*>("activeESPList"); - QListWidget *inactiveESPList = findChild<QListWidget*>("inactiveESPList"); - QTreeWidget *categoriesTree = findChild<QTreeWidget*>("categoriesTree"); - QHBoxLayout *thumbnailArea = findChild<QHBoxLayout*>("thumbnailArea"); - - m_FileTree = findChild<QTreeView*>("fileTree"); - - m_RootPath = modInfo->absolutePath(); QString metaFileName = m_RootPath.mid(0).append("/meta.ini"); m_Settings = new QSettings(metaFileName, QSettings::IniFormat); QLineEdit *modIDEdit = findChild<QLineEdit*>("modIDEdit"); - modIDEdit->setValidator(new QIntValidator(modIDEdit)); - modIDEdit->setText(QString("%1").arg(modInfo->getNexusID())); + ui->modIDEdit->setValidator(new QIntValidator(modIDEdit)); + ui->modIDEdit->setText(QString("%1").arg(modInfo->getNexusID())); ui->notesEdit->setText(modInfo->notes()); - m_FileSystemModel = new QFileSystemModel(this); - m_FileSystemModel->setReadOnly(false); - m_FileSystemModel->setRootPath(m_RootPath); - m_FileTree->setModel(m_FileSystemModel); - m_FileTree->setRootIndex(m_FileSystemModel->index(m_RootPath)); - m_FileTree->setColumnWidth(0, 300); - connect(&m_ThumbnailMapper, SIGNAL(mapped(const QString&)), this, SIGNAL(thumbnailClickedSignal(const QString&))); connect(this, SIGNAL(thumbnailClickedSignal(const QString&)), this, SLOT(thumbnailClicked(const QString&))); - - m_DeleteAction = new QAction(tr("&Delete"), m_FileTree); - m_RenameAction = new QAction(tr("&Rename"), m_FileTree); - m_HideAction = new QAction(tr("&Hide"), m_FileTree); - m_UnhideAction = new QAction(tr("&Unhide"), m_FileTree); - m_OpenAction = new QAction(tr("&Open"), m_FileTree); - m_NewFolderAction = new QAction(tr("&New Folder"), m_FileTree); - QObject::connect(m_DeleteAction, SIGNAL(triggered()), this, SLOT(deleteTriggered())); - QObject::connect(m_RenameAction, SIGNAL(triggered()), this, SLOT(renameTriggered())); - QObject::connect(m_OpenAction, SIGNAL(triggered()), this, SLOT(openTriggered())); - QObject::connect(m_NewFolderAction, SIGNAL(triggered()), this, SLOT(createDirectoryTriggered())); - QObject::connect(m_HideAction, SIGNAL(triggered()), this, SLOT(hideTriggered())); - connect(m_UnhideAction, SIGNAL(triggered()), this, SLOT(unhideTriggered())); connect(m_ModInfo.data(), SIGNAL(modDetailsUpdated(bool)), this, SLOT(modDetailsUpdated(bool))); - -// ui->descriptionView->setLinkDelegationPolicy(QWebPage::DelegateAllLinks); -// QObject::connect(ui->descriptionView, SIGNAL(linkClicked(QUrl)), this, SLOT(linkClicked(QUrl))); connect(ui->descriptionView, SIGNAL(anchorClicked(QUrl)), this, SLOT(linkClicked(QUrl))); if (directory->originExists(ToWString(modInfo->name()))) { @@ -126,32 +91,39 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo } } - addCategories(CategoryFactory::instance(), modInfo->getCategories(), categoriesTree->invisibleRootItem(), 0); - refreshPrimaryCategoriesBox(); - refreshLists(); - - int numTweaks = m_Settings->beginReadArray("INI Tweaks"); - for (int i = 0; i < numTweaks; ++i) { - m_Settings->setArrayIndex(i); - QList<QListWidgetItem*> items = iniTweaksList->findItems(m_Settings->value("name").toString(), Qt::MatchFixedString); - if (items.size() != 0) { - items.at(0)->setCheckState(Qt::Checked); - } + if (unmanaged) { + ui->tabWidget->setTabEnabled(TAB_INIFILES, false); + ui->tabWidget->setTabEnabled(TAB_CATEGORIES, false); + ui->tabWidget->setTabEnabled(TAB_NEXUS, false); + ui->tabWidget->setTabEnabled(TAB_FILETREE, false); + ui->tabWidget->setTabEnabled(TAB_NOTES, false); + } else { + initFiletree(modInfo); + initINITweaks(); + addCategories(CategoryFactory::instance(), modInfo->getCategories(), ui->categoriesTree->invisibleRootItem(), 0); + refreshPrimaryCategoriesBox(); } - m_Settings->endArray(); + refreshLists(); - QTabWidget *tabWidget = findChild<QTabWidget*>("tabWidget"); - tabWidget->setTabEnabled(TAB_TEXTFILES, textFileList->count() != 0); - tabWidget->setTabEnabled(TAB_IMAGES, thumbnailArea->count() != 0); - tabWidget->setTabEnabled(TAB_ESPS, (inactiveESPList->count() != 0) || (activeESPList->count() != 0)); - tabWidget->setTabEnabled(TAB_CONFLICTS, m_Origin != NULL); + ui->tabWidget->setTabEnabled(TAB_TEXTFILES, ui->textFileList->count() != 0); + ui->tabWidget->setTabEnabled(TAB_IMAGES, ui->thumbnailArea->count() != 0); + ui->tabWidget->setTabEnabled(TAB_ESPS, (ui->inactiveESPList->count() != 0) || (ui->activeESPList->count() != 0)); + ui->tabWidget->setTabEnabled(TAB_CONFLICTS, m_Origin != NULL); - if (tabWidget->currentIndex() == TAB_NEXUS) { + if (ui->tabWidget->currentIndex() == TAB_NEXUS) { activateNexusTab(); } ui->endorseBtn->setEnabled((m_ModInfo->endorsedState() == ModInfo::ENDORSED_FALSE) || (m_ModInfo->endorsedState() == ModInfo::ENDORSED_NEVER)); + + // activate first enabled tab + for (int i = 0; i < ui->tabWidget->count(); ++i) { + if (ui->tabWidget->isTabEnabled(i)) { + ui->tabWidget->setCurrentIndex(i); + break; + } + } } @@ -165,6 +137,46 @@ ModInfoDialog::~ModInfoDialog() } +void ModInfoDialog::initINITweaks() +{ + int numTweaks = m_Settings->beginReadArray("INI Tweaks"); + for (int i = 0; i < numTweaks; ++i) { + m_Settings->setArrayIndex(i); + QList<QListWidgetItem*> items = ui->iniTweaksList->findItems(m_Settings->value("name").toString(), Qt::MatchFixedString); + if (items.size() != 0) { + items.at(0)->setCheckState(Qt::Checked); + } + } + m_Settings->endArray(); +} + +void ModInfoDialog::initFiletree(ModInfo::Ptr modInfo) +{ + m_RootPath = modInfo->absolutePath(); + ui->fileTree = findChild<QTreeView*>("fileTree"); + + m_FileSystemModel = new QFileSystemModel(this); + m_FileSystemModel->setReadOnly(false); + m_FileSystemModel->setRootPath(m_RootPath); + ui->fileTree->setModel(m_FileSystemModel); + ui->fileTree->setRootIndex(m_FileSystemModel->index(m_RootPath)); + ui->fileTree->setColumnWidth(0, 300); + + m_DeleteAction = new QAction(tr("&Delete"), ui->fileTree); + m_RenameAction = new QAction(tr("&Rename"), ui->fileTree); + m_HideAction = new QAction(tr("&Hide"), ui->fileTree); + m_UnhideAction = new QAction(tr("&Unhide"), ui->fileTree); + m_OpenAction = new QAction(tr("&Open"), ui->fileTree); + m_NewFolderAction = new QAction(tr("&New Folder"), ui->fileTree); + QObject::connect(m_DeleteAction, SIGNAL(triggered()), this, SLOT(deleteTriggered())); + QObject::connect(m_RenameAction, SIGNAL(triggered()), this, SLOT(renameTriggered())); + QObject::connect(m_OpenAction, SIGNAL(triggered()), this, SLOT(openTriggered())); + QObject::connect(m_NewFolderAction, SIGNAL(triggered()), this, SLOT(createDirectoryTriggered())); + QObject::connect(m_HideAction, SIGNAL(triggered()), this, SLOT(hideTriggered())); + connect(m_UnhideAction, SIGNAL(triggered()), this, SLOT(unhideTriggered())); +} + + int ModInfoDialog::tabIndex(const QString &tabId) { for (int i = 0; i < ui->tabWidget->count(); ++i) { @@ -271,51 +283,52 @@ void ModInfoDialog::refreshLists() } } + if (m_RootPath.length() > 0) { + QDirIterator dirIterator(m_RootPath, QDir::Files, QDirIterator::Subdirectories); + while (dirIterator.hasNext()) { + QString fileName = dirIterator.next(); - QDirIterator dirIterator(m_RootPath, QDir::Files, QDirIterator::Subdirectories); - while (dirIterator.hasNext()) { - QString fileName = dirIterator.next(); - - if (fileName.endsWith(".txt", Qt::CaseInsensitive)) { - ui->textFileList->addItem(fileName.mid(m_RootPath.length() + 1)); - } else if ((fileName.endsWith(".ini", Qt::CaseInsensitive) || fileName.endsWith(".cfg", Qt::CaseInsensitive)) && - !fileName.endsWith("meta.ini")) { - QString namePart = fileName.mid(m_RootPath.length() + 1); - if (namePart.startsWith("INI Tweaks", Qt::CaseInsensitive)) { - QListWidgetItem *newItem = new QListWidgetItem(namePart.mid(11), ui->iniTweaksList); - newItem->setData(Qt::UserRole, namePart); - newItem->setFlags(newItem->flags() | Qt::ItemIsUserCheckable); - newItem->setCheckState(Qt::Unchecked); - ui->iniTweaksList->addItem(newItem); - } else { - ui->iniFileList->addItem(namePart); - } - } else if (fileName.endsWith(".esp", Qt::CaseInsensitive) || - fileName.endsWith(".esm", Qt::CaseInsensitive)) { - QString relativePath = fileName.mid(m_RootPath.length() + 1); - if (relativePath.contains('/')) { - QFileInfo fileInfo(fileName); - QListWidgetItem *newItem = new QListWidgetItem(fileInfo.fileName()); - newItem->setData(Qt::UserRole, relativePath); - ui->inactiveESPList->addItem(newItem); - } else { - ui->activeESPList->addItem(relativePath); - } - } else if ((fileName.endsWith(".png", Qt::CaseInsensitive)) || - (fileName.endsWith(".jpg", Qt::CaseInsensitive))) { - QImage image = QImage(fileName); - if (!image.isNull()) { - if (static_cast<float>(image.width()) / static_cast<float>(image.height()) > 1.34) { - image = image.scaledToWidth(128); + if (fileName.endsWith(".txt", Qt::CaseInsensitive)) { + ui->textFileList->addItem(fileName.mid(m_RootPath.length() + 1)); + } else if ((fileName.endsWith(".ini", Qt::CaseInsensitive) || fileName.endsWith(".cfg", Qt::CaseInsensitive)) && + !fileName.endsWith("meta.ini")) { + QString namePart = fileName.mid(m_RootPath.length() + 1); + if (namePart.startsWith("INI Tweaks", Qt::CaseInsensitive)) { + QListWidgetItem *newItem = new QListWidgetItem(namePart.mid(11), ui->iniTweaksList); + newItem->setData(Qt::UserRole, namePart); + newItem->setFlags(newItem->flags() | Qt::ItemIsUserCheckable); + newItem->setCheckState(Qt::Unchecked); + ui->iniTweaksList->addItem(newItem); + } else { + ui->iniFileList->addItem(namePart); + } + } else if (fileName.endsWith(".esp", Qt::CaseInsensitive) || + fileName.endsWith(".esm", Qt::CaseInsensitive)) { + QString relativePath = fileName.mid(m_RootPath.length() + 1); + if (relativePath.contains('/')) { + QFileInfo fileInfo(fileName); + QListWidgetItem *newItem = new QListWidgetItem(fileInfo.fileName()); + newItem->setData(Qt::UserRole, relativePath); + ui->inactiveESPList->addItem(newItem); } else { - image = image.scaledToHeight(96); + ui->activeESPList->addItem(relativePath); } + } else if ((fileName.endsWith(".png", Qt::CaseInsensitive)) || + (fileName.endsWith(".jpg", Qt::CaseInsensitive))) { + QImage image = QImage(fileName); + if (!image.isNull()) { + if (static_cast<float>(image.width()) / static_cast<float>(image.height()) > 1.34) { + image = image.scaledToWidth(128); + } else { + image = image.scaledToHeight(96); + } - QPushButton *thumbnailButton = new QPushButton(QPixmap::fromImage(image), ""); - thumbnailButton->setIconSize(QSize(image.width(), image.height())); - connect(thumbnailButton, SIGNAL(clicked()), &m_ThumbnailMapper, SLOT(map())); - m_ThumbnailMapper.setMapping(thumbnailButton, fileName); - ui->thumbnailArea->addWidget(thumbnailButton); + QPushButton *thumbnailButton = new QPushButton(QPixmap::fromImage(image), ""); + thumbnailButton->setIconSize(QSize(image.width(), image.height())); + connect(thumbnailButton, SIGNAL(clicked()), &m_ThumbnailMapper, SLOT(map())); + m_ThumbnailMapper.setMapping(thumbnailButton, fileName); + ui->thumbnailArea->addWidget(thumbnailButton); + } } } } @@ -460,7 +473,8 @@ void ModInfoDialog::openIniFile(const QString &fileName) QFile iniFile(fileName); iniFile.open(QIODevice::ReadOnly); QByteArray buffer = iniFile.readAll(); - QTextCodec *codec = QTextCodec::codecForUtfText(buffer, m_UTF8Codec); + + QTextCodec *codec = QTextCodec::codecForUtfText(buffer, QTextCodec::codecForName("utf-8")); QTextEdit *iniFileView = findChild<QTextEdit*>("iniFileView"); iniFileView->setText(codec->toUnicode(buffer)); iniFileView->setProperty("currentFile", fileName); @@ -929,7 +943,7 @@ void ModInfoDialog::renameTriggered() return; } - m_FileTree->edit(index); + ui->fileTree->edit(index); } @@ -999,18 +1013,18 @@ void ModInfoDialog::createDirectoryTriggered() return; } - m_FileTree->setCurrentIndex(newIndex); - m_FileTree->edit(newIndex); + ui->fileTree->setCurrentIndex(newIndex); + ui->fileTree->edit(newIndex); } void ModInfoDialog::on_fileTree_customContextMenuRequested(const QPoint &pos) { - QItemSelectionModel *selectionModel = m_FileTree->selectionModel(); + QItemSelectionModel *selectionModel = ui->fileTree->selectionModel(); m_FileSelection = selectionModel->selectedRows(0); -// m_FileSelection = m_FileTree->indexAt(pos); - QMenu menu(m_FileTree); +// m_FileSelection = ui->fileTree->indexAt(pos); + QMenu menu(ui->fileTree); menu.addAction(m_NewFolderAction); @@ -1038,7 +1052,7 @@ void ModInfoDialog::on_fileTree_customContextMenuRequested(const QPoint &pos) m_FileSelection.clear(); m_FileSelection.append(m_FileSystemModel->index(m_FileSystemModel->rootPath(), 0)); } - menu.exec(m_FileTree->mapToGlobal(pos)); + menu.exec(ui->fileTree->mapToGlobal(pos)); } @@ -1164,18 +1178,17 @@ void ModInfoDialog::on_overwriteTree_customContextMenuRequested(const QPoint &po { m_ConflictsContextItem = ui->overwriteTree->itemAt(pos.x(), pos.y()); - QMenu menu; if (m_ConflictsContextItem != NULL) { // offer to hide/unhide file, but not for files from archives -// if (!m_ConflictsContextItem->data(0, Qt::UserRole + 1).toBool()) { - if (!m_ConflictsContextItem->data(0, Qt::UserRole + 2).toBool()) { + if (!m_ConflictsContextItem->data(1, Qt::UserRole + 2).toBool()) { + QMenu menu; if (m_ConflictsContextItem->text(0).endsWith(ModInfo::s_HiddenExt)) { menu.addAction(tr("Un-Hide"), this, SLOT(unhideConflictFile())); } else { menu.addAction(tr("Hide"), this, SLOT(hideConflictFile())); } + menu.exec(ui->overwriteTree->mapToGlobal(pos)); } - menu.exec(ui->overwriteTree->mapToGlobal(pos)); } } diff --git a/src/modinfodialog.h b/src/modinfodialog.h index edfe59cf..63875b70 100644 --- a/src/modinfodialog.h +++ b/src/modinfodialog.h @@ -65,6 +65,7 @@ public: TAB_CONFLICTS, TAB_CATEGORIES, TAB_NEXUS, + TAB_NOTES, TAB_FILETREE }; @@ -76,7 +77,7 @@ public: * @param modInfo info structure about the mod to display * @param parent parend widget **/ - explicit ModInfoDialog(ModInfo::Ptr modInfo, const MOShared::DirectoryEntry *directory, QWidget *parent = 0); + explicit ModInfoDialog(ModInfo::Ptr modInfo, const MOShared::DirectoryEntry *directory, bool unmanaged, QWidget *parent = 0); ~ModInfoDialog(); /** @@ -121,6 +122,9 @@ public slots: private: + void initFiletree(ModInfo::Ptr modInfo); + void initINITweaks(); + void refreshLists(); void addCategories(const CategoryFactory &factory, const std::set<int> &enabledCategories, QTreeWidgetItem *root, int rootLevel); @@ -225,7 +229,6 @@ private: const MOShared::DirectoryEntry *m_Directory; MOShared::FilesOrigin *m_Origin; - QTextCodec *m_UTF8Codec; std::map<int, int> m_RealTabPos; diff --git a/src/modlist.cpp b/src/modlist.cpp index 66bc66b4..3f46b85f 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -166,25 +166,29 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const return QVariant(); } } else if (column == COL_CATEGORY) { - int category = modInfo->getPrimaryCategory(); - if (category != -1) { - CategoryFactory &categoryFactory = CategoryFactory::instance(); - if (categoryFactory.categoryExists(category)) { - try { - int categoryIdx = categoryFactory.getCategoryIndex(category); - return categoryFactory.getCategoryName(categoryIdx); - } catch (const std::exception &e) { - qCritical("failed to retrieve category name: %s", e.what()); + if (modInfo->hasFlag(ModInfo::FLAG_FOREIGN)) { + return tr("Non-MO"); + } else { + int category = modInfo->getPrimaryCategory(); + if (category != -1) { + CategoryFactory &categoryFactory = CategoryFactory::instance(); + if (categoryFactory.categoryExists(category)) { + try { + int categoryIdx = categoryFactory.getCategoryIndex(category); + return categoryFactory.getCategoryName(categoryIdx); + } catch (const std::exception &e) { + qCritical("failed to retrieve category name: %s", e.what()); + return QString(); + } + } else { + qWarning("category %d doesn't exist (may have been removed)", category); + modInfo->setCategory(category, false); return QString(); } } else { - qWarning("category %d doesn't exist (may have been removed)", category); - modInfo->setCategory(category, false); - return QString(); + //return tr("None"); + return QVariant(); } - } else { - //return tr("None"); - return QVariant(); } } else if (column == COL_INSTALLTIME) { // display installation time for mods that can be updated @@ -253,6 +257,8 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const if (modInfo->getHighlight() & ModInfo::HIGHLIGHT_INVALID) { result.setItalic(true); } + } else if ((column == COL_CATEGORY) && (modInfo->hasFlag(ModInfo::FLAG_FOREIGN))) { + result.setItalic(true); } else if (column == COL_VERSION) { if (modInfo->updateAvailable() || modInfo->downgradeAvailable()) { result.setWeight(QFont::Bold); @@ -547,7 +553,9 @@ void ModList::changeModPriority(std::vector<int> sourceIndices, int newPriority) for (std::vector<int>::const_iterator iter = sourceIndices.begin(); iter != sourceIndices.end(); ++iter) { + int oldPriority = m_Profile->getModPriority(*iter); m_Profile->setModPriority(*iter, newPriority); + m_ModMoved(ModInfo::getByIndex(*iter)->name(), oldPriority, newPriority); } emit layoutChanged(); @@ -648,13 +656,18 @@ bool ModList::setPriority(const QString &name, int newPriority) } } - bool ModList::onModStateChanged(const std::function<void (const QString &, IModList::ModStates)> &func) { auto conn = m_ModStateChanged.connect(func); return conn.connected(); } +bool ModList::onModMoved(const std::function<void (const QString &, int, int)> &func) +{ + auto conn = m_ModMoved.connect(func); + return conn.connected(); +} + bool ModList::dropURLs(const QMimeData *mimeData, int row, const QModelIndex &parent) { QStringList source; diff --git a/src/modlist.h b/src/modlist.h index e0bc9df9..b946e009 100644 --- a/src/modlist.h +++ b/src/modlist.h @@ -62,6 +62,7 @@ public: }; typedef boost::signals2::signal<void (const QString &, ModStates)> SignalModStateChanged; + typedef boost::signals2::signal<void (const QString &, int, int)> SignalModMoved; public: @@ -113,6 +114,9 @@ public: /// \copydoc MOBase::IModList::onModStateChanged virtual bool onModStateChanged(const std::function<void (const QString &, ModStates)> &func); + /// \copydoc MOBase::IModList::onModMoved + virtual bool onModMoved(const std::function<void (const QString &, int, int)> &func); + public: // implementation of virtual functions of QAbstractItemModel virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; @@ -276,11 +280,8 @@ private: TModInfoChange m_ChangeInfo; SignalModStateChanged m_ModStateChanged; + SignalModMoved m_ModMoved; - - // QAbstractItemModel interface - - // IModList interface }; #endif // MODLIST_H diff --git a/src/modlistsortproxy.cpp b/src/modlistsortproxy.cpp index 052e65b2..88b7bef0 100644 --- a/src/modlistsortproxy.cpp +++ b/src/modlistsortproxy.cpp @@ -230,10 +230,10 @@ bool ModListSortProxy::filterMatchesModAnd(ModInfo::Ptr info, bool enabled) cons for (auto iter = m_CategoryFilter.begin(); iter != m_CategoryFilter.end(); ++iter) { switch (*iter) { case CategoryFactory::CATEGORY_SPECIAL_CHECKED: { - if (!enabled) return false; + if (!enabled && !info->alwaysEnabled()) return false; } break; case CategoryFactory::CATEGORY_SPECIAL_UNCHECKED: { - if (enabled) return false; + if (enabled || info->alwaysEnabled()) return false; } break; case CategoryFactory::CATEGORY_SPECIAL_UPDATEAVAILABLE: { if (!info->updateAvailable() && !info->downgradeAvailable()) return false; @@ -248,6 +248,12 @@ bool ModListSortProxy::filterMatchesModAnd(ModInfo::Ptr info, bool enabled) cons ModInfo::EEndorsedState state = info->endorsedState(); if (state != ModInfo::ENDORSED_FALSE) return false; } break; + case CategoryFactory::CATEGORY_SPECIAL_MANAGED: { + if (info->hasFlag(ModInfo::FLAG_FOREIGN)) return false; + } break; + case CategoryFactory::CATEGORY_SPECIAL_UNMANAGED: { + if (!info->hasFlag(ModInfo::FLAG_FOREIGN)) return false; + } break; default: { if (!info->categorySet(*iter)) return false; } break; @@ -261,10 +267,10 @@ bool ModListSortProxy::filterMatchesModOr(ModInfo::Ptr info, bool enabled) const for (auto iter = m_CategoryFilter.begin(); iter != m_CategoryFilter.end(); ++iter) { switch (*iter) { case CategoryFactory::CATEGORY_SPECIAL_CHECKED: { - if (enabled) return true; + if (enabled || info->alwaysEnabled()) return true; } break; case CategoryFactory::CATEGORY_SPECIAL_UNCHECKED: { - if (!enabled) return true; + if (!enabled && !info->alwaysEnabled()) return true; } break; case CategoryFactory::CATEGORY_SPECIAL_UPDATEAVAILABLE: { if (info->updateAvailable() || info->downgradeAvailable()) return true; @@ -279,6 +285,12 @@ bool ModListSortProxy::filterMatchesModOr(ModInfo::Ptr info, bool enabled) const ModInfo::EEndorsedState state = info->endorsedState(); if ((state == ModInfo::ENDORSED_FALSE) && (state != ModInfo::ENDORSED_NEVER)) return true; } break; + case CategoryFactory::CATEGORY_SPECIAL_MANAGED: { + if (!info->hasFlag(ModInfo::FLAG_FOREIGN)) return true; + } break; + case CategoryFactory::CATEGORY_SPECIAL_UNMANAGED: { + if (info->hasFlag(ModInfo::FLAG_FOREIGN)) return true; + } break; default: { if (info->categorySet(*iter)) return true; } break; diff --git a/src/organizer.pro b/src/organizer.pro index c16196d7..70d8e021 100644 --- a/src/organizer.pro +++ b/src/organizer.pro @@ -221,9 +221,9 @@ CONFIG(debug, debug|release) { LIBS += -L$$OUT_PWD/../bsatk/release
LIBS += -L$$OUT_PWD/../uibase/release
LIBS += -L$$OUT_PWD/../boss_modified/release
- QMAKE_CXXFLAGS += /Zi /GL
+ QMAKE_CXXFLAGS += /Zi# /GL
# QMAKE_CXXFLAGS -= -O2
- QMAKE_LFLAGS += /DEBUG /LTCG /OPT:REF /OPT:ICF
+ QMAKE_LFLAGS += /DEBUG# /LTCG /OPT:REF /OPT:ICF
}
#QMAKE_CXXFLAGS_WARN_ON -= -W3
@@ -275,9 +275,6 @@ DEFINES += BOOST_DISABLE_ASSERTS NDEBUG HGID = $$system(hg id -i)
DEFINES += HGID=\\\"$${HGID}\\\"
-load(moc)
-include(../common.pri)
-QMAKE_MOC += $$MOCDEFINES
SRCDIR = $$PWD
SRCDIR ~= s,/,$$QMAKE_DIR_SEP,g
diff --git a/src/pluginlist.cpp b/src/pluginlist.cpp index 0a28a45f..4f363900 100644 --- a/src/pluginlist.cpp +++ b/src/pluginlist.cpp @@ -216,12 +216,12 @@ void PluginList::refresh(const QString &profileName, const DirectoryEntry &baseD } -void PluginList::enableESP(const QString &name) +void PluginList::enableESP(const QString &name, bool enable) { std::map<QString, int>::iterator iter = m_ESPsByName.find(name.toLower()); if (iter != m_ESPsByName.end()) { - m_ESPs[iter->second].m_Enabled = true; + m_ESPs[iter->second].m_Enabled = enable; startSaveTime(); } else { reportError(tr("esp not found: %1").arg(name)); @@ -677,6 +677,14 @@ bool PluginList::onRefreshed(const std::function<void ()> &callback) return conn.connected(); } + +bool PluginList::onPluginMoved(const std::function<void (const QString &, int, int)> &func) +{ + auto conn = m_PluginMoved.connect(func); + return conn.connected(); +} + + void PluginList::updateIndices() { m_ESPsByName.clear(); @@ -814,8 +822,8 @@ QVariant PluginList::data(const QModelIndex &modelIndex, int role) const text += "<br>There is an ini file connected to this esp. Its settings will be added to your game settings, overwriting " "in case of conflicts."; } else if (m_ESPs[index].m_IsDummy) { - text += "<br>This file is a dummy! It exists only so the bsa with the same name gets loaded. With MO you don't need this: " - "If you enable the archive with the same name in the \"Archive\" tab you can disable this plugin."; + text += "<br>This file is a dummy! It exists only so the bsa with the same name gets loaded. If you let MO manage archives you " + "don't need this: Enable the archive with the same name in the \"Archive\" tab and disable this plugin."; } toolTip += text; } @@ -947,6 +955,7 @@ void PluginList::setPluginPriority(int row, int &newPriority) m_ESPs.at(row).m_Priority = newPriorityTemp; emit dataChanged(index(row, 0), index(row, columnCount())); + m_PluginMoved(m_ESPs[row].m_Name, oldPriority, newPriorityTemp); } catch (const std::out_of_range&) { reportError(tr("failed to restore load order for %1").arg(m_ESPs[row].m_Name)); } diff --git a/src/pluginlist.h b/src/pluginlist.h index d041172f..57b78a12 100644 --- a/src/pluginlist.h +++ b/src/pluginlist.h @@ -86,6 +86,7 @@ public: }; typedef boost::signals2::signal<void ()> SignalRefreshed; + typedef boost::signals2::signal<void (const QString &, int, int)> SignalPluginMoved; public: @@ -103,16 +104,24 @@ public: * * @param profileName name of the current profile * @param baseDirectory the root directory structure representing the virtual data directory + * @param pluginsFile file that stores the list of enabled plugins + * @param loadOrderFile file that stored the load order (not an official file but used by many tools for skyrim) + * @param lockedOrderFile list of plugins that shouldn't change load order * @todo the profile is not used? If it was, we should pass the Profile-object instead **/ - void refresh(const QString &profileName, const MOShared::DirectoryEntry &baseDirectory, const QString &pluginsFile, const QString &loadOrderFile, const QString &lockedOrderFile); + void refresh(const QString &profileName + , const MOShared::DirectoryEntry &baseDirectory + , const QString &pluginsFile + , const QString &loadOrderFile + , const QString &lockedOrderFile); /** * @brief enable a plugin based on its name * * @param name name of the plugin to enable + * @param enable set to true to enable the esp, false to disable it **/ - void enableESP(const QString &name); + void enableESP(const QString &name, bool enable = true); /** * @brief test if a plugin is enabled @@ -166,8 +175,11 @@ public: * @param deleterFileName file to receive a list of files to hide from the virtual data tree. This is used to hide unchecked plugins if "hideUnchecked" is true * @param hideUnchecked if true, plugins that aren't enabled will be hidden from the virtual data directory **/ - void saveTo(const QString &pluginFileName, const QString &loadOrderFileName, const QString &lockedOrderFileName, - const QString &deleterFileName, bool hideUnchecked) const; + void saveTo(const QString &pluginFileName + , const QString &loadOrderFileName + , const QString &lockedOrderFileName + , const QString &deleterFileName + , bool hideUnchecked) const; /** * @brief save the current load order @@ -208,6 +220,7 @@ public: virtual bool isMaster(const QString &name) const; virtual QString origin(const QString &name) const; virtual bool onRefreshed(const std::function<void()> &callback); + virtual bool onPluginMoved(const std::function<void (const QString &, int, int)> &func); public: // implementation of the QAbstractTableModel interface @@ -322,6 +335,8 @@ private: QTemporaryFile m_TempFile; + SignalPluginMoved m_PluginMoved; + }; diff --git a/src/profile.cpp b/src/profile.cpp index 2d982790..50e6c3b2 100644 --- a/src/profile.cpp +++ b/src/profile.cpp @@ -296,6 +296,8 @@ void Profile::refreshModStatus() int numKnownMods = index; + int topInsert = 0; + // invert priority order to match that of the pluginlist. Also // give priorities to mods not referenced in the profile for (size_t i = 0; i < m_ModStatus.size(); ++i) { @@ -310,11 +312,28 @@ void Profile::refreshModStatus() if (static_cast<size_t>(index) >= m_ModStatus.size()) { throw MyException(tr("invalid index %1").arg(index)); } - m_ModStatus[i].m_Priority = index++; + if (modInfo->hasFlag(ModInfo::FLAG_FOREIGN)) { + m_ModStatus[i].m_Priority = --topInsert; + } else { + m_ModStatus[i].m_Priority = index++; + } // also, mark the mod-list as changed modStatusModified = true; } } + // to support insertion of new mods at the top we may now have mods with negative priority. shift them all up + // to align priority with 0 + if (topInsert < 0) { + int offset = topInsert * -1; + for (size_t i = 0; i < m_ModStatus.size(); ++i) { + ModInfo::Ptr modInfo = ModInfo::getByIndex(i); + if (modInfo->getFixedPriority() == INT_MAX) { + continue; + } + + m_ModStatus[i].m_Priority += offset; + } + } file.close(); updateIndices(); @@ -400,7 +419,6 @@ void Profile::setModEnabled(unsigned int index, bool enabled) // overwrite is always enabled return; } - if (enabled != m_ModStatus[index].m_Enabled) { m_ModStatus[index].m_Enabled = enabled; emit modStatusChanged(index); diff --git a/src/settings.cpp b/src/settings.cpp index b8c02c69..c2855a85 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -288,16 +288,15 @@ void Settings::setupLoadMechanism() } -bool Settings::enableQuickInstaller() -{ - return m_Settings.value("Settings/enable_quick_installer").toBool(); -} - bool Settings::useProxy() { return m_Settings.value("Settings/use_proxy", false).toBool(); } +bool Settings::displayForeign() +{ + return m_Settings.value("Settings/display_foreign", true).toBool(); +} void Settings::setMotDHash(uint hash) { @@ -545,6 +544,7 @@ void Settings::query(QWidget *parent) QLineEdit *appIDEdit = dialog.findChild<QLineEdit*>("appIDEdit"); QLineEdit *nmmVersionEdit = dialog.findChild<QLineEdit*>("nmmVersionEdit"); QCheckBox *hideUncheckedBox = dialog.findChild<QCheckBox*>("hideUncheckedBox"); + QCheckBox *displayForeignBox = dialog.findChild<QCheckBox*>("displayForeignBox"); // @@ -602,6 +602,7 @@ void Settings::query(QWidget *parent) showMetaBox->setChecked(metaDownloads()); hideUncheckedBox->setChecked(hideUncheckedPlugins()); + displayForeignBox->setChecked(displayForeign()); forceEnableBox->setChecked(forceEnableCoreFiles()); appIDEdit->setText(getSteamAppID()); @@ -716,6 +717,7 @@ void Settings::query(QWidget *parent) } m_Settings.setValue("Settings/offline_mode", offlineBox->isChecked()); m_Settings.setValue("Settings/use_proxy", proxyBox->isChecked()); + m_Settings.setValue("Settings/display_foreign", displayForeignBox->isChecked()); m_Settings.setValue("Settings/nmm_version", nmmVersionEdit->text()); diff --git a/src/settings.h b/src/settings.h index ccb70d8a..0b051831 100644 --- a/src/settings.h +++ b/src/settings.h @@ -176,16 +176,16 @@ public: void setupLoadMechanism(); /** - * @return true if the user has enabled the quick installer (default true) - **/ - bool enableQuickInstaller(); - - /** * @return true if the user configured the use of a network proxy */ bool useProxy(); /** + * @return true if the user wants to see non-official plugins installed outside MO in his mod list + */ + bool displayForeign(); + + /** * @brief sets the new motd hash **/ void setMotDHash(uint hash); diff --git a/src/settingsdialog.ui b/src/settingsdialog.ui index 2e0ffae1..f04e4d7a 100644 --- a/src/settingsdialog.ui +++ b/src/settingsdialog.ui @@ -749,6 +749,25 @@ Uncheck this if you want to use Mod Organizer with total conversions (like Nehri </widget>
</item>
<item>
+ <widget class="QCheckBox" name="displayForeignBox">
+ <property name="toolTip">
+ <string>Disable this to no longer display mods installed outside MO in the mod list (left pane). Assets from those mods will then be treated as having lowest mod priority together with the original game content.</string>
+ </property>
+ <property name="whatsThis">
+ <string>By default Mod Organizer will display esp+bsa bundles installed with foreign tools as mods (left pane). This allows you to control their priority in relation to other mods. This is particularly useful if you also use Steam Workshop to install mods.
+However, if you installed loose file mods outside MO which conflict with BSAs also installed outside MO those conflicts can't be resolved correctly.
+
+If you disable this feature, MO will only display official DLCs this way. Please note that plugins (esps and esms) displayed in the right pane are completely unaffected by this feature.</string>
+ </property>
+ <property name="text">
+ <string>Display mods installed outside MO</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
<widget class="QPushButton" name="bsaDateBtn">
<property name="toolTip">
<string>For Skyrim, this can be used instead of Archive Invalidation. It should make AI redundant for all Profiles.
diff --git a/src/shared/directoryentry.cpp b/src/shared/directoryentry.cpp index b7863284..5172346b 100644 --- a/src/shared/directoryentry.cpp +++ b/src/shared/directoryentry.cpp @@ -219,7 +219,6 @@ void FilesOrigin::setName(const std::wstring &name) std::vector<FileEntry::Ptr> FilesOrigin::getFiles() const
{
std::vector<FileEntry::Ptr> result;
-
for (auto iter = m_Files.begin(); iter != m_Files.end(); ++iter) {
result.push_back(m_FileRegister.lock()->getFile(*iter));
}
@@ -228,7 +227,6 @@ std::vector<FileEntry::Ptr> FilesOrigin::getFiles() const }
-
//
// FileEntry
//
diff --git a/src/shared/fallout3info.cpp b/src/shared/fallout3info.cpp index 1808d1d9..68fd515e 100644 --- a/src/shared/fallout3info.cpp +++ b/src/shared/fallout3info.cpp @@ -112,6 +112,16 @@ std::vector<std::wstring> Fallout3Info::getVanillaBSAs() (L"Fallout - Misc.bsa");
}
+std::vector<std::wstring> Fallout3Info::getDLCPlugins()
+{
+ return boost::assign::list_of (L"ThePitt.esm")
+ (L"Anchorage.esm")
+ (L"BrokenSteel.esm")
+ (L"PointLookout.esm")
+ (L"Zeta.esm")
+ ;
+}
+
std::vector<std::wstring> Fallout3Info::getIniFileNames()
{
return boost::assign::list_of(L"fallout.ini")(L"falloutprefs.ini");
diff --git a/src/shared/fallout3info.h b/src/shared/fallout3info.h index 9575103b..e9a818e2 100644 --- a/src/shared/fallout3info.h +++ b/src/shared/fallout3info.h @@ -58,6 +58,7 @@ public: virtual std::vector<std::wstring> getPrimaryPlugins();
virtual std::vector<std::wstring> getVanillaBSAs();
+ virtual std::vector<std::wstring> getDLCPlugins();
// file name of this games ini (no path)
virtual std::vector<std::wstring> getIniFileNames();
diff --git a/src/shared/falloutnvinfo.cpp b/src/shared/falloutnvinfo.cpp index e7e747e2..366c72c3 100644 --- a/src/shared/falloutnvinfo.cpp +++ b/src/shared/falloutnvinfo.cpp @@ -113,6 +113,21 @@ std::vector<std::wstring> FalloutNVInfo::getVanillaBSAs() (L"Fallout - Misc.bsa");
}
+std::vector<std::wstring> FalloutNVInfo::getDLCPlugins()
+{
+ return boost::assign::list_of (L"DeadMoney.esm")
+ (L"HonestHearts.esm")
+ (L"OldWorldBlues.esm")
+ (L"LonesomeRoad.esm")
+ (L"GunRunnersArsenal.esm")
+ (L"CaravanPack.esm")
+ (L"ClassicPack.esm")
+ (L"MercenaryPack.esm")
+ (L"TribalPack.esm")
+ ;
+}
+
+
std::vector<std::wstring> FalloutNVInfo::getIniFileNames()
{
return boost::assign::list_of(L"fallout.ini")(L"falloutprefs.ini");
diff --git a/src/shared/falloutnvinfo.h b/src/shared/falloutnvinfo.h index 8767fdb0..e6f6b5d0 100644 --- a/src/shared/falloutnvinfo.h +++ b/src/shared/falloutnvinfo.h @@ -60,6 +60,7 @@ public: virtual std::vector<std::wstring> getPrimaryPlugins();
virtual std::vector<std::wstring> getVanillaBSAs();
+ virtual std::vector<std::wstring> getDLCPlugins();
// file name of this games ini (no path)
virtual std::vector<std::wstring> getIniFileNames();
diff --git a/src/shared/gameinfo.cpp b/src/shared/gameinfo.cpp index b580a226..ebeff55a 100644 --- a/src/shared/gameinfo.cpp +++ b/src/shared/gameinfo.cpp @@ -222,7 +222,13 @@ std::wstring GameInfo::getSpecialPath(LPCWSTR name) const throw windows_error("failed to look up special folder", errorcode);
}
- return temp;
+ WCHAR temp2[MAX_PATH];
+ // try to expand variables in the path, if any
+ if (::ExpandEnvironmentStringsW(temp, temp2, MAX_PATH) != 0) {
+ return temp2;
+ } else {
+ return temp;
+ }
}
} // namespace MOShared
diff --git a/src/shared/gameinfo.h b/src/shared/gameinfo.h index 89c9402d..69cd38f6 100644 --- a/src/shared/gameinfo.h +++ b/src/shared/gameinfo.h @@ -130,6 +130,9 @@ public: virtual std::vector<std::wstring> getVanillaBSAs() = 0;
+ // get a set of esp/esm files that are part of known dlcs
+ virtual std::vector<std::wstring> getDLCPlugins() = 0;
+
// file name of this games ini file(s)
virtual std::vector<std::wstring> getIniFileNames() = 0;
diff --git a/src/shared/oblivioninfo.cpp b/src/shared/oblivioninfo.cpp index d8daa0f7..85f31042 100644 --- a/src/shared/oblivioninfo.cpp +++ b/src/shared/oblivioninfo.cpp @@ -117,6 +117,22 @@ std::vector<std::wstring> OblivionInfo::getVanillaBSAs() }
+std::vector<std::wstring> OblivionInfo::getDLCPlugins()
+{
+ return boost::assign::list_of (L"DLCShiveringIsles.esp")
+ (L"Knights.esp")
+ (L"DLCFrostcrag.esp")
+ (L"DLCSpellTomes.esp")
+ (L"DLCMehrunesRazor.esp")
+ (L"DLCOrrery.esp")
+ (L"DLCSpellTomes.esp")
+ (L"DLCThievesDen.esp")
+ (L"DLCVileLair.esp")
+ (L"DLCHorseArmor.esp")
+ ;
+}
+
+
std::vector<std::wstring> OblivionInfo::getIniFileNames()
{
return boost::assign::list_of(L"oblivion.ini")(L"oblivionprefs.ini");
diff --git a/src/shared/oblivioninfo.h b/src/shared/oblivioninfo.h index ba27aa40..f9c8fa47 100644 --- a/src/shared/oblivioninfo.h +++ b/src/shared/oblivioninfo.h @@ -56,6 +56,7 @@ public: virtual std::vector<std::wstring> getPrimaryPlugins();
virtual std::vector<std::wstring> getVanillaBSAs();
+ virtual std::vector<std::wstring> getDLCPlugins();
// file name of this games ini (no path)
virtual std::vector<std::wstring> getIniFileNames();
diff --git a/src/shared/skyriminfo.cpp b/src/shared/skyriminfo.cpp index bf7500e6..5711e2fd 100644 --- a/src/shared/skyriminfo.cpp +++ b/src/shared/skyriminfo.cpp @@ -144,7 +144,18 @@ std::vector<std::wstring> SkyrimInfo::getVanillaBSAs() (L"Skyrim - Meshes.bsa")
(L"Skyrim - Sounds.bsa")
(L"Skyrim - Voices.bsa")
- (L"Skyrim - VoicesExtra.bsa");
+ (L"Skyrim - VoicesExtra.bsa");
+}
+
+std::vector<std::wstring> SkyrimInfo::getDLCPlugins()
+{
+ return boost::assign::list_of (L"Dawnguard.esm")
+ (L"Dragonborn.esm")
+ (L"HearthFires.esm")
+ (L"HighResTexturePack01.esp")
+ (L"HighResTexturePack02.esp")
+ (L"HighResTexturePack03.esp")
+ ;
}
std::vector<std::wstring> SkyrimInfo::getIniFileNames()
@@ -305,4 +316,5 @@ std::vector<ExecutableInfo> SkyrimInfo::getExecutables() return result;
}
+
} // namespace MOShared
diff --git a/src/shared/skyriminfo.h b/src/shared/skyriminfo.h index 438beb41..61fb3fa2 100644 --- a/src/shared/skyriminfo.h +++ b/src/shared/skyriminfo.h @@ -63,6 +63,7 @@ public: virtual std::vector<std::wstring> getPrimaryPlugins();
virtual std::vector<std::wstring> getVanillaBSAs();
+ virtual std::vector<std::wstring> getDLCPlugins();
// file name of this games ini (no path)
virtual std::vector<std::wstring> getIniFileNames();
diff --git a/src/version.rc b/src/version.rc index 5e9b62c5..f589769d 100644 --- a/src/version.rc +++ b/src/version.rc @@ -1,7 +1,7 @@ #include "Winver.h"
-#define VER_FILEVERSION 1,2,2,0
-#define VER_FILEVERSION_STR "1,2,2,0\0"
+#define VER_FILEVERSION 1,2,5,0
+#define VER_FILEVERSION_STR "1,2,5,0\0"
VS_VERSION_INFO VERSIONINFO
FILEVERSION VER_FILEVERSION
|
