aboutsummaryrefslogtreecommitdiff
path: root/libs/game_bethesda/src/games/enderalse/gameenderalse.cpp
diff options
context:
space:
mode:
authorSulfurNitride <SulfurNitride@users.noreply.github.com>2026-02-11 02:37:39 -0600
committerSulfurNitride <SulfurNitride@users.noreply.github.com>2026-02-11 02:37:39 -0600
commit7ee008e150bc5bcf76082d726f719ee0fdfda982 (patch)
tree27fb39be241fdb5ac2734c574de678977d1856d0 /libs/game_bethesda/src/games/enderalse/gameenderalse.cpp
Fluorine Manager: full Linux port of Mod Organizer 2
Complete native Linux port with FUSE-based virtual filesystem, Proton/umu-run integration, and Flatpak packaging. Key features: - FUSE VFS replacing Windows USVFS (in-process + standalone helper for Flatpak) - Proton/GE-Proton/umu-run launcher with env var forwarding - Flatpak support (sandbox-aware VFS, NXM handler, umu-run) - Wine prefix management UI - Case-insensitive path resolution for Linux filesystems - QSettings-safe INI handling (avoids Bethesda INI corruption) - Portable instance support with auto-generated launcher scripts Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat (limited to 'libs/game_bethesda/src/games/enderalse/gameenderalse.cpp')
-rw-r--r--libs/game_bethesda/src/games/enderalse/gameenderalse.cpp347
1 files changed, 347 insertions, 0 deletions
diff --git a/libs/game_bethesda/src/games/enderalse/gameenderalse.cpp b/libs/game_bethesda/src/games/enderalse/gameenderalse.cpp
new file mode 100644
index 0000000..26587b8
--- /dev/null
+++ b/libs/game_bethesda/src/games/enderalse/gameenderalse.cpp
@@ -0,0 +1,347 @@
+#include "gameenderalse.h"
+
+#include "enderalsedataarchives.h"
+#include "enderalsegameplugins.h"
+#include "enderalselocalsavegames.h"
+#include "enderalsemoddatachecker.h"
+#include "enderalsemoddatacontent.h"
+#include "enderalsesavegame.h"
+#include "enderalsescriptextender.h"
+#include "enderalseunmanagedmods.h"
+#include "steamutility.h"
+
+#include "versioninfo.h"
+#include <executableinfo.h>
+#include <gamebryosavegameinfo.h>
+#include <ipluginlist.h>
+#include <pluginsetting.h>
+#include <utility.h>
+
+#include <QCoreApplication>
+#include <QDir>
+#include <QFileInfo>
+#include <QList>
+#include <QObject>
+#include <QString>
+#include <QStringList>
+
+#include "scopeguard.h"
+#include <memory>
+
+using namespace MOBase;
+
+GameEnderalSE::GameEnderalSE() {}
+
+void GameEnderalSE::setVariant(QString variant)
+{
+ m_GameVariant = variant;
+}
+
+void GameEnderalSE::checkVariants()
+{
+ QFileInfo gog_dll(m_GamePath + "\\Galaxy64.dll");
+ if (gog_dll.exists())
+ setVariant("GOG");
+ else
+ setVariant("Steam");
+}
+
+QDir GameEnderalSE::documentsDirectory() const
+{
+ return m_MyGamesPath;
+}
+
+void GameEnderalSE::detectGame()
+{
+ m_GamePath = identifyGamePath();
+ checkVariants();
+ m_MyGamesPath = determineMyGamesPath(gameDirectoryName());
+}
+
+QString GameEnderalSE::identifyGamePath() const
+{
+#ifdef _WIN32
+ QMap<QString, QString> paths = {
+ {"Software\\Bethesda Softworks\\" + gameName(), "Installed Path"},
+ {"Software\\GOG.com\\Games\\1708684988", "path"},
+ };
+ QString result;
+ try {
+ for (auto& path : paths.toStdMap()) {
+ result = findInRegistry(HKEY_LOCAL_MACHINE, path.first.toStdWString().c_str(),
+ path.second.toStdWString().c_str());
+ if (!result.isEmpty())
+ break;
+ }
+ } catch (MOBase::MyException) {
+ result = MOBase::findSteamGame("Enderal Special Edition",
+ "Data\\Enderal - Forgotten Stories.esm");
+ }
+ return result;
+#else
+ // Prefer exact Steam app-id resolution for Enderal SE.
+ QString result = parseSteamLocation("976620", "Enderal Special Edition");
+ if (!result.isEmpty() && looksValid(QDir(result))) {
+ return result;
+ }
+
+ result = MOBase::findSteamGame("Enderal Special Edition",
+ "Data/Enderal - Forgotten Stories.esm");
+ if (!result.isEmpty() && looksValid(QDir(result))) {
+ return result;
+ }
+
+ return GameGamebryo::identifyGamePath();
+#endif
+}
+
+void GameEnderalSE::setGamePath(const QString& path)
+{
+ m_GamePath = path;
+ checkVariants();
+ m_MyGamesPath = determineMyGamesPath(gameDirectoryName());
+}
+
+QDir GameEnderalSE::savesDirectory() const
+{
+ return QDir(m_MyGamesPath + "/Saves");
+}
+
+bool GameEnderalSE::isInstalled() const
+{
+ return !m_GamePath.isEmpty();
+}
+
+bool GameEnderalSE::init(IOrganizer* moInfo)
+{
+ if (!GameGamebryo::init(moInfo)) {
+ return false;
+ }
+
+ auto dataArchives = std::make_shared<EnderalSEDataArchives>(this);
+ registerFeature(std::make_shared<EnderalSEScriptExtender>(this));
+ registerFeature(dataArchives);
+ registerFeature(std::make_shared<EnderalSELocalSavegames>(this, "enderal.ini"));
+ registerFeature(std::make_shared<EnderalSEModDataChecker>(this));
+ registerFeature(std::make_shared<EnderalSEModDataContent>(moInfo->gameFeatures()));
+ registerFeature(std::make_shared<GamebryoSaveGameInfo>(this));
+ registerFeature(std::make_shared<EnderalSEGamePlugins>(moInfo));
+ registerFeature(std::make_shared<EnderalSEUnmangedMods>(this));
+
+ return true;
+}
+
+QString GameEnderalSE::gameName() const
+{
+ return "Enderal Special Edition";
+}
+
+QString GameEnderalSE::gameDirectoryName() const
+{
+ if (selectedVariant() == "GOG")
+ return "Enderal Special Edition GOG";
+ else
+ return "Enderal Special Edition";
+}
+
+QIcon GameEnderalSE::gameIcon() const
+{
+ return MOBase::iconForExecutable(gameDirectory().absoluteFilePath(getLauncherName()));
+}
+
+QList<ExecutableInfo> GameEnderalSE::executables() const
+{
+ return QList<ExecutableInfo>()
+ << ExecutableInfo("Enderal Special Edition (SKSE)",
+ findInGameFolder(m_Organizer->gameFeatures()
+ ->gameFeature<MOBase::ScriptExtender>()
+ ->loaderName()))
+ << ExecutableInfo("Enderal Special Edition Launcher",
+ findInGameFolder(getLauncherName()))
+ << ExecutableInfo("Creation Kit", findInGameFolder("CreationKit.exe"))
+ .withSteamAppId("1946180")
+ << ExecutableInfo("LOOT", QFileInfo(getLootPath()))
+ .withArgument("--game=\"Enderal Special Edition\"");
+}
+
+QList<ExecutableForcedLoadSetting> GameEnderalSE::executableForcedLoads() const
+{
+ return QList<ExecutableForcedLoadSetting>();
+}
+
+QString GameEnderalSE::binaryName() const
+{
+ return "skse64_loader.exe";
+}
+
+QString GameEnderalSE::getLauncherName() const
+{
+ return "Enderal Launcher.exe";
+}
+
+bool GameEnderalSE::looksValid(const QDir& folder) const
+{
+ // we need to check both launcher and binary because the binary also exists for
+ // Skyrim SE and the launcher for Enderal LE
+ return folder.exists(getLauncherName()) && folder.exists(binaryName());
+}
+
+QFileInfo GameEnderalSE::findInGameFolder(const QString& relativePath) const
+{
+ return QFileInfo(m_GamePath + "/" + relativePath);
+}
+
+QString GameEnderalSE::name() const
+{
+ return "Enderal Special Edition Support Plugin";
+}
+
+QString GameEnderalSE::localizedName() const
+{
+ return tr("Enderal Special Edition Support Plugin");
+}
+
+QString GameEnderalSE::author() const
+{
+ return "Archost, ZachHaber & MO2 Team";
+}
+
+QString GameEnderalSE::description() const
+{
+ return tr("Adds support for the game Enderal Special Edition.");
+}
+
+MOBase::VersionInfo GameEnderalSE::version() const
+{
+ return VersionInfo(1, 2, 0, VersionInfo::RELEASE_FINAL);
+}
+
+QList<PluginSetting> GameEnderalSE::settings() const
+{
+ return QList<PluginSetting>();
+}
+
+void GameEnderalSE::initializeProfile(const QDir& path, ProfileSettings settings) const
+{
+ if (settings.testFlag(IPluginGame::MODS)) {
+ copyToProfile(localAppFolder() + gameDirectoryName(), path, "plugins.txt");
+ }
+
+ if (settings.testFlag(IPluginGame::CONFIGURATION)) {
+ if (settings.testFlag(IPluginGame::PREFER_DEFAULTS) ||
+ !QFileInfo(myGamesPath() + "/Enderal.ini").exists()) {
+
+ // there is no default ini, actually they are going to put them in for us!
+ copyToProfile(gameDirectory().absolutePath(), path, "enderal_default.ini",
+ "Enderal.ini");
+ copyToProfile(gameDirectory().absolutePath(), path, "enderalprefs_default.ini",
+ "EnderalPrefs.ini");
+ } else {
+ copyToProfile(myGamesPath(), path, "Enderal.ini");
+ copyToProfile(myGamesPath(), path, "EnderalPrefs.ini");
+ }
+ }
+}
+
+QString GameEnderalSE::savegameExtension() const
+{
+ return "ess";
+}
+
+QString GameEnderalSE::savegameSEExtension() const
+{
+ return "skse";
+}
+
+std::shared_ptr<const GamebryoSaveGame>
+GameEnderalSE::makeSaveGame(QString filePath) const
+{
+ return std::make_shared<const EnderalSESaveGame>(filePath, this);
+}
+
+QString GameEnderalSE::steamAPPId() const
+{
+ if (selectedVariant() == "Steam")
+ return "976620";
+ return "";
+}
+
+QStringList GameEnderalSE::primaryPlugins() const
+{
+ return {"skyrim.esm", "update.esm", "dawnguard.esm", "hearthfires.esm",
+ "dragonborn.esm",
+
+ // these two plugins are considered "primary" for users but are not
+ // automatically loaded by the game so we need to force-write them
+ // to the plugin list
+ "enderal - forgotten stories.esm", "skyui_se.esp"};
+}
+
+QStringList GameEnderalSE::DLCPlugins() const
+{
+ return {};
+}
+
+QStringList GameEnderalSE::gameVariants() const
+{
+ return {"Steam", "GOG"};
+}
+
+QString GameEnderalSE::gameShortName() const
+{
+ return "EnderalSE";
+}
+
+QStringList GameEnderalSE::validShortNames() const
+{
+ return {"Skyrim", "SkyrimSE", "Enderal"};
+}
+
+QString GameEnderalSE::gameNexusName() const
+{
+ return "enderalspecialedition";
+}
+
+QStringList GameEnderalSE::iniFiles() const
+{
+ return {"Enderal.ini", "EnderalPrefs.ini"};
+}
+
+QStringList GameEnderalSE::CCPlugins() const
+{
+ return {};
+}
+
+IPluginGame::LoadOrderMechanism GameEnderalSE::loadOrderMechanism() const
+{
+ return IPluginGame::LoadOrderMechanism::PluginsTxt;
+}
+
+int GameEnderalSE::nexusModOrganizerID() const
+{
+ return 0;
+}
+
+int GameEnderalSE::nexusGameID() const
+{
+ return 3685;
+}
+
+QDir GameEnderalSE::gameDirectory() const
+{
+ return QDir(m_GamePath);
+}
+
+// Not to delete all the spaces...
+MappingType GameEnderalSE::mappings() const
+{
+ MappingType result;
+
+ for (const QString& profileFile : {"plugins.txt", "loadorder.txt"}) {
+ result.push_back({m_Organizer->profilePath() + "/" + profileFile,
+ localAppFolder() + "/" + gameDirectoryName() + "/" + profileFile,
+ false});
+ }
+
+ return result;
+}