From ee801f255a23dd7d4a2b331cee3efed45eda9bce Mon Sep 17 00:00:00 2001 From: Jeremy Rimpo Date: Wed, 19 Jun 2024 15:17:15 -0500 Subject: [PATCH] Various changes and fixes - Fix typo in unmanaged mods class name - Add struct to store content catalog data - Fetch relevant info in content catalog parser - Use unmanaged mods parseer in main game class (for CC plugins) - for loop fixes --- src/gamestarfield.cpp | 59 +++++++---------- src/starfieldunmanagedmods.cpp | 113 ++++++++++++++------------------- src/starfieldunmanagedmods.h | 16 +++-- 3 files changed, 83 insertions(+), 105 deletions(-) diff --git a/src/gamestarfield.cpp b/src/gamestarfield.cpp index 58f94d6..c213700 100644 --- a/src/gamestarfield.cpp +++ b/src/gamestarfield.cpp @@ -21,10 +21,6 @@ #include #include #include -#include -#include -#include -#include #include #include #include @@ -55,7 +51,7 @@ bool GameStarfield::init(IOrganizer* moInfo) std::make_shared(m_Organizer->gameFeatures())); registerFeature(std::make_shared(this)); registerFeature(std::make_shared(moInfo)); - registerFeature(std::make_shared(this, localAppFolder())); + registerFeature(std::make_shared(this, localAppFolder())); registerFeature(std::make_shared(dataArchives.get(), this)); m_Organizer->pluginList()->onRefreshed([&]() { @@ -318,23 +314,22 @@ QStringList GameStarfield::CCPlugins() const QStringList corePlugins = primaryPlugins() + DLCPlugins(); if (!testFilePresent()) { QFile file(gameDirectory().absoluteFilePath("Starfield.ccc")); - if (m_Organizer->pluginSetting(name(), "enable_loadorder_fix").toBool()) { + if (m_Organizer->pluginSetting(name(), "enable_loadorder_fix").toBool() && + !m_Organizer->profilePath().isEmpty()) { file.setFileName(m_Organizer->profilePath() + "/Starfield.ccc"); } if (file.open(QIODevice::ReadOnly)) { - if (file.size() == 0) { - return plugins; - } - while (!file.atEnd()) { - QByteArray line = file.readLine().trimmed(); - QString modName; - if ((line.size() > 0) && (line.at(0) != '#')) { - modName = QString::fromUtf8(line.constData()).toLower(); - } + if (file.size() > 0) { + while (!file.atEnd()) { + QByteArray line = file.readLine().trimmed(); + QString modName; + if ((line.size() > 0) && (line.at(0) != '#')) { + modName = QString::fromUtf8(line.constData()).toLower(); + } - if (modName.size() > 0) { - if (!plugins.contains(modName, Qt::CaseInsensitive)) { - if (corePlugins.contains(modName, Qt::CaseInsensitive)) { + if (modName.size() > 0) { + if (!plugins.contains(modName, Qt::CaseInsensitive) && + !corePlugins.contains(modName, Qt::CaseInsensitive)) { plugins.append(modName); } } @@ -343,28 +338,18 @@ QStringList GameStarfield::CCPlugins() const } } + std::shared_ptr unmanagedMods = + std::static_pointer_cast( + m_Organizer->gameFeatures()->gameFeature()); + // The ContentCatalog.txt appears to be the main repository where Starfiled stores // info about the installed Creations. We parse this to correctly mark unmanaged mods // as Creations. The StarfieldUnmanagedMods class handles parsing mod names and files. - QFile content(localAppFolder() + "/" + gameShortName() + "/ContentCatalog.txt"); - if (content.open(QIODevice::OpenModeFlag::ReadOnly)) { - auto contentData = content.readAll(); - QJsonDocument contentDoc = QJsonDocument::fromJson(contentData); - QJsonObject contentObj = contentDoc.object(); - for (auto mod : contentObj.keys()) { - if (mod == "ContentCatalog") - continue; - auto modData = contentObj.value(mod).toObject(); - auto modFiles = modData.value("Files").toArray(); - bool found = false; - for (auto file : modFiles) { - if (file.toString().endsWith(".esm", Qt::CaseInsensitive) || - file.toString().endsWith(".esl", Qt::CaseInsensitive) || - file.toString().endsWith(".esp", Qt::CaseInsensitive)) { - if (!plugins.contains(file.toString(), Qt::CaseInsensitive)) { - plugins.append(file.toString()); - } - } + if (unmanagedMods.get()) { + auto contentCatalog = unmanagedMods->parseContentCatalog(); + for (const auto& mod : contentCatalog) { + if (!plugins.contains(mod.first, Qt::CaseInsensitive)) { + plugins.append(mod.first); } } } diff --git a/src/starfieldunmanagedmods.cpp b/src/starfieldunmanagedmods.cpp index 6247320..9e2a530 100644 --- a/src/starfieldunmanagedmods.cpp +++ b/src/starfieldunmanagedmods.cpp @@ -1,21 +1,20 @@ #include "starfieldunmanagedmods.h" #include "log.h" -#include "scopeguard.h" #include #include #include #include -StarfieldUnmangedMods::StarfieldUnmangedMods(const GameStarfield* game, - const QString appDataFolder) +StarfieldUnmanagedMods::StarfieldUnmanagedMods(const GameStarfield* game, + const QString& appDataFolder) : GamebryoUnmangedMods(game), m_AppDataFolder(appDataFolder) {} -StarfieldUnmangedMods::~StarfieldUnmangedMods() {} +StarfieldUnmanagedMods::~StarfieldUnmanagedMods() {} -QStringList StarfieldUnmangedMods::mods(bool onlyOfficial) const +QStringList StarfieldUnmanagedMods::mods(bool onlyOfficial) const { QStringList result; @@ -40,7 +39,7 @@ QStringList StarfieldUnmangedMods::mods(bool onlyOfficial) const return result; } -QFileInfo StarfieldUnmangedMods::referenceFile(const QString& modName) const +QFileInfo StarfieldUnmanagedMods::referenceFile(const QString& modName) const { QFileInfoList files; QMap directories = {{"data", game()->dataDirectory()}}; @@ -55,52 +54,54 @@ QFileInfo StarfieldUnmangedMods::referenceFile(const QString& modName) const } } -QJsonObject StarfieldUnmangedMods::getContentCatalog() const +std::map +StarfieldUnmanagedMods::parseContentCatalog() const { QFile content(m_AppDataFolder + "/" + game()->gameShortName() + "/ContentCatalog.txt"); - if (content.exists()) { - if (content.open(QIODevice::OpenModeFlag::ReadOnly)) { - ON_BLOCK_EXIT([&content]() { - content.close(); - }); - auto contentData = content.readAll(); - QJsonParseError jsonError; - QJsonDocument contentDoc = QJsonDocument::fromJson(contentData, &jsonError); - if (jsonError.error) { - MOBase::log::warn(QObject::tr("ContentCatalog.txt appears to be corrupt: %1") - .arg(jsonError.errorString())); - } else { - return contentDoc.object(); + std::map contentCatalog; + if (content.open(QIODevice::OpenModeFlag::ReadOnly)) { + auto contentData = content.readAll(); + QJsonParseError jsonError; + QJsonDocument contentDoc = QJsonDocument::fromJson(contentData, &jsonError); + if (jsonError.error) { + MOBase::log::warn(QObject::tr("ContentCatalog.txt appears to be corrupt: %1") + .arg(jsonError.errorString())); + } else { + QJsonObject contentObj = contentDoc.object(); + for (const auto& mod : contentObj.keys()) { + if (mod == "ContentCatalog") + continue; + auto modInfo = contentObj.value(mod).toObject(); + QStringList pluginList; + QStringList files; + for (const auto& file : modInfo.value("Files").toArray()) { + QString fileName = file.toString(); + files.append(fileName); + if (fileName.endsWith(".esm", Qt::CaseInsensitive) || + fileName.endsWith(".esl", Qt::CaseInsensitive) || + fileName.endsWith(".esp", Qt::CaseInsensitive)) { + pluginList.append(fileName); + } + } + for (const auto& plugin : pluginList) { + contentCatalog[plugin] = ContentCatalog(); + contentCatalog[plugin].files = files; + contentCatalog[plugin].name = modInfo.value("Title").toString(); + } } } } - return QJsonObject(); + return contentCatalog; } -QStringList StarfieldUnmangedMods::secondaryFiles(const QString& modName) const +QStringList StarfieldUnmanagedMods::secondaryFiles(const QString& modName) const { QStringList files; - QJsonObject contentObj = getContentCatalog(); - for (auto mod : contentObj.keys()) { - if (mod == "ContentCatalog") - continue; - auto modData = contentObj.value(mod).toObject(); - auto modFiles = modData.value("Files").toArray(); - bool found = false; - for (auto file : modFiles) { - if (file.toString().startsWith(modName, Qt::CaseInsensitive)) { - found = true; - } - if (found) - break; - } - if (found) { - for (auto file : modFiles) { - if (!file.toString().endsWith(".esm") && !file.toString().endsWith(".esl") && - !file.toString().endsWith(".esp")) - files.append(file.toString()); - } + auto contentCatalog = parseContentCatalog(); + for (const auto& mod : contentCatalog) { + if (mod.first.startsWith(modName, Qt::CaseInsensitive)) { + files += mod.second.files; break; } } @@ -115,31 +116,15 @@ QStringList StarfieldUnmangedMods::secondaryFiles(const QString& modName) const return files; } -QString StarfieldUnmangedMods::displayName(const QString& modName) const +QString StarfieldUnmanagedMods::displayName(const QString& modName) const { - QFile content(m_AppDataFolder + "/" + game()->gameShortName() + - "/ContentCatalog.txt"); - QString name = modName; - QJsonObject contentObj = getContentCatalog(); - for (auto mod : contentObj.keys()) { - if (mod == "ContentCatalog") - continue; - auto modData = contentObj.value(mod).toObject(); - auto modFiles = modData.value("Files").toArray(); - bool found = false; - for (auto file : modFiles) { - if (file.toString().startsWith(modName, Qt::CaseInsensitive)) { - found = true; - } - if (found) - break; - } - if (found) { - name = modData.value("Title").toString(); - break; + auto contentCatalog = parseContentCatalog(); + for (const auto& mod : contentCatalog) { + if (mod.first.startsWith(modName, Qt::CaseInsensitive)) { + return mod.second.name; } } // unlike in earlier games, in fallout 4 the file name doesn't correspond to // the public name - return name; + return modName; } diff --git a/src/starfieldunmanagedmods.h b/src/starfieldunmanagedmods.h index 83985cd..abe14ef 100644 --- a/src/starfieldunmanagedmods.h +++ b/src/starfieldunmanagedmods.h @@ -7,11 +7,19 @@ #include -class StarfieldUnmangedMods : public GamebryoUnmangedMods +class StarfieldUnmanagedMods : public GamebryoUnmangedMods { + friend class GameStarfield; + + struct ContentCatalog + { + QString name; + QStringList files; + }; + public: - StarfieldUnmangedMods(const GameStarfield* game, const QString appDataFolder); - ~StarfieldUnmangedMods(); + StarfieldUnmanagedMods(const GameStarfield* game, const QString& appDataFolder); + ~StarfieldUnmanagedMods(); virtual QStringList mods(bool onlyOfficial) const override; virtual QFileInfo referenceFile(const QString& modName) const override; @@ -19,7 +27,7 @@ class StarfieldUnmangedMods : public GamebryoUnmangedMods virtual QString displayName(const QString& modName) const override; private: - QJsonObject getContentCatalog() const; + std::map parseContentCatalog() const; private: QString m_AppDataFolder;