diff --git a/src/organizercore.cpp b/src/organizercore.cpp index df098205a..f471ead19 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -469,12 +469,14 @@ void OrganizerCore::updateVFSParams(log::Levels logLevel, env::CoreDumpTypes coreDumpType, const QString& crashDumpsPath, std::chrono::seconds spawnDelay, - QString executableBlacklist) + QString executableBlacklist, + const QStringList& skipFileSuffixes, + const QStringList& skipDirectories) { setGlobalCoreDumpType(coreDumpType); m_USVFS.updateParams(logLevel, coreDumpType, crashDumpsPath, spawnDelay, - executableBlacklist); + executableBlacklist, skipFileSuffixes, skipDirectories); } void OrganizerCore::setLogLevel(log::Levels level) @@ -484,7 +486,8 @@ void OrganizerCore::setLogLevel(log::Levels level) updateVFSParams( m_Settings.diagnostics().logLevel(), m_Settings.diagnostics().coreDumpType(), QString::fromStdWString(getGlobalCoreDumpPath()), - m_Settings.diagnostics().spawnDelay(), m_Settings.executablesBlacklist()); + m_Settings.diagnostics().spawnDelay(), m_Settings.executablesBlacklist(), + m_Settings.skipFileSuffixes(), m_Settings.skipDirectories()); log::getDefault().setLevel(m_Settings.diagnostics().logLevel()); } diff --git a/src/organizercore.h b/src/organizercore.h index 726babfdf..923adc12d 100644 --- a/src/organizercore.h +++ b/src/organizercore.h @@ -339,7 +339,8 @@ class OrganizerCore : public QObject, public MOBase::IPluginDiagnose void updateVFSParams(MOBase::log::Levels logLevel, env::CoreDumpTypes coreDumpType, const QString& coreDumpsPath, std::chrono::seconds spawnDelay, - QString executableBlacklist); + QString executableBlacklist, const QStringList& skipFileSuffixes, + const QStringList& skipDirectories); void setLogLevel(MOBase::log::Levels level); diff --git a/src/settings.cpp b/src/settings.cpp index 04a094676..8b95a1d07 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -316,6 +316,34 @@ void Settings::setExecutablesBlacklist(const QString& s) set(m_Settings, "Settings", "executable_blacklist", s); } +QStringList Settings::skipFileSuffixes() const +{ + static const QStringList def = QStringList() << ".mohidden"; + + auto setting = get(m_Settings, "Settings", "skip_file_suffixes", def); + + return setting; +} + +void Settings::setSkipFileSuffixes(const QStringList& s) +{ + set(m_Settings, "Settings", "skip_file_suffixes", s); +} + +QStringList Settings::skipDirectories() const +{ + static const QStringList def = QStringList() << ".git"; + + auto setting = get(m_Settings, "Settings", "skip_directories", def); + + return setting; +} + +void Settings::setSkipDirectories(const QStringList& s) +{ + set(m_Settings, "Settings", "skip_directories", s); +} + void Settings::setMotdHash(uint hash) { set(m_Settings, "General", "motd_hash", hash); diff --git a/src/settings.h b/src/settings.h index 30254c066..613a63bb0 100644 --- a/src/settings.h +++ b/src/settings.h @@ -795,6 +795,12 @@ class Settings : public QObject bool isExecutableBlacklisted(const QString& s) const; void setExecutablesBlacklist(const QString& s); + QStringList skipFileSuffixes() const; + void setSkipFileSuffixes(const QStringList& s); + + QStringList skipDirectories() const; + void setSkipDirectories(const QStringList& s); + // ? looks obsolete, only used by dead code // unsigned int motdHash() const; diff --git a/src/settingsdialog.ui b/src/settingsdialog.ui index 84b5c343b..6011b1588 100644 --- a/src/settingsdialog.ui +++ b/src/settingsdialog.ui @@ -2016,6 +2016,60 @@ p, li { white-space: pre-wrap; } + + + + + For Skyrim, this can be used instead of Archive Invalidation. It should make AI redundant for all Profiles. + For the other games this is not a sufficient replacement for AI! + + + + + For Skyrim, this can be used instead of Archive Invalidation. It should make AI redundant for all Profiles. + For the other games this is not a sufficient replacement for AI! + + + + Back-date BSAs + + + + :/MO/gui/resources/emblem-readonly.png:/MO/gui/resources/emblem-readonly.png + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + @@ -2037,21 +2091,34 @@ programs you are intentionally running. - + - For Skyrim, this can be used instead of Archive Invalidation. It should make AI redundant for all Profiles. -For the other games this is not a sufficient replacement for AI! + Files to skip or ignore from the virtual file system. - For Skyrim, this can be used instead of Archive Invalidation. It should make AI redundant for all Profiles. -For the other games this is not a sufficient replacement for AI! + Files to skip or ignore from the virtual file system. - Back-date BSAs + Skip File Suffixes - - - :/MO/gui/resources/emblem-readonly.png:/MO/gui/resources/emblem-readonly.png + + false + + + + + + + Directories to skip or ignore from the virtual file system. + + + Directories to skip or ignore from the virtual file system. + + + Skip Directories + + + false diff --git a/src/settingsdialogworkarounds.cpp b/src/settingsdialogworkarounds.cpp index 36e5dd44a..849e2085e 100644 --- a/src/settingsdialogworkarounds.cpp +++ b/src/settingsdialogworkarounds.cpp @@ -34,6 +34,8 @@ WorkaroundsSettingsTab::WorkaroundsSettingsTab(Settings& s, SettingsDialog& d) // buttons m_ExecutableBlacklist = settings().executablesBlacklist(); + m_SkipFileSuffixes = settings().skipFileSuffixes(); + m_SkipDirectories = settings().skipDirectories(); QObject::connect(ui->bsaDateBtn, &QPushButton::clicked, [&] { on_bsaDateBtn_clicked(); @@ -41,6 +43,12 @@ WorkaroundsSettingsTab::WorkaroundsSettingsTab(Settings& s, SettingsDialog& d) QObject::connect(ui->execBlacklistBtn, &QPushButton::clicked, [&] { on_execBlacklistBtn_clicked(); }); + QObject::connect(ui->skipFileSuffixBtn, &QPushButton::clicked, [&] { + on_skipFileSuffixBtn_clicked(); + }); + QObject::connect(ui->skipDirectoriesBtn, &QPushButton::clicked, [&] { + on_skipDirectoriesBtn_clicked(); + }); QObject::connect(ui->resetGeometryBtn, &QPushButton::clicked, [&] { on_resetGeometryBtn_clicked(); }); @@ -69,6 +77,8 @@ void WorkaroundsSettingsTab::update() // buttons settings().setExecutablesBlacklist(m_ExecutableBlacklist); + settings().setSkipFileSuffixes(m_SkipFileSuffixes); + settings().setSkipDirectories(m_SkipDirectories); } bool WorkaroundsSettingsTab::changeBlacklistNow(QWidget* parent, Settings& settings) @@ -114,6 +124,71 @@ WorkaroundsSettingsTab::changeBlacklistLater(QWidget* parent, const QString& cur return blacklist.join(";"); } +std::optional +WorkaroundsSettingsTab::changeSkipFileSuffixes(QWidget* parent, + const QStringList& current) +{ + bool ok = false; + + QString result = QInputDialog::getMultiLineText( + parent, QObject::tr("Skip File Suffixes"), + QObject::tr( + "Enter one file suffix per line to be skipped / ignored from the virtual " + "file system.\n" + "Not to be confused with file extensions, file suffixes are simply how the " + "filename ends.\n\n" + "Example:\n" + " .txt - Would skip all files that end with .txt, .txt\n" + " some_file.txt - Would skip all files that end with some_file.txt, some_file.txt"), + current.join("\n"), &ok); + + if (!ok) { + return {}; + } + + QStringList fileSuffixes; + for (auto& suffix : result.split("\n")) { + auto trimmed = suffix.trimmed(); + if (!trimmed.isEmpty()) { + fileSuffixes << trimmed; + } + } + + return fileSuffixes; +} + +std::optional +WorkaroundsSettingsTab::changeSkipDirectories(QWidget* parent, + const QStringList& current) +{ + bool ok = false; + + QString result = QInputDialog::getMultiLineText( + parent, QObject::tr("Skip Directories"), + QObject::tr( + "Enter one directory per line to be skipped / ignored from the virtual " + "file system.\n\n" + "Example:\n" + " .git\n" + " instructions"), + current.join("\n"), &ok); + + if (!ok) { + return {}; + } + + QStringList directories; + for (auto& dir : result.split("\n")) { + auto trimmed = dir.trimmed(); + if (!trimmed.isEmpty()) { + directories << trimmed; + } + } + + return directories; +} + void WorkaroundsSettingsTab::on_execBlacklistBtn_clicked() { if (auto s = changeBlacklistLater(parentWidget(), m_ExecutableBlacklist)) { @@ -121,6 +196,20 @@ void WorkaroundsSettingsTab::on_execBlacklistBtn_clicked() } } +void WorkaroundsSettingsTab::on_skipFileSuffixBtn_clicked() +{ + if (auto s = changeSkipFileSuffixes(parentWidget(), m_SkipFileSuffixes)) { + m_SkipFileSuffixes = *s; + } +} + +void WorkaroundsSettingsTab::on_skipDirectoriesBtn_clicked() +{ + if (auto s = changeSkipDirectories(parentWidget(), m_SkipDirectories)) { + m_SkipDirectories = *s; + } +} + void WorkaroundsSettingsTab::on_bsaDateBtn_clicked() { const auto* game = qApp->property("managed_game").value(); diff --git a/src/settingsdialogworkarounds.h b/src/settingsdialogworkarounds.h index 8c86fd69e..d31bb371c 100644 --- a/src/settingsdialogworkarounds.h +++ b/src/settingsdialogworkarounds.h @@ -20,13 +20,29 @@ class WorkaroundsSettingsTab : public SettingsTab static std::optional changeBlacklistLater(QWidget* parent, const QString& current); + // shows the blacklist dialog from the given string and returns the new + // blacklist if the user accepted it + // + static std::optional changeSkipFileSuffixes(QWidget* parent, + const QStringList& current); + + // shows the blacklist dialog from the given string and returns the new + // blacklist if the user accepted it + // + static std::optional changeSkipDirectories(QWidget* parent, + const QStringList& current); + void update(); private: QString m_ExecutableBlacklist; + QStringList m_SkipFileSuffixes; + QStringList m_SkipDirectories; void on_bsaDateBtn_clicked(); void on_execBlacklistBtn_clicked(); + void on_skipFileSuffixBtn_clicked(); + void on_skipDirectoriesBtn_clicked(); void on_resetGeometryBtn_clicked(); }; diff --git a/src/settingsutilities.h b/src/settingsutilities.h index 11b2af1c9..cd55464e5 100644 --- a/src/settingsutilities.h +++ b/src/settingsutilities.h @@ -32,6 +32,12 @@ struct ValueConverter } }; +template <> +struct ValueConverter +{ + static QString convert(const QStringList& t) { return t.join(", "); } +}; + bool shouldLogSetting(const QString& displayName); template diff --git a/src/usvfsconnector.cpp b/src/usvfsconnector.cpp index 4a54f9fd9..7b75b870d 100644 --- a/src/usvfsconnector.cpp +++ b/src/usvfsconnector.cpp @@ -160,6 +160,21 @@ UsvfsConnector::UsvfsConnector() usvfsBlacklistExecutable(buf.data()); } + usvfsClearSkipFileSuffixes(); + for (auto& suffix : s.skipFileSuffixes()) { + if (suffix.isEmpty()) { + continue; + } + std::wstring buf = suffix.toStdWString(); + usvfsAddSkipFileSuffix(buf.data()); + } + + usvfsClearSkipDirectories(); + for (auto& dir : s.skipDirectories()) { + std::wstring buf = dir.toStdWString(); + usvfsAddSkipDirectory(buf.data()); + } + usvfsClearLibraryForceLoads(); m_LogWorker.moveToThread(&m_WorkerThread); @@ -228,7 +243,9 @@ void UsvfsConnector::updateParams(MOBase::log::Levels logLevel, env::CoreDumpTypes coreDumpType, const QString& crashDumpsPath, std::chrono::seconds spawnDelay, - QString executableBlacklist) + QString executableBlacklist, + const QStringList& skipFileSuffixes, + const QStringList& skipDirectories) { using namespace std::chrono; @@ -248,6 +265,21 @@ void UsvfsConnector::updateParams(MOBase::log::Levels logLevel, std::wstring buf = exec.toStdWString(); usvfsBlacklistExecutable(buf.data()); } + + usvfsClearSkipFileSuffixes(); + for (auto& suffix : skipFileSuffixes) { + if (suffix.isEmpty()) { + continue; + } + std::wstring buf = suffix.toStdWString(); + usvfsAddSkipFileSuffix(buf.data()); + } + + usvfsClearSkipDirectories(); + for (auto& dir : skipDirectories) { + std::wstring buf = dir.toStdWString(); + usvfsAddSkipDirectory(buf.data()); + } } void UsvfsConnector::updateForcedLibraries( diff --git a/src/usvfsconnector.h b/src/usvfsconnector.h index 798d59837..d578ddd49 100644 --- a/src/usvfsconnector.h +++ b/src/usvfsconnector.h @@ -84,7 +84,8 @@ class UsvfsConnector : public QObject void updateParams(MOBase::log::Levels logLevel, env::CoreDumpTypes coreDumpType, const QString& crashDumpsPath, std::chrono::seconds spawnDelay, - QString executableBlacklist); + QString executableBlacklist, const QStringList& skipFileSuffixes, + const QStringList& skipDirectories); void updateForcedLibraries( const QList& forcedLibraries);