diff options
Diffstat (limited to 'libs/basic_games_native/src')
| -rw-r--r-- | libs/basic_games_native/src/CMakeLists.txt | 11 | ||||
| -rw-r--r-- | libs/basic_games_native/src/basicgameplugin.cpp | 380 | ||||
| -rw-r--r-- | libs/basic_games_native/src/basicgameplugin.h | 73 | ||||
| -rw-r--r-- | libs/basic_games_native/src/basicgamesproxy.cpp | 99 | ||||
| -rw-r--r-- | libs/basic_games_native/src/basicgamesproxy.h | 41 | ||||
| -rw-r--r-- | libs/basic_games_native/src/gamedefs.cpp | 1024 | ||||
| -rw-r--r-- | libs/basic_games_native/src/gamedefs.h | 61 | ||||
| -rw-r--r-- | libs/basic_games_native/src/steamutils.cpp | 156 | ||||
| -rw-r--r-- | libs/basic_games_native/src/steamutils.h | 14 |
9 files changed, 1859 insertions, 0 deletions
diff --git a/libs/basic_games_native/src/CMakeLists.txt b/libs/basic_games_native/src/CMakeLists.txt new file mode 100644 index 0000000..136cdcf --- /dev/null +++ b/libs/basic_games_native/src/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.16) + +file(GLOB basic_games_native_SOURCES CONFIGURE_DEPENDS + ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/*.h +) + +add_library(basic_games_native SHARED ${basic_games_native_SOURCES}) +mo2_configure_plugin(basic_games_native NO_SOURCES WARNINGS OFF) +target_link_libraries(basic_games_native PRIVATE mo2::uibase) +mo2_install_plugin(basic_games_native) diff --git a/libs/basic_games_native/src/basicgameplugin.cpp b/libs/basic_games_native/src/basicgameplugin.cpp new file mode 100644 index 0000000..8c73e4c --- /dev/null +++ b/libs/basic_games_native/src/basicgameplugin.cpp @@ -0,0 +1,380 @@ +#include "basicgameplugin.h" +#include "steamutils.h" + +#include <uibase/versioninfo.h> + +#include <QDateTime> +#include <QDirIterator> +#include <QStandardPaths> + +// Simple ISaveGame implementation for basic games +class BasicSaveGame : public MOBase::ISaveGame +{ +public: + BasicSaveGame(const QString& filePath) + : m_filePath(filePath), m_fileInfo(filePath) + { + } + + QString getFilepath() const override { return m_filePath; } + + QDateTime getCreationTime() const override + { + return m_fileInfo.lastModified(); + } + + QString getName() const override + { + return m_fileInfo.completeBaseName(); + } + + QString getSaveGroupIdentifier() const override { return ""; } + + QStringList allFiles() const override { return {m_filePath}; } + +private: + QString m_filePath; + QFileInfo m_fileInfo; +}; + +BasicGamePlugin::BasicGamePlugin(const GameDefinition& def) : m_def(def) {} + +bool BasicGamePlugin::init(MOBase::IOrganizer* organizer) +{ + m_organizer = organizer; + return true; +} + +QString BasicGamePlugin::name() const +{ + return m_def.pluginName; +} + +QString BasicGamePlugin::localizedName() const +{ + return m_def.pluginName + " (Native)"; +} + +QString BasicGamePlugin::author() const +{ + return m_def.author; +} + +QString BasicGamePlugin::description() const +{ + return "Adds support for " + m_def.gameName; +} + +MOBase::VersionInfo BasicGamePlugin::version() const +{ + return MOBase::VersionInfo(m_def.version); +} + +QList<MOBase::PluginSetting> BasicGamePlugin::settings() const +{ + return {}; +} + +QString BasicGamePlugin::gameName() const +{ + return m_def.gameName; +} + +void BasicGamePlugin::detectGame() +{ + // Try Steam first + for (int steamId : m_def.steamAppIds) { + QString path = findSteamGamePath(steamId); + if (!path.isEmpty()) { + setGamePath(path); + return; + } + } + + // GOG via Heroic launcher + if (!m_def.gogAppIds.isEmpty()) { + // Check Heroic GOG installed games + QStringList heroicPaths = { + QDir::homePath() + "/.config/heroic/gog_store/installed.json", + QDir::homePath() + + "/.var/app/com.heroicgameslauncher.hgl/config/heroic/" + "gog_store/installed.json", + }; + for (const auto& heroicPath : heroicPaths) { + QFile file(heroicPath); + if (!file.open(QIODevice::ReadOnly)) + continue; + QByteArray data = file.readAll(); + // Simple JSON parsing for install_path + for (int gogId : m_def.gogAppIds) { + QString idStr = QString::number(gogId); + if (data.contains(idStr.toUtf8())) { + // Find the install_path for this entry + int idx = data.indexOf(idStr.toUtf8()); + int pathIdx = data.indexOf("install_path", idx); + if (pathIdx >= 0) { + // Find the value after "install_path" + int colonIdx = data.indexOf(':', pathIdx); + int quoteStart = data.indexOf('"', colonIdx + 1); + int quoteEnd = data.indexOf('"', quoteStart + 1); + if (quoteStart >= 0 && quoteEnd > quoteStart) { + QString path = + QString::fromUtf8(data.mid(quoteStart + 1, quoteEnd - quoteStart - 1)); + if (QDir(path).exists()) { + setGamePath(path); + return; + } + } + } + } + } + } + } +} + +void BasicGamePlugin::initializeProfile(const QDir& directory, + ProfileSettings settings) const +{ + // Create the profile directory if needed + if (!directory.exists()) { + directory.mkpath("."); + } +} + +std::vector<std::shared_ptr<const MOBase::ISaveGame>> +BasicGamePlugin::listSaves(QDir folder) const +{ + std::vector<std::shared_ptr<const MOBase::ISaveGame>> saves; + + if (!m_def.saveExtension.isEmpty() && folder.exists()) { + QStringList filters; + filters << "*." + m_def.saveExtension; + QStringList entries = folder.entryList(filters, QDir::Files, QDir::Time); + for (const auto& entry : entries) { + saves.push_back( + std::make_shared<BasicSaveGame>(folder.filePath(entry))); + } + } + + return saves; +} + +bool BasicGamePlugin::isInstalled() const +{ + return m_installed; +} + +QIcon BasicGamePlugin::gameIcon() const +{ + return QIcon(); +} + +QDir BasicGamePlugin::gameDirectory() const +{ + return QDir(m_gameDir); +} + +QDir BasicGamePlugin::dataDirectory() const +{ + QString dataDir = resolveVariables(m_def.dataDirectory); + if (dataDir.isEmpty()) { + return gameDirectory(); + } + QDir dir(dataDir); + if (dir.isAbsolute()) { + return dir; + } + return QDir(m_gameDir + "/" + dataDir); +} + +void BasicGamePlugin::setGamePath(const QString& path) +{ + m_gameDir = path; + m_installed = !path.isEmpty() && QDir(path).exists(); +} + +QDir BasicGamePlugin::documentsDirectory() const +{ + if (m_def.documentsDirectory.isEmpty()) { + // Default: try My Games/<gameName> then <gameName> under Documents + QString docs = + QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); + QDir myGames(docs + "/My Games/" + m_def.gameName); + if (myGames.exists()) + return myGames; + QDir plain(docs + "/" + m_def.gameName); + if (plain.exists()) + return plain; + return QDir(); + } + return QDir(resolveVariables(m_def.documentsDirectory)); +} + +QDir BasicGamePlugin::savesDirectory() const +{ + if (m_def.savesDirectory.isEmpty()) { + return documentsDirectory(); + } + return QDir(resolveVariables(m_def.savesDirectory)); +} + +QList<MOBase::ExecutableInfo> BasicGamePlugin::executables() const +{ + QList<MOBase::ExecutableInfo> list; + QDir dir = gameDirectory(); + QFileInfo binary(dir.filePath(m_def.binaryName)); + if (binary.exists()) { + list.append(MOBase::ExecutableInfo(m_def.gameName, binary)); + } + if (!m_def.launcherName.isEmpty()) { + QFileInfo launcher(dir.filePath(m_def.launcherName)); + if (launcher.exists()) { + list.append(MOBase::ExecutableInfo(m_def.gameName + " Launcher", launcher)); + } + } + return list; +} + +QList<MOBase::ExecutableForcedLoadSetting> +BasicGamePlugin::executableForcedLoads() const +{ + return {}; +} + +QString BasicGamePlugin::steamAPPId() const +{ + if (!m_def.steamAppIds.isEmpty()) { + return QString::number(m_def.steamAppIds.first()); + } + return ""; +} + +QStringList BasicGamePlugin::primaryPlugins() const +{ + return m_def.primaryPlugins; +} + +QStringList BasicGamePlugin::gameVariants() const +{ + return {}; +} + +void BasicGamePlugin::setGameVariant(const QString&) {} + +QString BasicGamePlugin::binaryName() const +{ + return m_def.binaryName; +} + +QString BasicGamePlugin::gameShortName() const +{ + return m_def.gameShortName; +} + +QStringList BasicGamePlugin::validShortNames() const +{ + return m_def.validShortNames; +} + +QString BasicGamePlugin::gameNexusName() const +{ + return m_def.gameNexusName; +} + +QStringList BasicGamePlugin::iniFiles() const +{ + QStringList resolved; + for (const auto& ini : m_def.iniFiles) { + resolved.append(resolveVariables(ini)); + } + return resolved; +} + +QStringList BasicGamePlugin::DLCPlugins() const +{ + return m_def.dlcPlugins; +} + +MOBase::IPluginGame::LoadOrderMechanism +BasicGamePlugin::loadOrderMechanism() const +{ + return m_def.loadOrderMechanism; +} + +MOBase::IPluginGame::SortMechanism BasicGamePlugin::sortMechanism() const +{ + return m_def.sortMechanism; +} + +int BasicGamePlugin::nexusGameID() const +{ + return m_def.nexusGameId; +} + +bool BasicGamePlugin::looksValid(QDir const& dir) const +{ + return dir.exists(m_def.binaryName); +} + +QString BasicGamePlugin::gameVersion() const +{ + // On Linux we can't easily read PE version resources + // Return empty; MO2 will show "N/A" + return ""; +} + +QString BasicGamePlugin::getLauncherName() const +{ + return m_def.launcherName; +} + +QString BasicGamePlugin::getSupportURL() const +{ + return m_def.supportURL; +} + +QString BasicGamePlugin::resolveVariables(const QString& input) const +{ + if (input.isEmpty()) + return input; + + QString result = input; + + // Normalize backslashes to forward slashes first + result.replace('\\', '/'); + + // %DOCUMENTS% + if (result.contains("%DOCUMENTS%")) { + QString docs = + QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); + result.replace("%DOCUMENTS%", docs); + } + + // %USERPROFILE% - on Linux, resolve to the Wine prefix userprofile + if (result.contains("%USERPROFILE%")) { + QString userProfile; + // Try the global Fluorine prefix + QString prefixPath = + QDir::homePath() + + "/.local/share/fluorine/Prefix/pfx/drive_c/users/steamuser"; + if (QDir(prefixPath).exists()) { + userProfile = prefixPath; + } else { + // Fallback to home directory + userProfile = QDir::homePath(); + } + result.replace("%USERPROFILE%", userProfile); + } + + // %GAME_PATH% + if (result.contains("%GAME_PATH%")) { + result.replace("%GAME_PATH%", m_gameDir); + } + + // %GAME_DOCUMENTS% + if (result.contains("%GAME_DOCUMENTS%")) { + result.replace("%GAME_DOCUMENTS%", documentsDirectory().absolutePath()); + } + + return result; +} diff --git a/libs/basic_games_native/src/basicgameplugin.h b/libs/basic_games_native/src/basicgameplugin.h new file mode 100644 index 0000000..17f4ec9 --- /dev/null +++ b/libs/basic_games_native/src/basicgameplugin.h @@ -0,0 +1,73 @@ +#ifndef BASICGAMEPLUGIN_H +#define BASICGAMEPLUGIN_H + +#include "gamedefs.h" + +#include <uibase/iplugingame.h> +#include <uibase/imoinfo.h> + +#include <QDir> +#include <QIcon> +#include <QString> + +class BasicGamePlugin : public MOBase::IPluginGame +{ + Q_OBJECT + Q_INTERFACES(MOBase::IPlugin MOBase::IPluginGame) + +public: + explicit BasicGamePlugin(const GameDefinition& def); + + // IPlugin + bool init(MOBase::IOrganizer* organizer) override; + QString name() const override; + QString localizedName() const override; + QString author() const override; + QString description() const override; + MOBase::VersionInfo version() const override; + QList<MOBase::PluginSetting> settings() const override; + + // IPluginGame + QString gameName() const override; + void detectGame() override; + void initializeProfile(const QDir& directory, + ProfileSettings settings) const override; + std::vector<std::shared_ptr<const MOBase::ISaveGame>> + listSaves(QDir folder) const override; + bool isInstalled() const override; + QIcon gameIcon() const override; + QDir gameDirectory() const override; + QDir dataDirectory() const override; + void setGamePath(const QString& path) override; + QDir documentsDirectory() const override; + QDir savesDirectory() const override; + QList<MOBase::ExecutableInfo> executables() const override; + QList<MOBase::ExecutableForcedLoadSetting> executableForcedLoads() const override; + QString steamAPPId() const override; + QStringList primaryPlugins() const override; + QStringList gameVariants() const override; + void setGameVariant(const QString& variant) override; + QString binaryName() const override; + QString gameShortName() const override; + QStringList validShortNames() const override; + QString gameNexusName() const override; + QStringList iniFiles() const override; + QStringList DLCPlugins() const override; + LoadOrderMechanism loadOrderMechanism() const override; + SortMechanism sortMechanism() const override; + int nexusGameID() const override; + bool looksValid(QDir const& dir) const override; + QString gameVersion() const override; + QString getLauncherName() const override; + QString getSupportURL() const override; + +private: + QString resolveVariables(const QString& input) const; + + GameDefinition m_def; + MOBase::IOrganizer* m_organizer = nullptr; + QString m_gameDir; + bool m_installed = false; +}; + +#endif // BASICGAMEPLUGIN_H diff --git a/libs/basic_games_native/src/basicgamesproxy.cpp b/libs/basic_games_native/src/basicgamesproxy.cpp new file mode 100644 index 0000000..f7efefd --- /dev/null +++ b/libs/basic_games_native/src/basicgamesproxy.cpp @@ -0,0 +1,99 @@ +#include "basicgamesproxy.h" +#include "basicgameplugin.h" +#include "gamedefs.h" + +#include <uibase/versioninfo.h> + +BasicGamesProxy::BasicGamesProxy() {} + +BasicGamesProxy::~BasicGamesProxy() +{ + // Clean up all loaded plugins + for (auto& list : m_loaded) { + qDeleteAll(list); + } + m_loaded.clear(); +} + +bool BasicGamesProxy::init(MOBase::IOrganizer* organizer) +{ + m_organizer = organizer; + return true; +} + +QString BasicGamesProxy::name() const +{ + return "Basic Games Native"; +} + +QString BasicGamesProxy::author() const +{ + return "Fluorine Manager"; +} + +QString BasicGamesProxy::description() const +{ + return "Native C++ implementation of basic game plugins"; +} + +MOBase::VersionInfo BasicGamesProxy::version() const +{ + return MOBase::VersionInfo(1, 0, 0); +} + +QList<MOBase::PluginSetting> BasicGamesProxy::settings() const +{ + return {}; +} + +QStringList BasicGamesProxy::pluginList(const QDir&) const +{ + QStringList list; + const auto& defs = allGameDefinitions(); + for (size_t i = 0; i < defs.size(); ++i) { + list.append(QString("native_game_%1").arg(i)); + } + return list; +} + +QList<QObject*> BasicGamesProxy::load(const QString& identifier) +{ + // Already loaded? + auto it = m_loaded.find(identifier); + if (it != m_loaded.end()) { + return it.value(); + } + + QList<QObject*> plugins; + + // Parse the index from the identifier + if (!identifier.startsWith("native_game_")) + return plugins; + + bool ok = false; + int index = identifier.mid(12).toInt(&ok); + if (!ok) + return plugins; + + const auto& defs = allGameDefinitions(); + if (index < 0 || index >= static_cast<int>(defs.size())) + return plugins; + + auto* plugin = new BasicGamePlugin(defs[index]); + if (m_organizer) { + plugin->init(m_organizer); + } + plugins.append(plugin); + + m_loaded.insert(identifier, plugins); + return plugins; +} + +void BasicGamesProxy::unload(const QString& identifier) +{ + auto it = m_loaded.find(identifier); + if (it != m_loaded.end()) { + qDeleteAll(it.value()); + m_loaded.erase(it); + } +} diff --git a/libs/basic_games_native/src/basicgamesproxy.h b/libs/basic_games_native/src/basicgamesproxy.h new file mode 100644 index 0000000..2c2858f --- /dev/null +++ b/libs/basic_games_native/src/basicgamesproxy.h @@ -0,0 +1,41 @@ +#ifndef BASICGAMESPROXY_H +#define BASICGAMESPROXY_H + +#include <uibase/ipluginproxy.h> + +#include <QHash> +#include <QList> +#include <QObject> +#include <QString> + +class BasicGamesProxy : public QObject, public MOBase::IPluginProxy +{ + Q_OBJECT + Q_INTERFACES(MOBase::IPlugin MOBase::IPluginProxy) +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + Q_PLUGIN_METADATA(IID "com.tannin.ModOrganizer.PluginProxy/1.0") +#endif + +public: + BasicGamesProxy(); + ~BasicGamesProxy() override; + + // IPlugin + bool init(MOBase::IOrganizer* organizer) override; + QString name() const override; + QString author() const override; + QString description() const override; + MOBase::VersionInfo version() const override; + QList<MOBase::PluginSetting> settings() const override; + + // IPluginProxy + QStringList pluginList(const QDir& pluginPath) const override; + QList<QObject*> load(const QString& identifier) override; + void unload(const QString& identifier) override; + +private: + MOBase::IOrganizer* m_organizer = nullptr; + QHash<QString, QList<QObject*>> m_loaded; +}; + +#endif // BASICGAMESPROXY_H diff --git a/libs/basic_games_native/src/gamedefs.cpp b/libs/basic_games_native/src/gamedefs.cpp new file mode 100644 index 0000000..d626d23 --- /dev/null +++ b/libs/basic_games_native/src/gamedefs.cpp @@ -0,0 +1,1024 @@ +#include "gamedefs.h" + +// clang-format off +const std::vector<GameDefinition>& allGameDefinitions() +{ + static const std::vector<GameDefinition> defs = { + // 1. Batman: Arkham City + { + "Batman: Arkham City Plugin", "Paynamia", "0.5.3", + "Batman: Arkham City", "batmanarkhamcity", {}, + {200260}, {1260066469}, {"Egret"}, + "Binaries/Win32/BatmanAC.exe", "Binaries/Win32/BmLauncher.exe", + "", 372, + "BmGame", + "%DOCUMENTS%/WB Games/Batman Arkham City GOTY/BmGame/Config", + "", "sgd", + {"UserEngine.ini", "UserGame.ini", "UserInput.ini"}, + "", {}, {}, {}, {} + }, + // 2. Assetto Corsa + { + "Assetto Corsa Support Plugin", "Deorder", "0.0.1", + "Assetto Corsa", "ac", {}, + {244210}, {}, {}, + "AssettoCorsa.exe", "", + "", 0, + "", + "%DOCUMENTS%/Assetto Corsa", + "", "", + {}, + "https://github.com/ModOrganizer2/modorganizer-basic_games/wiki/Game:-Assetto-Corsa", + {}, {}, {}, {} + }, + // 3. Baldur's Gate 3 + { + "Baldur's Gate 3 Plugin", "daescha", "0.1.0", + "Baldur's Gate 3", "baldursgate3", {"bg3"}, + {1086940}, {1456460669}, {}, + "bin/bg3.exe", "Launcher/LariLauncher.exe", + "baldursgate3", 3474, + "", + "%USERPROFILE%/AppData/Local/Larian Studios/Baldur's Gate 3", + "%GAME_DOCUMENTS%/PlayerProfiles/Public/Savegames/Story", "lsv", + {}, + "", + MOBase::IPluginGame::LoadOrderMechanism::PluginsTxt, + MOBase::IPluginGame::SortMechanism::NONE, + {}, {} + }, + // 4. Black & White 2 + { + "Black & White 2 Support Plugin", "Ilyu", "1.0.1", + "Black & White 2", "BW2", {}, + {}, {}, {}, + "white.exe", "", + "blackandwhite2", 0, + "%GAME_PATH%", + "%DOCUMENTS%/Black & White 2", + "%GAME_DOCUMENTS%/Profiles", "", + {}, + "https://github.com/ModOrganizer2/modorganizer-basic_games/wiki/Game:-Black-&-White-2", + {}, {}, {}, {} + }, + // 5. Black & White 2 Battle of the Gods + { + "Black & White 2 Battle of the Gods Support Plugin", "Ilyu", "1.0.1", + "Black & White 2 Battle of the Gods", "BOTG", {}, + {}, {}, {}, + "BattleOfTheGods.exe", "", + "blackandwhite2", 0, + "%GAME_PATH%", + "%DOCUMENTS%/Black & White 2 - Battle of the Gods", + "%GAME_DOCUMENTS%/Profiles", "", + {}, + "https://github.com/ModOrganizer2/modorganizer-basic_games/wiki/Game:-Black-&-White-2", + {}, {}, {}, {} + }, + // 6. Blade & Sorcery + { + "Blade & Sorcery Plugin", "R3z Shark & Silarn & Jonny_Bro", "0.5.1", + "Blade & Sorcery", "bladeandsorcery", {}, + {629730}, {}, {}, + "BladeAndSorcery.exe", "", + "", 0, + "BladeAndSorcery_Data/StreamingAssets/Mods", + "%DOCUMENTS%/My Games/BladeAndSorcery", + "%GAME_DOCUMENTS%/Saves/Default", "chr", + {}, + "https://github.com/ModOrganizer2/modorganizer-basic_games/wiki/Game:-Blade-&-Sorcery", + {}, {}, {}, {} + }, + // 7. Borderlands + { + "Borderlands 1 Support Plugin", "Miner Of Worlds, RedxYeti, mopioid", "1.0.0", + "Borderlands", "Borderlands", {}, + {8980}, {}, {}, + "Binaries/Borderlands.exe", "", + "Borderlands GOTY", 0, + ".", + "%DOCUMENTS%/My Games/Borderlands", + "%GAME_DOCUMENTS%/savedata", "sav", + {}, + "", {}, {}, {}, {} + }, + // 8. Control + { + "Control Support Plugin", "Zash", "1.0.0", + "Control", "control", {}, + {870780}, {2049187585}, {"calluna"}, + "Control.exe", "", + "", 2936, + "", + "", "", "", + {}, + "", {}, {}, {}, {} + }, + // 9. Cyberpunk 2077 + { + "Cyberpunk 2077 Support Plugin", "6788, Zash", "3.0.1", + "Cyberpunk 2077", "cyberpunk2077", {}, + {1091500}, {1423049311}, {"77f2b98e2cef40c8a7437518bf420e47"}, + "bin/x64/Cyberpunk2077.exe", "REDprelauncher.exe", + "", 0, + "%GAME_PATH%", + "%USERPROFILE%/AppData/Local/CD Projekt Red/Cyberpunk 2077", + "%USERPROFILE%/Saved Games/CD Projekt Red/Cyberpunk 2077", "dat", + {"UserSettings.json"}, + "https://github.com/ModOrganizer2/modorganizer-basic_games/wiki/Game:-Cyberpunk-2077", + {}, {}, {}, {} + }, + // 10. Dragon Age 2 + { + "Dragon Age 2 Support Plugin", "Patchier", "1.0.1", + "Dragon Age 2", "dragonage2", {}, + {1238040}, {}, {}, + "bin_ship/DragonAge2.exe", "", + "", 0, + "%DOCUMENTS%/BioWare/Dragon Age 2/packages/core/override", + "", + "%DOCUMENTS%/BioWare/Dragon Age 2/Characters", "das", + {}, + "https://github.com/ModOrganizer2/modorganizer-basic_games/wiki/Game:-Dragon-Age-II", + {}, {}, {}, {} + }, + // 11. Daggerfall Unity + { + "Daggerfall Unity Support Plugin", "HomerSimpleton", "1.0.0", + "Daggerfall Unity", "daggerfallunity", {}, + {}, {}, {}, + "DaggerfallUnity.exe", "DaggerfallUnity.exe", + "", 0, + "%GAME_PATH%/DaggerfallUnity_Data/StreamingAssets", + "", "", "", + {}, + "https://github.com/ModOrganizer2/modorganizer-basic_games/wiki/Game:-Daggerfall-Unity", + {}, {}, {}, {} + }, + // 12. Dragon Age: Origins + { + "Dragon Age Origins Support Plugin", "Patchier", "1.1.1", + "Dragon Age: Origins", "dragonage", {}, + {17450, 47810}, {1949616134}, {}, + "bin_ship/DAOrigins.exe", "", + "", 0, + "%DOCUMENTS%/BioWare/Dragon Age/packages/core/override", + "", + "%DOCUMENTS%/BioWare/Dragon Age/Characters", "das", + {}, + "https://github.com/ModOrganizer2/modorganizer-basic_games/wiki/Game:-Dragon-Age:-Origins", + {}, {}, {}, {} + }, + // 13. Darkest Dungeon + { + "DarkestDungeon", "erri120", "0.2.0", + "Darkest Dungeon", "darkestdungeon", {}, + {262060}, {1719198803}, {}, + "_windows/win64/Darkest.exe", "", + "darkestdungeon", 804, + "", + "%DOCUMENTS%/Darkest", + "%GAME_DOCUMENTS%", "", + {}, + "https://github.com/ModOrganizer2/modorganizer-basic_games/wiki/Game:-Darkest-Dungeon", + {}, {}, {}, {} + }, + // 14. Dark Messiah of Might & Magic + { + "Dark Messiah of Might and Magic Support Plugin", "Holt59", "0.1.0", + "Dark Messiah of Might & Magic", "darkmessiahofmightandmagic", {}, + {2100}, {}, {}, + "mm.exe", "", + "darkmessiahofmightandmagic", 628, + "mm", + "%GAME_PATH%/mm", + "%GAME_PATH%/mm/SAVE", "sav", + {}, + "https://github.com/ModOrganizer2/modorganizer-basic_games/wiki/Game:-Dark-Messiah-of-Might-&-Magic", + {}, {}, {}, {} + }, + // 15. Dark Souls II: Scholar of the First Sin + { + "DarkSouls2Sotfs", "raehik", "0.1.0", + "Dark Souls II: Scholar of the First Sin", "darksouls2sotfs", {}, + {335300}, {}, {}, + "Game/DarkSoulsII.exe", "", + "darksouls2", 482, + "Game", + "%USERPROFILE%/AppData/Roaming/DarkSoulsII", + "%USERPROFILE%/AppData/Roaming/DarkSoulsII", "sl2", + {}, + "https://github.com/ModOrganizer2/modorganizer-basic_games/wiki/Game:-Dark-Souls-2-Sotfs", + {}, {}, {}, {} + }, + // 16. Dark Souls + { + "DarkSouls", "Holt59", "0.1.0", + "Dark Souls", "darksouls", {}, + {211420}, {}, {}, + "DATA/DARKSOULS.exe", "", + "darksouls", 162, + "DATA", + "%DOCUMENTS%/NBGI/DarkSouls", + "%DOCUMENTS%/NBGI/DarkSouls", "sl2", + {}, + "https://github.com/ModOrganizer2/modorganizer-basic_games/wiki/Game:-Dark-Souls", + {}, {}, {}, {} + }, + // 17. Dispatch + { + "Dispatch Support Plugin", "Syer10", "0.1.0", + "Dispatch", "dispatch", {}, + {2592160}, {}, {}, + "Dispatch/Binaries/Win64/Dispatch-Win64-Shipping.exe", "", + "dispatch", 0, + "Dispatch/Content/", + "", "", "", + {}, + "https://github.com/ModOrganizer2/modorganizer-basic_games/wiki/Game:-Dispatch", + {}, {}, {}, {} + }, + // 18. Divinity: Original Sin (Enhanced Edition) + { + "Divinity: Original Sin (Enhanced Edition) Support Plugin", "LostDragonist", "1.0.0", + "Divinity: Original Sin (Enhanced Edition)", "divinityoriginalsinenhancededition", + {"divinityoriginalsin"}, + {373420}, {1445516929, 1445524575}, {}, + "Shipping/EoCApp.exe", "", + "divinityoriginalsinenhancededition", 1995, + "Data", + "%USERPROFILE%/Documents/Larian Studios/Divinity Original Sin Enhanced Edition", + "%USERPROFILE%/Documents/Larian Studios/Divinity Original Sin Enhanced Edition/PlayerProfiles", + "lsv", + {}, + "https://github.com/ModOrganizer2/modorganizer-basic_games/wiki/Game:-Divinity:-Original-Sin", + {}, {}, {}, {} + }, + // 19. Divinity: Original Sin (Classic) + { + "Divinity: Original Sin (Classic) Support Plugin", "LostDragonist", "1.0.0", + "Divinity: Original Sin (Classic)", "divinityoriginalsin", + {"divinityoriginalsin"}, + {230230}, {}, {}, + "Shipping/EoCApp.exe", "", + "divinityoriginalsin", 573, + "Data", + "%USERPROFILE%/Documents/Larian Studios/Divinity Original Sin", + "%USERPROFILE%/Documents/Larian Studios/Divinity Original Sin/PlayerProfiles", + "lsv", + {}, + "https://github.com/ModOrganizer2/modorganizer-basic_games/wiki/Game:-Divinity:-Original-Sin", + {}, {}, {}, {} + }, + // 20. Dragon's Dogma: Dark Arisen + { + "Dragon's Dogma: Dark Arisen Support Plugin", "Luca/EzioTheDeadPoet", "1.0.0", + "Dragon's Dogma: Dark Arisen", "dragonsdogma", {}, + {367500}, {1242384383}, {}, + "DDDA.exe", "", + "dragonsdogma", 0, + "nativePC", + "", "", "", + {}, + "https://github.com/ModOrganizer2/modorganizer-basic_games/wiki/Game:-Dragon's-Dogma:-Dark-Arisen", + {}, {}, {}, {} + }, + // 21. Dungeon Siege I + { + "Dungeon Siege I", "mrudat", "0.0.1", + "Dungeon Siege I", "dungeonsiege1", {}, + {39190}, {1142020247}, {}, + "DungeonSiege.exe", "", + "dungeonsiege1", 541, + "", + "%DOCUMENTS%/Dungeon Siege", + "%GAME_DOCUMENTS%/Save", "dssave", + {"DungeonSiege.ini"}, + "https://github.com/ModOrganizer2/modorganizer-basic_games/wiki/Game:-Dungeon-Siege-I", + {}, {}, {}, {} + }, + // 22. Dungeon Siege II + { + "Dungeon Siege II", "Holt59", "0.1.1", + "Dungeon Siege II", "dungeonsiegeii", {}, + {39200}, {1142020247}, {}, + "DungeonSiege2.exe", "", + "dungeonsiegeii", 2078, + "", + "%DOCUMENTS%/My Games/Dungeon Siege 2", + "%GAME_DOCUMENTS%/Save", "ds2party", + {"DungeonSiege2.ini"}, + "https://github.com/ModOrganizer2/modorganizer-basic_games/wiki/Game:-Dungeon-Siege-II", + {}, {}, {}, {} + }, + // 23. F1 23 + { + "F1 23 Support Plugin", "ju5tA1ex", "1.0.0", + "F1 23", "F1 23", {}, + {2108330}, {}, {}, + "F1_23.exe", "", + "", 0, + "", + "", "", "", + {}, + "", {}, {}, {}, {} + }, + // 24. Fantasy Life I + { + "Fantasy Life I Support Plugin", "AmeliaCute", "0.2.2", + "FANTASY LIFE i", "fantasylifei", {"fli"}, + {2993780}, {}, {}, + "Game/Binaries/Win64/NFL1-Win64-Shipping.exe", "", + "fantasylifeithegirlwhostealstime", 0, + "Game/Content/", + "", "", "", + {}, + "https://github.com/ModOrganizer2/modorganizer-basic_games/wiki/Game:-Fantasy-Life-I:-The-Girl-Who-Steals-Time", + {}, {}, {}, {} + }, + // 25. Final Fantasy VII Rebirth + { + "Final Fantasy 7 Rebirth Support Plugin", "diegofesanto, TheUnlocked", "0.0.1", + "Final Fantasy VII Rebirth", "finalfantasy7rebirth", {}, + {2909400}, {}, {}, + "End/Binaries/Win64/ff7rebirth_.exe", "", + "finalfantasy7rebirth", 0, + "_ROOT", + "", "", "sav", + {}, + "", {}, {}, {}, {} + }, + // 26. Final Fantasy VII Remake + { + "Final Fantasy VII Remake Support Plugin", "TheUnlocked", "1.0.0", + "Final Fantasy VII Remake", "finalfantasy7remake", {}, + {1462040}, {}, {}, + "ff7remake.exe", "", + "finalfantasy7remake", 0, + "_ROOT", + "", "", "sav", + {}, + "", {}, {}, {}, {} + }, + // 27. GTA III - Definitive Edition + { + "Grand Theft Auto III - Definitive Edition Support Plugin", "dekart811", "1.0", + "GTA III - Definitive Edition", "grandtheftautothetrilogy", {}, + {}, {}, {}, + "Gameface/Binaries/Win64/LibertyCity.exe", "", + "grandtheftautothetrilogy", 0, + "Gameface/Content/Paks/~mods", + "%USERPROFILE%/Documents/Rockstar Games/GTA III Definitive Edition/Config/WindowsNoEditor", + "%GAME_DOCUMENTS%/../../SaveGames", "sav", + {"GameUserSettings.ini", "CustomSettings.ini"}, + "https://github.com/ModOrganizer2/modorganizer-basic_games/wiki/Game:-Grand-Theft-Auto:-The-Trilogy", + {}, {}, {}, {} + }, + // 28. GTA: San Andreas - Definitive Edition + { + "Grand Theft Auto: San Andreas - Definitive Edition Support Plugin", "dekart811", "1.0", + "GTA: San Andreas - Definitive Edition", "grandtheftautothetrilogy", {}, + {}, {}, {}, + "Gameface/Binaries/Win64/SanAndreas.exe", "", + "grandtheftautothetrilogy", 0, + "Gameface/Content/Paks/~mods", + "%USERPROFILE%/Documents/Rockstar Games/GTA San Andreas Definitive Edition/Config/WindowsNoEditor", + "%GAME_DOCUMENTS%/../../SaveGames", "sav", + {"GameUserSettings.ini", "CustomSettings.ini"}, + "https://github.com/ModOrganizer2/modorganizer-basic_games/wiki/Game:-Grand-Theft-Auto:-The-Trilogy", + {}, {}, {}, {} + }, + // 29. GTA: Vice City - Definitive Edition + { + "Grand Theft Auto: Vice City - Definitive Edition Support Plugin", "dekart811", "1.0", + "GTA: Vice City - Definitive Edition", "grandtheftautothetrilogy", {}, + {}, {}, {}, + "Gameface/Binaries/Win64/ViceCity.exe", "", + "grandtheftautothetrilogy", 0, + "Gameface/Content/Paks/~mods", + "%USERPROFILE%/Documents/Rockstar Games/GTA Vice City Definitive Edition/Config/WindowsNoEditor", + "%GAME_DOCUMENTS%/../../SaveGames", "sav", + {"GameUserSettings.ini", "CustomSettings.ini"}, + "https://github.com/ModOrganizer2/modorganizer-basic_games/wiki/Game:-Grand-Theft-Auto:-The-Trilogy", + {}, {}, {}, {} + }, + // 30. Kerbal Space Program + { + "Kerbal Space Program Support Plugin", "LaughingHyena", "1.0.0", + "Kerbal Space Program", "kerbalspaceprogram", {}, + {220200, 283740, 982970}, {}, {}, + "KSP_x64.exe", "", + "kerbalspaceprogram", 0, + "GameData", + "", + "%GAME_PATH%/saves", "sfs", + {}, + "https://github.com/ModOrganizer2/modorganizer-basic_games/wiki/Game:-Kerbal-Space-Program", + {}, {}, {}, {} + }, + // 31. Kingdom Come: Deliverance + { + "Kingdom Come Deliverance Support Plugin", "Silencer711", "1.0.0", + "Kingdom Come: Deliverance", "kingdomcomedeliverance", {}, + {379430}, {1719198803}, {"Eel"}, + "bin/Win64/KingdomCome.exe", "", + "kingdomcomedeliverance", 2298, + "mods", + "%GAME_PATH%", + "%USERPROFILE%/Saved Games/kingdomcome/saves", "whs", + {"custom.cfg", "system.cfg", "user.cfg"}, + "https://github.com/ModOrganizer2/modorganizer-basic_games/wiki/Game:-Kingdom-Come:-Deliverance", + {}, {}, {}, {} + }, + // 32. Yu-Gi-Oh! Master Duel + { + "Yu-Gi-Oh! Master Duel Support Plugin", "The Conceptionist & uwx", "1.0.2", + "Yu-Gi-Oh! Master Duel", "masterduel", {}, + {1449850}, {}, {}, + "masterduel.exe", "", + "yugiohmasterduel", 4272, + "", + "", "", "", + {}, + "", {}, {}, {}, {} + }, + // 33. METAL GEAR SOLID 2: Sons of Liberty - MC + { + "METAL GEAR SOLID 2: Sons of Liberty - Master Collection Version Support Plugin", + "AkiraJkr", "1.0.0", + "METAL GEAR SOLID 2: Sons of Liberty - Master Collection Version", + "metalgearsolid2mc", {}, + {2131640}, {}, {}, + "METAL GEAR SOLID2.exe", "launcher.exe", + "metalgearsolid2mc", 0, + "", + "", "", "", + {}, + "", {}, {}, {}, {} + }, + // 34. METAL GEAR SOLID 3: Snake Eater - MC + { + "METAL GEAR SOLID 3: Snake Eater - Master Collection Version Support Plugin", + "AkiraJkr", "1.0.0", + "METAL GEAR SOLID 3: Snake Eater - Master Collection Version", + "metalgearsolid3mc", {}, + {2131650}, {}, {}, + "METAL GEAR SOLID3.exe", "launcher.exe", + "metalgearsolid3mc", 0, + "", + "", "", "", + {}, + "", {}, {}, {}, {} + }, + // 35. Mirror's Edge + { + "Mirror's Edge Support Plugin", "Luca/EzioTheDeadPoet", "1.0.0", + "Mirror's Edge", "mirrorsedge", {}, + {17410}, {1893001152}, {}, + "Binaries/MirrorsEdge.exe", "", + "", 0, + "TdGame", + "", "", "", + {}, + "https://github.com/ModOrganizer2/modorganizer-basic_games/wiki/Game:-Mirror's-Edge", + {}, {}, {}, {} + }, + // 36. Monster Hunter: Rise + { + "Monster Hunter: Rise Support Plugin", "RodolfoFigueroa", "1.0.0", + "Monster Hunter: Rise", "monsterhunterrise", {}, + {1446780}, {}, {}, + "MonsterHunterRise.exe", "", + "", 4095, + "%GAME_PATH%", + "", "", "bin", + {}, + "", {}, {}, {}, {} + }, + // 37. Monster Hunter: Wilds + { + "Monster Hunter: Wilds Support Plugin", "AbyssDragnonModding", "1.0.0", + "Monster Hunter: Wilds", "monsterhunterwilds", {}, + {2246340}, {}, {}, + "MonsterHunterWilds.exe", "", + "", 6993, + "%GAME_PATH%", + "", "", "bin", + {}, + "", {}, {}, {}, {} + }, + // 38. Monster Hunter: World + { + "Monster Hunter: World Support Plugin", "prz", "1.0.0", + "Monster Hunter: World", "monsterhunterworld", {}, + {582010}, {}, {}, + "MonsterHunterWorld.exe", "MonsterHunterWorld.exe", + "monsterhunterworld", 2531, + "%GAME_PATH%", + "", "", "dat", + {}, + "https://github.com/ModOrganizer2/modorganizer-basic_games/wiki/Game:-Monster-Hunter:-World", + {}, {}, {}, {} + }, + // 39. Mount & Blade II: Bannerlord + { + "Mount & Blade II: Bannerlord", "Holt59", "0.1.1", + "Mount & Blade II: Bannerlord", "mountandblade2bannerlord", {}, + {261550}, {}, {}, + "bin/Win64_Shipping_Client/TaleWorlds.MountAndBlade.Launcher.exe", "", + "", 3174, + "Modules", + "%DOCUMENTS%/Mount and Blade II Bannerlord/Configs", + "%DOCUMENTS%/Mount and Blade II Bannerlord/Game Saves", "", + {}, + "https://github.com/ModOrganizer2/modorganizer-basic_games/wiki/Game:-Mount-&-Blade-II:-Bannerlord", + {}, {}, {}, {} + }, + // 40. Microsoft Flight Simulator 2020 + { + "Microsoft Flight Simulator 2020 Support Plugin", "Deorder", "0.0.1", + "Microsoft Flight Simulator 2020", "msfs2020", {}, + {1250410}, {}, {}, + "FlightSimulator.exe", "", + "", 0, + "", + "", "", "", + {}, + "https://github.com/ModOrganizer2/modorganizer-basic_games/wiki/Game:-Microsoft-Flight-Simulator-(2020)", + {}, {}, {}, {} + }, + // 41. Need for Speed: High Stakes + { + "Need for Speed: High Stakes Support Plugin", "uwx", "1.0.0", + "Need for Speed: High Stakes", "nfshs", {}, + {}, {}, {}, + "nfshs.exe", "", + "needforspeedhighstakes", 6032, + "", + "", "", "", + {}, + "", {}, {}, {}, {} + }, + // 42. NieR:Automata + { + "NieR:Automata Support Plugin", "Luca/EzioTheDeadPoet", "1.0.0", + "NieR:Automata", "nierautomata", {}, + {524220}, {}, {}, + "NieRAutomata.exe", "", + "nierautomata", 0, + "", + "", "", "", + {}, + "", {}, {}, {}, {} + }, + // 43. No Man's Sky + { + "No Man's Sky Support Plugin", "Luca/EzioTheDeadPoet", "1.0.0", + "No Man's Sky", "nomanssky", {}, + {275850}, {1446213994}, {}, + "Binaries/NMS.exe", "", + "nomanssky", 1634, + "GAMEDATA/MODS", + "", + "%USERPROFILE%/AppData/Roaming/HelloGames/NMS", "hg", + {}, + "https://github.com/ModOrganizer2/modorganizer-basic_games/wiki/Game:-No-Man's-Sky", + {}, {}, {}, {} + }, + // 44. Oblivion Remastered + { + "Oblivion Remastered Plugin", "Silarn", "0.1.0", + "Oblivion Remastered", "oblivionremastered", {}, + {2623190}, {}, {}, + "OblivionRemastered.exe", "", + "", 7587, + "%GAME_PATH%/OblivionRemastered/Content/Dev/ObvData/Data", + "%GAME_PATH%/OblivionRemastered/Content/Dev/ObvData", + "%DOCUMENTS%/My Games/Oblivion Remastered/Saved/SaveGames", "sav", + {"Oblivion.ini"}, + "https://github.com/ModOrganizer2/modorganizer-basic_games/wiki/Game:-Elder-Scrolls-IV:-Oblivion-Remastered", + MOBase::IPluginGame::LoadOrderMechanism::PluginsTxt, + MOBase::IPluginGame::SortMechanism::LOOT, + {"Oblivion.esm", "DLCBattlehornCastle.esp", "DLCFrostcrag.esp", + "DLCHorseArmor.esp", "DLCMehrunesRazor.esp", "DLCOrrery.esp", + "DLCShiveringIsles.esp", "DLCSpellTomes.esp", "DLCThievesDen.esp", + "DLCVileLair.esp", "Knights.esp", "AltarESPMain.esp", + "AltarDeluxe.esp", "AltarESPLocal.esp"}, + {} + }, + // 45. Ready or Not + { + "Ready or Not Support Plugin", "Ra2-IFV", "0.0.0.1", + "Ready or Not", "readyornot", {"ron"}, + {1144200}, {}, {}, + "ReadyOrNot/Binaries/Win64/ReadyOrNot-Win64-Shipping.exe", "ReadyOrNot.exe", + "readyornot", 0, + "ReadyOrNot/Content/Paks", + "%USERPROFILE%/AppData/Local/ReadyOrNot", + "%USERPROFILE%/AppData/Local/ReadyOrNot/Saved/SaveGames", "sav", + {"%GAME_DOCUMENTS%/Saved/Config/Windows/Game.ini", + "%GAME_DOCUMENTS%/Saved/Config/Windows/GameUserSettings.ini"}, + "", {}, {}, {}, {} + }, + // 46. Schedule I + { + "Schedule I Support Plugin", "shellbj", "1.0.0", + "Schedule I", "scheduleI", {"schedule1", "scheduleI"}, + {3164500}, {}, {}, + "Schedule I.exe", "", + "schedule1", 7381, + "", + "", + "%USERPROFILE%/AppData/LocalLow/TVGS/Schedule I/Saves", "", + {}, + "https://github.com/ModOrganizer2/modorganizer-basic_games/wiki/Game:-Schedule-I", + {}, {}, {}, {} + }, + // 47. Sekiro: Shadows Die Twice + { + "Sekiro: Shadows Die Twice Support Plugin", "Kane Dou", "1.0.0", + "Sekiro: Shadows Die Twice", "sekiro", {}, + {814380}, {}, {}, + "sekiro.exe", "", + "", 0, + "mods", + "", "", "sl2", + {}, + "", {}, {}, {}, {} + }, + // 48. Silent Hill 2 Remake + { + "Silent Hill 2 Remake Support Plugin", "HomerSimpleton Returns", "1.0", + "Silent Hill 2 Remake", "silenthill2", {}, + {}, {1225972913, 2051029707}, {}, + "SHProto/Binaries/Win64/SHProto-Win64-Shipping.exe", "SHProto.exe", + "silenthill2", 0, + "%GAME_PATH%", + "", "", "", + {}, + "https://github.com/ModOrganizer2/modorganizer-basic_games/wiki/Game:-Silent-Hill-2-Remake", + {}, {}, {}, {} + }, + // 49. Hollow Knight: Silksong + { + "Hollow Knight: Silksong Support Plugin", "Nikirack", "1.0.0", + "Hollow Knight: Silksong", "hollowknightsilksong", {}, + {1030300}, {}, {}, + "Hollow Knight Silksong.exe", "", + "hollowknightsilksong", 0, + "", + "", + "%USERPROFILE%/AppData/LocalLow/Team Cherry/Hollow Knight Silksong", "", + {}, + "https://github.com/ModOrganizer2/modorganizer-basic_games/wiki/Game:-Hollow-Knight-Silksong", + {}, {}, {}, {} + }, + // 50. The Sims 4 + { + "The Sims 4 Support Plugin", "R3z Shark, xieve", "1.0.0", + "The Sims 4", "thesims4", {}, + {1222670}, {}, {}, + "Game/Bin/TS4_x64.exe", "", + "", 0, + "%DOCUMENTS%/Electronic Arts/The Sims 4/Mods", + "%DOCUMENTS%/Electronic Arts/The Sims 4/Mods", + "", "", + {}, + "", {}, {}, {}, {} + }, + // 51. STALKER Anomaly + { + "STALKER Anomaly", "Qudix", "0.5.0", + "STALKER Anomaly", "stalkeranomaly", {}, + {}, {}, {}, + "AnomalyLauncher.exe", "", + "stalkeranomaly", 3743, + "", + "%GAME_PATH%/appdata", + "%GAME_DOCUMENTS%/savedgames", "scop", + {}, + "https://github.com/ModOrganizer2/modorganizer-basic_games/wiki/Game:-S.T.A.L.K.E.R.-Anomaly", + {}, {}, {}, {} + }, + // 52. Stardew Valley + { + "Stardew Valley Support Plugin", "Syer10", "0.1.0", + "Stardew Valley", "stardewvalley", {}, + {413150}, {1453375253}, {}, + "Stardew Valley.exe", "", + "stardewvalley", 1303, + "mods", + "%DOCUMENTS%/StardewValley", + "%GAME_DOCUMENTS%/Saves", "", + {}, + "https://github.com/ModOrganizer2/modorganizer-basic_games/wiki/Game:-Stardew-Valley", + {}, {}, {}, {} + }, + // 53. Starsector + { + "Starsector Support Plugin", "ddbb07", "1.0.1", + "Starsector", "starsector", {}, + {}, {}, {}, + "starsector.exe", "", + "starsector", 0, + "mods", + "", + "%GAME_PATH%/saves", "", + {}, + "https://github.com/ModOrganizer2/modorganizer-basic_games/wiki/Game:-Starsector", + {}, {}, {}, {} + }, + // 54. STAR WARS Empire at War: Forces of Corruption + { + "STAR WARS Empire at War - Force of Corruption", "erri120", "1.0.0", + "STAR WARS Empire at War: Forces of Corruption", "starwarsempireatwar", {}, + {32470}, {1421404887}, {}, + "corruption/StarWarsG.exe", "", + "starwarsempireatwar", 453, + "corruption/Data", + "", "", "", + {}, + "https://github.com/ModOrganizer2/modorganizer-basic_games/wiki/Game:-Star-Wars:-Empire-At-War", + {}, {}, {}, {} + }, + // 55. STAR WARS Empire at War (base) + { + "STAR WARS Empire at War", "erri120", "1.0.0", + "STAR WARS Empire at War", "starwarsempireatwar", {}, + {32470}, {1421404887}, {}, + "GameData/StarWarsG.exe", "", + "starwarsempireatwar", 453, + "GameData/Data", + "", "", "", + {}, + "https://github.com/ModOrganizer2/modorganizer-basic_games/wiki/Game:-Star-Wars:-Empire-At-War", + {}, {}, {}, {} + }, + // 56. Subnautica: Below Zero + { + "Subnautica Below Zero Support Plugin", "dekart811, Zash", "2.3", + "Subnautica: Below Zero", "subnauticabelowzero", {}, + {848450}, {}, {"foxglove"}, + "SubnauticaZero.exe", "", + "subnauticabelowzero", 0, + "_ROOT", + "%GAME_PATH%", + "%USERPROFILE%/AppData/LocalLow/Unknown Worlds/Subnautica Below Zero/SubnauticaZero/SavedGames", + "", + {}, + "https://github.com/ModOrganizer2/modorganizer-basic_games/wiki/Game:-Subnautica:-Below-Zero", + {}, {}, {}, {} + }, + // 57. Subnautica + { + "Subnautica Support Plugin", "dekart811, Zash", "2.3", + "Subnautica", "subnautica", {}, + {264710}, {}, {"Jaguar"}, + "Subnautica.exe", "", + "subnautica", 0, + "_ROOT", + "%GAME_PATH%", + "%GAME_PATH%/SNAppData/SavedGames", "", + {}, + "https://github.com/ModOrganizer2/modorganizer-basic_games/wiki/Game:-Subnautica", + {}, {}, {}, {} + }, + // 58. Test Drive Unlimited 2 + { + "Test Drive Unlimited 2 Support Plugin", "uwx", "1.0.0", + "Test Drive Unlimited 2", "tdu2", {}, + {9930}, {}, {}, + "UpLauncher.exe", "", + "testdriveunlimited2", 2353, + "", + "", "", "", + {}, + "", {}, {}, {}, {} + }, + // 59. Test Drive Unlimited + { + "Test Drive Unlimited Support Plugin", "uwx", "1.0.0", + "Test Drive Unlimited", "tdu", {}, + {}, {}, {}, + "TestDriveUnlimited.exe", "", + "testdriveunlimited", 4615, + "", + "", "", "", + {}, + "", {}, {}, {}, {} + }, + // 60. The Binding of Isaac: Rebirth + { + "The Binding of Isaac: Rebirth - Support Plugin", "Luca/EzioTheDeadPoet", "0.1.0", + "The Binding of Isaac: Rebirth", "thebindingofisaacrebirth", {}, + {250900}, {}, {}, + "isaac-ng.exe", "", + "thebindingofisaacrebirth", 1293, + "%DOCUMENTS%/My Games/Binding of Isaac Afterbirth+ Mods", + "%DOCUMENTS%/My Games/Binding of Isaac Afterbirth+", + "", "", + {"options.ini"}, + "https://github.com/ModOrganizer2/modorganizer-basic_games/wiki/Game:-The-Binding-of-Isaac:-Rebirth", + {}, {}, {}, {} + }, + // 61. Tony Hawk's Pro Skater 3 + { + "Tony Hawk's Pro Skater 3 Support Plugin", "uwx", "1.0.0", + "Tony Hawk's Pro Skater 3", "thps3", {}, + {}, {}, {}, + "Skate3.exe", "", + "", 0, + "Data", + "", "", "", + {}, + "", {}, {}, {}, {} + }, + // 62. Tony Hawk's Pro Skater 4 + { + "Tony Hawk's Pro Skater 4 Support Plugin", "uwx", "1.0.0", + "Tony Hawk's Pro Skater 4", "thps4", {}, + {}, {}, {}, + "Skate4.exe", "", + "", 0, + "Data", + "", "", "", + {}, + "", {}, {}, {}, {} + }, + // 63. Tony Hawk's Underground 2 + { + "Tony Hawk's Underground 2 Support Plugin", "uwx", "1.0.0", + "Tony Hawk's Underground 2", "thug2", {}, + {}, {}, {}, + "THUG2.exe", "", + "", 0, + "Data", + "", "", "", + {}, + "", {}, {}, {}, {} + }, + // 64. Tony Hawk's Underground + { + "Tony Hawk's Underground Support Plugin", "uwx", "1.0.0", + "Tony Hawk's Underground", "thug", {}, + {}, {}, {}, + "THUG.exe", "", + "", 0, + "Data", + "", "", "", + {}, + "", {}, {}, {}, {} + }, + // 65. Trackmania United Forever + { + "Trackmania United Forever Support Plugin", "uwx", "1.0.0", + "Trackmania United Forever", "tmuf", {}, + {7200}, {}, {}, + "TmForeverLauncher.exe", "", + "trackmaniaunited", 1500, + "GameData", + "", "", "", + {}, + "", {}, {}, {}, {} + }, + // 66. Train Simulator + { + "Train Simulator Classic Support Plugin", "Ryan Young", "1.1.0", + "Train Simulator", "railworks", {}, + {24010}, {}, {}, + "RailWorks.exe", "", + "", 0, + "", + "", "", "", + {}, + "https://github.com/ModOrganizer2/modorganizer-basic_games/wiki/Game:-Train-Simulator-Classic", + {}, {}, {}, {} + }, + // 67. Valheim + { + "Valheim Support Plugin", "Zash", "1.3", + "Valheim", "valheim", {}, + {892970, 896660, 1223920}, {}, {}, + "valheim.exe", "", + "", 3667, + "", + "", + "%USERPROFILE%/AppData/LocalLow/IronGate/Valheim", "", + {}, + "https://github.com/ModOrganizer2/modorganizer-basic_games/wiki/Game:-Valheim", + {}, {}, {}, {} + }, + // 68. Valkyria Chronicles + { + "Valkyria Chronicles Support Plugin", "Ketsuban", "1.0.0", + "Valkyria Chronicles", "vc1", {}, + {294860}, {}, {}, + "Valkyria.exe", "Launcher.exe", + "", 0, + "%GAME_PATH%", + "", + "%GAME_PATH%/savedata", "", + {}, + "", {}, {}, {}, {} + }, + // 69. Vampire - The Masquerade: Bloodlines + { + "Vampire - The Masquerade: Bloodlines Support Plugin", "John", "1.0.0", + "Vampire - The Masquerade: Bloodlines", "vampirebloodlines", {}, + {2600}, {1207659240}, {}, + "vampire.exe", "", + "vampirebloodlines", 437, + "vampire", + "%GAME_PATH%/vampire/cfg", + "%GAME_PATH%/vampire/SAVE", "sav", + {"autoexec.cfg", "user.cfg"}, + "https://github.com/ModOrganizer2/modorganizer-basic_games/wiki/Game:-Vampire:-The-Masquerade-Bloodlines", + {}, {}, {}, {} + }, + // 70. The Witcher: Enhanced Edition + { + "Witcher 1 Support Plugin", "erri120", "1.0.0", + "The Witcher: Enhanced Edition", "witcher", {}, + {20900}, {1207658924}, {}, + "System/witcher.exe", "", + "witcher", 150, + "Data", + "%DOCUMENTS%/The Witcher", + "%GAME_DOCUMENTS%/saves", "TheWitcherSave", + {}, + "https://github.com/ModOrganizer2/modorganizer-basic_games/wiki/Game:-The-Witcher", + {}, {}, {}, {} + }, + // 71. The Witcher 2: Assassins of Kings + { + "Witcher 2 Support Plugin", "DefinitelyNotSade", "1.0.0", + "The Witcher 2: Assassins of Kings", "witcher2", {}, + {20920}, {1207658930}, {}, + "bin/witcher2.exe", "Launcher.exe", + "witcher2", 0, + "CookedPC", + "%DOCUMENTS%/witcher 2/Config", + "%GAME_DOCUMENTS%/../gamesaves", "sav", + {"User.ini", "Rendering.ini", "Community.ini", "UserContent.ini", + "DIMapping.ini", "Input_QWERTY.ini", "Input_AZERTY.ini", "Input_QWERTZ.ini"}, + "", {}, {}, {}, {} + }, + // 72. The Witcher 3: Wild Hunt + { + "Witcher 3 Support Plugin", "Holt59", "1.0.0", + "The Witcher 3: Wild Hunt", "witcher3", {}, + {499450, 292030}, {1640424747, 1495134320, 1207664663, 1207664643}, {}, + "bin/x64/witcher3.exe", "", + "witcher3", 952, + "Mods", + "%DOCUMENTS%/The Witcher 3", + "%GAME_DOCUMENTS%/gamesaves", "sav", + {"user.settings", "input.settings"}, + "https://github.com/ModOrganizer2/modorganizer-basic_games/wiki/Game:-The-Witcher-3", + {}, {}, {}, {} + }, + // 73. X4: Foundations + { + "X4 Foundations Support Plugin", "Twinki,BrandonM4", "0.1.0", + "X4: Foundations", "x4foundations", {}, + {392160}, {}, {}, + "x4.exe", "", + "", 2659, + "extensions", + "%DOCUMENTS%/Egosoft/X4", + "", "", + {}, + "", {}, {}, {}, {} + }, + // 74. X-Plane 11 + { + "X-Plane 11 Support Plugin", "Deorder", "0.0.1", + "X-Plane 11", "xp11", {}, + {}, {}, {}, + "X-Plane.exe", "", + "", 0, + "", + "", "", "", + {}, + "https://github.com/ModOrganizer2/modorganizer-basic_games/wiki/Game:-X-Plane-11", + {}, {}, {}, {} + }, + // 75. Zeus and Poseidon + { + "Zeus and Poseidon Support Plugin", "Holt59", "1.0.0", + "Zeus and Poseidon", "zeusandposeidon", {}, + {566050}, {1207659039}, {}, + "Zeus.exe", "", + "", 0, + "Adventures", + "%GAME_PATH%", + "%GAME_PATH%/Save", "sav", + {}, + "https://github.com/ModOrganizer2/modorganizer-basic_games/wiki/Game:-Zeus-Poseidon", + {}, {}, {}, {} + }, + }; + // clang-format on + + return defs; +} diff --git a/libs/basic_games_native/src/gamedefs.h b/libs/basic_games_native/src/gamedefs.h new file mode 100644 index 0000000..bda93eb --- /dev/null +++ b/libs/basic_games_native/src/gamedefs.h @@ -0,0 +1,61 @@ +#ifndef GAMEDEFS_H +#define GAMEDEFS_H + +#include <uibase/iplugingame.h> + +#include <QList> +#include <QString> +#include <QStringList> + +#include <vector> + +struct GameDefinition { + // Plugin metadata + QString pluginName; + QString author; + QString version; // parsed as VersionInfo string + + // Game identity + QString gameName; + QString gameShortName; + QStringList validShortNames; + + // Store IDs + QList<int> steamAppIds; + QList<int> gogAppIds; + QStringList epicAppIds; + + // Executables + QString binaryName; + QString launcherName; + + // Nexus + QString gameNexusName; + int nexusGameId = 0; + + // Directories (may contain %DOCUMENTS%, %USERPROFILE%, %GAME_PATH%, + // %GAME_DOCUMENTS%) + QString dataDirectory; + QString documentsDirectory; + QString savesDirectory; + QString saveExtension; + + // Configuration + QStringList iniFiles; + QString supportURL; + + // Load order / sorting + MOBase::IPluginGame::LoadOrderMechanism loadOrderMechanism = + MOBase::IPluginGame::LoadOrderMechanism::None; + MOBase::IPluginGame::SortMechanism sortMechanism = + MOBase::IPluginGame::SortMechanism::NONE; + + // Plugins + QStringList primaryPlugins; + QStringList dlcPlugins; +}; + +// Returns the complete list of all game definitions +const std::vector<GameDefinition>& allGameDefinitions(); + +#endif // GAMEDEFS_H diff --git a/libs/basic_games_native/src/steamutils.cpp b/libs/basic_games_native/src/steamutils.cpp new file mode 100644 index 0000000..28fac2b --- /dev/null +++ b/libs/basic_games_native/src/steamutils.cpp @@ -0,0 +1,156 @@ +#include "steamutils.h" + +#include <QDir> +#include <QFile> +#include <QRegularExpression> +#include <QStandardPaths> +#include <QTextStream> + +static QStringList findSteamPaths() +{ + QStringList paths; + QString home = QDir::homePath(); + + QStringList candidates = { + home + "/.local/share/Steam", + home + "/.steam/debian-installation", + home + "/.steam/steam", + home + "/.var/app/com.valvesoftware.Steam/data/Steam", + home + "/.var/app/com.valvesoftware.Steam/.local/share/Steam", + home + "/snap/steam/common/.local/share/Steam", + }; + + for (const auto& candidate : candidates) { + QDir dir(candidate); + if (dir.exists("steamapps") || QFile::exists(candidate + "/steam.pid")) { + // Resolve symlinks to avoid duplicates + QString canonical = QFileInfo(candidate).canonicalFilePath(); + if (!canonical.isEmpty() && !paths.contains(canonical)) { + paths.append(canonical); + } + } + } + + return paths; +} + +static QStringList parseLibraryFolders(const QString& vdfPath) +{ + QStringList folders; + QFile file(vdfPath); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) + return folders; + + QTextStream in(&file); + QString content = in.readAll(); + + // Match "path" entries in VDF format: "path" "/path/to/library" + static QRegularExpression re(R"--("path"\s+"([^"]+)")--"); + auto it = re.globalMatch(content); + while (it.hasNext()) { + auto match = it.next(); + QString path = match.captured(1); + if (QDir(path).exists()) { + folders.append(path); + } + } + + return folders; +} + +static QStringList getAllLibraryFolders(const QString& steamPath) +{ + QStringList folders; + folders.append(steamPath); + + // Parse libraryfolders.vdf + for (const auto& vdfName : + {"steamapps/libraryfolders.vdf", "config/libraryfolders.vdf"}) { + QString vdfPath = steamPath + "/" + vdfName; + for (const auto& path : parseLibraryFolders(vdfPath)) { + if (!folders.contains(path)) { + folders.append(path); + } + } + } + + return folders; +} + +struct AppManifest { + int appId = 0; + QString name; + QString installDir; + int stateFlags = 0; + + bool isInstalled() const { return (stateFlags & 4) != 0; } +}; + +static AppManifest parseAppManifest(const QString& path) +{ + AppManifest manifest; + QFile file(path); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) + return manifest; + + QTextStream in(&file); + QString content = in.readAll(); + + static QRegularExpression reAppId(R"--("appid"\s+"(\d+)")--"); + static QRegularExpression reName(R"--("name"\s+"([^"]+)")--"); + static QRegularExpression reInstallDir(R"--("installdir"\s+"([^"]+)")--"); + static QRegularExpression reState(R"--("StateFlags"\s+"(\d+)")--"); + + auto m = reAppId.match(content); + if (m.hasMatch()) + manifest.appId = m.captured(1).toInt(); + + m = reName.match(content); + if (m.hasMatch()) + manifest.name = m.captured(1); + + m = reInstallDir.match(content); + if (m.hasMatch()) + manifest.installDir = m.captured(1); + + m = reState.match(content); + if (m.hasMatch()) + manifest.stateFlags = m.captured(1).toInt(); + + return manifest; +} + +QHash<int, QString> findSteamGames() +{ + QHash<int, QString> games; + + for (const auto& steamPath : findSteamPaths()) { + for (const auto& libraryPath : getAllLibraryFolders(steamPath)) { + QDir steamapps(libraryPath + "/steamapps"); + if (!steamapps.exists()) + continue; + + QStringList manifests = + steamapps.entryList({"appmanifest_*.acf"}, QDir::Files); + for (const auto& manifestFile : manifests) { + auto manifest = parseAppManifest(steamapps.filePath(manifestFile)); + if (manifest.appId > 0 && manifest.isInstalled() && + !manifest.installDir.isEmpty()) { + QString installPath = + steamapps.filePath("common/" + manifest.installDir); + if (QDir(installPath).exists()) { + games.insert(manifest.appId, installPath); + } + } + } + } + } + + return games; +} + +QString findSteamGamePath(int appId) +{ + auto games = findSteamGames(); + return games.value(appId); +} diff --git a/libs/basic_games_native/src/steamutils.h b/libs/basic_games_native/src/steamutils.h new file mode 100644 index 0000000..5ef11b3 --- /dev/null +++ b/libs/basic_games_native/src/steamutils.h @@ -0,0 +1,14 @@ +#ifndef STEAMUTILS_H +#define STEAMUTILS_H + +#include <QHash> +#include <QString> + +// Scans all Steam library folders and returns a map of +// Steam App ID -> installation path +QHash<int, QString> findSteamGames(); + +// Find the installation path for a specific Steam app ID +QString findSteamGamePath(int appId); + +#endif // STEAMUTILS_H |
