diff options
Diffstat (limited to 'libs/game_bethesda/src/games/starfield/gamestarfield.cpp')
| -rw-r--r-- | libs/game_bethesda/src/games/starfield/gamestarfield.cpp | 438 |
1 files changed, 438 insertions, 0 deletions
diff --git a/libs/game_bethesda/src/games/starfield/gamestarfield.cpp b/libs/game_bethesda/src/games/starfield/gamestarfield.cpp new file mode 100644 index 0000000..c6c211a --- /dev/null +++ b/libs/game_bethesda/src/games/starfield/gamestarfield.cpp @@ -0,0 +1,438 @@ +#include "gamestarfield.h" + +#include "ipluginlist.h" + +#include "starfieldbsainvalidation.h" +#include "starfielddataarchives.h" +#include "starfieldgameplugins.h" +#include "starfieldmoddatachecker.h" +#include "starfieldmoddatacontent.h" +#include "starfieldsavegame.h" +#include "starfieldscriptextender.h" +#include "starfieldunmanagedmods.h" + +#include "versioninfo.h" +#include <executableinfo.h> +#include <gamebryolocalsavegames.h> +#include <gamebryosavegameinfo.h> +#include <pluginsetting.h> + +#include <QCoreApplication> +#include <QDesktopServices> +#include <QDir> +#include <QFileInfo> +#include <QList> +#include <QObject> +#include <QString> +#include <QStringList> + +#include <memory> + +#include "utility.h" + +using namespace MOBase; + +const unsigned int GameStarfield::PROBLEM_ESP; +const unsigned int GameStarfield::PROBLEM_TEST_FILE; + +GameStarfield::GameStarfield() {} + +bool GameStarfield::init(IOrganizer* moInfo) +{ + if (!GameGamebryo::init(moInfo)) { + return false; + } + + auto dataArchives = std::make_shared<StarfieldDataArchives>(this); + registerFeature(std::make_shared<StarfieldScriptExtender>(this)); + registerFeature(dataArchives); + registerFeature( + std::make_shared<GamebryoLocalSavegames>(this, "StarfieldCustom.ini")); + registerFeature(std::make_shared<StarfieldModDataChecker>(this)); + registerFeature( + std::make_shared<StarfieldModDataContent>(m_Organizer->gameFeatures())); + registerFeature(std::make_shared<GamebryoSaveGameInfo>(this)); + registerFeature(std::make_shared<StarfieldGamePlugins>(moInfo)); + registerFeature(std::make_shared<StarfieldUnmanagedMods>(this, localAppFolder())); + registerFeature(std::make_shared<StarfieldBSAInvalidation>(dataArchives.get(), this)); + + return true; +} + +QString GameStarfield::gameName() const +{ + return "Starfield"; +} + +void GameStarfield::detectGame() +{ + m_GamePath = identifyGamePath(); + m_MyGamesPath = determineMyGamesPath("Starfield"); +} + +QString GameStarfield::identifyGamePath() const +{ + return parseSteamLocation(steamAPPId(), gameName()); +} + +QDir GameStarfield::dataDirectory() const +{ + QDir dataDir = documentsDirectory().absoluteFilePath("Data"); + if (!dataDir.exists()) + dataDir.mkdir(dataDir.path()); + return documentsDirectory().absoluteFilePath("Data"); +} + +QMap<QString, QDir> GameStarfield::secondaryDataDirectories() const +{ + QMap<QString, QDir> directories; + directories.insert("game_data", gameDirectory().absoluteFilePath("Data")); + return directories; +} + +QList<ExecutableInfo> GameStarfield::executables() const +{ + return QList<ExecutableInfo>() + << ExecutableInfo("SFSE", + findInGameFolder(m_Organizer->gameFeatures() + ->gameFeature<MOBase::ScriptExtender>() + ->loaderName())) + << ExecutableInfo("Starfield", findInGameFolder(binaryName())) + << ExecutableInfo("Creation Kit", findInGameFolder("CreationKit.exe")) + .withSteamAppId("2722710") + << ExecutableInfo("LOOT", QFileInfo(getLootPath())) + .withArgument("--game=\"Starfield\""); +} + +QList<ExecutableForcedLoadSetting> GameStarfield::executableForcedLoads() const +{ + return QList<ExecutableForcedLoadSetting>(); +} + +QString GameStarfield::name() const +{ + return "Starfield Support Plugin"; +} + +QString GameStarfield::localizedName() const +{ + return tr("Starfield Support Plugin"); +} + +QString GameStarfield::author() const +{ + return "Silarn"; +} + +QString GameStarfield::description() const +{ + return tr("Adds support for the game Starfield."); +} + +MOBase::VersionInfo GameStarfield::version() const +{ + return VersionInfo(1, 1, 0, VersionInfo::RELEASE_FINAL); +} + +QList<PluginSetting> GameStarfield::settings() const +{ + return QList<PluginSetting>() + << PluginSetting( + "enable_esp_warning", + tr("Show a warning when ESP plugins are enabled in the load order."), + true) + << PluginSetting("enable_management_warnings", + tr("Show a warning when plugins.txt management is invalid."), + true); +} + +MappingType GameStarfield::mappings() const +{ + MappingType result; + if (testFilePlugins().isEmpty()) { + for (const QString& profileFile : {"plugins.txt", "loadorder.txt"}) { + result.push_back({m_Organizer->profilePath() + "/" + profileFile, + localAppFolder() + "/" + gameShortName() + "/" + profileFile, + false}); + } + } + return result; +} + +void GameStarfield::initializeProfile(const QDir& path, ProfileSettings settings) const +{ + if (settings.testFlag(IPluginGame::MODS)) { + copyToProfile(localAppFolder() + "/Starfield", path, "plugins.txt"); + } + + if (settings.testFlag(IPluginGame::CONFIGURATION)) { + copyToProfile(myGamesPath(), path, "StarfieldPrefs.ini"); + copyToProfile(myGamesPath(), path, "StarfieldCustom.ini"); + } +} + +QString GameStarfield::savegameExtension() const +{ + return "sfs"; +} + +QString GameStarfield::savegameSEExtension() const +{ + return "sfse"; +} + +std::shared_ptr<const GamebryoSaveGame> +GameStarfield::makeSaveGame(QString filePath) const +{ + return std::make_shared<const StarfieldSaveGame>(filePath, this); +} + +QString GameStarfield::steamAPPId() const +{ + return "1716740"; +} + +QStringList GameStarfield::testFilePlugins() const +{ + QStringList plugins; + if (m_Organizer != nullptr && m_Organizer->profile() != nullptr) { + QString customIni( + m_Organizer->profile()->absoluteIniFilePath("StarfieldCustom.ini")); + if (QFile(customIni).exists()) { + for (int i = 1; i <= 10; ++i) { + QString setting("sTestFile"); + setting += std::to_string(i); + QString plugin = GameGamebryo::readIniValue(customIni, "General", setting, ""); + if (!plugin.isEmpty() && !plugins.contains(plugin, Qt::CaseInsensitive)) { + plugins.append(plugin); + } + } + } + } + return plugins; +} + +QStringList GameStarfield::primaryPlugins() const +{ + 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; +} + +QStringList GameStarfield::enabledPlugins() const +{ + return {}; +} + +QStringList GameStarfield::gameVariants() const +{ + return {"Regular"}; +} + +QString GameStarfield::gameShortName() const +{ + return "Starfield"; +} + +QString GameStarfield::gameNexusName() const +{ + return "starfield"; +} + +QStringList GameStarfield::iniFiles() const +{ + return {"StarfieldPrefs.ini", "StarfieldCustom.ini"}; +} + +bool GameStarfield::prepareIni(const QString& exec) +{ + return true; // no need to write to Starfield.ini +} + +QStringList GameStarfield::DLCPlugins() const +{ + return {"Constellation.esm", "ShatteredSpace.esm"}; +} + +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 = {}; + if (!testFilePresent()) { + 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(); + QString modName; + if ((line.size() > 0) && (line.at(0) != '#')) { + modName = QString::fromUtf8(line.constData()); + } + if (modName.size() > 0) { + 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>()); + + // 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. + if (unmanagedMods.get()) { + auto contentCatalog = unmanagedMods->parseContentCatalog(); + for (const auto& mod : contentCatalog) { + if (!plugins.contains(mod.first, Qt::CaseInsensitive)) { + plugins.append(mod.first); + } + } + } + return plugins; +} + +IPluginGame::SortMechanism GameStarfield::sortMechanism() const +{ + return IPluginGame::SortMechanism::LOOT; +} + +IPluginGame::LoadOrderMechanism GameStarfield::loadOrderMechanism() const +{ + if (!testFilePresent()) + return IPluginGame::LoadOrderMechanism::PluginsTxt; + return IPluginGame::LoadOrderMechanism::None; +} + +int GameStarfield::nexusModOrganizerID() const +{ + return 0; +} + +int GameStarfield::nexusGameID() const +{ + return 4187; +} + +// Start Diagnose +std::vector<unsigned int> GameStarfield::activeProblems() const +{ + std::vector<unsigned int> result; + if (m_Organizer->managedGame() == this) { + if (m_Organizer->pluginSetting(name(), "enable_esp_warning").toBool() && + activeESP()) + result.push_back(PROBLEM_ESP); + if (m_Organizer->pluginSetting(name(), "enable_management_warnings").toBool()) { + if (testFilePresent()) + result.push_back(PROBLEM_TEST_FILE); + } + } + return result; +} + +bool GameStarfield::activeESP() const +{ + m_Active_ESPs.clear(); + std::set<QString> enabledPlugins; + + QStringList esps = m_Organizer->findFiles("", [](const QString& fileName) -> bool { + return fileName.endsWith(".esp", FileNameComparator::CaseSensitivity); + }); + + for (const QString& esp : esps) { + QString baseName = QFileInfo(esp).fileName(); + if (m_Organizer->pluginList()->state(baseName) == IPluginList::STATE_ACTIVE) { + m_Active_ESPs.insert(baseName); + } + } + + if (!m_Active_ESPs.empty()) + return true; + return false; +} + +bool GameStarfield::testFilePresent() const +{ + if (!testFilePlugins().isEmpty()) + return true; + return false; +} + +QString GameStarfield::shortDescription(unsigned int key) const +{ + switch (key) { + case PROBLEM_ESP: + return tr("You have active ESP plugins in Starfield"); + case PROBLEM_TEST_FILE: + return tr("sTestFile entries are present"); + } + return ""; +} + +QString GameStarfield::fullDescription(unsigned int key) const +{ + switch (key) { + case PROBLEM_ESP: { + QString espInfo = SetJoin(m_Active_ESPs, ", "); + return tr("<p>ESP plugins are not ideal for Starfield. In addition to being unable " + "to sort them alongside ESM or master-flagged plugins, certain record " + "references are always kept loaded by the game. This consumes " + "unnecessary resources and limits the game's ability to load what it " + "needs.</p>" + "<p>Ideally, plugins should be saved as ESM files upon release. It can " + "also be released as an ESL plugin, however there are additional " + "concerns with the way light plugins are currently handled and should " + "only be used when absolutely certain about what you're doing.</p>" + "<p>Notably, xEdit does not currently support saving ESP files.</p>" + "<h4>Current ESPs:</h4><p>%1</p>") + .arg(espInfo); + } + case PROBLEM_TEST_FILE: { + return tr("<p>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.</p>"); + } + } + return ""; +} + +bool GameStarfield::hasGuidedFix(unsigned int key) const +{ + return false; +} + +void GameStarfield::startGuidedFix(unsigned int key) const {} |
