Skip to content

Commit

Permalink
Shattered space update (#32)
Browse files Browse the repository at this point in the history
* Add support for the vehicle update
* New save format (v140) which adds another plugin list flag for 'is custom plugin'; remaining changes are compatible with existing parser
* New core plugin (SFBGS004.esm)
* Remove obsolete CCC primary plugin fix
* Update CCC parsing to ignore duplicate plugins
  • Loading branch information
Silarn authored Oct 3, 2024
1 parent 1b71dc1 commit ddd90cc
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 66 deletions.
99 changes: 34 additions & 65 deletions src/gamestarfield.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,36 +53,9 @@ bool GameStarfield::init(IOrganizer* moInfo)
registerFeature(std::make_shared<StarfieldUnmanagedMods>(this, localAppFolder()));
registerFeature(std::make_shared<StarfieldBSAInvalidation>(dataArchives.get(), this));

m_Organizer->pluginList()->onRefreshed([&]() {
setCCCFile();
});

return true;
}

/*
* This is used to write the primary plugins to a profile-based Starfield.ccc file. We
* map this into the game directory with the VFS. The game does not currently ship with
* this file but does still read it like SkyrimSE and Fallout 4. We can make use of it
* to correct the current behavior where core plugins are loaded after parsing
* plugins.txt leading to ambiguous load orders.
*/
void GameStarfield::setCCCFile() const
{
if (m_Organizer->profilePath().isEmpty())
return;
if (m_Organizer->pluginSetting(name(), "enable_loadorder_fix").toBool()) {
QFile cccFile(m_Organizer->profilePath() + "/Starfield.ccc");
if (cccFile.open(QIODevice::WriteOnly)) {
auto plugins = primaryPlugins();
for (auto plugin : plugins) {
cccFile.write(plugin.toUtf8());
cccFile.write("\n");
}
}
}
}

QString GameStarfield::gameName() const
{
return "Starfield";
Expand Down Expand Up @@ -167,18 +140,11 @@ QList<PluginSetting> GameStarfield::settings() const
true)
<< PluginSetting("enable_management_warnings",
tr("Show a warning when plugins.txt management is invalid."),
true)
<< PluginSetting("enable_loadorder_fix",
tr("Utilize Starfield.ccc to affix core plugin load order "
"(will override existing file)."),
true);
}

MappingType GameStarfield::mappings() const
{
if (m_Organizer->pluginSetting(name(), "enable_loadorder_fix").toBool()) {
setCCCFile();
}
MappingType result;
if (testFilePlugins().isEmpty()) {
for (const QString& profileFile : {"plugins.txt", "loadorder.txt"}) {
Expand All @@ -187,14 +153,6 @@ MappingType GameStarfield::mappings() const
false});
}
}
if (m_Organizer->pluginSetting(name(), "enable_loadorder_fix").toBool()) {
// map the Starfield.ccc from the profile to both the game folder and the My Games
// folder (used by LOOT for instance)
result.push_back({m_Organizer->profilePath() + "/" + "Starfield.ccc",
gameDirectory().absolutePath() + "/" + "Starfield.ccc", false});
result.push_back({m_Organizer->profilePath() + "/" + "Starfield.ccc",
myGamesPath() + "/" + "Starfield.ccc", false});
}
return result;
}

Expand Down Expand Up @@ -255,18 +213,26 @@ QStringList GameStarfield::testFilePlugins() const

QStringList GameStarfield::primaryPlugins() const
{
QStringList plugins = {"Starfield.esm", "Constellation.esm",
"OldMars.esm", "BlueprintShips-Starfield.esm",
"SFBGS007.esm", "SFBGS008.esm",
"SFBGS006.esm", "SFBGS003.esm",
"SFBGS004.esm"};
QStringList plugins = {"Starfield.esm", "Constellation.esm",
"ShatteredSpace.esm", "OldMars.esm",
"SFBGS003.esm", "SFBGS004.esm",
"SFBGS006.esm", "SFBGS007.esm",
"SFBGS008.esm", "BlueprintShips-Starfield.esm"};

for (auto plugin : CCCPlugins()) {
if (!plugins.contains(plugin, Qt::CaseInsensitive)) {
plugins.append(plugin);
}
}

auto testPlugins = testFilePlugins();
if (loadOrderMechanism() == LoadOrderMechanism::None) {
plugins << enabledPlugins();
plugins << testPlugins;
}

plugins.removeDuplicates();

return plugins;
}

Expand Down Expand Up @@ -302,42 +268,45 @@ bool GameStarfield::prepareIni(const QString& exec)

QStringList GameStarfield::DLCPlugins() const
{
return {};
return {"Constellation.esm", "ShatteredSpace.esm"};
}

QStringList GameStarfield::CCPlugins() const
QStringList GameStarfield::CCCPlugins() const
{
// While the CCC file appears to be mostly legacy, we need to parse it since the game
// will still read it and there are some compatibility reason to use it for
// force-loading the core game plugins.
QStringList plugins = {};
QStringList corePlugins = primaryPlugins() + DLCPlugins();
QStringList plugins = {};
if (!testFilePresent()) {
QFile file(gameDirectory().absoluteFilePath("Starfield.ccc"));
if (m_Organizer->pluginSetting(name(), "enable_loadorder_fix").toBool() &&
!m_Organizer->profilePath().isEmpty()) {
file.setFileName(m_Organizer->profilePath() + "/Starfield.ccc");
QFile myDocsCCCFile(myGamesPath() + "\Starfield.ccc");
QFile gameCCCFile(gameDirectory().absoluteFilePath("Starfield.ccc"));
QFile* file;
if (myDocsCCCFile.exists()) {
file = &myDocsCCCFile;
} else {
file = &gameCCCFile;
}
if (file.open(QIODevice::ReadOnly)) {
if (file.size() > 0) {
while (!file.atEnd()) {
QByteArray line = file.readLine().trimmed();
if (file->open(QIODevice::ReadOnly)) {
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();
modName = QString::fromUtf8(line.constData());
}

if (modName.size() > 0) {
if (!plugins.contains(modName, Qt::CaseInsensitive) &&
!corePlugins.contains(modName, Qt::CaseInsensitive)) {
plugins.append(modName);
}
plugins.append(modName);
}
}
}
}
}
return plugins;
}

QStringList GameStarfield::CCPlugins() const
{
QStringList plugins = {};
std::shared_ptr<StarfieldUnmanagedMods> unmanagedMods =
std::static_pointer_cast<StarfieldUnmanagedMods>(
m_Organizer->gameFeatures()->gameFeature<MOBase::UnmanagedMods>());
Expand Down
2 changes: 1 addition & 1 deletion src/gamestarfield.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,9 @@ class GameStarfield : public GameGamebryo, public MOBase::IPluginDiagnose
QString savegameSEExtension() const override;

private:
QStringList CCCPlugins() const;
bool activeESP() const;
bool testFilePresent() const;
void setCCCFile() const;

private:
static const unsigned int PROBLEM_ESP = 1;
Expand Down
5 changes: 5 additions & 0 deletions src/starfieldgameplugins.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ bool StarfieldGamePlugins::mediumPluginsAreSupported()
return true;
}

bool StarfieldGamePlugins::blueprintPluginsAreSupported()
{
return true;
}

void StarfieldGamePlugins::writePluginList(const IPluginList* pluginList,
const QString& filePath)
{
Expand Down
1 change: 1 addition & 0 deletions src/starfieldgameplugins.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class StarfieldGamePlugins : public CreationGamePlugins

protected:
virtual bool mediumPluginsAreSupported() override;
virtual bool blueprintPluginsAreSupported() override;
virtual void writePluginList(const MOBase::IPluginList* pluginList,
const QString& filePath) override;
virtual QStringList readPluginList(MOBase::IPluginList* pluginList) override;
Expand Down

0 comments on commit ddd90cc

Please sign in to comment.