Skip to content

Commit

Permalink
Add Creation parsing and fix issues
Browse files Browse the repository at this point in the history
- Read creations from LocalAppData
- Don't include CC files in primary plugins
- Don't parse CCC file if sTestFile set
- Fix parsing of secondary data directories
  • Loading branch information
Silarn committed Jun 16, 2024
1 parent 6936659 commit 42a8296
Show file tree
Hide file tree
Showing 3 changed files with 162 additions and 34 deletions.
74 changes: 54 additions & 20 deletions src/gamestarfield.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@
#include <QDesktopServices>
#include <QDir>
#include <QFileInfo>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonValue>
#include <QList>
#include <QObject>
#include <QString>
Expand Down Expand Up @@ -52,7 +56,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));
registerFeature(std::make_shared<StarfieldUnmangedMods>(this, localAppFolder()));
registerFeature(std::make_shared<StarfieldBSAInvalidation>(dataArchives.get(), this));

return true;
Expand Down Expand Up @@ -228,8 +232,6 @@ QStringList GameStarfield::primaryPlugins() const
if (loadOrderMechanism() == LoadOrderMechanism::None) {
plugins << enabledPlugins();
plugins << testPlugins;
} else {
plugins << CCPlugins();
}

return plugins;
Expand Down Expand Up @@ -273,28 +275,60 @@ QStringList GameStarfield::DLCPlugins() const
QStringList GameStarfield::CCPlugins() const
{
QStringList plugins = {};
QFile file(gameDirectory().absoluteFilePath("Starfield.ccc"));
if (file.open(QIODevice::ReadOnly)) {
ON_BLOCK_EXIT([&file]() {
file.close();
});

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();
QStringList corePlugins = primaryPlugins() + DLCPlugins();
if (!testFilePresent()) {
QFile file(gameDirectory().absoluteFilePath("Starfield.ccc"));
if (file.open(QIODevice::ReadOnly)) {
ON_BLOCK_EXIT([&file]() {
file.close();
});

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 (modName.size() > 0) {
if (!plugins.contains(modName, Qt::CaseInsensitive)) {
if (corePlugins.contains(modName, Qt::CaseInsensitive)) {
plugins.append(modName);
}
}
}
}
}
}

if (modName.size() > 0) {
if (!plugins.contains(modName, Qt::CaseInsensitive)) {
plugins.append(modName);
QFile content(localAppFolder() + "/" + gameShortName() +
"/ContentCatalog.txt");
if (content.exists()) {
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());
}
}
}
}
}
content.close();
}
return plugins;
}
Expand Down
115 changes: 102 additions & 13 deletions src/starfieldunmanagedmods.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
#include "starfieldunmanagedmods.h"

StarfieldUnmangedMods::StarfieldUnmangedMods(const GameGamebryo* game)
: GamebryoUnmangedMods(game)
#include <QFile>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonValue>

StarfieldUnmangedMods::StarfieldUnmangedMods(const GameStarfield* game,
const QString appDataFolder)
: GamebryoUnmangedMods(game), m_AppDataFolder(appDataFolder)
{}

StarfieldUnmangedMods::~StarfieldUnmangedMods() {}
Expand All @@ -16,32 +23,114 @@ QStringList StarfieldUnmangedMods::mods(bool onlyOfficial) const
for (QString plugin : otherPlugins) {
pluginList.removeAll(plugin);
}
QDir dataDir(game()->dataDirectory());
for (const QString& fileName : dataDir.entryList({"*.esp", "*.esl", "*.esm"})) {
if (!pluginList.contains(fileName, Qt::CaseInsensitive)) {
if (!onlyOfficial || pluginList.contains(fileName, Qt::CaseInsensitive)) {
result.append(fileName.chopped(4)); // trims the extension off
QMap<QString, QDir> directories = {{"data", game()->dataDirectory()}};
directories.insert(game()->secondaryDataDirectories());
for (QDir directory : directories) {
for (const QString& fileName : directory.entryList({"*.esp", "*.esl", "*.esm"})) {
if (!pluginList.contains(fileName, Qt::CaseInsensitive)) {
if (!onlyOfficial || pluginList.contains(fileName, Qt::CaseInsensitive)) {
result.append(fileName.chopped(4)); // trims the extension off
}
}
}
}

return result;
}

QFileInfo StarfieldUnmangedMods::referenceFile(const QString& modName) const
{
QFileInfoList files;
QMap<QString, QDir> directories = {{"data", game()->dataDirectory()}};
directories.insert(game()->secondaryDataDirectories());
for (QDir directory : directories) {
files += directory.entryInfoList(QStringList() << modName + ".es*");
}
if (files.size() > 0) {
return files.at(0);
} else {
return QFileInfo();
}
}

QStringList StarfieldUnmangedMods::secondaryFiles(const QString& modName) const
{
QFile content(m_AppDataFolder + "/" + game()->gameShortName() +
"/ContentCatalog.txt");
QStringList files;
if (content.exists()) {
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().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());
}
break;
}
}
}
content.close();
}
// file extension in FO4 is .ba2 instead of bsa
QStringList archives;
QDir dataDir = game()->dataDirectory();
for (const QString& archiveName : dataDir.entryList({modName + "*.ba2"})) {
archives.append(dataDir.absoluteFilePath(archiveName));
QMap<QString, QDir> directories = {{"data", game()->dataDirectory()}};
directories.insert(game()->secondaryDataDirectories());
for (QDir directory : directories) {
for (const QString& archiveName : directory.entryList({modName + "*.ba2"})) {
files.append(directory.absoluteFilePath(archiveName));
}
}
return archives;
return files;
}

QString StarfieldUnmangedMods::displayName(const QString& modName) const
{
QFile content(m_AppDataFolder + "/" + game()->gameShortName() +
"/ContentCatalog.txt");
QString name = modName;
if (content.exists()) {
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().startsWith(modName, Qt::CaseInsensitive)) {
found = true;
}
if (found)
break;
}
if (found) {
name = modData.value("Title").toString();
break;
}
}
}
content.close();
}
// unlike in earlier games, in fallout 4 the file name doesn't correspond to
// the public name
return modName;
return name;
}
7 changes: 6 additions & 1 deletion src/starfieldunmanagedmods.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,21 @@

#include "gamebryounmanagedmods.h"
#include <gamegamebryo.h>
#include "gamestarfield.h"

class StarfieldUnmangedMods : public GamebryoUnmangedMods
{
public:
StarfieldUnmangedMods(const GameGamebryo* game);
StarfieldUnmangedMods(const GameStarfield* game, const QString appDataFolder);
~StarfieldUnmangedMods();

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:
QString m_AppDataFolder;
};

#endif // STARFIELDUNMANAGEDMODS_H

0 comments on commit 42a8296

Please sign in to comment.