aboutsummaryrefslogtreecommitdiff
path: root/libs/game_bethesda/src/gamebryo/gamebryobsainvalidation.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libs/game_bethesda/src/gamebryo/gamebryobsainvalidation.cpp')
-rw-r--r--libs/game_bethesda/src/gamebryo/gamebryobsainvalidation.cpp165
1 files changed, 165 insertions, 0 deletions
diff --git a/libs/game_bethesda/src/gamebryo/gamebryobsainvalidation.cpp b/libs/game_bethesda/src/gamebryo/gamebryobsainvalidation.cpp
new file mode 100644
index 0000000..6170305
--- /dev/null
+++ b/libs/game_bethesda/src/gamebryo/gamebryobsainvalidation.cpp
@@ -0,0 +1,165 @@
+#include "gamebryobsainvalidation.h"
+
+#include "dummybsa.h"
+#include "iplugingame.h"
+#include "iprofile.h"
+#include "registry.h"
+#include <imoinfo.h>
+#include <utility.h>
+
+#include <QDir>
+#include <QFileInfo>
+#include <QStringList>
+
+#include "gamegamebryo.h"
+
+GamebryoBSAInvalidation::GamebryoBSAInvalidation(MOBase::DataArchives* dataArchives,
+ const QString& iniFilename,
+ MOBase::IPluginGame const* game)
+ : m_DataArchives(dataArchives), m_IniFileName(iniFilename), m_Game(game)
+{}
+
+bool GamebryoBSAInvalidation::isInvalidationBSA(const QString& bsaName)
+{
+ static QStringList invalidation{invalidationBSAName()};
+
+ for (const QString& file : invalidation) {
+ if (file.compare(bsaName, Qt::CaseInsensitive) == 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void GamebryoBSAInvalidation::deactivate(MOBase::IProfile* profile)
+{
+ prepareProfile(profile);
+}
+
+void GamebryoBSAInvalidation::activate(MOBase::IProfile* profile)
+{
+ prepareProfile(profile);
+}
+
+// Case-insensitive file resolution (same logic as gamegamebryo.cpp).
+static QString resolveIniPathCaseInsensitive(const QString& filePath)
+{
+#ifdef _WIN32
+ return filePath;
+#else
+ if (QFileInfo(filePath).exists()) {
+ return filePath;
+ }
+ QFileInfo info(filePath);
+ QDir dir(info.path());
+ if (!dir.exists()) {
+ return filePath;
+ }
+ const QString target = info.fileName();
+ const QStringList entries = dir.entryList(QDir::Files | QDir::Hidden);
+ for (const QString& entry : entries) {
+ if (entry.compare(target, Qt::CaseInsensitive) == 0) {
+ return dir.absoluteFilePath(entry);
+ }
+ }
+ return filePath;
+#endif
+}
+
+bool GamebryoBSAInvalidation::prepareProfile(MOBase::IProfile* profile)
+{
+ bool dirty = false;
+ QString basePath = profile->localSettingsEnabled()
+ ? profile->absolutePath()
+ : m_Game->documentsDirectory().absolutePath();
+
+ // Ensure the target INI has adequate content before writing to it.
+ // On Linux, the game launcher may not work, leaving INI files missing
+ // or as empty stubs. Seed from the game's default INI if needed.
+ const auto* gamebryo = dynamic_cast<const GameGamebryo*>(m_Game);
+ if (gamebryo) {
+ const_cast<GameGamebryo*>(gamebryo)->ensureIniFilesExist(basePath);
+ }
+
+ // Resolve case-insensitively (e.g., fallout.ini vs Fallout.ini on Linux)
+ QString iniFilePath = resolveIniPathCaseInsensitive(basePath + "/" + m_IniFileName);
+
+ // write bInvalidateOlderFiles = 1, if needed
+ QString setting =
+ GameGamebryo::readIniValue(iniFilePath, "Archive", "bInvalidateOlderFiles", "0");
+ if (setting.toLong() != 1) {
+ dirty = true;
+ if (!MOBase::WriteRegistryValue("Archive", "bInvalidateOlderFiles", "1",
+ iniFilePath)) {
+ qWarning("failed to activate BSA invalidation in \"%s\"",
+ qUtf8Printable(m_IniFileName));
+ }
+ }
+
+ if (profile->invalidationActive(nullptr)) {
+
+ // add the dummy bsa to the archive string, if needed
+ QStringList archives = m_DataArchives->archives(profile);
+ bool bsaInstalled = false;
+ for (const QString& archive : archives) {
+ if (isInvalidationBSA(archive)) {
+ bsaInstalled = true;
+ break;
+ }
+ }
+ if (!bsaInstalled) {
+ m_DataArchives->addArchive(profile, 0, invalidationBSAName());
+ dirty = true;
+ }
+
+ // create the dummy bsa if necessary
+ QString bsaFile = m_Game->dataDirectory().absoluteFilePath(invalidationBSAName());
+ if (!QFile::exists(bsaFile)) {
+ DummyBSA bsa(bsaVersion());
+ bsa.write(bsaFile);
+ dirty = true;
+ }
+
+ // write SInvalidationFile = "", if needed
+ QString sInvalidation = GameGamebryo::readIniValue(
+ iniFilePath, "Archive", "SInvalidationFile", "ArchiveInvalidation.txt");
+ if (sInvalidation != "") {
+ dirty = true;
+ if (!MOBase::WriteRegistryValue("Archive", "SInvalidationFile", "", iniFilePath)) {
+ qWarning("failed to activate BSA invalidation in \"%s\"",
+ qUtf8Printable(m_IniFileName));
+ }
+ }
+ } else {
+
+ // remove the dummy bsa from the archive string, if needed
+ QStringList archivesBefore = m_DataArchives->archives(profile);
+ for (const QString& archive : archivesBefore) {
+ if (isInvalidationBSA(archive)) {
+ m_DataArchives->removeArchive(profile, archive);
+ dirty = true;
+ }
+ }
+
+ // delete the dummy bsa, if needed
+ QString bsaFile = m_Game->dataDirectory().absoluteFilePath(invalidationBSAName());
+ if (QFile::exists(bsaFile)) {
+ MOBase::shellDeleteQuiet(bsaFile);
+ dirty = true;
+ }
+
+ // write SInvalidationFile = "ArchiveInvalidation.txt", if needed
+ QString sInvalidation2 =
+ GameGamebryo::readIniValue(iniFilePath, "Archive", "SInvalidationFile", "");
+ if (sInvalidation2 != "ArchiveInvalidation.txt") {
+ dirty = true;
+ if (!MOBase::WriteRegistryValue("Archive", "SInvalidationFile",
+ "ArchiveInvalidation.txt", iniFilePath)) {
+ qWarning("failed to activate BSA invalidation in \"%s\"",
+ qUtf8Printable(m_IniFileName));
+ }
+ }
+ }
+
+ return dirty;
+}