Skip to content

Commit

Permalink
Various changes and fixes
Browse files Browse the repository at this point in the history
- 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
  • Loading branch information
Silarn committed Jun 19, 2024
1 parent 8224b5e commit ee801f2
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 105 deletions.
59 changes: 22 additions & 37 deletions src/gamestarfield.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,6 @@
#include <QDesktopServices>
#include <QDir>
#include <QFileInfo>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonValue>
#include <QList>
#include <QObject>
#include <QString>
Expand Down Expand Up @@ -55,7 +51,7 @@ bool GameStarfield::init(IOrganizer* moInfo)
std::make_shared<StarfieldModDataContent>(m_Organizer->gameFeatures()));
registerFeature(std::make_shared<GamebryoSaveGameInfo>(this));
registerFeature(std::make_shared<StarfieldGamePlugins>(moInfo));
registerFeature(std::make_shared<StarfieldUnmangedMods>(this, localAppFolder()));
registerFeature(std::make_shared<StarfieldUnmanagedMods>(this, localAppFolder()));
registerFeature(std::make_shared<StarfieldBSAInvalidation>(dataArchives.get(), this));

m_Organizer->pluginList()->onRefreshed([&]() {
Expand Down Expand Up @@ -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);
}
}
Expand All @@ -343,28 +338,18 @@ QStringList GameStarfield::CCPlugins() const
}
}

std::shared_ptr<StarfieldUnmanagedMods> unmanagedMods =
std::static_pointer_cast<StarfieldUnmanagedMods>(
m_Organizer->gameFeatures()->gameFeature<MOBase::UnmanagedMods>());

// 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);
}
}
}
Expand Down
113 changes: 49 additions & 64 deletions src/starfieldunmanagedmods.cpp
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
#include "starfieldunmanagedmods.h"

#include "log.h"
#include "scopeguard.h"

#include <QFile>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonValue>

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;

Expand All @@ -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<QString, QDir> directories = {{"data", game()->dataDirectory()}};
Expand All @@ -55,52 +54,54 @@ QFileInfo StarfieldUnmangedMods::referenceFile(const QString& modName) const
}
}

QJsonObject StarfieldUnmangedMods::getContentCatalog() const
std::map<QString, StarfieldUnmanagedMods::ContentCatalog>
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<QString, StarfieldUnmanagedMods::ContentCatalog> 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;
}
}
Expand All @@ -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;
}
16 changes: 12 additions & 4 deletions src/starfieldunmanagedmods.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,27 @@

#include <QJsonObject>

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;
virtual QStringList secondaryFiles(const QString& modName) const override;
virtual QString displayName(const QString& modName) const override;

private:
QJsonObject getContentCatalog() const;
std::map<QString, ContentCatalog> parseContentCatalog() const;

private:
QString m_AppDataFolder;
Expand Down

0 comments on commit ee801f2

Please sign in to comment.