From 67f36d8f9ff047bb73776107e60028cf00d26c1a Mon Sep 17 00:00:00 2001 From: Jeremy Rimpo Date: Mon, 10 Jun 2024 19:04:07 -0500 Subject: [PATCH 1/6] Various updates for the Creation update - Remove light warnings and plugins txt enabler checks - Remove overlay code for new 'medium' plugin type --- src/gamestarfield.cpp | 135 ++--------------------------------- src/gamestarfield.h | 10 +-- src/starfieldgameplugins.cpp | 2 +- src/starfieldgameplugins.h | 2 +- 4 files changed, 7 insertions(+), 142 deletions(-) diff --git a/src/gamestarfield.cpp b/src/gamestarfield.cpp index b521587..74fded5 100644 --- a/src/gamestarfield.cpp +++ b/src/gamestarfield.cpp @@ -138,21 +138,9 @@ QList GameStarfield::settings() const "enable_esp_warning", tr("Show a warning when ESP plugins are enabled in the load order."), true) - << PluginSetting( - "enable_esl_warning", - tr("Show a warning when light plugins are enabled in the load order."), - true) - << PluginSetting("enable_overlay_warning", - tr("Show a warning when overlay-flagged plugins ar enabled " - "in the load order."), - true) << PluginSetting("enable_management_warnings", tr("Show a warning when plugins.txt management is invalid."), true) - << PluginSetting("bypass_plugins_enabler_check", - tr("Bypass check for Plugins.txt Enabler. This may be useful " - "if you use the ASI loader."), - false) << PluginSetting("enable_loot_sorting", tr("As of this release LOOT Starfield support is minimal to " "nonexistant. Toggle this to enable it anyway."), @@ -230,7 +218,8 @@ QStringList GameStarfield::testFilePlugins() const QStringList GameStarfield::primaryPlugins() const { QStringList plugins = {"Starfield.esm", "Constellation.esm", "OldMars.esm", - "SFBGS006.esm", "SFBGS007.esm", "SFBGS008.esm"}; + "SFBGS003.esm", "SFBGS006.esm", "SFBGS007.esm", + "SFBGS008.esm"}; auto testPlugins = testFilePlugins(); if (loadOrderMechanism() == LoadOrderMechanism::None) { @@ -317,9 +306,7 @@ IPluginGame::SortMechanism GameStarfield::sortMechanism() const IPluginGame::LoadOrderMechanism GameStarfield::loadOrderMechanism() const { - if (!testFilePresent() && - (pluginsTxtEnablerPresent() || - m_Organizer->pluginSetting(name(), "bypass_plugins_enabler_check").toBool())) + if (!testFilePresent()) return IPluginGame::LoadOrderMechanism::PluginsTxt; return IPluginGame::LoadOrderMechanism::None; } @@ -342,17 +329,9 @@ std::vector GameStarfield::activeProblems() const if (m_Organizer->pluginSetting(name(), "enable_esp_warning").toBool() && activeESP()) result.push_back(PROBLEM_ESP); - if (m_Organizer->pluginSetting(name(), "enable_esl_warning").toBool() && - activeESL()) - result.push_back(PROBLEM_ESL); - if (m_Organizer->pluginSetting(name(), "enable_overlay_warning").toBool() && - activeOverlay()) - result.push_back(PROBLEM_OVERLAY); if (m_Organizer->pluginSetting(name(), "enable_management_warnings").toBool()) { if (testFilePresent()) result.push_back(PROBLEM_TEST_FILE); - else if (!pluginsTxtEnablerPresent()) - result.push_back(PROBLEM_PLUGINS_TXT); } } return result; @@ -379,59 +358,6 @@ bool GameStarfield::activeESP() const return false; } -bool GameStarfield::activeESL() const -{ - m_Active_ESLs.clear(); - std::set enabledPlugins; - - QStringList esps = m_Organizer->findFiles("", [](const QString& fileName) -> bool { - return fileName.endsWith(".esp", FileNameComparator::CaseSensitivity) || - fileName.endsWith(".esm", FileNameComparator::CaseSensitivity) || - fileName.endsWith(".esl", FileNameComparator::CaseSensitivity); - }); - - for (const QString& esp : esps) { - QString baseName = QFileInfo(esp).fileName(); - if (primaryPlugins().contains(baseName, Qt::CaseInsensitive)) - continue; - if (m_Organizer->pluginList()->state(baseName) == IPluginList::STATE_ACTIVE && - !m_Organizer->pluginList()->hasNoRecords(baseName)) - if (m_Organizer->pluginList()->hasLightExtension(baseName) || - m_Organizer->pluginList()->isLightFlagged(baseName)) - m_Active_ESLs.insert(baseName); - } - - if (!m_Active_ESLs.empty()) - return true; - return false; -} - -bool GameStarfield::activeOverlay() const -{ - m_Active_Overlays.clear(); - std::set enabledPlugins; - - QStringList esps = m_Organizer->findFiles("", [](const QString& fileName) -> bool { - return fileName.endsWith(".esp", FileNameComparator::CaseSensitivity) || - fileName.endsWith(".esm", FileNameComparator::CaseSensitivity) || - fileName.endsWith(".esl", FileNameComparator::CaseSensitivity); - }); - - for (const QString& esp : esps) { - QString baseName = QFileInfo(esp).fileName(); - if (primaryPlugins().contains(baseName, Qt::CaseInsensitive)) - continue; - if (m_Organizer->pluginList()->state(baseName) == IPluginList::STATE_ACTIVE) { - if (m_Organizer->pluginList()->isOverlayFlagged(baseName)) - m_Active_Overlays.insert(baseName); - } - } - - if (!m_Active_Overlays.empty()) - return true; - return false; -} - bool GameStarfield::testFilePresent() const { if (!testFilePlugins().isEmpty()) @@ -439,28 +365,13 @@ bool GameStarfield::testFilePresent() const return false; } -bool GameStarfield::pluginsTxtEnablerPresent() const -{ - auto files = m_Organizer->findFiles("sfse\\plugins", {"sfpluginstxtenabler.dll"}); - files += m_Organizer->findFiles("", {"sfpluginstxtenabler.asi"}); - if (files.isEmpty()) - return false; - return true; -} - QString GameStarfield::shortDescription(unsigned int key) const { switch (key) { case PROBLEM_ESP: return tr("You have active ESP plugins in Starfield"); - case PROBLEM_ESL: - return tr("You have active ESL plugins in Starfield"); - case PROBLEM_OVERLAY: - return tr("You have active overlay plugins"); case PROBLEM_TEST_FILE: return tr("sTestFile entries are present"); - case PROBLEM_PLUGINS_TXT: - return tr("Plugins.txt Enabler missing"); } return ""; } @@ -483,56 +394,18 @@ QString GameStarfield::fullDescription(unsigned int key) const "

Current ESPs:

%1

") .arg(espInfo); } - case PROBLEM_ESL: { - QString eslInfo = SetJoin(m_Active_ESLs, ", "); - return tr("

Light plugins work differently in Starfield. They use a different " - "base form ID compared with standard plugin files.

" - "

What this means is that you can't just change a standard plugin to a " - "light plugin at will, it can and will break any dependent plugin. If " - "you do so, be absolutely certain no other plugins use that plugin as a " - "master.

" - "

Notably, xEdit does not currently support saving or loading ESL " - "files under these conditions.

" - "

Current ESLs:

%1

") - .arg(eslInfo); - } - case PROBLEM_OVERLAY: { - QString overlayInfo = SetJoin(m_Active_Overlays, ", "); - return tr("

Overlay-flagged plugins are not currently recommended. In theory, " - "they should allow you to update existing records without utilizing " - "additional load order slots. Unfortunately, it appears that the game " - "still allocates the slots as if these were standard plugins. Therefore, " - "at the moment there is no real use for this plugin flag.

" - "

Notably, xEdit does not currently support saving or loading " - "overlay-flagged files under these conditions.

" - "

Current Overlays:

%1

") - .arg(overlayInfo); - } case PROBLEM_TEST_FILE: { return tr("

You have plugin managment enabled but you still have sTestFile " "settings in your StarfieldCustom.ini. These must be removed or the game " "will not read the plugins.txt file. Management is still disabled.

"); } - case PROBLEM_PLUGINS_TXT: { - return tr("

You have plugin management turned on but do not have the Plugins.txt " - "Enabler SFSE plugin installed. Plugin file management for Starfield " - "will not work without this SFSE plugin.

"); - } } return ""; } bool GameStarfield::hasGuidedFix(unsigned int key) const { - if (key == PROBLEM_PLUGINS_TXT) - return true; return false; } -void GameStarfield::startGuidedFix(unsigned int key) const -{ - if (key == PROBLEM_PLUGINS_TXT) { - QDesktopServices::openUrl( - QUrl("https://www.nexusmods.com/starfield/mods/4157?tab=files")); - } -} +void GameStarfield::startGuidedFix(unsigned int key) const {} diff --git a/src/gamestarfield.h b/src/gamestarfield.h index 7fde2e8..ac8d270 100644 --- a/src/gamestarfield.h +++ b/src/gamestarfield.h @@ -70,21 +70,13 @@ class GameStarfield : public GameGamebryo, public MOBase::IPluginDiagnose private: bool activeESP() const; - bool activeESL() const; - bool activeOverlay() const; bool testFilePresent() const; - bool pluginsTxtEnablerPresent() const; private: static const unsigned int PROBLEM_ESP = 1; - static const unsigned int PROBLEM_ESL = 2; - static const unsigned int PROBLEM_OVERLAY = 3; - static const unsigned int PROBLEM_TEST_FILE = 4; - static const unsigned int PROBLEM_PLUGINS_TXT = 5; + static const unsigned int PROBLEM_TEST_FILE = 2; mutable std::set m_Active_ESPs; - mutable std::set m_Active_ESLs; - mutable std::set m_Active_Overlays; }; #endif // GAMEStarfield_H diff --git a/src/starfieldgameplugins.cpp b/src/starfieldgameplugins.cpp index 995aa58..3653aca 100644 --- a/src/starfieldgameplugins.cpp +++ b/src/starfieldgameplugins.cpp @@ -6,7 +6,7 @@ StarfieldGamePlugins::StarfieldGamePlugins(MOBase::IOrganizer* organizer) : CreationGamePlugins(organizer) {} -bool StarfieldGamePlugins::overridePluginsAreSupported() +bool StarfieldGamePlugins::mediumPluginsAreSupported() { return true; } diff --git a/src/starfieldgameplugins.h b/src/starfieldgameplugins.h index 652ef70..c654306 100644 --- a/src/starfieldgameplugins.h +++ b/src/starfieldgameplugins.h @@ -13,7 +13,7 @@ class StarfieldGamePlugins : public CreationGamePlugins StarfieldGamePlugins(MOBase::IOrganizer* organizer); protected: - virtual bool overridePluginsAreSupported() override; + virtual bool mediumPluginsAreSupported() override; virtual void writePluginList(const MOBase::IPluginList* pluginList, const QString& filePath) override; virtual QStringList readPluginList(MOBase::IPluginList* pluginList) override; From d46f05e668a96554d4898754572f6f237402b8d3 Mon Sep 17 00:00:00 2001 From: Jeremy Rimpo Date: Mon, 10 Jun 2024 19:12:25 -0500 Subject: [PATCH 2/6] Formatting fix --- src/gamestarfield.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gamestarfield.h b/src/gamestarfield.h index ac8d270..42e2748 100644 --- a/src/gamestarfield.h +++ b/src/gamestarfield.h @@ -73,8 +73,8 @@ class GameStarfield : public GameGamebryo, public MOBase::IPluginDiagnose bool testFilePresent() const; private: - static const unsigned int PROBLEM_ESP = 1; - static const unsigned int PROBLEM_TEST_FILE = 2; + static const unsigned int PROBLEM_ESP = 1; + static const unsigned int PROBLEM_TEST_FILE = 2; mutable std::set m_Active_ESPs; }; From 6eb6244a5fd54eb783267608113c63679aa9b389 Mon Sep 17 00:00:00 2001 From: Jeremy Rimpo Date: Tue, 11 Jun 2024 01:48:08 -0500 Subject: [PATCH 3/6] Updates for new starfield save data --- src/starfieldsavegame.cpp | 36 ++++++++++++++++++++---------------- src/starfieldsavegame.h | 5 +++-- 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/src/starfieldsavegame.cpp b/src/starfieldsavegame.cpp index 1b75c65..0196daa 100644 --- a/src/starfieldsavegame.cpp +++ b/src/starfieldsavegame.cpp @@ -5,14 +5,15 @@ #include "gamestarfield.h" StarfieldSaveGame::StarfieldSaveGame(QString const& fileName, GameStarfield const* game) - : GamebryoSaveGame(fileName, game, true) + : GamebryoSaveGame(fileName, game, true, true) { FileWrapper file(getFilepath(), "BCPS"); getData(file); FILETIME creationTime; - fetchInformationFields(file, m_SaveNumber, m_PCName, m_PCLevel, m_PCLocation, - creationTime); + unsigned char saveVersion; + fetchInformationFields(file, m_SaveNumber, saveVersion, m_PCName, m_PCLevel, + m_PCLocation, creationTime); file.closeCompressedData(); file.close(); @@ -51,18 +52,18 @@ void StarfieldSaveGame::getData(FileWrapper& file) const } void StarfieldSaveGame::fetchInformationFields( - FileWrapper& file, unsigned long& saveNumber, QString& playerName, - unsigned short& playerLevel, QString& playerLocation, FILETIME& creationTime) const + FileWrapper& file, unsigned long& saveNumber, unsigned char& saveVersion, + QString& playerName, unsigned short& playerLevel, QString& playerLocation, + FILETIME& creationTime) const { char fileID[12]; // SFS_SAVEGAME unsigned int headerSize; unsigned int version; - unsigned char unknown; // file.read(fileID, 12); - headerSize = file.readInt(12); - version = file.readInt(); - unknown = file.readChar(); - saveNumber = file.readInt(); + headerSize = file.readInt(12); + saveNumber = file.readInt(); + saveVersion = file.readChar(); + version = file.readInt(); file.read(playerName); unsigned int temp; @@ -91,6 +92,7 @@ std::unique_ptr StarfieldSaveGame::fetchDataFields getData(file); FILETIME creationTime; + unsigned char saveVersion; { QString dummyName, dummyLocation; @@ -98,22 +100,24 @@ std::unique_ptr StarfieldSaveGame::fetchDataFields unsigned long dummySaveNumber; FILETIME dummyTime; - fetchInformationFields(file, dummySaveNumber, dummyName, dummyLevel, dummyLocation, - dummyTime); + fetchInformationFields(file, dummySaveNumber, saveVersion, dummyName, dummyLevel, + dummyLocation, dummyTime); } + bool extraInfo = saveVersion >= 122; + QStringList gamePlugins = m_Game->primaryPlugins() + m_Game->enabledPlugins(); + QString ignore; std::unique_ptr fields = std::make_unique(); - // fields->Screenshot = file.readImage(384, true); - uint8_t saveGameVersion = file.readChar(12); file.read(ignore); // game version file.read(ignore); // game version again? file.readInt(); // plugin info size - fields->Plugins = file.readPlugins(); - fields->LightPlugins = file.readLightPlugins(); + fields->Plugins = file.readPlugins(0, extraInfo, gamePlugins); + fields->LightPlugins = file.readLightPlugins(0, extraInfo, gamePlugins); + fields->MediumPlugins = file.readMediumPlugins(0, extraInfo, gamePlugins); file.closeCompressedData(); file.close(); diff --git a/src/starfieldsavegame.h b/src/starfieldsavegame.h index d8189bf..3be0b27 100644 --- a/src/starfieldsavegame.h +++ b/src/starfieldsavegame.h @@ -18,8 +18,9 @@ class StarfieldSaveGame : public GamebryoSaveGame void getData(FileWrapper& file) const; void fetchInformationFields(FileWrapper& file, unsigned long& saveNumber, - QString& playerName, unsigned short& playerLevel, - QString& playerLocation, FILETIME& creationTime) const; + unsigned char& saveVersion, QString& playerName, + unsigned short& playerLevel, QString& playerLocation, + FILETIME& creationTime) const; std::unique_ptr fetchDataFields() const override; }; From 8ca6b82abc2961d4e28883eaf8bfecd9d0357f81 Mon Sep 17 00:00:00 2001 From: Jeremy Rimpo Date: Tue, 11 Jun 2024 02:10:59 -0500 Subject: [PATCH 4/6] Add version check to parse medium plugins --- src/starfieldsavegame.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/starfieldsavegame.cpp b/src/starfieldsavegame.cpp index 0196daa..01d0822 100644 --- a/src/starfieldsavegame.cpp +++ b/src/starfieldsavegame.cpp @@ -110,14 +110,15 @@ std::unique_ptr StarfieldSaveGame::fetchDataFields QString ignore; std::unique_ptr fields = std::make_unique(); - uint8_t saveGameVersion = file.readChar(12); + file.readChar(12); file.read(ignore); // game version file.read(ignore); // game version again? file.readInt(); // plugin info size - fields->Plugins = file.readPlugins(0, extraInfo, gamePlugins); - fields->LightPlugins = file.readLightPlugins(0, extraInfo, gamePlugins); - fields->MediumPlugins = file.readMediumPlugins(0, extraInfo, gamePlugins); + fields->Plugins = file.readPlugins(0, extraInfo, gamePlugins); + fields->LightPlugins = file.readLightPlugins(0, extraInfo, gamePlugins); + if (saveVersion >= 122) + fields->MediumPlugins = file.readMediumPlugins(0, extraInfo, gamePlugins); file.closeCompressedData(); file.close(); From e4aa73fb036a00e65eb8501c7614ada5a7ba6208 Mon Sep 17 00:00:00 2001 From: Jeremy Rimpo Date: Tue, 11 Jun 2024 21:52:17 -0500 Subject: [PATCH 5/6] Add creation kit to default executables --- src/gamestarfield.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/gamestarfield.cpp b/src/gamestarfield.cpp index 74fded5..1296749 100644 --- a/src/gamestarfield.cpp +++ b/src/gamestarfield.cpp @@ -97,6 +97,8 @@ QList GameStarfield::executables() const ->gameFeature() ->loaderName())) << ExecutableInfo("Starfield", findInGameFolder(binaryName())) + << ExecutableInfo("Creation Kit", findInGameFolder("CreationKit.exe")) + .withSteamAppId("2722710") << ExecutableInfo("LOOT", QFileInfo(getLootPath())) .withArgument("--game=\"Starfield\""); } From 219991d97652b0c24fe981751f602d85fcddd09f Mon Sep 17 00:00:00 2001 From: Jeremy Rimpo Date: Tue, 11 Jun 2024 21:52:52 -0500 Subject: [PATCH 6/6] Move BlueprintShips-Starfield.esm to primary plugins --- src/gamestarfield.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/gamestarfield.cpp b/src/gamestarfield.cpp index 1296749..77781ca 100644 --- a/src/gamestarfield.cpp +++ b/src/gamestarfield.cpp @@ -219,9 +219,10 @@ QStringList GameStarfield::testFilePlugins() const QStringList GameStarfield::primaryPlugins() const { - QStringList plugins = {"Starfield.esm", "Constellation.esm", "OldMars.esm", - "SFBGS003.esm", "SFBGS006.esm", "SFBGS007.esm", - "SFBGS008.esm"}; + QStringList plugins = {"Starfield.esm", "Constellation.esm", + "OldMars.esm", "BlueprintShips-Starfield.esm", + "SFBGS003.esm", "SFBGS006.esm", + "SFBGS007.esm", "SFBGS008.esm"}; auto testPlugins = testFilePlugins(); if (loadOrderMechanism() == LoadOrderMechanism::None) { @@ -236,7 +237,7 @@ QStringList GameStarfield::primaryPlugins() const QStringList GameStarfield::enabledPlugins() const { - return {"BlueprintShips-Starfield.esm"}; + return {}; } QStringList GameStarfield::gameVariants() const