aboutsummaryrefslogtreecommitdiff
path: root/libs/game_bethesda/src/games/ttw
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/ttw
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/ttw')
-rw-r--r--libs/game_bethesda/src/games/ttw/CMakeLists.txt20
-rw-r--r--libs/game_bethesda/src/games/ttw/falloutttwbsainvalidation.cpp16
-rw-r--r--libs/game_bethesda/src/games/ttw/falloutttwbsainvalidation.h20
-rw-r--r--libs/game_bethesda/src/games/ttw/falloutttwdataarchives.cpp31
-rw-r--r--libs/game_bethesda/src/games/ttw/falloutttwdataarchives.h23
-rw-r--r--libs/game_bethesda/src/games/ttw/falloutttwmoddatachecker.h51
-rw-r--r--libs/game_bethesda/src/games/ttw/falloutttwmoddatacontent.h22
-rw-r--r--libs/game_bethesda/src/games/ttw/falloutttwsavegame.cpp79
-rw-r--r--libs/game_bethesda/src/games/ttw/falloutttwsavegame.h23
-rw-r--r--libs/game_bethesda/src/games/ttw/falloutttwscriptextender.cpp18
-rw-r--r--libs/game_bethesda/src/games/ttw/falloutttwscriptextender.h17
-rw-r--r--libs/game_bethesda/src/games/ttw/game_ttw_en.ts22
-rw-r--r--libs/game_bethesda/src/games/ttw/gamefalloutttw.cpp364
-rw-r--r--libs/game_bethesda/src/games/ttw/gamefalloutttw.h77
-rw-r--r--libs/game_bethesda/src/games/ttw/gamefalloutttw.json1
15 files changed, 784 insertions, 0 deletions
diff --git a/libs/game_bethesda/src/games/ttw/CMakeLists.txt b/libs/game_bethesda/src/games/ttw/CMakeLists.txt
new file mode 100644
index 0000000..27149ec
--- /dev/null
+++ b/libs/game_bethesda/src/games/ttw/CMakeLists.txt
@@ -0,0 +1,20 @@
+cmake_minimum_required(VERSION 3.16)
+
+add_library(game_ttw SHARED
+ falloutttwbsainvalidation.cpp
+ falloutttwbsainvalidation.h
+ falloutttwdataarchives.cpp
+ falloutttwdataarchives.h
+ falloutttwmoddatachecker.h
+ falloutttwmoddatacontent.h
+ falloutttwsavegame.cpp
+ falloutttwsavegame.h
+ falloutttwscriptextender.cpp
+ falloutttwscriptextender.h
+ gamefalloutttw.cpp
+ gamefalloutttw.h
+)
+mo2_configure_plugin(game_ttw NO_SOURCES WARNINGS 4)
+mo2_default_source_group()
+target_link_libraries(game_ttw PRIVATE game_gamebryo)
+mo2_install_plugin(game_ttw)
diff --git a/libs/game_bethesda/src/games/ttw/falloutttwbsainvalidation.cpp b/libs/game_bethesda/src/games/ttw/falloutttwbsainvalidation.cpp
new file mode 100644
index 0000000..67a7817
--- /dev/null
+++ b/libs/game_bethesda/src/games/ttw/falloutttwbsainvalidation.cpp
@@ -0,0 +1,16 @@
+#include "falloutttwbsainvalidation.h"
+
+FalloutTTWBSAInvalidation::FalloutTTWBSAInvalidation(MOBase::DataArchives* dataArchives,
+ MOBase::IPluginGame const* game)
+ : GamebryoBSAInvalidation(dataArchives, "fallout.ini", game)
+{}
+
+QString FalloutTTWBSAInvalidation::invalidationBSAName() const
+{
+ return "Fallout - Invalidation.bsa";
+}
+
+unsigned long FalloutTTWBSAInvalidation::bsaVersion() const
+{
+ return 0x68;
+}
diff --git a/libs/game_bethesda/src/games/ttw/falloutttwbsainvalidation.h b/libs/game_bethesda/src/games/ttw/falloutttwbsainvalidation.h
new file mode 100644
index 0000000..96062d3
--- /dev/null
+++ b/libs/game_bethesda/src/games/ttw/falloutttwbsainvalidation.h
@@ -0,0 +1,20 @@
+#ifndef FALLOUTTTWBSAINVALIDATION_H
+#define FALLOUTTTWBSAINVALIDATION_H
+
+#include "falloutttwdataarchives.h"
+#include "gamebryobsainvalidation.h"
+
+#include <memory>
+
+class FalloutTTWBSAInvalidation : public GamebryoBSAInvalidation
+{
+public:
+ FalloutTTWBSAInvalidation(MOBase::DataArchives* dataArchives,
+ MOBase::IPluginGame const* game);
+
+private:
+ virtual QString invalidationBSAName() const override;
+ virtual unsigned long bsaVersion() const override;
+};
+
+#endif // FALLOUTTTWBSAINVALIDATION_H
diff --git a/libs/game_bethesda/src/games/ttw/falloutttwdataarchives.cpp b/libs/game_bethesda/src/games/ttw/falloutttwdataarchives.cpp
new file mode 100644
index 0000000..3ffb076
--- /dev/null
+++ b/libs/game_bethesda/src/games/ttw/falloutttwdataarchives.cpp
@@ -0,0 +1,31 @@
+#include "falloutttwdataarchives.h"
+#include <utility.h>
+
+QStringList FalloutTTWDataArchives::vanillaArchives() const
+{
+ return {"Fallout - Textures.bsa", "Fallout - Textures2.bsa", "Fallout - Meshes.bsa",
+ "Fallout - Voices1.bsa", "Fallout - Sound.bsa", "Fallout - Misc.bsa"};
+}
+
+QStringList FalloutTTWDataArchives::archives(const MOBase::IProfile* profile) const
+{
+ QStringList result;
+
+ QString iniFile = profile->localSettingsEnabled()
+ ? QDir(profile->absolutePath()).absoluteFilePath("fallout.ini")
+ : localGameDirectory().absoluteFilePath("fallout.ini");
+ result.append(getArchivesFromKey(iniFile, "SArchiveList"));
+
+ return result;
+}
+
+void FalloutTTWDataArchives::writeArchiveList(MOBase::IProfile* profile,
+ const QStringList& before)
+{
+ QString list = before.join(", ");
+
+ QString iniFile = profile->localSettingsEnabled()
+ ? QDir(profile->absolutePath()).absoluteFilePath("fallout.ini")
+ : localGameDirectory().absoluteFilePath("fallout.ini");
+ setArchivesToKey(iniFile, "SArchiveList", list);
+}
diff --git a/libs/game_bethesda/src/games/ttw/falloutttwdataarchives.h b/libs/game_bethesda/src/games/ttw/falloutttwdataarchives.h
new file mode 100644
index 0000000..eb9d7c7
--- /dev/null
+++ b/libs/game_bethesda/src/games/ttw/falloutttwdataarchives.h
@@ -0,0 +1,23 @@
+#ifndef FALLOUTTTWDATAARCHIVES_H
+#define FALLOUTTTWDATAARCHIVES_H
+
+#include <QDir>
+#include <QString>
+#include <QStringList>
+#include <gamebryodataarchives.h>
+#include <iprofile.h>
+
+class FalloutTTWDataArchives : public GamebryoDataArchives
+{
+public:
+ using GamebryoDataArchives::GamebryoDataArchives;
+
+ virtual QStringList vanillaArchives() const override;
+ virtual QStringList archives(const MOBase::IProfile* profile) const override;
+
+private:
+ virtual void writeArchiveList(MOBase::IProfile* profile,
+ const QStringList& before) override;
+};
+
+#endif // FALLOUTTTWDATAARCHIVES_H
diff --git a/libs/game_bethesda/src/games/ttw/falloutttwmoddatachecker.h b/libs/game_bethesda/src/games/ttw/falloutttwmoddatachecker.h
new file mode 100644
index 0000000..12ed881
--- /dev/null
+++ b/libs/game_bethesda/src/games/ttw/falloutttwmoddatachecker.h
@@ -0,0 +1,51 @@
+#ifndef FALLOUTTTW_MODATACHECKER_H
+#define FALLOUTTTW_MODATACHECKER_H
+
+#include <gamebryomoddatachecker.h>
+
+class FalloutTTWModDataChecker : public GamebryoModDataChecker
+{
+public:
+ using GamebryoModDataChecker::GamebryoModDataChecker;
+
+protected:
+ virtual const FileNameSet& possibleFolderNames() const override
+ {
+ static FileNameSet result{"fonts",
+ "interface",
+ "menus",
+ "meshes",
+ "music",
+ "scripts",
+ "shaders",
+ "sound",
+ "strings",
+ "textures",
+ "trees",
+ "video",
+ "facegen",
+ "materials",
+ "nvse",
+ "distantlod",
+ "asi",
+ "Tools",
+ "MCM",
+ "distantland",
+ "mits",
+ "dllplugins",
+ "CalienteTools",
+ "shadersfx",
+ "config",
+ "KEYWORDS",
+ "BaseObjectSwapper",
+ "RaceMenuPresets"};
+ return result;
+ }
+ virtual const FileNameSet& possibleFileExtensions() const override
+ {
+ static FileNameSet result{"esp", "esm", "bsa", "modgroups", "ini"};
+ return result;
+ }
+};
+
+#endif // FALLOUTTTW_MODATACHECKER_H
diff --git a/libs/game_bethesda/src/games/ttw/falloutttwmoddatacontent.h b/libs/game_bethesda/src/games/ttw/falloutttwmoddatacontent.h
new file mode 100644
index 0000000..37f9d20
--- /dev/null
+++ b/libs/game_bethesda/src/games/ttw/falloutttwmoddatacontent.h
@@ -0,0 +1,22 @@
+#ifndef FALLOUTTTW_MODDATACONTENT_H
+#define FALLOUTTTW_MODDATACONTENT_H
+
+#include <gamebryomoddatacontent.h>
+#include <ifiletree.h>
+
+class FalloutTTWModDataContent : public GamebryoModDataContent
+{
+public:
+ /**
+ *
+ */
+ FalloutTTWModDataContent(MOBase::IGameFeatures const* gameFeatures)
+ : GamebryoModDataContent(gameFeatures)
+ {
+ // Just need to disable some contents:
+ m_Enabled[CONTENT_MCM] = false;
+ m_Enabled[CONTENT_SKYPROC] = false;
+ }
+};
+
+#endif // FALLOUTTTW_MODDATACONTENT_H
diff --git a/libs/game_bethesda/src/games/ttw/falloutttwsavegame.cpp b/libs/game_bethesda/src/games/ttw/falloutttwsavegame.cpp
new file mode 100644
index 0000000..9f41082
--- /dev/null
+++ b/libs/game_bethesda/src/games/ttw/falloutttwsavegame.cpp
@@ -0,0 +1,79 @@
+#include "falloutttwsavegame.h"
+
+#include "gamefalloutttw.h"
+
+FalloutTTWSaveGame::FalloutTTWSaveGame(QString const& fileName,
+ GameFalloutTTW const* game)
+ : GamebryoSaveGame(fileName, game)
+{
+ FileWrapper file(getFilepath(), "FO3SAVEGAME");
+ uint32_t width, height;
+ fetchInformationFields(file, width, height, m_SaveNumber, m_PCName, m_PCLevel,
+ m_PCLocation);
+}
+
+void FalloutTTWSaveGame::fetchInformationFields(FileWrapper& file, uint32_t& width,
+ uint32_t& height,
+ uint32_t& saveNumber,
+ QString& playerName,
+ unsigned short& playerLevel,
+ QString& playerLocation) const
+{
+ file.skip<uint32_t>(); // Save header size
+
+ file.skip<uint32_t>(); // File version?
+ file.skip<unsigned char>(); // Delimiter
+
+ // A huge wodge of text with no length but a delimiter. Given the null bytes
+ // in it I presume it's fixed length (64 bytes + delim) but I have no
+ // definite spec
+ for (unsigned char ignore = 0; ignore != 0x7c;) {
+ file.read(ignore); // unknown
+ }
+
+ file.setHasFieldMarkers(true);
+ file.setPluginString(GamebryoSaveGame::StringType::TYPE_BZSTRING);
+
+ file.read(width);
+ file.read(height);
+ file.read(saveNumber);
+ file.read(playerName);
+
+ QString whatthis;
+ file.read(whatthis);
+
+ long level;
+ file.read(level);
+ playerLevel = level;
+ file.read(playerLocation);
+}
+
+std::unique_ptr<GamebryoSaveGame::DataFields>
+FalloutTTWSaveGame::fetchDataFields() const
+{
+ FileWrapper file(getFilepath(), "FO3SAVEGAME");
+
+ std::unique_ptr<DataFields> fields = std::make_unique<DataFields>();
+
+ uint32_t width, height;
+ {
+ QString dummyName, dummyLocation;
+ unsigned short dummyLevel;
+ uint32_t dummySaveNumber;
+
+ fetchInformationFields(file, width, height, dummySaveNumber, dummyName, dummyLevel,
+ dummyLocation);
+ }
+
+ QString playtime;
+ file.read(playtime);
+
+ fields->Screenshot = file.readImage(width, height, 256);
+
+ file.skip<char>(5); // unknown (1 byte), plugin size (4 bytes)
+
+ file.setPluginString(GamebryoSaveGame::StringType::TYPE_BSTRING);
+ fields->Plugins = file.readPlugins();
+
+ return fields;
+}
diff --git a/libs/game_bethesda/src/games/ttw/falloutttwsavegame.h b/libs/game_bethesda/src/games/ttw/falloutttwsavegame.h
new file mode 100644
index 0000000..f8460b3
--- /dev/null
+++ b/libs/game_bethesda/src/games/ttw/falloutttwsavegame.h
@@ -0,0 +1,23 @@
+#ifndef FALLOUTTTWSAVEGAME_H
+#define FALLOUTTTWSAVEGAME_H
+
+#include "gamebryosavegame.h"
+
+class GameFalloutTTW;
+
+class FalloutTTWSaveGame : public GamebryoSaveGame
+{
+public:
+ FalloutTTWSaveGame(QString const& fileName, GameFalloutTTW const* game);
+
+protected:
+ // Fetch easy-to-access information.
+ void fetchInformationFields(FileWrapper& wrapper, uint32_t& width,
+ uint32_t& height, uint32_t& saveNumber,
+ QString& playerName, unsigned short& playerLevel,
+ QString& playerLocation) const;
+
+ std::unique_ptr<DataFields> fetchDataFields() const override;
+};
+
+#endif // FALLOUTTTWSAVEGAME_H
diff --git a/libs/game_bethesda/src/games/ttw/falloutttwscriptextender.cpp b/libs/game_bethesda/src/games/ttw/falloutttwscriptextender.cpp
new file mode 100644
index 0000000..ab9474e
--- /dev/null
+++ b/libs/game_bethesda/src/games/ttw/falloutttwscriptextender.cpp
@@ -0,0 +1,18 @@
+#include "falloutttwscriptextender.h"
+
+#include <QString>
+#include <QStringList>
+
+FalloutTTWScriptExtender::FalloutTTWScriptExtender(GameGamebryo const* game)
+ : GamebryoScriptExtender(game)
+{}
+
+QString FalloutTTWScriptExtender::BinaryName() const
+{
+ return "nvse_loader.exe";
+}
+
+QString FalloutTTWScriptExtender::PluginPath() const
+{
+ return "nvse/plugins";
+}
diff --git a/libs/game_bethesda/src/games/ttw/falloutttwscriptextender.h b/libs/game_bethesda/src/games/ttw/falloutttwscriptextender.h
new file mode 100644
index 0000000..d1e4c9a
--- /dev/null
+++ b/libs/game_bethesda/src/games/ttw/falloutttwscriptextender.h
@@ -0,0 +1,17 @@
+#ifndef FALLOUTTTWSCRIPTEXTENDER_H
+#define FALLOUTTTWSCRIPTEXTENDER_H
+
+#include "gamebryoscriptextender.h"
+
+class GameGamebryo;
+
+class FalloutTTWScriptExtender : public GamebryoScriptExtender
+{
+public:
+ FalloutTTWScriptExtender(const GameGamebryo* game);
+
+ virtual QString BinaryName() const override;
+ virtual QString PluginPath() const override;
+};
+
+#endif // FALLOUTTTWSCRIPTEXTENDER_H
diff --git a/libs/game_bethesda/src/games/ttw/game_ttw_en.ts b/libs/game_bethesda/src/games/ttw/game_ttw_en.ts
new file mode 100644
index 0000000..9d967f9
--- /dev/null
+++ b/libs/game_bethesda/src/games/ttw/game_ttw_en.ts
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="en_US">
+<context>
+ <name>GameFalloutTTW</name>
+ <message>
+ <location filename="gamefalloutttw.cpp" line="185"/>
+ <source>Fallout TTW Support Plugin</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="gamefalloutttw.cpp" line="195"/>
+ <source>Adds support for the game Fallout TTW</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="gamefalloutttw.cpp" line="207"/>
+ <source>While not recommended by the TTW modding community, enables LOOT sorting</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS>
diff --git a/libs/game_bethesda/src/games/ttw/gamefalloutttw.cpp b/libs/game_bethesda/src/games/ttw/gamefalloutttw.cpp
new file mode 100644
index 0000000..34eb1b2
--- /dev/null
+++ b/libs/game_bethesda/src/games/ttw/gamefalloutttw.cpp
@@ -0,0 +1,364 @@
+#include "gamefalloutttw.h"
+
+#include "falloutttwbsainvalidation.h"
+#include "falloutttwdataarchives.h"
+#include "falloutttwmoddatachecker.h"
+#include "falloutttwmoddatacontent.h"
+#include "falloutttwsavegame.h"
+#include "falloutttwscriptextender.h"
+
+#include "executableinfo.h"
+#include "pluginsetting.h"
+#include "versioninfo.h"
+#include <gamebryogameplugins.h>
+#include <gamebryolocalsavegames.h>
+#include <gamebryosavegameinfo.h>
+#include <gamebryounmanagedmods.h>
+
+#include <QCoreApplication>
+#include <QDir>
+#include <QFileInfo>
+#include <QList>
+#include <QObject>
+#include <QString>
+#include <QStringList>
+
+#include <memory>
+
+using namespace MOBase;
+
+GameFalloutTTW::GameFalloutTTW() {}
+
+bool GameFalloutTTW::init(IOrganizer* moInfo)
+{
+ if (!GameGamebryo::init(moInfo)) {
+ return false;
+ }
+
+ auto dataArchives = std::make_shared<FalloutTTWDataArchives>(this);
+ registerFeature(std::make_shared<FalloutTTWScriptExtender>(this));
+ registerFeature(dataArchives);
+ registerFeature(
+ std::make_shared<FalloutTTWBSAInvalidation>(dataArchives.get(), this));
+ registerFeature(std::make_shared<GamebryoSaveGameInfo>(this));
+ registerFeature(std::make_shared<GamebryoLocalSavegames>(this, "fallout.ini"));
+ registerFeature(std::make_shared<FalloutTTWModDataChecker>(this));
+ registerFeature(
+ std::make_shared<FalloutTTWModDataContent>(m_Organizer->gameFeatures()));
+ registerFeature(std::make_shared<GamebryoGamePlugins>(moInfo));
+ registerFeature(std::make_shared<GamebryoUnmangedMods>(this));
+
+ return true;
+}
+
+void GameFalloutTTW::setVariant(QString variant)
+{
+ m_GameVariant = variant;
+}
+
+void GameFalloutTTW::checkVariants()
+{
+ QFileInfo gog_dll(m_GamePath + "\\Galaxy.dll");
+ QFileInfo epic_dll(m_GamePath + "\\EOSSDK-Win32-Shipping.dll");
+ if (gog_dll.exists())
+ setVariant("GOG");
+ else if (epic_dll.exists())
+ setVariant("Epic Games");
+ else
+ setVariant("Steam");
+}
+
+QDir GameFalloutTTW::documentsDirectory() const
+{
+ return m_MyGamesPath;
+}
+
+QString GameFalloutTTW::identifyGamePath() const
+{
+ QString result;
+#ifdef _WIN32
+ QString path = "Software\\Bethesda Softworks\\FalloutNV";
+ result = findInRegistry(HKEY_LOCAL_MACHINE, path.toStdWString().c_str(),
+ L"Installed Path");
+#else
+ result = GameGamebryo::identifyGamePath();
+#endif
+ // EPIC Game Store
+ if (result.isEmpty()) {
+ /**
+ * Basegame: 5daeb974a22a435988892319b3a4f476
+ * Dead Money: b290229eb58045cbab9501640f3278f3
+ * Honest Hearts: 562d4a2c1b3147b089a7c453e3ddbcbe
+ * Old World Blues: c8dae1ab0570475a8b38a9041e614840
+ * Lonesome Road: 4fa3d8d9b2cb4714a19a38d1a598be8f
+ * Gun Runners' Arsenal: 7dcfb9cd9d134728b2646466c34c7b3b
+ * Courier's Stash: ee9a44b4530942499ef1c8c390731fce
+ */
+ result = parseEpicGamesLocation({"5daeb974a22a435988892319b3a4f476"});
+ if (QFileInfo(result).isDir()) {
+ QDir startPath = QDir(result);
+ auto subDirs = startPath.entryList({"Fallout New Vegas*"},
+ QDir::Dirs | QDir::NoDotAndDotDot);
+ if (!subDirs.isEmpty())
+ result += "/" + subDirs.first();
+ }
+ }
+ return result;
+}
+
+void GameFalloutTTW::setGamePath(const QString& path)
+{
+ m_GamePath = path;
+ checkVariants();
+ m_MyGamesPath = determineMyGamesPath(gameDirectoryName());
+}
+
+QDir GameFalloutTTW::savesDirectory() const
+{
+ return QDir(m_MyGamesPath + "/Saves");
+}
+
+QString GameFalloutTTW::myGamesPath() const
+{
+ return m_MyGamesPath;
+}
+
+bool GameFalloutTTW::isInstalled() const
+{
+ return !m_GamePath.isEmpty();
+}
+
+QString GameFalloutTTW::gameName() const
+{
+ return "TTW";
+}
+
+QString GameFalloutTTW::displayGameName() const
+{
+ return "Tale of Two Wastelands";
+}
+
+QString GameFalloutTTW::gameDirectoryName() const
+{
+ if (selectedVariant() == "Epic Games")
+ return "FalloutNV_Epic";
+ else
+ return "FalloutNV";
+}
+
+void GameFalloutTTW::detectGame()
+{
+ m_GamePath = identifyGamePath();
+ checkVariants();
+ m_MyGamesPath = determineMyGamesPath(gameDirectoryName());
+}
+
+QList<ExecutableInfo> GameFalloutTTW::executables() const
+{
+ ExecutableInfo game("Tale of Two Wastelands", findInGameFolder(binaryName()));
+ ExecutableInfo launcher("Fallout Launcher", findInGameFolder(getLauncherName()));
+ QList<ExecutableInfo> extraExecutables =
+ QList<ExecutableInfo>() << ExecutableInfo("GECK", findInGameFolder("geck.exe"))
+ << ExecutableInfo("LOOT", QFileInfo(getLootPath()))
+ .withArgument("--game=\"FalloutNV\"");
+ if (selectedVariant() != "Epic Games") {
+ extraExecutables.prepend(ExecutableInfo(
+ "NVSE", findInGameFolder(m_Organizer->gameFeatures()
+ ->gameFeature<MOBase::ScriptExtender>()
+ ->loaderName())));
+ } else {
+ game.withArgument("-EpicPortal");
+ launcher.withArgument("-EpicPortal");
+ }
+ QList<ExecutableInfo> executables = {game, launcher};
+ executables += extraExecutables;
+ return executables;
+}
+
+QList<ExecutableForcedLoadSetting> GameFalloutTTW::executableForcedLoads() const
+{
+ return QList<ExecutableForcedLoadSetting>();
+}
+
+QString GameFalloutTTW::name() const
+{
+ return "Fallout TTW Support Plugin";
+}
+
+QString GameFalloutTTW::localizedName() const
+{
+ return tr("Fallout TTW Support Plugin");
+}
+
+QString GameFalloutTTW::author() const
+{
+ return "SuperSandro2000 & MO2 Team";
+}
+
+QString GameFalloutTTW::description() const
+{
+ return tr("Adds support for the game Fallout TTW");
+}
+
+MOBase::VersionInfo GameFalloutTTW::version() const
+{
+ return VersionInfo(1, 6, 0, VersionInfo::RELEASE_FINAL);
+}
+
+QList<PluginSetting> GameFalloutTTW::settings() const
+{
+ return QList<PluginSetting>()
+ << PluginSetting("enable_loot_sorting",
+ tr("While not recommended by the TTW modding community, "
+ "enables LOOT sorting"),
+ false);
+}
+
+void GameFalloutTTW::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() + "/fallout.ini").exists()) {
+ copyToProfile(gameDirectory().absolutePath(), path, "fallout_default.ini",
+ "fallout.ini");
+ } else {
+ copyToProfile(myGamesPath(), path, "fallout.ini");
+ }
+
+ copyToProfile(myGamesPath(), path, "falloutprefs.ini");
+ copyToProfile(myGamesPath(), path, "falloutcustom.ini");
+ copyToProfile(myGamesPath(), path, "GECKCustom.ini");
+ copyToProfile(myGamesPath(), path, "GECKPrefs.ini");
+ }
+}
+
+QString GameFalloutTTW::savegameExtension() const
+{
+ return "fos";
+}
+
+QString GameFalloutTTW::savegameSEExtension() const
+{
+ return "nvse";
+}
+
+std::shared_ptr<const GamebryoSaveGame>
+GameFalloutTTW::makeSaveGame(QString filePath) const
+{
+ return std::make_shared<const FalloutTTWSaveGame>(filePath, this);
+}
+
+QString GameFalloutTTW::steamAPPId() const
+{
+ if (selectedVariant() == "Steam") {
+ if (m_GamePath.endsWith("enplczru")) {
+ return "22490";
+ } else {
+ return "22380";
+ }
+ }
+ return QString();
+}
+
+QStringList GameFalloutTTW::primaryPlugins() const
+{
+ return {"falloutnv.esm", "deadmoney.esm", "honesthearts.esm",
+ "oldworldblues.esm", "lonesomeroad.esm", "gunrunnersarsenal.esm",
+ "fallout3.esm", "anchorage.esm", "thepitt.esm",
+ "brokensteel.esm", "pointlookout.esm", "zeta.esm",
+ "caravanpack.esm", "classicpack.esm", "mercenarypack.esm",
+ "tribalpack.esm", "taleoftwowastelands.esm", "YUPTTW.esm"};
+}
+
+QStringList GameFalloutTTW::gameVariants() const
+{
+ return {"Steam", "GOG", "Epic Games"};
+}
+
+QString GameFalloutTTW::binaryName() const
+{
+ return "FalloutNV.exe";
+}
+
+QString GameFalloutTTW::gameShortName() const
+{
+ return "TTW";
+}
+
+QStringList GameFalloutTTW::primarySources() const
+{
+ return {"FalloutNV"};
+}
+
+QStringList GameFalloutTTW::validShortNames() const
+{
+ return {"FalloutNV", "Fallout3"};
+}
+
+QString GameFalloutTTW::gameNexusName() const
+{
+ return "";
+}
+
+QStringList GameFalloutTTW::iniFiles() const
+{
+ return {"fallout.ini", "falloutprefs.ini", "falloutcustom.ini", "GECKCustom.ini",
+ "GECKPrefs.ini"};
+}
+
+QStringList GameFalloutTTW::DLCPlugins() const
+{
+ return {};
+}
+
+MOBase::IPluginGame::SortMechanism GameFalloutTTW::sortMechanism() const
+{
+ if (m_Organizer->pluginSetting(name(), "enable_loot_sorting").toBool())
+ return IPluginGame::SortMechanism::LOOT;
+ return IPluginGame::SortMechanism::NONE;
+}
+
+QString GameFalloutTTW::lootGameName() const
+{
+ return "FalloutNV";
+}
+
+int GameFalloutTTW::nexusModOrganizerID() const
+{
+ return 0;
+}
+
+int GameFalloutTTW::nexusGameID() const
+{
+ return 0;
+}
+
+QDir GameFalloutTTW::gameDirectory() const
+{
+ return QDir(m_GamePath);
+}
+
+QString GameFalloutTTW::getLauncherName() const
+{
+ return "FalloutNVLauncher.exe";
+}
+
+MappingType GameFalloutTTW::mappings() const
+{
+ MappingType result;
+
+ for (const QString& profileFile : {"plugins.txt", "loadorder.txt"}) {
+ result.push_back({m_Organizer->profilePath() + "/" + profileFile,
+ localAppFolder() + "/FalloutNV/" + profileFile, false});
+ if (selectedVariant() == "Epic Games") {
+ result.push_back(
+ {m_Organizer->profilePath() + "/" + profileFile,
+ localAppFolder() + "/" + gameDirectoryName() + "/" + profileFile, false});
+ }
+ }
+ return result;
+}
diff --git a/libs/game_bethesda/src/games/ttw/gamefalloutttw.h b/libs/game_bethesda/src/games/ttw/gamefalloutttw.h
new file mode 100644
index 0000000..7f989b0
--- /dev/null
+++ b/libs/game_bethesda/src/games/ttw/gamefalloutttw.h
@@ -0,0 +1,77 @@
+#ifndef GAMEFALLOUTTTW_H
+#define GAMEFALLOUTTTW_H
+
+#include "gamegamebryo.h"
+
+#include <QObject>
+#include <QtGlobal>
+
+class GameFalloutTTW : public GameGamebryo
+{
+ Q_OBJECT
+#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
+ Q_PLUGIN_METADATA(IID "org.tannin.GameFalloutTTW" FILE "gamefalloutttw.json")
+#endif
+
+public:
+ GameFalloutTTW();
+
+ bool init(MOBase::IOrganizer* moInfo) override;
+
+public: // IPluginGame interface
+ virtual QString gameName() const override;
+ virtual QString displayGameName() const override;
+ virtual void detectGame() override;
+ virtual QList<MOBase::ExecutableInfo> executables() const override;
+ virtual QList<MOBase::ExecutableForcedLoadSetting>
+ executableForcedLoads() const override;
+ virtual void initializeProfile(const QDir& path,
+ ProfileSettings settings) const override;
+ virtual QString steamAPPId() const override;
+ virtual QStringList primaryPlugins() const override;
+ virtual QStringList gameVariants() const override;
+ virtual QString binaryName() const override;
+ virtual QString gameShortName() const override;
+ virtual QStringList validShortNames() const override;
+ virtual QString gameNexusName() const override;
+ virtual QStringList iniFiles() const override;
+ virtual QStringList DLCPlugins() const override;
+ virtual int nexusModOrganizerID() const override;
+ virtual int nexusGameID() const override;
+ virtual QStringList primarySources() const override;
+ virtual SortMechanism sortMechanism() const override;
+ virtual QString lootGameName() const override;
+ virtual QString getLauncherName() const override;
+
+ virtual bool isInstalled() const override;
+ virtual void setGamePath(const QString& path) override;
+ virtual QDir gameDirectory() const override;
+
+public: // IPlugin interface
+ virtual QString name() const override;
+ virtual QString localizedName() const override;
+ virtual QString author() const override;
+ virtual QString description() const override;
+ virtual MOBase::VersionInfo version() const override;
+ virtual QList<MOBase::PluginSetting> settings() const override;
+
+public: // IPluginFileMapper interface
+ virtual MappingType mappings() const override;
+
+protected:
+ QString gameDirectoryName() const;
+ QDir documentsDirectory() const;
+ QDir savesDirectory() const;
+ QString myGamesPath() const;
+
+ void setVariant(QString variant);
+ void checkVariants();
+
+protected:
+ virtual QString identifyGamePath() const override;
+ virtual QString savegameExtension() const override;
+ virtual QString savegameSEExtension() const override;
+ std::shared_ptr<const GamebryoSaveGame> makeSaveGame(QString filePath) const override;
+};
+
+#endif // GAMEFALLOUTTTW_H
diff --git a/libs/game_bethesda/src/games/ttw/gamefalloutttw.json b/libs/game_bethesda/src/games/ttw/gamefalloutttw.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/libs/game_bethesda/src/games/ttw/gamefalloutttw.json
@@ -0,0 +1 @@
+{}