aboutsummaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
authorSulfurNitride <SulfurNitride@users.noreply.github.com>2026-04-12 02:40:00 -0500
committerSulfurNitride <SulfurNitride@users.noreply.github.com>2026-04-12 02:40:00 -0500
commite0fb66428a9b78b4c326df8d7e30429a5b271d74 (patch)
tree0fd83fe790ecb7d5cd3cfb36535e45d1b7118098 /libs
parente985dc340097831d4e5625941bded217abaa57b8 (diff)
Remove dead plugin scaffolds and duplicate lib dirs
The *_native C++ ports were replaced by bundled Python + .py plugins in de4db79, but the source dirs were left behind without any CMake wiring — causing confusion when editing them appears to do nothing. Same story for libs/dds_header (stale duplicate of libs/preview_dds, unrelated to the actively-used libs/dds-header) and libs/game_gamebryo (superseded by libs/game_bethesda/src/gamebryo). Removes: - libs/basic_games_native - libs/form43_checker_native - libs/installer_omod_native - libs/preview_dds_native - libs/rootbuilder_native - libs/script_extender_checker_native - libs/dds_header - libs/game_gamebryo
Diffstat (limited to 'libs')
-rw-r--r--libs/basic_games_native/CMakeLists.txt5
-rw-r--r--libs/basic_games_native/src/CMakeLists.txt11
-rw-r--r--libs/basic_games_native/src/basicgameplugin.cpp448
-rw-r--r--libs/basic_games_native/src/basicgameplugin.h73
-rw-r--r--libs/basic_games_native/src/basicgamesproxy.cpp99
-rw-r--r--libs/basic_games_native/src/basicgamesproxy.h41
-rw-r--r--libs/basic_games_native/src/gamedefs.cpp1024
-rw-r--r--libs/basic_games_native/src/gamedefs.h61
-rw-r--r--libs/basic_games_native/src/steamutils.cpp178
-rw-r--r--libs/basic_games_native/src/steamutils.h14
-rw-r--r--libs/dds_header/.editorconfig9
-rw-r--r--libs/dds_header/.gitignore109
-rw-r--r--libs/dds_header/.pre-commit-config.yaml15
-rw-r--r--libs/dds_header/CMakeLists.txt5
-rw-r--r--libs/dds_header/CMakePresets.json11
-rw-r--r--libs/dds_header/LICENSE674
-rw-r--r--libs/dds_header/README.md2
-rw-r--r--libs/dds_header/src/CMakeLists.txt6
-rw-r--r--libs/dds_header/src/DDS/DDSDefinitions.py808
-rw-r--r--libs/dds_header/src/DDS/DDSFile.py220
-rw-r--r--libs/dds_header/src/DDS/__init__.py0
-rw-r--r--libs/dds_header/src/DDS/glstuff.py205
-rw-r--r--libs/dds_header/src/DDSPreview.py528
-rw-r--r--libs/dds_header/src/DDSPreview_en.ts128
-rw-r--r--libs/dds_header/vcpkg.json15
-rw-r--r--libs/form43_checker_native/CMakeLists.txt5
-rw-r--r--libs/form43_checker_native/src/CMakeLists.txt11
-rw-r--r--libs/form43_checker_native/src/form43checker.cpp131
-rw-r--r--libs/form43_checker_native/src/form43checker.h53
-rw-r--r--libs/game_gamebryo/.clang-format41
-rw-r--r--libs/game_gamebryo/.git-blame-ignore-revs1
-rw-r--r--libs/game_gamebryo/.gitattributes7
-rw-r--r--libs/game_gamebryo/.github/workflows/build.yml17
-rw-r--r--libs/game_gamebryo/.github/workflows/linting.yml16
-rw-r--r--libs/game_gamebryo/.gitignore5
-rw-r--r--libs/game_gamebryo/CMakeLists.txt12
-rw-r--r--libs/game_gamebryo/README.md5
-rw-r--r--libs/game_gamebryo/src/creation/CMakeLists.txt10
-rw-r--r--libs/game_gamebryo/src/creation/creationgameplugins.cpp183
-rw-r--r--libs/game_gamebryo/src/creation/creationgameplugins.h22
-rw-r--r--libs/game_gamebryo/src/creation/game_creation_en.ts12
-rw-r--r--libs/game_gamebryo/src/gamebryo/CMakeLists.txt10
-rw-r--r--libs/game_gamebryo/src/gamebryo/dummybsa.cpp192
-rw-r--r--libs/game_gamebryo/src/gamebryo/dummybsa.h59
-rw-r--r--libs/game_gamebryo/src/gamebryo/game_gamebryo_en.ts170
-rw-r--r--libs/game_gamebryo/src/gamebryo/gamebryobsainvalidation.cpp133
-rw-r--r--libs/game_gamebryo/src/gamebryo/gamebryobsainvalidation.h36
-rw-r--r--libs/game_gamebryo/src/gamebryo/gamebryodataarchives.cpp80
-rw-r--r--libs/game_gamebryo/src/gamebryo/gamebryodataarchives.h37
-rw-r--r--libs/game_gamebryo/src/gamebryo/gamebryogameplugins.cpp248
-rw-r--r--libs/game_gamebryo/src/gamebryo/gamebryogameplugins.h38
-rw-r--r--libs/game_gamebryo/src/gamebryo/gamebryolocalsavegames.cpp142
-rw-r--r--libs/game_gamebryo/src/gamebryo/gamebryolocalsavegames.h54
-rw-r--r--libs/game_gamebryo/src/gamebryo/gamebryomoddatachecker.cpp76
-rw-r--r--libs/game_gamebryo/src/gamebryo/gamebryomoddatachecker.h46
-rw-r--r--libs/game_gamebryo/src/gamebryo/gamebryomoddatacontent.cpp116
-rw-r--r--libs/game_gamebryo/src/gamebryo/gamebryomoddatacontent.h75
-rw-r--r--libs/game_gamebryo/src/gamebryo/gamebryosavegame.cpp606
-rw-r--r--libs/game_gamebryo/src/gamebryo/gamebryosavegame.h240
-rw-r--r--libs/game_gamebryo/src/gamebryo/gamebryosavegameinfo.cpp100
-rw-r--r--libs/game_gamebryo/src/gamebryo/gamebryosavegameinfo.h23
-rw-r--r--libs/game_gamebryo/src/gamebryo/gamebryosavegameinfowidget.cpp199
-rw-r--r--libs/game_gamebryo/src/gamebryo/gamebryosavegameinfowidget.h30
-rw-r--r--libs/game_gamebryo/src/gamebryo/gamebryosavegameinfowidget.ui209
-rw-r--r--libs/game_gamebryo/src/gamebryo/gamebryoscriptextender.cpp46
-rw-r--r--libs/game_gamebryo/src/gamebryo/gamebryoscriptextender.h31
-rw-r--r--libs/game_gamebryo/src/gamebryo/gamebryounmanagedmods.cpp51
-rw-r--r--libs/game_gamebryo/src/gamebryo/gamebryounmanagedmods.h26
-rw-r--r--libs/game_gamebryo/src/gamebryo/gamegamebryo.cpp478
-rw-r--r--libs/game_gamebryo/src/gamebryo/gamegamebryo.h151
-rw-r--r--libs/game_gamebryo/src/gamebryo/vdf_parser.h739
-rw-r--r--libs/installer_omod_native/CMakeLists.txt5
-rw-r--r--libs/installer_omod_native/src/CMakeLists.txt14
-rw-r--r--libs/installer_omod_native/src/omodinstaller.cpp472
-rw-r--r--libs/installer_omod_native/src/omodinstaller.h101
-rw-r--r--libs/preview_dds_native/CMakeLists.txt5
-rw-r--r--libs/preview_dds_native/src/CMakeLists.txt17
-rw-r--r--libs/preview_dds_native/src/ddsfile.cpp132
-rw-r--r--libs/preview_dds_native/src/ddsfile.h47
-rw-r--r--libs/preview_dds_native/src/ddsformat.cpp619
-rw-r--r--libs/preview_dds_native/src/ddsformat.h200
-rw-r--r--libs/preview_dds_native/src/ddspreview.cpp126
-rw-r--r--libs/preview_dds_native/src/ddspreview.h44
-rw-r--r--libs/preview_dds_native/src/ddswidget.cpp252
-rw-r--r--libs/preview_dds_native/src/ddswidget.h36
-rw-r--r--libs/rootbuilder_native/CMakeLists.txt3
-rw-r--r--libs/rootbuilder_native/src/CMakeLists.txt18
-rw-r--r--libs/rootbuilder_native/src/rootbuilder.cpp674
-rw-r--r--libs/rootbuilder_native/src/rootbuilder.h79
-rw-r--r--libs/script_extender_checker_native/CMakeLists.txt5
-rw-r--r--libs/script_extender_checker_native/src/CMakeLists.txt11
-rw-r--r--libs/script_extender_checker_native/src/scriptextenderchecker.cpp363
-rw-r--r--libs/script_extender_checker_native/src/scriptextenderchecker.h72
93 files changed, 0 insertions, 12956 deletions
diff --git a/libs/basic_games_native/CMakeLists.txt b/libs/basic_games_native/CMakeLists.txt
deleted file mode 100644
index 58bad3b..0000000
--- a/libs/basic_games_native/CMakeLists.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-cmake_minimum_required(VERSION 3.16)
-
-project(basic_games_native)
-
-add_subdirectory(src)
diff --git a/libs/basic_games_native/src/CMakeLists.txt b/libs/basic_games_native/src/CMakeLists.txt
deleted file mode 100644
index 136cdcf..0000000
--- a/libs/basic_games_native/src/CMakeLists.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-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
deleted file mode 100644
index 5f41f3a..0000000
--- a/libs/basic_games_native/src/basicgameplugin.cpp
+++ /dev/null
@@ -1,448 +0,0 @@
-#include "basicgameplugin.h"
-#include "steamutils.h"
-
-#include <uibase/utility.h>
-#include <uibase/versioninfo.h>
-
-#include <uibase/log.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()) {
- MOBase::log::debug("detectGame: '{}' found via Steam appid {} at '{}'",
- m_def.gameName, steamId, path);
- 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 MOBase::iconForExecutable(
- gameDirectory().absoluteFilePath(binaryName()));
-}
-
-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
-{
- // Primary check: binary at game root.
- // Use QFileInfo with the absolute path directly — QDir::exists() with
- // relative paths containing subdirectories can have edge-case issues on
- // some filesystems / Qt builds.
- const QString absPath = dir.absoluteFilePath(m_def.binaryName);
- if (QFileInfo::exists(absPath)) {
- return true;
- }
-
- // Case-insensitive fallback: Linux filesystems are case-sensitive but
- // Steam sometimes creates directories with different casing than expected.
- // Walk the path components and try case-insensitive matching.
- if (m_def.binaryName.contains('/')) {
- QStringList parts = QString(m_def.binaryName).replace('\\', '/').split('/');
- QString current = dir.absolutePath();
- bool found = true;
- for (const QString& part : parts) {
- QDir d(current);
- if (d.exists(part)) {
- current = d.absoluteFilePath(part);
- continue;
- }
- // Try case-insensitive match
- const QStringList entries =
- d.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
- bool matched = false;
- for (const QString& entry : entries) {
- if (entry.compare(part, Qt::CaseInsensitive) == 0) {
- current = d.absoluteFilePath(entry);
- matched = true;
- break;
- }
- }
- if (!matched) {
- found = false;
- break;
- }
- }
- if (found) {
- MOBase::log::debug("looksValid: found '{}' via case-insensitive match in '{}'",
- m_def.binaryName, dir.absolutePath());
- return true;
- }
- }
-
- // Fallback: check if the data directory exists relative to this path.
- // Some UE5 games (e.g. Oblivion Remastered) have their root exe nested
- // under a subdirectory even though the appmanifest installdir points one
- // level up. If the unique data path resolves, it's the right directory.
- if (!m_def.dataDirectory.isEmpty()) {
- QString dataDir = m_def.dataDirectory;
- dataDir.replace('\\', '/');
- // Only handle %GAME_PATH% here; other vars need context we don't have
- if (!dataDir.contains('%') || dataDir.startsWith("%GAME_PATH%")) {
- dataDir.replace("%GAME_PATH%", dir.absolutePath());
- if (!dataDir.contains('%') && QDir(dataDir).exists())
- return true;
- }
- }
-
- MOBase::log::debug("looksValid: '{}' not found in '{}'", m_def.binaryName,
- dir.absolutePath());
- return false;
-}
-
-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
deleted file mode 100644
index 17f4ec9..0000000
--- a/libs/basic_games_native/src/basicgameplugin.h
+++ /dev/null
@@ -1,73 +0,0 @@
-#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
deleted file mode 100644
index f7efefd..0000000
--- a/libs/basic_games_native/src/basicgamesproxy.cpp
+++ /dev/null
@@ -1,99 +0,0 @@
-#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
deleted file mode 100644
index 2c2858f..0000000
--- a/libs/basic_games_native/src/basicgamesproxy.h
+++ /dev/null
@@ -1,41 +0,0 @@
-#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
deleted file mode 100644
index d626d23..0000000
--- a/libs/basic_games_native/src/gamedefs.cpp
+++ /dev/null
@@ -1,1024 +0,0 @@
-#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
deleted file mode 100644
index bda93eb..0000000
--- a/libs/basic_games_native/src/gamedefs.h
+++ /dev/null
@@ -1,61 +0,0 @@
-#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
deleted file mode 100644
index ffae0f1..0000000
--- a/libs/basic_games_native/src/steamutils.cpp
+++ /dev/null
@@ -1,178 +0,0 @@
-#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",
- };
-
- // When Steam launches an app (e.g. in game mode) it sets
- // STEAM_COMPAT_CLIENT_INSTALL_PATH to its own root. Add that as a
- // candidate so we find the right installation even if $HOME resolves
- // differently inside the gamescope session.
- const QString steamEnvPath =
- QString::fromLocal8Bit(qgetenv("STEAM_COMPAT_CLIENT_INSTALL_PATH"));
- if (!steamEnvPath.isEmpty()) {
- candidates.prepend(steamEnvPath);
- }
-
- 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)) {
- // Canonicalize before dedup so /home/ and /var/home/ paths compare equal
- QString canonical = QFileInfo(path).canonicalFilePath();
- if (canonical.isEmpty())
- canonical = path;
- if (!folders.contains(canonical)) {
- folders.append(canonical);
- }
- }
- }
-
- 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;
-}
-
-// Cache: built once per process, cleared on explicit refresh.
-static QHash<int, QString> s_steamGamesCache;
-static bool s_steamGamesCacheValid = false;
-
-QHash<int, QString> findSteamGames()
-{
- if (s_steamGamesCacheValid)
- return s_steamGamesCache;
-
- 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);
- }
- }
- }
- }
- }
-
- s_steamGamesCache = games;
- s_steamGamesCacheValid = true;
- return games;
-}
-
-QString findSteamGamePath(int appId)
-{
- return findSteamGames().value(appId);
-}
diff --git a/libs/basic_games_native/src/steamutils.h b/libs/basic_games_native/src/steamutils.h
deleted file mode 100644
index 5ef11b3..0000000
--- a/libs/basic_games_native/src/steamutils.h
+++ /dev/null
@@ -1,14 +0,0 @@
-#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
diff --git a/libs/dds_header/.editorconfig b/libs/dds_header/.editorconfig
deleted file mode 100644
index f64592a..0000000
--- a/libs/dds_header/.editorconfig
+++ /dev/null
@@ -1,9 +0,0 @@
-root = true
-
-[*.py]
-indent_style = space
-indent_size = 4
-
-[{CMakeLists.txt, *.cmake}]
-indent_style = space
-indent_size = 4
diff --git a/libs/dds_header/.gitignore b/libs/dds_header/.gitignore
deleted file mode 100644
index 5afe969..0000000
--- a/libs/dds_header/.gitignore
+++ /dev/null
@@ -1,109 +0,0 @@
-# Mod Organizer Umbrella stuff
-/msbuild.log
-/*std*.log
-/*build
-
-# Byte-compiled / optimized / DLL files
-__pycache__/
-*.py[cod]
-*$py.class
-
-# C extensions
-*.so
-
-# Distribution / packaging
-.Python
-build/
-develop-eggs/
-dist/
-downloads/
-eggs/
-.eggs/
-lib/
-lib64/
-parts/
-sdist/
-var/
-wheels/
-*.egg-info/
-.installed.cfg
-*.egg
-MANIFEST
-
-# PyInstaller
-# Usually these files are written by a python script from a template
-# before PyInstaller builds the exe, so as to inject date/other infos into it.
-*.manifest
-*.spec
-
-# Installer logs
-pip-log.txt
-pip-delete-this-directory.txt
-
-# Unit test / coverage reports
-htmlcov/
-.tox/
-.coverage
-.coverage.*
-.cache
-nosetests.xml
-coverage.xml
-*.cover
-.hypothesis/
-.pytest_cache/
-
-# Translations
-*.mo
-*.pot
-
-# Django stuff:
-*.log
-local_settings.py
-db.sqlite3
-
-# Flask stuff:
-instance/
-.webassets-cache
-
-# Scrapy stuff:
-.scrapy
-
-# Sphinx documentation
-docs/_build/
-
-# PyBuilder
-target/
-
-# Jupyter Notebook
-.ipynb_checkpoints
-
-# pyenv
-.python-version
-
-# celery beat schedule file
-celerybeat-schedule
-
-# SageMath parsed files
-*.sage.py
-
-# Environments
-.env
-.venv
-env/
-venv/
-ENV/
-env.bak/
-venv.bak/
-
-# Spyder project settings
-.spyderproject
-.spyproject
-
-# Rope project settings
-.ropeproject
-
-# mkdocs documentation
-/site
-
-# mypy
-.mypy_cache/
diff --git a/libs/dds_header/.pre-commit-config.yaml b/libs/dds_header/.pre-commit-config.yaml
deleted file mode 100644
index 4b7d480..0000000
--- a/libs/dds_header/.pre-commit-config.yaml
+++ /dev/null
@@ -1,15 +0,0 @@
-repos:
- - repo: https://github.com/pre-commit/pre-commit-hooks
- rev: v5.0.0
- hooks:
- - id: trailing-whitespace
- - id: end-of-file-fixer
- - id: check-merge-conflict
- - id: check-case-conflict
-
-ci:
- autofix_commit_msg: "[pre-commit.ci] Auto fixes from pre-commit.com hooks."
- autofix_prs: true
- autoupdate_commit_msg: "[pre-commit.ci] Pre-commit autoupdate."
- autoupdate_schedule: quarterly
- submodules: false
diff --git a/libs/dds_header/CMakeLists.txt b/libs/dds_header/CMakeLists.txt
deleted file mode 100644
index a9ad8a8..0000000
--- a/libs/dds_header/CMakeLists.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-cmake_minimum_required(VERSION 3.16)
-
-project(DDSPreview LANGUAGES NONE)
-
-add_subdirectory(src)
diff --git a/libs/dds_header/CMakePresets.json b/libs/dds_header/CMakePresets.json
deleted file mode 100644
index 7726456..0000000
--- a/libs/dds_header/CMakePresets.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "configurePresets": [
- {
- "binaryDir": "${sourceDir}/vsbuild",
- "toolchainFile": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake",
- "generator": "Visual Studio 17 2022",
- "name": "vs2022-windows"
- }
- ],
- "version": 4
-}
diff --git a/libs/dds_header/LICENSE b/libs/dds_header/LICENSE
deleted file mode 100644
index f288702..0000000
--- a/libs/dds_header/LICENSE
+++ /dev/null
@@ -1,674 +0,0 @@
- GNU GENERAL PUBLIC LICENSE
- Version 3, 29 June 2007
-
- Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
- Preamble
-
- The GNU General Public License is a free, copyleft license for
-software and other kinds of works.
-
- The licenses for most software and other practical works are designed
-to take away your freedom to share and change the works. By contrast,
-the GNU General Public License is intended to guarantee your freedom to
-share and change all versions of a program--to make sure it remains free
-software for all its users. We, the Free Software Foundation, use the
-GNU General Public License for most of our software; it applies also to
-any other work released this way by its authors. You can apply it to
-your programs, too.
-
- When we speak of free software, we are referring to freedom, not
-price. Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-them if you wish), that you receive source code or can get it if you
-want it, that you can change the software or use pieces of it in new
-free programs, and that you know you can do these things.
-
- To protect your rights, we need to prevent others from denying you
-these rights or asking you to surrender the rights. Therefore, you have
-certain responsibilities if you distribute copies of the software, or if
-you modify it: responsibilities to respect the freedom of others.
-
- For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must pass on to the recipients the same
-freedoms that you received. You must make sure that they, too, receive
-or can get the source code. And you must show them these terms so they
-know their rights.
-
- Developers that use the GNU GPL protect your rights with two steps:
-(1) assert copyright on the software, and (2) offer you this License
-giving you legal permission to copy, distribute and/or modify it.
-
- For the developers' and authors' protection, the GPL clearly explains
-that there is no warranty for this free software. For both users' and
-authors' sake, the GPL requires that modified versions be marked as
-changed, so that their problems will not be attributed erroneously to
-authors of previous versions.
-
- Some devices are designed to deny users access to install or run
-modified versions of the software inside them, although the manufacturer
-can do so. This is fundamentally incompatible with the aim of
-protecting users' freedom to change the software. The systematic
-pattern of such abuse occurs in the area of products for individuals to
-use, which is precisely where it is most unacceptable. Therefore, we
-have designed this version of the GPL to prohibit the practice for those
-products. If such problems arise substantially in other domains, we
-stand ready to extend this provision to those domains in future versions
-of the GPL, as needed to protect the freedom of users.
-
- Finally, every program is threatened constantly by software patents.
-States should not allow patents to restrict development and use of
-software on general-purpose computers, but in those that do, we wish to
-avoid the special danger that patents applied to a free program could
-make it effectively proprietary. To prevent this, the GPL assures that
-patents cannot be used to render the program non-free.
-
- The precise terms and conditions for copying, distribution and
-modification follow.
-
- TERMS AND CONDITIONS
-
- 0. Definitions.
-
- "This License" refers to version 3 of the GNU General Public License.
-
- "Copyright" also means copyright-like laws that apply to other kinds of
-works, such as semiconductor masks.
-
- "The Program" refers to any copyrightable work licensed under this
-License. Each licensee is addressed as "you". "Licensees" and
-"recipients" may be individuals or organizations.
-
- To "modify" a work means to copy from or adapt all or part of the work
-in a fashion requiring copyright permission, other than the making of an
-exact copy. The resulting work is called a "modified version" of the
-earlier work or a work "based on" the earlier work.
-
- A "covered work" means either the unmodified Program or a work based
-on the Program.
-
- To "propagate" a work means to do anything with it that, without
-permission, would make you directly or secondarily liable for
-infringement under applicable copyright law, except executing it on a
-computer or modifying a private copy. Propagation includes copying,
-distribution (with or without modification), making available to the
-public, and in some countries other activities as well.
-
- To "convey" a work means any kind of propagation that enables other
-parties to make or receive copies. Mere interaction with a user through
-a computer network, with no transfer of a copy, is not conveying.
-
- An interactive user interface displays "Appropriate Legal Notices"
-to the extent that it includes a convenient and prominently visible
-feature that (1) displays an appropriate copyright notice, and (2)
-tells the user that there is no warranty for the work (except to the
-extent that warranties are provided), that licensees may convey the
-work under this License, and how to view a copy of this License. If
-the interface presents a list of user commands or options, such as a
-menu, a prominent item in the list meets this criterion.
-
- 1. Source Code.
-
- The "source code" for a work means the preferred form of the work
-for making modifications to it. "Object code" means any non-source
-form of a work.
-
- A "Standard Interface" means an interface that either is an official
-standard defined by a recognized standards body, or, in the case of
-interfaces specified for a particular programming language, one that
-is widely used among developers working in that language.
-
- The "System Libraries" of an executable work include anything, other
-than the work as a whole, that (a) is included in the normal form of
-packaging a Major Component, but which is not part of that Major
-Component, and (b) serves only to enable use of the work with that
-Major Component, or to implement a Standard Interface for which an
-implementation is available to the public in source code form. A
-"Major Component", in this context, means a major essential component
-(kernel, window system, and so on) of the specific operating system
-(if any) on which the executable work runs, or a compiler used to
-produce the work, or an object code interpreter used to run it.
-
- The "Corresponding Source" for a work in object code form means all
-the source code needed to generate, install, and (for an executable
-work) run the object code and to modify the work, including scripts to
-control those activities. However, it does not include the work's
-System Libraries, or general-purpose tools or generally available free
-programs which are used unmodified in performing those activities but
-which are not part of the work. For example, Corresponding Source
-includes interface definition files associated with source files for
-the work, and the source code for shared libraries and dynamically
-linked subprograms that the work is specifically designed to require,
-such as by intimate data communication or control flow between those
-subprograms and other parts of the work.
-
- The Corresponding Source need not include anything that users
-can regenerate automatically from other parts of the Corresponding
-Source.
-
- The Corresponding Source for a work in source code form is that
-same work.
-
- 2. Basic Permissions.
-
- All rights granted under this License are granted for the term of
-copyright on the Program, and are irrevocable provided the stated
-conditions are met. This License explicitly affirms your unlimited
-permission to run the unmodified Program. The output from running a
-covered work is covered by this License only if the output, given its
-content, constitutes a covered work. This License acknowledges your
-rights of fair use or other equivalent, as provided by copyright law.
-
- You may make, run and propagate covered works that you do not
-convey, without conditions so long as your license otherwise remains
-in force. You may convey covered works to others for the sole purpose
-of having them make modifications exclusively for you, or provide you
-with facilities for running those works, provided that you comply with
-the terms of this License in conveying all material for which you do
-not control copyright. Those thus making or running the covered works
-for you must do so exclusively on your behalf, under your direction
-and control, on terms that prohibit them from making any copies of
-your copyrighted material outside their relationship with you.
-
- Conveying under any other circumstances is permitted solely under
-the conditions stated below. Sublicensing is not allowed; section 10
-makes it unnecessary.
-
- 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
-
- No covered work shall be deemed part of an effective technological
-measure under any applicable law fulfilling obligations under article
-11 of the WIPO copyright treaty adopted on 20 December 1996, or
-similar laws prohibiting or restricting circumvention of such
-measures.
-
- When you convey a covered work, you waive any legal power to forbid
-circumvention of technological measures to the extent such circumvention
-is effected by exercising rights under this License with respect to
-the covered work, and you disclaim any intention to limit operation or
-modification of the work as a means of enforcing, against the work's
-users, your or third parties' legal rights to forbid circumvention of
-technological measures.
-
- 4. Conveying Verbatim Copies.
-
- You may convey verbatim copies of the Program's source code as you
-receive it, in any medium, provided that you conspicuously and
-appropriately publish on each copy an appropriate copyright notice;
-keep intact all notices stating that this License and any
-non-permissive terms added in accord with section 7 apply to the code;
-keep intact all notices of the absence of any warranty; and give all
-recipients a copy of this License along with the Program.
-
- You may charge any price or no price for each copy that you convey,
-and you may offer support or warranty protection for a fee.
-
- 5. Conveying Modified Source Versions.
-
- You may convey a work based on the Program, or the modifications to
-produce it from the Program, in the form of source code under the
-terms of section 4, provided that you also meet all of these conditions:
-
- a) The work must carry prominent notices stating that you modified
- it, and giving a relevant date.
-
- b) The work must carry prominent notices stating that it is
- released under this License and any conditions added under section
- 7. This requirement modifies the requirement in section 4 to
- "keep intact all notices".
-
- c) You must license the entire work, as a whole, under this
- License to anyone who comes into possession of a copy. This
- License will therefore apply, along with any applicable section 7
- additional terms, to the whole of the work, and all its parts,
- regardless of how they are packaged. This License gives no
- permission to license the work in any other way, but it does not
- invalidate such permission if you have separately received it.
-
- d) If the work has interactive user interfaces, each must display
- Appropriate Legal Notices; however, if the Program has interactive
- interfaces that do not display Appropriate Legal Notices, your
- work need not make them do so.
-
- A compilation of a covered work with other separate and independent
-works, which are not by their nature extensions of the covered work,
-and which are not combined with it such as to form a larger program,
-in or on a volume of a storage or distribution medium, is called an
-"aggregate" if the compilation and its resulting copyright are not
-used to limit the access or legal rights of the compilation's users
-beyond what the individual works permit. Inclusion of a covered work
-in an aggregate does not cause this License to apply to the other
-parts of the aggregate.
-
- 6. Conveying Non-Source Forms.
-
- You may convey a covered work in object code form under the terms
-of sections 4 and 5, provided that you also convey the
-machine-readable Corresponding Source under the terms of this License,
-in one of these ways:
-
- a) Convey the object code in, or embodied in, a physical product
- (including a physical distribution medium), accompanied by the
- Corresponding Source fixed on a durable physical medium
- customarily used for software interchange.
-
- b) Convey the object code in, or embodied in, a physical product
- (including a physical distribution medium), accompanied by a
- written offer, valid for at least three years and valid for as
- long as you offer spare parts or customer support for that product
- model, to give anyone who possesses the object code either (1) a
- copy of the Corresponding Source for all the software in the
- product that is covered by this License, on a durable physical
- medium customarily used for software interchange, for a price no
- more than your reasonable cost of physically performing this
- conveying of source, or (2) access to copy the
- Corresponding Source from a network server at no charge.
-
- c) Convey individual copies of the object code with a copy of the
- written offer to provide the Corresponding Source. This
- alternative is allowed only occasionally and noncommercially, and
- only if you received the object code with such an offer, in accord
- with subsection 6b.
-
- d) Convey the object code by offering access from a designated
- place (gratis or for a charge), and offer equivalent access to the
- Corresponding Source in the same way through the same place at no
- further charge. You need not require recipients to copy the
- Corresponding Source along with the object code. If the place to
- copy the object code is a network server, the Corresponding Source
- may be on a different server (operated by you or a third party)
- that supports equivalent copying facilities, provided you maintain
- clear directions next to the object code saying where to find the
- Corresponding Source. Regardless of what server hosts the
- Corresponding Source, you remain obligated to ensure that it is
- available for as long as needed to satisfy these requirements.
-
- e) Convey the object code using peer-to-peer transmission, provided
- you inform other peers where the object code and Corresponding
- Source of the work are being offered to the general public at no
- charge under subsection 6d.
-
- A separable portion of the object code, whose source code is excluded
-from the Corresponding Source as a System Library, need not be
-included in conveying the object code work.
-
- A "User Product" is either (1) a "consumer product", which means any
-tangible personal property which is normally used for personal, family,
-or household purposes, or (2) anything designed or sold for incorporation
-into a dwelling. In determining whether a product is a consumer product,
-doubtful cases shall be resolved in favor of coverage. For a particular
-product received by a particular user, "normally used" refers to a
-typical or common use of that class of product, regardless of the status
-of the particular user or of the way in which the particular user
-actually uses, or expects or is expected to use, the product. A product
-is a consumer product regardless of whether the product has substantial
-commercial, industrial or non-consumer uses, unless such uses represent
-the only significant mode of use of the product.
-
- "Installation Information" for a User Product means any methods,
-procedures, authorization keys, or other information required to install
-and execute modified versions of a covered work in that User Product from
-a modified version of its Corresponding Source. The information must
-suffice to ensure that the continued functioning of the modified object
-code is in no case prevented or interfered with solely because
-modification has been made.
-
- If you convey an object code work under this section in, or with, or
-specifically for use in, a User Product, and the conveying occurs as
-part of a transaction in which the right of possession and use of the
-User Product is transferred to the recipient in perpetuity or for a
-fixed term (regardless of how the transaction is characterized), the
-Corresponding Source conveyed under this section must be accompanied
-by the Installation Information. But this requirement does not apply
-if neither you nor any third party retains the ability to install
-modified object code on the User Product (for example, the work has
-been installed in ROM).
-
- The requirement to provide Installation Information does not include a
-requirement to continue to provide support service, warranty, or updates
-for a work that has been modified or installed by the recipient, or for
-the User Product in which it has been modified or installed. Access to a
-network may be denied when the modification itself materially and
-adversely affects the operation of the network or violates the rules and
-protocols for communication across the network.
-
- Corresponding Source conveyed, and Installation Information provided,
-in accord with this section must be in a format that is publicly
-documented (and with an implementation available to the public in
-source code form), and must require no special password or key for
-unpacking, reading or copying.
-
- 7. Additional Terms.
-
- "Additional permissions" are terms that supplement the terms of this
-License by making exceptions from one or more of its conditions.
-Additional permissions that are applicable to the entire Program shall
-be treated as though they were included in this License, to the extent
-that they are valid under applicable law. If additional permissions
-apply only to part of the Program, that part may be used separately
-under those permissions, but the entire Program remains governed by
-this License without regard to the additional permissions.
-
- When you convey a copy of a covered work, you may at your option
-remove any additional permissions from that copy, or from any part of
-it. (Additional permissions may be written to require their own
-removal in certain cases when you modify the work.) You may place
-additional permissions on material, added by you to a covered work,
-for which you have or can give appropriate copyright permission.
-
- Notwithstanding any other provision of this License, for material you
-add to a covered work, you may (if authorized by the copyright holders of
-that material) supplement the terms of this License with terms:
-
- a) Disclaiming warranty or limiting liability differently from the
- terms of sections 15 and 16 of this License; or
-
- b) Requiring preservation of specified reasonable legal notices or
- author attributions in that material or in the Appropriate Legal
- Notices displayed by works containing it; or
-
- c) Prohibiting misrepresentation of the origin of that material, or
- requiring that modified versions of such material be marked in
- reasonable ways as different from the original version; or
-
- d) Limiting the use for publicity purposes of names of licensors or
- authors of the material; or
-
- e) Declining to grant rights under trademark law for use of some
- trade names, trademarks, or service marks; or
-
- f) Requiring indemnification of licensors and authors of that
- material by anyone who conveys the material (or modified versions of
- it) with contractual assumptions of liability to the recipient, for
- any liability that these contractual assumptions directly impose on
- those licensors and authors.
-
- All other non-permissive additional terms are considered "further
-restrictions" within the meaning of section 10. If the Program as you
-received it, or any part of it, contains a notice stating that it is
-governed by this License along with a term that is a further
-restriction, you may remove that term. If a license document contains
-a further restriction but permits relicensing or conveying under this
-License, you may add to a covered work material governed by the terms
-of that license document, provided that the further restriction does
-not survive such relicensing or conveying.
-
- If you add terms to a covered work in accord with this section, you
-must place, in the relevant source files, a statement of the
-additional terms that apply to those files, or a notice indicating
-where to find the applicable terms.
-
- Additional terms, permissive or non-permissive, may be stated in the
-form of a separately written license, or stated as exceptions;
-the above requirements apply either way.
-
- 8. Termination.
-
- You may not propagate or modify a covered work except as expressly
-provided under this License. Any attempt otherwise to propagate or
-modify it is void, and will automatically terminate your rights under
-this License (including any patent licenses granted under the third
-paragraph of section 11).
-
- However, if you cease all violation of this License, then your
-license from a particular copyright holder is reinstated (a)
-provisionally, unless and until the copyright holder explicitly and
-finally terminates your license, and (b) permanently, if the copyright
-holder fails to notify you of the violation by some reasonable means
-prior to 60 days after the cessation.
-
- Moreover, your license from a particular copyright holder is
-reinstated permanently if the copyright holder notifies you of the
-violation by some reasonable means, this is the first time you have
-received notice of violation of this License (for any work) from that
-copyright holder, and you cure the violation prior to 30 days after
-your receipt of the notice.
-
- Termination of your rights under this section does not terminate the
-licenses of parties who have received copies or rights from you under
-this License. If your rights have been terminated and not permanently
-reinstated, you do not qualify to receive new licenses for the same
-material under section 10.
-
- 9. Acceptance Not Required for Having Copies.
-
- You are not required to accept this License in order to receive or
-run a copy of the Program. Ancillary propagation of a covered work
-occurring solely as a consequence of using peer-to-peer transmission
-to receive a copy likewise does not require acceptance. However,
-nothing other than this License grants you permission to propagate or
-modify any covered work. These actions infringe copyright if you do
-not accept this License. Therefore, by modifying or propagating a
-covered work, you indicate your acceptance of this License to do so.
-
- 10. Automatic Licensing of Downstream Recipients.
-
- Each time you convey a covered work, the recipient automatically
-receives a license from the original licensors, to run, modify and
-propagate that work, subject to this License. You are not responsible
-for enforcing compliance by third parties with this License.
-
- An "entity transaction" is a transaction transferring control of an
-organization, or substantially all assets of one, or subdividing an
-organization, or merging organizations. If propagation of a covered
-work results from an entity transaction, each party to that
-transaction who receives a copy of the work also receives whatever
-licenses to the work the party's predecessor in interest had or could
-give under the previous paragraph, plus a right to possession of the
-Corresponding Source of the work from the predecessor in interest, if
-the predecessor has it or can get it with reasonable efforts.
-
- You may not impose any further restrictions on the exercise of the
-rights granted or affirmed under this License. For example, you may
-not impose a license fee, royalty, or other charge for exercise of
-rights granted under this License, and you may not initiate litigation
-(including a cross-claim or counterclaim in a lawsuit) alleging that
-any patent claim is infringed by making, using, selling, offering for
-sale, or importing the Program or any portion of it.
-
- 11. Patents.
-
- A "contributor" is a copyright holder who authorizes use under this
-License of the Program or a work on which the Program is based. The
-work thus licensed is called the contributor's "contributor version".
-
- A contributor's "essential patent claims" are all patent claims
-owned or controlled by the contributor, whether already acquired or
-hereafter acquired, that would be infringed by some manner, permitted
-by this License, of making, using, or selling its contributor version,
-but do not include claims that would be infringed only as a
-consequence of further modification of the contributor version. For
-purposes of this definition, "control" includes the right to grant
-patent sublicenses in a manner consistent with the requirements of
-this License.
-
- Each contributor grants you a non-exclusive, worldwide, royalty-free
-patent license under the contributor's essential patent claims, to
-make, use, sell, offer for sale, import and otherwise run, modify and
-propagate the contents of its contributor version.
-
- In the following three paragraphs, a "patent license" is any express
-agreement or commitment, however denominated, not to enforce a patent
-(such as an express permission to practice a patent or covenant not to
-sue for patent infringement). To "grant" such a patent license to a
-party means to make such an agreement or commitment not to enforce a
-patent against the party.
-
- If you convey a covered work, knowingly relying on a patent license,
-and the Corresponding Source of the work is not available for anyone
-to copy, free of charge and under the terms of this License, through a
-publicly available network server or other readily accessible means,
-then you must either (1) cause the Corresponding Source to be so
-available, or (2) arrange to deprive yourself of the benefit of the
-patent license for this particular work, or (3) arrange, in a manner
-consistent with the requirements of this License, to extend the patent
-license to downstream recipients. "Knowingly relying" means you have
-actual knowledge that, but for the patent license, your conveying the
-covered work in a country, or your recipient's use of the covered work
-in a country, would infringe one or more identifiable patents in that
-country that you have reason to believe are valid.
-
- If, pursuant to or in connection with a single transaction or
-arrangement, you convey, or propagate by procuring conveyance of, a
-covered work, and grant a patent license to some of the parties
-receiving the covered work authorizing them to use, propagate, modify
-or convey a specific copy of the covered work, then the patent license
-you grant is automatically extended to all recipients of the covered
-work and works based on it.
-
- A patent license is "discriminatory" if it does not include within
-the scope of its coverage, prohibits the exercise of, or is
-conditioned on the non-exercise of one or more of the rights that are
-specifically granted under this License. You may not convey a covered
-work if you are a party to an arrangement with a third party that is
-in the business of distributing software, under which you make payment
-to the third party based on the extent of your activity of conveying
-the work, and under which the third party grants, to any of the
-parties who would receive the covered work from you, a discriminatory
-patent license (a) in connection with copies of the covered work
-conveyed by you (or copies made from those copies), or (b) primarily
-for and in connection with specific products or compilations that
-contain the covered work, unless you entered into that arrangement,
-or that patent license was granted, prior to 28 March 2007.
-
- Nothing in this License shall be construed as excluding or limiting
-any implied license or other defenses to infringement that may
-otherwise be available to you under applicable patent law.
-
- 12. No Surrender of Others' Freedom.
-
- If conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License. If you cannot convey a
-covered work so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you may
-not convey it at all. For example, if you agree to terms that obligate you
-to collect a royalty for further conveying from those to whom you convey
-the Program, the only way you could satisfy both those terms and this
-License would be to refrain entirely from conveying the Program.
-
- 13. Use with the GNU Affero General Public License.
-
- Notwithstanding any other provision of this License, you have
-permission to link or combine any covered work with a work licensed
-under version 3 of the GNU Affero General Public License into a single
-combined work, and to convey the resulting work. The terms of this
-License will continue to apply to the part which is the covered work,
-but the special requirements of the GNU Affero General Public License,
-section 13, concerning interaction through a network will apply to the
-combination as such.
-
- 14. Revised Versions of this License.
-
- The Free Software Foundation may publish revised and/or new versions of
-the GNU General Public License from time to time. Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
- Each version is given a distinguishing version number. If the
-Program specifies that a certain numbered version of the GNU General
-Public License "or any later version" applies to it, you have the
-option of following the terms and conditions either of that numbered
-version or of any later version published by the Free Software
-Foundation. If the Program does not specify a version number of the
-GNU General Public License, you may choose any version ever published
-by the Free Software Foundation.
-
- If the Program specifies that a proxy can decide which future
-versions of the GNU General Public License can be used, that proxy's
-public statement of acceptance of a version permanently authorizes you
-to choose that version for the Program.
-
- Later license versions may give you additional or different
-permissions. However, no additional obligations are imposed on any
-author or copyright holder as a result of your choosing to follow a
-later version.
-
- 15. Disclaimer of Warranty.
-
- THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
-APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
-HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
-OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
-THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
-IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
-ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
- 16. Limitation of Liability.
-
- IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
-THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
-GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
-USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
-DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
-PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
-EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGES.
-
- 17. Interpretation of Sections 15 and 16.
-
- If the disclaimer of warranty and limitation of liability provided
-above cannot be given local legal effect according to their terms,
-reviewing courts shall apply local law that most closely approximates
-an absolute waiver of all civil liability in connection with the
-Program, unless a warranty or assumption of liability accompanies a
-copy of the Program in return for a fee.
-
- END OF TERMS AND CONDITIONS
-
- How to Apply These Terms to Your New Programs
-
- If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
- To do so, attach the following notices to the program. It is safest
-to attach them to the start of each source file to most effectively
-state the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
- <one line to give the program's name and a brief idea of what it does.>
- Copyright (C) <year> <name of author>
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-Also add information on how to contact you by electronic and paper mail.
-
- If the program does terminal interaction, make it output a short
-notice like this when it starts in an interactive mode:
-
- <program> Copyright (C) <year> <name of author>
- This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
- This is free software, and you are welcome to redistribute it
- under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License. Of course, your program's commands
-might be different; for a GUI interface, you would use an "about box".
-
- You should also get your employer (if you work as a programmer) or school,
-if any, to sign a "copyright disclaimer" for the program, if necessary.
-For more information on this, and how to apply and follow the GNU GPL, see
-<https://www.gnu.org/licenses/>.
-
- The GNU General Public License does not permit incorporating your program
-into proprietary programs. If your program is a subroutine library, you
-may consider it more useful to permit linking proprietary applications with
-the library. If this is what you want to do, use the GNU Lesser General
-Public License instead of this License. But first, please read
-<https://www.gnu.org/licenses/why-not-lgpl.html>.
diff --git a/libs/dds_header/README.md b/libs/dds_header/README.md
deleted file mode 100644
index 02ff9a7..0000000
--- a/libs/dds_header/README.md
+++ /dev/null
@@ -1,2 +0,0 @@
-# modorganizer-preview_dds
-DDS preview plugin for Mod Organizer 2.
diff --git a/libs/dds_header/src/CMakeLists.txt b/libs/dds_header/src/CMakeLists.txt
deleted file mode 100644
index f4f0587..0000000
--- a/libs/dds_header/src/CMakeLists.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-cmake_minimum_required(VERSION 3.16)
-
-find_package(mo2-cmake CONFIG REQUIRED)
-
-add_custom_target(DDSPreview ALL)
-mo2_configure_python(DDSPreview SIMPLE)
diff --git a/libs/dds_header/src/DDS/DDSDefinitions.py b/libs/dds_header/src/DDS/DDSDefinitions.py
deleted file mode 100644
index 2d44812..0000000
--- a/libs/dds_header/src/DDS/DDSDefinitions.py
+++ /dev/null
@@ -1,808 +0,0 @@
-import dataclasses
-from enum import IntEnum, IntFlag, auto
-import struct
-from typing import ClassVar, List
-
-from .glstuff import GL_IMAGE_FORMAT, CompressedGLTextureFormat, UncompressedGLTextureFormat
-
-DDS_MAGIC_NUMBER = b"DDS " # b"\x20\x53\x44\x44"
-
-
-class IntEnumFromZero(IntEnum):
- def _generate_next_value_(name, start, count, last_values):
- return count
-
-
-class D3D10_RESOURCE_DIMENSION(IntEnumFromZero):
- D3D10_RESOURCE_DIMENSION_UNKNOWN = auto()
- D3D10_RESOURCE_DIMENSION_BUFFER = auto()
- D3D10_RESOURCE_DIMENSION_TEXTURE1D = auto()
- D3D10_RESOURCE_DIMENSION_TEXTURE2D = auto()
- D3D10_RESOURCE_DIMENSION_TEXTURE3D = auto()
-
-
-class DXGI_FORMAT(IntEnumFromZero):
- DXGI_FORMAT_UNKNOWN = auto()
- DXGI_FORMAT_R32G32B32A32_TYPELESS = auto()
- DXGI_FORMAT_R32G32B32A32_FLOAT = auto()
- DXGI_FORMAT_R32G32B32A32_UINT = auto()
- DXGI_FORMAT_R32G32B32A32_SINT = auto()
- DXGI_FORMAT_R32G32B32_TYPELESS = auto()
- DXGI_FORMAT_R32G32B32_FLOAT = auto()
- DXGI_FORMAT_R32G32B32_UINT = auto()
- DXGI_FORMAT_R32G32B32_SINT = auto()
- DXGI_FORMAT_R16G16B16A16_TYPELESS = auto()
- DXGI_FORMAT_R16G16B16A16_FLOAT = auto()
- DXGI_FORMAT_R16G16B16A16_UNORM = auto()
- DXGI_FORMAT_R16G16B16A16_UINT = auto()
- DXGI_FORMAT_R16G16B16A16_SNORM = auto()
- DXGI_FORMAT_R16G16B16A16_SINT = auto()
- DXGI_FORMAT_R32G32_TYPELESS = auto()
- DXGI_FORMAT_R32G32_FLOAT = auto()
- DXGI_FORMAT_R32G32_UINT = auto()
- DXGI_FORMAT_R32G32_SINT = auto()
- DXGI_FORMAT_R32G8X24_TYPELESS = auto()
- DXGI_FORMAT_D32_FLOAT_S8X24_UINT = auto()
- DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS = auto()
- DXGI_FORMAT_X32_TYPELESS_G8X24_UINT = auto()
- DXGI_FORMAT_R10G10B10A2_TYPELESS = auto()
- DXGI_FORMAT_R10G10B10A2_UNORM = auto()
- DXGI_FORMAT_R10G10B10A2_UINT = auto()
- DXGI_FORMAT_R11G11B10_FLOAT = auto()
- DXGI_FORMAT_R8G8B8A8_TYPELESS = auto()
- DXGI_FORMAT_R8G8B8A8_UNORM = auto()
- DXGI_FORMAT_R8G8B8A8_UNORM_SRGB = auto()
- DXGI_FORMAT_R8G8B8A8_UINT = auto()
- DXGI_FORMAT_R8G8B8A8_SNORM = auto()
- DXGI_FORMAT_R8G8B8A8_SINT = auto()
- DXGI_FORMAT_R16G16_TYPELESS = auto()
- DXGI_FORMAT_R16G16_FLOAT = auto()
- DXGI_FORMAT_R16G16_UNORM = auto()
- DXGI_FORMAT_R16G16_UINT = auto()
- DXGI_FORMAT_R16G16_SNORM = auto()
- DXGI_FORMAT_R16G16_SINT = auto()
- DXGI_FORMAT_R32_TYPELESS = auto()
- DXGI_FORMAT_D32_FLOAT = auto()
- DXGI_FORMAT_R32_FLOAT = auto()
- DXGI_FORMAT_R32_UINT = auto()
- DXGI_FORMAT_R32_SINT = auto()
- DXGI_FORMAT_R24G8_TYPELESS = auto()
- DXGI_FORMAT_D24_UNORM_S8_UINT = auto()
- DXGI_FORMAT_R24_UNORM_X8_TYPELESS = auto()
- DXGI_FORMAT_X24_TYPELESS_G8_UINT = auto()
- DXGI_FORMAT_R8G8_TYPELESS = auto()
- DXGI_FORMAT_R8G8_UNORM = auto()
- DXGI_FORMAT_R8G8_UINT = auto()
- DXGI_FORMAT_R8G8_SNORM = auto()
- DXGI_FORMAT_R8G8_SINT = auto()
- DXGI_FORMAT_R16_TYPELESS = auto()
- DXGI_FORMAT_R16_FLOAT = auto()
- DXGI_FORMAT_D16_UNORM = auto()
- DXGI_FORMAT_R16_UNORM = auto()
- DXGI_FORMAT_R16_UINT = auto()
- DXGI_FORMAT_R16_SNORM = auto()
- DXGI_FORMAT_R16_SINT = auto()
- DXGI_FORMAT_R8_TYPELESS = auto()
- DXGI_FORMAT_R8_UNORM = auto()
- DXGI_FORMAT_R8_UINT = auto()
- DXGI_FORMAT_R8_SNORM = auto()
- DXGI_FORMAT_R8_SINT = auto()
- DXGI_FORMAT_A8_UNORM = auto()
- DXGI_FORMAT_R1_UNORM = auto()
- DXGI_FORMAT_R9G9B9E5_SHAREDEXP = auto()
- DXGI_FORMAT_R8G8_B8G8_UNORM = auto()
- DXGI_FORMAT_G8R8_G8B8_UNORM = auto()
- DXGI_FORMAT_BC1_TYPELESS = auto()
- DXGI_FORMAT_BC1_UNORM = auto()
- DXGI_FORMAT_BC1_UNORM_SRGB = auto()
- DXGI_FORMAT_BC2_TYPELESS = auto()
- DXGI_FORMAT_BC2_UNORM = auto()
- DXGI_FORMAT_BC2_UNORM_SRGB = auto()
- DXGI_FORMAT_BC3_TYPELESS = auto()
- DXGI_FORMAT_BC3_UNORM = auto()
- DXGI_FORMAT_BC3_UNORM_SRGB = auto()
- DXGI_FORMAT_BC4_TYPELESS = auto()
- DXGI_FORMAT_BC4_UNORM = auto()
- DXGI_FORMAT_BC4_SNORM = auto()
- DXGI_FORMAT_BC5_TYPELESS = auto()
- DXGI_FORMAT_BC5_UNORM = auto()
- DXGI_FORMAT_BC5_SNORM = auto()
- DXGI_FORMAT_B5G6R5_UNORM = auto()
- DXGI_FORMAT_B5G5R5A1_UNORM = auto()
- DXGI_FORMAT_B8G8R8A8_UNORM = auto()
- DXGI_FORMAT_B8G8R8X8_UNORM = auto()
- DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM = auto()
- DXGI_FORMAT_B8G8R8A8_TYPELESS = auto()
- DXGI_FORMAT_B8G8R8A8_UNORM_SRGB = auto()
- DXGI_FORMAT_B8G8R8X8_TYPELESS = auto()
- DXGI_FORMAT_B8G8R8X8_UNORM_SRGB = auto()
- DXGI_FORMAT_BC6H_TYPELESS = auto()
- DXGI_FORMAT_BC6H_UF16 = auto()
- DXGI_FORMAT_BC6H_SF16 = auto()
- DXGI_FORMAT_BC7_TYPELESS = auto()
- DXGI_FORMAT_BC7_UNORM = auto()
- DXGI_FORMAT_BC7_UNORM_SRGB = auto()
- DXGI_FORMAT_AYUV = auto()
- DXGI_FORMAT_Y410 = auto()
- DXGI_FORMAT_Y416 = auto()
- DXGI_FORMAT_NV12 = auto()
- DXGI_FORMAT_P010 = auto()
- DXGI_FORMAT_P016 = auto()
- DXGI_FORMAT_420_OPAQUE = auto()
- DXGI_FORMAT_YUY2 = auto()
- DXGI_FORMAT_Y210 = auto()
- DXGI_FORMAT_Y216 = auto()
- DXGI_FORMAT_NV11 = auto()
- DXGI_FORMAT_AI44 = auto()
- DXGI_FORMAT_IA44 = auto()
- DXGI_FORMAT_P8 = auto()
- DXGI_FORMAT_A8P8 = auto()
- DXGI_FORMAT_B4G4R4A4_UNORM = auto()
- DXGI_FORMAT_P208 = auto()
- DXGI_FORMAT_V208 = auto()
- DXGI_FORMAT_V408 = auto()
- DXGI_FORMAT_FORCE_UINT = auto()
-
-
-def DataclassFromBytes(dataclass):
- class LoadableDataclass(dataclass):
- def __init__(self, bytes=None):
- super(LoadableDataclass, self).__init__()
- if bytes:
- self.fromBytes(bytes)
-
- def fromStream(self, byteStream):
- self.fromBytes(byteStream.read(struct.calcsize(self.structFormat)))
-
- def fromBytes(self, bytes):
- loaded = struct.unpack(self.structFormat, bytes)
- fields = dataclasses.fields(self)
- memberIndex = 0
- for field in fields:
- if field.metadata and "count" in field.metadata:
- # We have a list
- listed = field.type.__args__[0]
- myList = []
- for i in range(field.metadata["count"]):
- myList.append(listed(loaded[memberIndex]))
- memberIndex += 1
- self.__dict__[field.name] = myList
- else:
- self.__dict__[field.name] = field.type(loaded[memberIndex])
- memberIndex += 1
-
- return LoadableDataclass
-
-
-@DataclassFromBytes
-@dataclasses.dataclass
-class DDS_PIXELFORMAT:
- structFormat: ClassVar[str] = "<II4sI4s4s4s4s"
-
- class Flags(IntFlag):
- DDPF_ALPHAPIXELS = 0x1
- DDPF_ALPHA = 0x2
- DDPF_FOURCC = 0x4
- DDPF_RGB = 0x40
- DDPF_YUV = 0x200
- DDPF_LUMINANCE = 0x20000
-
- dwSize: int = 0
- dwFlags: Flags = 0
- dwFourCC: bytes = dataclasses.field(default_factory=lambda: b"\x00\x00\x00\x00")
- dwRGBBitCount: int = 0
- dwRBitMask: bytes = dataclasses.field(default_factory=lambda: b"\x00\x00\x00\x00")
- dwGBitMask: bytes = dataclasses.field(default_factory=lambda: b"\x00\x00\x00\x00")
- dwBBitMask: bytes = dataclasses.field(default_factory=lambda: b"\x00\x00\x00\x00")
- dwABitMask: bytes = dataclasses.field(default_factory=lambda: b"\x00\x00\x00\x00")
-
-
-@DataclassFromBytes
-@dataclasses.dataclass
-class DDS_HEADER:
- structFormat: ClassVar[str] = "<IIIIIII IIIIIIIIIII 32s IIIII"
-
- class Flags(IntFlag):
- DDSD_CAPS = 0x1
- DDSD_HEIGHT = 0x2
- DDSD_WIDTH = 0x4
- DDSD_PITCH = 0x8
- DDSD_PIXELFORMAT = 0x1000
- DDSD_MIPMAPCOUNT = 0x20000
- DDSD_LINEARSIZE = 0x80000
- DDSD_DEPTH = 0x800000
-
- class Caps(IntFlag):
- DDSCAPS_COMPLEX = 0x8
- DDSCAPS_MIPMAP = 0x400000
- DDSCAPS_TEXTURE = 0x1000
-
- class Caps2(IntFlag):
- DDSCAPS2_CUBEMAP = 0x200
- DDSCAPS2_CUBEMAP_POSITIVEX = 0x400
- DDSCAPS2_CUBEMAP_NEGATIVEX = 0x800
- DDSCAPS2_CUBEMAP_POSITIVEY = 0x1000
- DDSCAPS2_CUBEMAP_NEGATIVEY = 0x2000
- DDSCAPS2_CUBEMAP_POSITIVEZ = 0x4000
- DDSCAPS2_CUBEMAP_NEGATIVEZ = 0x8000
- DDSCAPS2_VOLUME = 0x200000
-
- dwSize: int = 0
- dwFlags: Flags = 0
- dwHeight: int = 0
- dwWidth: int = 0
- dwPitchOrLinearSize: int = 0
- dwDepth: int = 0
- dwMipMapCount: int = 0
- dwReserved1: List[int] = dataclasses.field(default_factory=lambda: [0] * 11, metadata={"count": 11})
- ddspf: DDS_PIXELFORMAT = dataclasses.field(default_factory=DDS_PIXELFORMAT)
- dwCaps: Caps = 0
- dwCaps2: Caps2 = 0
- dwCaps3: int = 0
- dwCaps4: int = 0
- dwReserved2: int = 0
-
-
-@DataclassFromBytes
-@dataclasses.dataclass
-class DDS_HEADER_DXT10:
- structFormat: ClassVar[str] = "<IIIII"
-
- class MiscFlag(IntFlag):
- DDS_RESOURCE_MISC_TEXTURECUBE = 0x4
-
- class MiscFlags2(IntFlag):
- DDS_ALPHA_MODE_UNKNOWN = 0x0
- DDS_ALPHA_MODE_STRAIGHT = 0x1
- DDS_ALPHA_MODE_PREMULTIPLIED = 0x2
- DDS_ALPHA_MODE_OPAQUE = 0x3
- DDS_ALPHA_MODE_CUSTOM = 0x4
-
- dxgiFormat: DXGI_FORMAT = 0
- resourceDimension: D3D10_RESOURCE_DIMENSION = 0
- miscFlag: MiscFlag = 0
- arraySize: int = 0
- miscFlags2: MiscFlags2 = 0
-
-
-class UnsupportedDDSFormatException(Exception):
- """Thrown when an unsupported DDS format or broken DDS file is given"""
- pass
-
-
-def fourCCToDXGI(fourCC):
- converter = {
- b"DXT1": DXGI_FORMAT.DXGI_FORMAT_BC1_UNORM,
- b"DXT3": DXGI_FORMAT.DXGI_FORMAT_BC2_UNORM,
- b"DXT5": DXGI_FORMAT.DXGI_FORMAT_BC3_UNORM,
- b"BC4U": DXGI_FORMAT.DXGI_FORMAT_BC4_UNORM,
- b"BC4S": DXGI_FORMAT.DXGI_FORMAT_BC4_SNORM,
- b"ATI2": DXGI_FORMAT.DXGI_FORMAT_BC5_UNORM,
- b"BC5U": DXGI_FORMAT.DXGI_FORMAT_BC5_UNORM,
- b"BC5S": DXGI_FORMAT.DXGI_FORMAT_BC5_SNORM,
- b"RGBG": DXGI_FORMAT.DXGI_FORMAT_R8G8_B8G8_UNORM,
- b"GRGB": DXGI_FORMAT.DXGI_FORMAT_G8R8_G8B8_UNORM,
- (36).to_bytes(4, byteorder='little'): DXGI_FORMAT.DXGI_FORMAT_R16G16B16A16_UNORM,
- (110).to_bytes(4, byteorder='little'): DXGI_FORMAT.DXGI_FORMAT_R16G16B16A16_SNORM,
- (111).to_bytes(4, byteorder='little'): DXGI_FORMAT.DXGI_FORMAT_R16_FLOAT,
- (112).to_bytes(4, byteorder='little'): DXGI_FORMAT.DXGI_FORMAT_R16G16_FLOAT,
- (113).to_bytes(4, byteorder='little'): DXGI_FORMAT.DXGI_FORMAT_R16G16B16A16_FLOAT,
- (114).to_bytes(4, byteorder='little'): DXGI_FORMAT.DXGI_FORMAT_R32_FLOAT,
- (115).to_bytes(4, byteorder='little'): DXGI_FORMAT.DXGI_FORMAT_R32G32_FLOAT,
- (116).to_bytes(4, byteorder='little'): DXGI_FORMAT.DXGI_FORMAT_R32G32B32A32_FLOAT
- # Note: Some formats with a four character code don't have a DXGI format, e.g. pre-multiplied BC2/3 images
- }
- if fourCC in converter:
- return converter[fourCC]
-
-
-dxgiToGL = {
- DXGI_FORMAT.DXGI_FORMAT_UNKNOWN: None,
- DXGI_FORMAT.DXGI_FORMAT_R32G32B32A32_TYPELESS: None,
- DXGI_FORMAT.DXGI_FORMAT_R32G32B32A32_FLOAT: UncompressedGLTextureFormat(None, GL_IMAGE_FORMAT.GL_RGBA32F,
- GL_IMAGE_FORMAT.GL_RGBA,
- GL_IMAGE_FORMAT.GL_FLOAT),
- DXGI_FORMAT.DXGI_FORMAT_R32G32B32A32_UINT: UncompressedGLTextureFormat(None, GL_IMAGE_FORMAT.GL_RGBA32UI,
- GL_IMAGE_FORMAT.GL_RGBA_INTEGER,
- GL_IMAGE_FORMAT.GL_UNSIGNED_INT),
- DXGI_FORMAT.DXGI_FORMAT_R32G32B32A32_SINT: UncompressedGLTextureFormat(None, GL_IMAGE_FORMAT.GL_RGBA32I,
- GL_IMAGE_FORMAT.GL_RGBA_INTEGER,
- GL_IMAGE_FORMAT.GL_INT),
- DXGI_FORMAT.DXGI_FORMAT_R32G32B32_TYPELESS: None,
- DXGI_FORMAT.DXGI_FORMAT_R32G32B32_FLOAT: UncompressedGLTextureFormat(None, GL_IMAGE_FORMAT.GL_RGB32F,
- GL_IMAGE_FORMAT.GL_RGB,
- GL_IMAGE_FORMAT.GL_FLOAT),
- DXGI_FORMAT.DXGI_FORMAT_R32G32B32_UINT: UncompressedGLTextureFormat(None, GL_IMAGE_FORMAT.GL_RGB32UI,
- GL_IMAGE_FORMAT.GL_RGB_INTEGER,
- GL_IMAGE_FORMAT.GL_UNSIGNED_INT),
- DXGI_FORMAT.DXGI_FORMAT_R32G32B32_SINT: UncompressedGLTextureFormat(None, GL_IMAGE_FORMAT.GL_RGB32I,
- GL_IMAGE_FORMAT.GL_RGB_INTEGER,
- GL_IMAGE_FORMAT.GL_INT),
- DXGI_FORMAT.DXGI_FORMAT_R16G16B16A16_TYPELESS: None,
- DXGI_FORMAT.DXGI_FORMAT_R16G16B16A16_FLOAT: UncompressedGLTextureFormat(((3, 0), [b"GL_ARB_half_float_pixel"]),
- GL_IMAGE_FORMAT.GL_RGBA16F,
- GL_IMAGE_FORMAT.GL_RGBA,
- GL_IMAGE_FORMAT.GL_HALF_FLOAT),
- DXGI_FORMAT.DXGI_FORMAT_R16G16B16A16_UNORM: UncompressedGLTextureFormat(None, GL_IMAGE_FORMAT.GL_RGBA16,
- GL_IMAGE_FORMAT.GL_RGBA,
- GL_IMAGE_FORMAT.GL_UNSIGNED_SHORT),
- DXGI_FORMAT.DXGI_FORMAT_R16G16B16A16_UINT: UncompressedGLTextureFormat(None, GL_IMAGE_FORMAT.GL_RGBA16UI,
- GL_IMAGE_FORMAT.GL_RGBA_INTEGER,
- GL_IMAGE_FORMAT.GL_UNSIGNED_SHORT),
- DXGI_FORMAT.DXGI_FORMAT_R16G16B16A16_SNORM: UncompressedGLTextureFormat(None, GL_IMAGE_FORMAT.GL_RGBA16_SNORM,
- GL_IMAGE_FORMAT.GL_RGBA,
- GL_IMAGE_FORMAT.GL_SHORT),
- DXGI_FORMAT.DXGI_FORMAT_R16G16B16A16_SINT: UncompressedGLTextureFormat(None, GL_IMAGE_FORMAT.GL_RGBA16I,
- GL_IMAGE_FORMAT.GL_RGBA_INTEGER,
- GL_IMAGE_FORMAT.GL_SHORT),
- DXGI_FORMAT.DXGI_FORMAT_R32G32_TYPELESS: None,
- DXGI_FORMAT.DXGI_FORMAT_R32G32_FLOAT: UncompressedGLTextureFormat(None, GL_IMAGE_FORMAT.GL_RG32F,
- GL_IMAGE_FORMAT.GL_RG, GL_IMAGE_FORMAT.GL_FLOAT),
- DXGI_FORMAT.DXGI_FORMAT_R32G32_UINT: UncompressedGLTextureFormat(None, GL_IMAGE_FORMAT.GL_RG32UI,
- GL_IMAGE_FORMAT.GL_RG_INTEGER,
- GL_IMAGE_FORMAT.GL_UNSIGNED_INT),
- DXGI_FORMAT.DXGI_FORMAT_R32G32_SINT: UncompressedGLTextureFormat(None, GL_IMAGE_FORMAT.GL_RG32I,
- GL_IMAGE_FORMAT.GL_RG, GL_IMAGE_FORMAT.GL_INT),
- DXGI_FORMAT.DXGI_FORMAT_R32G8X24_TYPELESS: None,
- DXGI_FORMAT.DXGI_FORMAT_D32_FLOAT_S8X24_UINT: None,
- DXGI_FORMAT.DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS: UncompressedGLTextureFormat(None, GL_IMAGE_FORMAT.GL_R32F,
- GL_IMAGE_FORMAT.GL_RG,
- GL_IMAGE_FORMAT.GL_FLOAT),
- DXGI_FORMAT.DXGI_FORMAT_X32_TYPELESS_G8X24_UINT: None,
- DXGI_FORMAT.DXGI_FORMAT_R10G10B10A2_TYPELESS: None,
- DXGI_FORMAT.DXGI_FORMAT_R10G10B10A2_UNORM: UncompressedGLTextureFormat(None, GL_IMAGE_FORMAT.GL_RGB10_A2,
- GL_IMAGE_FORMAT.GL_RGBA,
- GL_IMAGE_FORMAT.GL_UNSIGNED_INT_2_10_10_10_REV),
- DXGI_FORMAT.DXGI_FORMAT_R10G10B10A2_UINT: UncompressedGLTextureFormat(None, GL_IMAGE_FORMAT.GL_RGB10_A2UI,
- GL_IMAGE_FORMAT.GL_RGBA_INTEGER,
- GL_IMAGE_FORMAT.GL_UNSIGNED_INT_2_10_10_10_REV),
- DXGI_FORMAT.DXGI_FORMAT_R11G11B10_FLOAT: UncompressedGLTextureFormat(((-1, 0), [b"EXT_packed_float"]),
- GL_IMAGE_FORMAT.GL_R11F_G11F_B10F,
- GL_IMAGE_FORMAT.GL_RGB,
- GL_IMAGE_FORMAT.GL_UNSIGNED_INT_10F_11F_11F_REV_EXT),
- DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_TYPELESS: None,
- DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_UNORM: UncompressedGLTextureFormat(None, GL_IMAGE_FORMAT.GL_RGBA8,
- GL_IMAGE_FORMAT.GL_RGBA,
- GL_IMAGE_FORMAT.GL_UNSIGNED_BYTE),
- DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: UncompressedGLTextureFormat(((2, 1), [b"GL_EXT_texture_sRGB"]),
- GL_IMAGE_FORMAT.GL_SRGB8_ALPHA8,
- GL_IMAGE_FORMAT.GL_RGBA,
- GL_IMAGE_FORMAT.GL_UNSIGNED_BYTE),
- DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_UINT: UncompressedGLTextureFormat(None, GL_IMAGE_FORMAT.GL_RGBA8UI,
- GL_IMAGE_FORMAT.GL_RGBA_INTEGER,
- GL_IMAGE_FORMAT.GL_UNSIGNED_BYTE),
- DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_SNORM: UncompressedGLTextureFormat(None, GL_IMAGE_FORMAT.GL_RGBA8_SNORM,
- GL_IMAGE_FORMAT.GL_RGBA,
- GL_IMAGE_FORMAT.GL_BYTE),
- DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_SINT: UncompressedGLTextureFormat(None, GL_IMAGE_FORMAT.GL_RGBA8I,
- GL_IMAGE_FORMAT.GL_RGBA_INTEGER,
- GL_IMAGE_FORMAT.GL_BYTE),
- DXGI_FORMAT.DXGI_FORMAT_R16G16_TYPELESS: None,
- DXGI_FORMAT.DXGI_FORMAT_R16G16_FLOAT: UncompressedGLTextureFormat(((3, 0), [b"GL_ARB_half_float_pixel"]),
- GL_IMAGE_FORMAT.GL_RG16F, GL_IMAGE_FORMAT.GL_RG,
- GL_IMAGE_FORMAT.GL_HALF_FLOAT),
- DXGI_FORMAT.DXGI_FORMAT_R16G16_UNORM: UncompressedGLTextureFormat(None, GL_IMAGE_FORMAT.GL_RG16,
- GL_IMAGE_FORMAT.GL_RG,
- GL_IMAGE_FORMAT.GL_UNSIGNED_SHORT),
- DXGI_FORMAT.DXGI_FORMAT_R16G16_UINT: UncompressedGLTextureFormat(None, GL_IMAGE_FORMAT.GL_RG16UI,
- GL_IMAGE_FORMAT.GL_RG_INTEGER,
- GL_IMAGE_FORMAT.GL_UNSIGNED_SHORT),
- DXGI_FORMAT.DXGI_FORMAT_R16G16_SNORM: UncompressedGLTextureFormat(None, GL_IMAGE_FORMAT.GL_RG16_SNORM,
- GL_IMAGE_FORMAT.GL_RG, GL_IMAGE_FORMAT.GL_SHORT),
- DXGI_FORMAT.DXGI_FORMAT_R16G16_SINT: UncompressedGLTextureFormat(None, GL_IMAGE_FORMAT.GL_RG16I,
- GL_IMAGE_FORMAT.GL_RG_INTEGER,
- GL_IMAGE_FORMAT.GL_SHORT),
- DXGI_FORMAT.DXGI_FORMAT_R32_TYPELESS: None,
- DXGI_FORMAT.DXGI_FORMAT_D32_FLOAT: None,
- DXGI_FORMAT.DXGI_FORMAT_R32_FLOAT: UncompressedGLTextureFormat(None, GL_IMAGE_FORMAT.GL_R32F,
- GL_IMAGE_FORMAT.GL_RED, GL_IMAGE_FORMAT.GL_FLOAT),
- DXGI_FORMAT.DXGI_FORMAT_R32_UINT: UncompressedGLTextureFormat(None, GL_IMAGE_FORMAT.GL_R32UI,
- GL_IMAGE_FORMAT.GL_RED_INTEGER,
- GL_IMAGE_FORMAT.GL_UNSIGNED_INT),
- DXGI_FORMAT.DXGI_FORMAT_R32_SINT: UncompressedGLTextureFormat(None, GL_IMAGE_FORMAT.GL_R32I,
- GL_IMAGE_FORMAT.GL_RED_INTEGER,
- GL_IMAGE_FORMAT.GL_INT),
- DXGI_FORMAT.DXGI_FORMAT_R24G8_TYPELESS: None,
- DXGI_FORMAT.DXGI_FORMAT_D24_UNORM_S8_UINT: None,
- DXGI_FORMAT.DXGI_FORMAT_R24_UNORM_X8_TYPELESS: None,
- DXGI_FORMAT.DXGI_FORMAT_X24_TYPELESS_G8_UINT: None,
- DXGI_FORMAT.DXGI_FORMAT_R8G8_TYPELESS: None,
- DXGI_FORMAT.DXGI_FORMAT_R8G8_UNORM: UncompressedGLTextureFormat(None, GL_IMAGE_FORMAT.GL_RG8, GL_IMAGE_FORMAT.GL_RG,
- GL_IMAGE_FORMAT.GL_UNSIGNED_BYTE),
- DXGI_FORMAT.DXGI_FORMAT_R8G8_UINT: UncompressedGLTextureFormat(None, GL_IMAGE_FORMAT.GL_RG8UI,
- GL_IMAGE_FORMAT.GL_RG_INTEGER,
- GL_IMAGE_FORMAT.GL_UNSIGNED_BYTE),
- DXGI_FORMAT.DXGI_FORMAT_R8G8_SNORM: UncompressedGLTextureFormat(None, GL_IMAGE_FORMAT.GL_RG8_SNORM,
- GL_IMAGE_FORMAT.GL_RG, GL_IMAGE_FORMAT.GL_BYTE),
- DXGI_FORMAT.DXGI_FORMAT_R8G8_SINT: UncompressedGLTextureFormat(None, GL_IMAGE_FORMAT.GL_RG8I,
- GL_IMAGE_FORMAT.GL_RG_INTEGER,
- GL_IMAGE_FORMAT.GL_BYTE),
- DXGI_FORMAT.DXGI_FORMAT_R16_TYPELESS: None,
- DXGI_FORMAT.DXGI_FORMAT_R16_FLOAT: UncompressedGLTextureFormat(((3, 0), [b"GL_ARB_half_float_pixel"]),
- GL_IMAGE_FORMAT.GL_R16F, GL_IMAGE_FORMAT.GL_RED,
- GL_IMAGE_FORMAT.GL_HALF_FLOAT),
- DXGI_FORMAT.DXGI_FORMAT_D16_UNORM: None,
- DXGI_FORMAT.DXGI_FORMAT_R16_UNORM: UncompressedGLTextureFormat(None, GL_IMAGE_FORMAT.GL_R16, GL_IMAGE_FORMAT.GL_RED,
- GL_IMAGE_FORMAT.GL_UNSIGNED_SHORT),
- DXGI_FORMAT.DXGI_FORMAT_R16_UINT: UncompressedGLTextureFormat(None, GL_IMAGE_FORMAT.GL_R16UI,
- GL_IMAGE_FORMAT.GL_RED_INTEGER,
- GL_IMAGE_FORMAT.GL_UNSIGNED_SHORT),
- DXGI_FORMAT.DXGI_FORMAT_R16_SNORM: UncompressedGLTextureFormat(None, GL_IMAGE_FORMAT.GL_R16_SNORM,
- GL_IMAGE_FORMAT.GL_RED, GL_IMAGE_FORMAT.GL_SHORT),
- DXGI_FORMAT.DXGI_FORMAT_R16_SINT: UncompressedGLTextureFormat(None, GL_IMAGE_FORMAT.GL_R16I,
- GL_IMAGE_FORMAT.GL_RED_INTEGER,
- GL_IMAGE_FORMAT.GL_SHORT),
- DXGI_FORMAT.DXGI_FORMAT_R8_TYPELESS: None,
- DXGI_FORMAT.DXGI_FORMAT_R8_UNORM: UncompressedGLTextureFormat(None, GL_IMAGE_FORMAT.GL_R8, GL_IMAGE_FORMAT.GL_RED,
- GL_IMAGE_FORMAT.GL_UNSIGNED_BYTE),
- DXGI_FORMAT.DXGI_FORMAT_R8_UINT: UncompressedGLTextureFormat(None, GL_IMAGE_FORMAT.GL_R8UI,
- GL_IMAGE_FORMAT.GL_RED_INTEGER,
- GL_IMAGE_FORMAT.GL_UNSIGNED_BYTE),
- DXGI_FORMAT.DXGI_FORMAT_R8_SNORM: UncompressedGLTextureFormat(None, GL_IMAGE_FORMAT.GL_R8_SNORM,
- GL_IMAGE_FORMAT.GL_RED, GL_IMAGE_FORMAT.GL_BYTE),
- DXGI_FORMAT.DXGI_FORMAT_R8_SINT: UncompressedGLTextureFormat(None, GL_IMAGE_FORMAT.GL_R8I,
- GL_IMAGE_FORMAT.GL_RED_INTEGER,
- GL_IMAGE_FORMAT.GL_BYTE),
- DXGI_FORMAT.DXGI_FORMAT_A8_UNORM: UncompressedGLTextureFormat(None, GL_IMAGE_FORMAT.GL_ALPHA8,
- GL_IMAGE_FORMAT.GL_ALPHA,
- GL_IMAGE_FORMAT.GL_UNSIGNED_BYTE),
- DXGI_FORMAT.DXGI_FORMAT_R1_UNORM: None,
- DXGI_FORMAT.DXGI_FORMAT_R9G9B9E5_SHAREDEXP: UncompressedGLTextureFormat(
- ((3, 0), [b"GL_EXT_texture_shared_exponent"]), GL_IMAGE_FORMAT.GL_RGB9_E5, GL_IMAGE_FORMAT.GL_RGB,
- GL_IMAGE_FORMAT.GL_UNSIGNED_INT_5_9_9_9_REV_EXT),
- DXGI_FORMAT.DXGI_FORMAT_R8G8_B8G8_UNORM: None,
- DXGI_FORMAT.DXGI_FORMAT_G8R8_G8B8_UNORM: None,
- DXGI_FORMAT.DXGI_FORMAT_BC1_TYPELESS: None,
- DXGI_FORMAT.DXGI_FORMAT_BC1_UNORM: CompressedGLTextureFormat(((-1, 0), [b"GL_EXT_texture_compression_s3tc"]),
- GL_IMAGE_FORMAT.GL_COMPRESSED_RGBA_S3TC_DXT1_EXT),
- DXGI_FORMAT.DXGI_FORMAT_BC1_UNORM_SRGB: CompressedGLTextureFormat(((-1, 0), [b"GL_EXT_texture_compression_s3tc"]),
- GL_IMAGE_FORMAT.GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT),
- DXGI_FORMAT.DXGI_FORMAT_BC2_TYPELESS: None,
- DXGI_FORMAT.DXGI_FORMAT_BC2_UNORM: CompressedGLTextureFormat(((-1, 0), [b"GL_EXT_texture_compression_s3tc"]),
- GL_IMAGE_FORMAT.GL_COMPRESSED_RGBA_S3TC_DXT3_EXT),
- DXGI_FORMAT.DXGI_FORMAT_BC2_UNORM_SRGB: CompressedGLTextureFormat(((-1, 0), [b"GL_EXT_texture_compression_s3tc"]),
- GL_IMAGE_FORMAT.GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT),
- DXGI_FORMAT.DXGI_FORMAT_BC3_TYPELESS: None,
- DXGI_FORMAT.DXGI_FORMAT_BC3_UNORM: CompressedGLTextureFormat(((-1, 0), [b"GL_EXT_texture_compression_s3tc"]),
- GL_IMAGE_FORMAT.GL_COMPRESSED_RGBA_S3TC_DXT5_EXT),
- DXGI_FORMAT.DXGI_FORMAT_BC3_UNORM_SRGB: CompressedGLTextureFormat(((-1, 0), [b"GL_EXT_texture_compression_s3tc"]),
- GL_IMAGE_FORMAT.GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT),
- DXGI_FORMAT.DXGI_FORMAT_BC4_TYPELESS: None,
- DXGI_FORMAT.DXGI_FORMAT_BC4_UNORM: CompressedGLTextureFormat(((3, 0), [b"GL_ARB_texture_compression_rgtc"]),
- GL_IMAGE_FORMAT.GL_COMPRESSED_RED_RGTC1),
- DXGI_FORMAT.DXGI_FORMAT_BC4_SNORM: CompressedGLTextureFormat(((3, 0), [b"GL_ARB_texture_compression_rgtc"]),
- GL_IMAGE_FORMAT.GL_COMPRESSED_SIGNED_RED_RGTC1),
- DXGI_FORMAT.DXGI_FORMAT_BC5_TYPELESS: None,
- DXGI_FORMAT.DXGI_FORMAT_BC5_UNORM: CompressedGLTextureFormat(((3, 0), [b"GL_ARB_texture_compression_rgtc"]),
- GL_IMAGE_FORMAT.GL_COMPRESSED_RG_RGTC2),
- DXGI_FORMAT.DXGI_FORMAT_BC5_SNORM: CompressedGLTextureFormat(((3, 0), [b"GL_ARB_texture_compression_rgtc"]),
- GL_IMAGE_FORMAT.GL_COMPRESSED_SIGNED_RG_RGTC2),
- DXGI_FORMAT.DXGI_FORMAT_B5G6R5_UNORM: UncompressedGLTextureFormat(None, GL_IMAGE_FORMAT.GL_RGB,
- GL_IMAGE_FORMAT.GL_RGB,
- GL_IMAGE_FORMAT.GL_UNSIGNED_SHORT_5_6_5_REV),
- DXGI_FORMAT.DXGI_FORMAT_B5G5R5A1_UNORM: UncompressedGLTextureFormat(None, GL_IMAGE_FORMAT.GL_RGB5_A1,
- GL_IMAGE_FORMAT.GL_BGRA,
- GL_IMAGE_FORMAT.GL_UNSIGNED_SHORT_1_5_5_5_REV),
- DXGI_FORMAT.DXGI_FORMAT_B8G8R8A8_UNORM: UncompressedGLTextureFormat(None, GL_IMAGE_FORMAT.GL_RGBA8,
- GL_IMAGE_FORMAT.GL_BGRA,
- GL_IMAGE_FORMAT.GL_UNSIGNED_BYTE),
- DXGI_FORMAT.DXGI_FORMAT_B8G8R8X8_UNORM: UncompressedGLTextureFormat(None, GL_IMAGE_FORMAT.GL_RGB8,
- GL_IMAGE_FORMAT.GL_BGRA,
- GL_IMAGE_FORMAT.GL_UNSIGNED_BYTE),
- DXGI_FORMAT.DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM: None,
- DXGI_FORMAT.DXGI_FORMAT_B8G8R8A8_TYPELESS: None,
- DXGI_FORMAT.DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: UncompressedGLTextureFormat(((2, 1), [b"GL_EXT_texture_sRGB"]),
- GL_IMAGE_FORMAT.GL_SRGB8_ALPHA8,
- GL_IMAGE_FORMAT.GL_BGRA,
- GL_IMAGE_FORMAT.GL_UNSIGNED_BYTE),
- DXGI_FORMAT.DXGI_FORMAT_B8G8R8X8_TYPELESS: None,
- DXGI_FORMAT.DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: UncompressedGLTextureFormat(((2, 1), [b"GL_EXT_texture_sRGB"]),
- GL_IMAGE_FORMAT.GL_SRGB8,
- GL_IMAGE_FORMAT.GL_BGRA,
- GL_IMAGE_FORMAT.GL_UNSIGNED_BYTE),
- DXGI_FORMAT.DXGI_FORMAT_BC6H_TYPELESS: None,
- DXGI_FORMAT.DXGI_FORMAT_BC6H_UF16: CompressedGLTextureFormat(((4, 2), [b"GL_ARB_texture_compression_bptc"]),
- GL_IMAGE_FORMAT.GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT),
- DXGI_FORMAT.DXGI_FORMAT_BC6H_SF16: CompressedGLTextureFormat(((4, 2), [b"GL_ARB_texture_compression_bptc"]),
- GL_IMAGE_FORMAT.GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT),
- DXGI_FORMAT.DXGI_FORMAT_BC7_TYPELESS: None,
- DXGI_FORMAT.DXGI_FORMAT_BC7_UNORM: CompressedGLTextureFormat(((4, 2), [b"GL_ARB_texture_compression_bptc"]),
- GL_IMAGE_FORMAT.GL_COMPRESSED_RGBA_BPTC_UNORM),
- DXGI_FORMAT.DXGI_FORMAT_BC7_UNORM_SRGB: CompressedGLTextureFormat(((4, 2), [b"GL_ARB_texture_compression_bptc"]),
- GL_IMAGE_FORMAT.GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM),
- DXGI_FORMAT.DXGI_FORMAT_AYUV: None,
- DXGI_FORMAT.DXGI_FORMAT_Y410: None,
- DXGI_FORMAT.DXGI_FORMAT_Y416: None,
- DXGI_FORMAT.DXGI_FORMAT_NV12: None,
- DXGI_FORMAT.DXGI_FORMAT_P010: None,
- DXGI_FORMAT.DXGI_FORMAT_P016: None,
- DXGI_FORMAT.DXGI_FORMAT_420_OPAQUE: None,
- DXGI_FORMAT.DXGI_FORMAT_YUY2: None,
- DXGI_FORMAT.DXGI_FORMAT_Y210: None,
- DXGI_FORMAT.DXGI_FORMAT_Y216: None,
- DXGI_FORMAT.DXGI_FORMAT_NV11: None,
- DXGI_FORMAT.DXGI_FORMAT_AI44: None,
- DXGI_FORMAT.DXGI_FORMAT_IA44: None,
- DXGI_FORMAT.DXGI_FORMAT_P8: None,
- DXGI_FORMAT.DXGI_FORMAT_A8P8: None,
- DXGI_FORMAT.DXGI_FORMAT_B4G4R4A4_UNORM: UncompressedGLTextureFormat(None, GL_IMAGE_FORMAT.GL_RGBA4,
- GL_IMAGE_FORMAT.GL_BGRA,
- GL_IMAGE_FORMAT.GL_UNSIGNED_SHORT_4_4_4_4_REV),
- DXGI_FORMAT.DXGI_FORMAT_P208: None,
- DXGI_FORMAT.DXGI_FORMAT_V208: None,
- DXGI_FORMAT.DXGI_FORMAT_V408: None,
- DXGI_FORMAT.DXGI_FORMAT_FORCE_UINT: None
-}
-
-
-def buildConverter(byteCount, usedBitCounts={8}, bitmasks=None, intmasks=None):
- # A converter converts the data to boring BGRA with enough bits per channel to fit the original
- glFormat = GL_IMAGE_FORMAT.GL_BGRA
- biggestComponent = max(usedBitCounts)
- biggestComponent = (biggestComponent + 7) // 8
- if biggestComponent == 1:
- packFormat = "=4B"
- glType = GL_IMAGE_FORMAT.GL_UNSIGNED_BYTE
- glInternalFormat = GL_IMAGE_FORMAT.GL_RGBA8
- elif biggestComponent == 2:
- packFormat = "=4H"
- glType = GL_IMAGE_FORMAT.GL_UNSIGNED_SHORT
- glInternalFormat = GL_IMAGE_FORMAT.GL_RGBA16
- elif biggestComponent <= 4:
- biggestComponent == 4
- packFormat = "=4I"
- glType = GL_IMAGE_FORMAT.GL_UNSIGNED_INT
- glInternalFormat = GL_IMAGE_FORMAT.GL_RGBA32F
- multiplier = 255 ** biggestComponent
-
- if byteCount == 1:
- unpackFormat = "B"
- unpackCombiner = lambda x: x
- elif byteCount == 2:
- unpackFormat = "H"
- unpackCombiner = lambda x: x
- elif byteCount == 3:
- unpackFormat = "3s"
-
- def unpackCombiner(x):
- x = struct.unpack("<HB", x)
- return x[0] + (x[1] << 16)
- elif byteCount == 4:
- unpackFormat = "I"
- unpackCombiner = lambda x: x
-
- if intmasks:
- rIntMask, gIntMask, bIntMask, aIntMask = intmasks
- else:
- rIntMask = 0
- gIntMask = 0
- bIntMask = 0
- aIntMask = 0
-
- if bitmasks:
- if "r" in bitmasks:
- rIntMask = unpackCombiner(*struct.unpack("<" + unpackFormat, bitmasks["r"][:byteCount]))
- if "g" in bitmasks:
- gIntMask = unpackCombiner(*struct.unpack("<" + unpackFormat, bitmasks["g"][:byteCount]))
- if "b" in bitmasks:
- bIntMask = unpackCombiner(*struct.unpack("<" + unpackFormat, bitmasks["b"][:byteCount]))
- if "a" in bitmasks:
- aIntMask = unpackCombiner(*struct.unpack("<" + unpackFormat, bitmasks["a"][:byteCount]))
- if "luminance" in bitmasks:
- rIntMask = unpackCombiner(*struct.unpack("<" + unpackFormat, bitmasks["luminance"][:byteCount]))
- gIntMask = unpackCombiner(*struct.unpack("<" + unpackFormat, bitmasks["luminance"][:byteCount]))
- bIntMask = unpackCombiner(*struct.unpack("<" + unpackFormat, bitmasks["luminance"][:byteCount]))
-
- rDivisor = 2 ** bin(rIntMask).count("1") - 1
- gDivisor = 2 ** bin(gIntMask).count("1") - 1
- bDivisor = 2 ** bin(bIntMask).count("1") - 1
- aDivisor = 2 ** bin(aIntMask).count("1") - 1
-
- rShift = (rIntMask & -rIntMask).bit_length() - 1
- gShift = (gIntMask & -gIntMask).bit_length() - 1
- bShift = (bIntMask & -bIntMask).bit_length() - 1
- aShift = (aIntMask & -aIntMask).bit_length() - 1
-
- def convert(imageData):
- length = len(imageData) // byteCount
- unpackString = "<" + ((str(length) + unpackFormat) if unpackFormat.isalpha() else unpackFormat * length)
- unpacked = struct.unpack(unpackString, imageData)
- repacked = bytearray(length * biggestComponent * 4)
- repackIndex = 0
- for pixel in unpacked:
- pixel = unpackCombiner(pixel)
- if rIntMask:
- red = (((rIntMask & pixel) >> rShift) * multiplier) // rDivisor
- else:
- red = 0
- if gIntMask:
- green = (((gIntMask & pixel) >> gShift) * multiplier) // gDivisor
- else:
- green = 0
- if bIntMask:
- blue = (((bIntMask & pixel) >> bShift) * multiplier) // bDivisor
- else:
- blue = 0
- if aIntMask:
- alpha = (((aIntMask & pixel) >> aShift) * multiplier) // aDivisor
- else:
- alpha = multiplier
- struct.pack_into(packFormat, repacked, repackIndex, blue, green, red, alpha)
- repackIndex += biggestComponent * 4
- return bytes(repacked)
-
- return (convert, glInternalFormat, glFormat, glType)
-
-
-def getGLFormat(pixelFormat, dxt10Header=None):
- # Half or more of this function may be unreachable or otherwise redundant.
- glInternalFormat = None
- dxgiFormat = None
- if dxt10Header:
- dxgiFormat = dxt10Header.dxgiFormat
- flags = pixelFormat.dwFlags
- if flags & pixelFormat.Flags.DDPF_FOURCC:
- fourCC = pixelFormat.dwFourCC
- if fourCC == b"DX10" and not dxt10Header:
- raise UnsupportedDDSFormatException()
- dxgiFormat = dxt10Header.dxgiFormat if dxt10Header else fourCCToDXGI(fourCC)
-
- if dxgiFormat:
- return dxgiToGL[dxgiFormat]
- elif flags & (
- pixelFormat.Flags.DDPF_ALPHA | pixelFormat.Flags.DDPF_RGB | pixelFormat.Flags.DDPF_YUV | pixelFormat.Flags.DDPF_LUMINANCE):
- compressed = False
-
- rBitmask = None
- gBitmask = None
- bBitmask = None
- aBitmask = None
- lumBitmask = None
- if flags & (pixelFormat.Flags.DDPF_ALPHA | pixelFormat.Flags.DDPF_ALPHAPIXELS):
- aBitmask = pixelFormat.dwABitMask
- if flags & (pixelFormat.Flags.DDPF_RGB | pixelFormat.Flags.DDPF_YUV):
- rBitmask = pixelFormat.dwRBitMask
- gBitmask = pixelFormat.dwGBitMask
- bBitmask = pixelFormat.dwBBitMask
- if flags & pixelFormat.Flags.DDPF_LUMINANCE:
- lumBitmask = pixelFormat.dwRBitMask
-
- def bitCount(theBytes):
- count = 0
- for byte in theBytes:
- count += bin(byte).count("1")
- return count
-
- def firstBit(theBytes):
- index = 0
- for byte in theBytes:
- if byte != 0:
- return index + format(byte, 'b').find("1")
- index += 8
-
- bitCounts = dict()
- starts = dict()
- namedBitmasks = dict()
- if rBitmask:
- bitCounts["r"] = bitCount(rBitmask)
- starts["r"] = firstBit(rBitmask)
- namedBitmasks["r"] = rBitmask
- if gBitmask:
- bitCounts["g"] = bitCount(gBitmask)
- starts["g"] = firstBit(gBitmask)
- namedBitmasks["g"] = gBitmask
- if bBitmask:
- bitCounts["b"] = bitCount(bBitmask)
- starts["b"] = firstBit(bBitmask)
- namedBitmasks["b"] = bBitmask
- if aBitmask:
- bitCounts["a"] = bitCount(aBitmask)
- starts["a"] = firstBit(aBitmask)
- namedBitmasks["a"] = aBitmask
- if lumBitmask:
- bitCounts["luminance"] = bitCount(lumBitmask)
- starts["luminance"] = firstBit(lumBitmask)
- namedBitmasks["luminance"] = lumBitmask
-
- toSort = []
- for key in starts:
- toSort.append((starts[key], key))
- toSort.sort()
-
- glInternalFormatName = ["GL"]
- glRequirements = None
-
- desc = "GL_"
- lastBitCount = -1
- usedBitCounts = set()
- for pos, channel in toSort:
- if lastBitCount != bitCounts[channel]:
- if lastBitCount != -1:
- desc += str(lastBitCount)
- lastBitCount = bitCounts[channel]
- usedBitCounts.add(lastBitCount)
- desc += channel
- if len(usedBitCounts) != 1:
- desc += str(lastBitCount)
-
- desc = desc.upper()
- glFormat = GL_IMAGE_FORMAT[desc] if desc in GL_IMAGE_FORMAT.__members__ else None
-
- numComponents = len(toSort)
- byteCount = (pixelFormat.dwRGBBitCount + 7) // 8
- needsConversion = False
- if len(usedBitCounts) == 1:
- if lastBitCount == 8:
- glType = GL_IMAGE_FORMAT.GL_UNSIGNED_BYTE
- elif lastBitCount == 16:
- glType = GL_IMAGE_FORMAT.GL_UNSIGNED_SHORT
- elif lastBitCount == 32:
- glType = GL_IMAGE_FORMAT.GL_UNSIGNED_INT
- elif lastBitCount == 4:
- if numComponents == 4:
- glType = GL_IMAGE_FORMAT.GL_UNSIGNED_SHORT_4_4_4_4_REV
- else:
- needsConversion = True
- if not glFormat or numComponents * lastBitCount != pixelFormat.dwRGBBitCount:
- needsConversion = True
-
- if needsConversion:
- convert, glInternalFormat, glFormat, glType = buildConverter(byteCount, bitmasks=namedBitmasks)
- else:
- convert = None
-
- if not glInternalFormat:
- glInternalFormatName = "_".join(glInternalFormatName)
- glInternalFormat = GL_IMAGE_FORMAT[
- glInternalFormatName] if glInternalFormatName in GL_IMAGE_FORMAT.__members__ else GL_IMAGE_FORMAT.GL_RGBA
-
- if compressed:
- return CompressedGLTextureFormat(glRequirements, glInternalFormat)
- else:
- return UncompressedGLTextureFormat(glRequirements, glInternalFormat, glFormat, glType, convert)
-
-
-def sizeFromFormat(dxgiFormat, width, height):
- blockCompressed = False
- if dxgiFormat in {DXGI_FORMAT.DXGI_FORMAT_BC1_TYPELESS,
- DXGI_FORMAT.DXGI_FORMAT_BC1_UNORM,
- DXGI_FORMAT.DXGI_FORMAT_BC1_UNORM_SRGB,
- DXGI_FORMAT.DXGI_FORMAT_BC4_TYPELESS,
- DXGI_FORMAT.DXGI_FORMAT_BC4_UNORM,
- DXGI_FORMAT.DXGI_FORMAT_BC4_SNORM}:
- blockCompressed = True
- blockSize = 8
- elif dxgiFormat in {DXGI_FORMAT.DXGI_FORMAT_BC2_TYPELESS,
- DXGI_FORMAT.DXGI_FORMAT_BC2_UNORM,
- DXGI_FORMAT.DXGI_FORMAT_BC2_UNORM_SRGB,
- DXGI_FORMAT.DXGI_FORMAT_BC3_TYPELESS,
- DXGI_FORMAT.DXGI_FORMAT_BC3_UNORM,
- DXGI_FORMAT.DXGI_FORMAT_BC3_UNORM_SRGB,
- DXGI_FORMAT.DXGI_FORMAT_BC5_TYPELESS,
- DXGI_FORMAT.DXGI_FORMAT_BC5_UNORM,
- DXGI_FORMAT.DXGI_FORMAT_BC5_SNORM,
- DXGI_FORMAT.DXGI_FORMAT_BC6H_TYPELESS,
- DXGI_FORMAT.DXGI_FORMAT_BC6H_UF16,
- DXGI_FORMAT.DXGI_FORMAT_BC6H_SF16,
- DXGI_FORMAT.DXGI_FORMAT_BC7_TYPELESS,
- DXGI_FORMAT.DXGI_FORMAT_BC7_UNORM,
- DXGI_FORMAT.DXGI_FORMAT_BC7_UNORM_SRGB}:
- blockCompressed = True
- blockSize = 16
-
- if blockCompressed:
- return max(1, ((width + 3) // 4)) * max(1, ((height + 3) // 4)) * blockSize
-
- if dxgiFormat <= DXGI_FORMAT.DXGI_FORMAT_B8G8R8X8_UNORM_SRGB:
- name = dxgiFormat.name + "_"
- count = 0
- currentNum = ""
- for char in name:
- if char.isdecimal():
- currentNum += char
- elif currentNum != "":
- count += int(currentNum)
- currentNum = ""
- pixelSize = (count + 7) // 8
- return width * height * pixelSize
- pass
diff --git a/libs/dds_header/src/DDS/DDSFile.py b/libs/dds_header/src/DDS/DDSFile.py
deleted file mode 100644
index 4592074..0000000
--- a/libs/dds_header/src/DDS/DDSFile.py
+++ /dev/null
@@ -1,220 +0,0 @@
-import io
-
-from PyQt6.QtCore import QCoreApplication, qCritical, QFile, QIODeviceBase
-from PyQt6.QtOpenGL import QOpenGLTexture
-
-from . import DDSDefinitions
-from .glstuff import GLTextureFormat
-
-
-class DDSReadException(Exception):
- """Thrown if there was an error reading a DDS file"""
- pass
-
-
-ddsCubemapFaces = {
- DDSDefinitions.DDS_HEADER.Caps2.DDSCAPS2_CUBEMAP_POSITIVEX: QOpenGLTexture.CubeMapFace.CubeMapPositiveX,
- DDSDefinitions.DDS_HEADER.Caps2.DDSCAPS2_CUBEMAP_NEGATIVEX: QOpenGLTexture.CubeMapFace.CubeMapNegativeX,
- DDSDefinitions.DDS_HEADER.Caps2.DDSCAPS2_CUBEMAP_POSITIVEY: QOpenGLTexture.CubeMapFace.CubeMapPositiveY,
- DDSDefinitions.DDS_HEADER.Caps2.DDSCAPS2_CUBEMAP_NEGATIVEY: QOpenGLTexture.CubeMapFace.CubeMapNegativeY,
- DDSDefinitions.DDS_HEADER.Caps2.DDSCAPS2_CUBEMAP_POSITIVEZ: QOpenGLTexture.CubeMapFace.CubeMapPositiveZ,
- DDSDefinitions.DDS_HEADER.Caps2.DDSCAPS2_CUBEMAP_NEGATIVEZ: QOpenGLTexture.CubeMapFace.CubeMapNegativeZ}
-
-
-class DDSFile:
- def __init__(self, fileData: bytes, fileName: str):
- self.fileName = fileName
- self.header = DDSDefinitions.DDS_HEADER()
- self.dxt10Header = None
- self.glFormat: GLTextureFormat = None
- self.fileData = fileData
- self.data = None
- self.isCubemap = None
-
- @classmethod
- def fromFile(cls, fileName: str):
- file = QFile(fileName)
- if file.open(QIODeviceBase.OpenModeFlag.ReadOnly):
- fileData = file.readAll()
- else:
- raise DDSReadException()
- return cls(fileData.data(), fileName)
-
- def load(self):
- with io.BytesIO(self.fileData) as file:
- magicNumber = file.read(4)
- if magicNumber != DDSDefinitions.DDS_MAGIC_NUMBER:
- qCritical(self.tr("Magic number mismatch."))
- raise DDSReadException()
-
- self.header.fromStream(file)
-
- if self.header.ddspf.dwFlags & DDSDefinitions.DDS_PIXELFORMAT.Flags.DDPF_FOURCC:
- fourCC = self.header.ddspf.dwFourCC
- if fourCC == b"DX10":
- self.dxt10Header = DDSDefinitions.DDS_HEADER_DXT10()
- self.dxt10Header.fromStream(file)
- else:
- fourCC = None
-
- self.glFormat = DDSDefinitions.getGLFormat(self.header.ddspf, self.dxt10Header)
- self.data = []
- # Do this once per layer/mip level whatever, (times one per scanline if uncompressed). Also, potentially recompute this based on the format and size in case writers lie.
- # self.data.append(file.read(self.header.dwPitchOrLinearSize))
-
- layerCount = 1
- if self.header.dwCaps2 & DDSDefinitions.DDS_HEADER.Caps2.DDSCAPS2_CUBEMAP:
- self.isCubemap = True
- layerCount = 0
- for face in ddsCubemapFaces:
- if self.header.dwCaps2 & face:
- layerCount += 1
- else:
- self.isCubemap = False
-
- for layer in range(layerCount):
- nextWidth = self.header.dwWidth
- nextHeight = self.header.dwHeight
- mipCount = self.mipLevels()
- for level in range(mipCount):
- if self.header.ddspf.dwFlags & (
- DDSDefinitions.DDS_PIXELFORMAT.Flags.DDPF_ALPHA | DDSDefinitions.DDS_PIXELFORMAT.Flags.DDPF_RGB | DDSDefinitions.DDS_PIXELFORMAT.Flags.DDPF_YUV | DDSDefinitions.DDS_PIXELFORMAT.Flags.DDPF_LUMINANCE):
- size = nextWidth * nextHeight * ((self.header.ddspf.dwRGBBitCount + 7) // 8)
- elif fourCC:
- if self.dxt10Header:
- dxgiFormat = self.dxt10Header.dxgiFormat
- else:
- dxgiFormat = DDSDefinitions.fourCCToDXGI(fourCC)
- size = DDSDefinitions.sizeFromFormat(dxgiFormat, nextWidth, nextHeight)
- self.data.append(file.read(size))
- nextWidth = max(nextWidth // 2, 1)
- nextHeight = max(nextHeight // 2, 1)
-
- def getDescription(self):
- format = ""
- # DX10 header says the format enum
- if self.dxt10Header is not None:
- format = self.dxt10Header.dxgiFormat.name.replace("DXGI_FORMAT_", "")
- # Pixel Format says the FourCC
- elif self.header.ddspf.dwFlags & DDSDefinitions.DDS_PIXELFORMAT.Flags.DDPF_FOURCC:
- fourCC = self.header.ddspf.dwFourCC
- format = self.tr("{0} (equivalent to {1})").format(fourCC.decode('ascii'),
- DDSDefinitions.fourCCToDXGI(fourCC).name.replace(
- "DXGI_FORMAT_", ""))
- # We've got bitmasks for the colour channels
- else:
- # This could be prettier if there was logic to detect that certain common bitmasks represented things more easily represented, like RGBA8
- if self.header.ddspf.dwFlags & (
- DDSDefinitions.DDS_PIXELFORMAT.Flags.DDPF_RGB | DDSDefinitions.DDS_PIXELFORMAT.Flags.DDPF_YUV):
- format += self.tr("Red bitmask {0}, Green bitmask {1}, Blue bitmask {2}").format(
- self.header.ddspf.dwRBitMask.hex().upper(), self.header.ddspf.dwGBitMask.hex().upper(),
- self.header.ddspf.dwBBitMask.hex().upper())
- if self.header.ddspf.dwFlags & DDSDefinitions.DDS_PIXELFORMAT.Flags.DDPF_LUMINANCE:
- if format != "":
- format += ", "
- format += self.tr("Luminance bitmask {0}").format(self.header.ddspf.dwRBitMask.hex().upper())
- if self.header.ddspf.dwFlags & (
- DDSDefinitions.DDS_PIXELFORMAT.Flags.DDPF_ALPHA | DDSDefinitions.DDS_PIXELFORMAT.Flags.DDPF_ALPHAPIXELS):
- if format != "":
- format += ", "
- format += self.tr("Alpha bitmask {0}").format(self.header.ddspf.dwABitMask.hex().upper())
-
- size = self.tr("{0}×{1}").format(self.header.dwWidth, self.header.dwHeight)
-
- dimensions = self.tr("Cubemap") if self.isCubemap else self.tr("2D")
-
- mipmaps = self.tr("Mipmapped") if self.mipLevels() != 1 else self.tr("No mipmaps")
-
- return self.tr("{0}, {1} {2}, {3}").format(format, size, dimensions, mipmaps)
-
- def mipLevels(self):
- if self.header.dwFlags & DDSDefinitions.DDS_HEADER.Flags.DDSD_MIPMAPCOUNT:
- return self.header.dwMipMapCount
- else:
- return 1
-
- def asQOpenGLTexture(self, gl, context):
- if not self.data:
- return
-
- if self.glFormat.requirements:
- minVersion, extensions = self.glFormat.requirements
- glVersion = (gl.glGetIntegerv(gl.GL_MAJOR_VERSION), gl.glGetIntegerv(gl.GL_MINOR_VERSION))
- if glVersion < minVersion or minVersion < (1, 0):
- compatible = False
- for extension in extensions:
- if context.hasExtension(extension):
- compatible = True
- break
- if not compatible:
- qCritical(self.tr("OpenGL driver incompatible with texture format."))
- return None
-
- if self.header.dwCaps2 & DDSDefinitions.DDS_HEADER.Caps2.DDSCAPS2_CUBEMAP:
- texture = QOpenGLTexture(QOpenGLTexture.Target.TargetCubeMap)
- if self.header.dwWidth != self.header.dwHeight:
- qCritical(self.tr("Cubemap faces must be square"))
- return None
- else:
- # Assume GL_TEXTURE_2D for now
- texture = QOpenGLTexture(QOpenGLTexture.Target.Target2D)
- # Assume single layer for now
- # self.texture.setLayers(1)
- mipCount = self.mipLevels()
- texture.setAutoMipMapGenerationEnabled(False)
- texture.setMipLevels(mipCount)
- texture.setMipLevelRange(0, mipCount - 1)
- texture.setSize(self.header.dwWidth, self.header.dwHeight)
- texture.setFormat(QOpenGLTexture.TextureFormat(self.glFormat.internalFormat))
- texture.allocateStorage()
-
- if self.header.dwCaps2 & DDSDefinitions.DDS_HEADER.Caps2.DDSCAPS2_CUBEMAP:
- # Lisa hasn't whipped David Wang into shape yet. At least there are fewer bugs than under Raja.
- # The specific bug has been reported and AMD "will try to reproduce it soon"
- # MO 2.5.0: Radeon-specific code is causing crashing on the latest drivers
- # Some cubemaps fail to render with or without these modifications
- # noDSA = "Radeon" in gl.glGetString(gl.GL_RENDERER) and self.glFormat.compressed
- noDSA = False
- if noDSA:
- texture.bind()
- faceIndex = 0
- for face in ddsCubemapFaces:
- if self.header.dwCaps2 & face:
- for i in range(mipCount):
- if self.glFormat.compressed:
- if not noDSA:
- texture.setCompressedData(i, 0, ddsCubemapFaces[face],
- len(self.data[faceIndex * mipCount + i]),
- self.data[faceIndex * mipCount + i])
- else:
- gl.glCompressedTexSubImage2D(ddsCubemapFaces[face], i, 0, 0,
- max(self.header.dwWidth // 2 ** i, 1),
- max(self.header.dwHeight // 2 ** i, 1),
- self.glFormat.internalFormat,
- len(self.data[faceIndex * mipCount + i]),
- self.data[faceIndex * mipCount + i])
- else:
- texture.setData(i, 0, ddsCubemapFaces[face], QOpenGLTexture.PixelFormat(self.glFormat.format), QOpenGLTexture.PixelType(self.glFormat.type),
- self.glFormat.converter(self.data[faceIndex * mipCount + i]))
- faceIndex += 1
- if noDSA:
- texture.release()
- else:
- for i in range(mipCount):
- if self.glFormat.compressed:
- texture.setCompressedData(i, 0, len(self.data[i]), self.data[i])
- else:
-
- texture.setData(i, 0, QOpenGLTexture.PixelFormat(self.glFormat.format), QOpenGLTexture.PixelType(self.glFormat.type),
- self.glFormat.converter(self.data[i]))
-
- texture.setWrapMode(QOpenGLTexture.WrapMode.ClampToEdge)
-
- if self.glFormat.samplerType != "F":
- # integer textures can't be filtered
- texture.setMinMagFilters(QOpenGLTexture.Filter.NearestMipMapNearest, QOpenGLTexture.Filter.Nearest)
-
- return texture
-
- def tr(self, str):
- return QCoreApplication.translate("DDSFile", str)
diff --git a/libs/dds_header/src/DDS/__init__.py b/libs/dds_header/src/DDS/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/libs/dds_header/src/DDS/__init__.py
+++ /dev/null
diff --git a/libs/dds_header/src/DDS/glstuff.py b/libs/dds_header/src/DDS/glstuff.py
deleted file mode 100644
index 314d48f..0000000
--- a/libs/dds_header/src/DDS/glstuff.py
+++ /dev/null
@@ -1,205 +0,0 @@
-from enum import IntEnum
-
-
-class GL_IMAGE_FORMAT(IntEnum):
- GL_BYTE = 0x1400
- GL_UNSIGNED_BYTE = 0x1401
- GL_SHORT = 0x1402
- GL_UNSIGNED_SHORT = 0x1403
- GL_INT = 0x1404
- GL_UNSIGNED_INT = 0x1405
- GL_FLOAT = 0x1406
- GL_HALF_FLOAT = 0x140B
- GL_COLOR_INDEX = 0x1900
- GL_STENCIL_INDEX = 0x1901
- GL_DEPTH_COMPONENT = 0x1902
- GL_RED = 0x1903
- GL_GREEN = 0x1904
- GL_BLUE = 0x1905
- GL_ALPHA = 0x1906
- GL_RGB = 0x1907
- GL_RGBA = 0x1908
- GL_LUMINANCE = 0x1909
- GL_LUMINANCE_ALPHA = 0x190A
- GL_BITMAP = 0x1A00
- GL_R3_G3_B2 = 0x2A10
- GL_UNSIGNED_BYTE_3_3_2 = 0x8032
- GL_UNSIGNED_SHORT_4_4_4_4 = 0x8033
- GL_UNSIGNED_SHORT_5_5_5_1 = 0x8034
- GL_UNSIGNED_INT_8_8_8_8 = 0x8035
- GL_UNSIGNED_INT_10_10_10_2 = 0x8036
- GL_ALPHA4 = 0x803B
- GL_ALPHA8 = 0x803C
- GL_ALPHA12 = 0x803D
- GL_ALPHA16 = 0x803E
- GL_LUMINANCE4 = 0x803F
- GL_LUMINANCE8 = 0x8040
- GL_LUMINANCE12 = 0x8041
- GL_LUMINANCE16 = 0x8042
- GL_LUMINANCE4_ALPHA4 = 0x8043
- GL_LUMINANCE6_ALPHA2 = 0x8044
- GL_LUMINANCE8_ALPHA8 = 0x8045
- GL_LUMINANCE12_ALPHA4 = 0x8046
- GL_LUMINANCE12_ALPHA12 = 0x8047
- GL_LUMINANCE16_ALPHA16 = 0x8048
- GL_INTENSITY = 0x8049
- GL_INTENSITY4 = 0x804A
- GL_INTENSITY8 = 0x804B
- GL_INTENSITY12 = 0x804C
- GL_INTENSITY16 = 0x804D
- GL_RGB4 = 0x804F
- GL_RGB5 = 0x8050
- GL_RGB8 = 0x8051
- GL_RGB10 = 0x8052
- GL_RGB12 = 0x8053
- GL_RGB16 = 0x8054
- GL_RGBA2 = 0x8055
- GL_RGBA4 = 0x8056
- GL_RGB5_A1 = 0x8057
- GL_RGBA8 = 0x8058
- GL_RGB10_A2 = 0x8059
- GL_RGBA12 = 0x805A
- GL_RGBA16 = 0x805B
- GL_BGR = 0x80E0
- GL_BGRA = 0x80E1
- GL_DEPTH_COMPONENT16 = 0x81A5
- GL_DEPTH_COMPONENT24 = 0x81A6
- GL_DEPTH_COMPONENT32 = 0x81A7
- GL_COMPRESSED_RED = 0x8225
- GL_COMPRESSED_RG = 0x8226
- GL_RG = 0x8227
- GL_RG_INTEGER = 0x8228
- GL_R8 = 0x8229
- GL_R16 = 0x822A
- GL_RG8 = 0x822B
- GL_RG16 = 0x822C
- GL_R16F = 0x822D
- GL_R32F = 0x822E
- GL_RG16F = 0x822F
- GL_RG32F = 0x8230
- GL_R8I = 0x8231
- GL_R8UI = 0x8232
- GL_R16I = 0x8233
- GL_R16UI = 0x8234
- GL_R32I = 0x8235
- GL_R32UI = 0x8236
- GL_RG8I = 0x8237
- GL_RG8UI = 0x8238
- GL_RG16I = 0x8239
- GL_RG16UI = 0x823A
- GL_RG32I = 0x823B
- GL_RG32UI = 0x823C
- GL_UNSIGNED_BYTE_2_3_3_REV = 0x8362
- GL_UNSIGNED_SHORT_5_6_5 = 0x8363
- GL_UNSIGNED_SHORT_5_6_5_REV = 0x8364
- GL_UNSIGNED_SHORT_4_4_4_4_REV = 0x8365
- GL_UNSIGNED_SHORT_1_5_5_5_REV = 0x8366
- GL_UNSIGNED_INT_8_8_8_8_REV = 0x8367
- GL_UNSIGNED_INT_2_10_10_10_REV = 0x8368
- GL_COMPRESSED_RGB_S3TC_DXT1_EXT = 0x83F0
- GL_COMPRESSED_RGBA_S3TC_DXT1_EXT = 0x83F1
- GL_COMPRESSED_RGBA_S3TC_DXT3_EXT = 0x83F2
- GL_COMPRESSED_RGBA_S3TC_DXT5_EXT = 0x83F3
- GL_COMPRESSED_ALPHA = 0x84E9
- GL_COMPRESSED_LUMINANCE = 0x84EA
- GL_COMPRESSED_LUMINANCE_ALPHA = 0x84EB
- GL_COMPRESSED_INTENSITY = 0x84EC
- GL_COMPRESSED_RGB = 0x84ED
- GL_COMPRESSED_RGBA = 0x84EE
- GL_DEPTH_STENCIL = 0x84F9
- GL_RGBA32F = 0x8814
- GL_RGB32F = 0x8815
- GL_RGBA16F = 0x881A
- GL_RGB16F = 0x881B
- GL_R11F_G11F_B10F = 0x8C3A
- GL_UNSIGNED_INT_10F_11F_11F_REV_EXT = 0x8C3B # EXT_packed_float
- GL_RGB9_E5 = 0x8C3D
- GL_UNSIGNED_INT_5_9_9_9_REV_EXT = 0x8C3E # EXT_texture_shared_exponent
- GL_SRGB = 0x8C40
- GL_SRGB8 = 0x8C41
- GL_SRGB_ALPHA = 0x8C42
- GL_SRGB8_ALPHA8 = 0x8C43
- GL_SLUMINANCE_ALPHA = 0x8C44
- GL_SLUMINANCE8_ALPHA8 = 0x8C45
- GL_SLUMINANCE = 0x8C46
- GL_SLUMINANCE8 = 0x8C47
- GL_COMPRESSED_SRGB = 0x8C48
- GL_COMPRESSED_SRGB_ALPHA = 0x8C49
- GL_COMPRESSED_SRGB_S3TC_DXT1_EXT = 0x8C4C
- GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT = 0x8C4D
- GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT = 0x8C4E
- GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT = 0x8C4F
- GL_RGBA32UI = 0x8D70
- GL_RGB32UI = 0x8D71
- GL_RGBA16UI = 0x8D76
- GL_RGB16UI = 0x8D77
- GL_RGBA8UI = 0x8D7C
- GL_RGB8UI = 0x8D7D
- GL_RGBA32I = 0x8D82
- GL_RGB32I = 0x8D83
- GL_RGBA16I = 0x8D88
- GL_RGB16I = 0x8D89
- GL_RGBA8I = 0x8D8E
- GL_RGB8I = 0x8D8F
- GL_RED_INTEGER = 0x8D94
- GL_RGB_INTEGER = 0x8D98
- GL_RGBA_INTEGER = 0x8D99
- GL_BGR_INTEGER = 0x8D9A
- GL_BGRA_INTEGER = 0x8D9B
- GL_COMPRESSED_RED_RGTC1 = 0x8DBB
- GL_COMPRESSED_SIGNED_RED_RGTC1 = 0x8DBC
- GL_COMPRESSED_RG_RGTC2 = 0x8DBD
- GL_COMPRESSED_SIGNED_RG_RGTC2 = 0x8DBE
- GL_COMPRESSED_RGBA_BPTC_UNORM = 0x8E8C
- GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM = 0x8E8D
- GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT = 0x8E8E
- GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT = 0x8E8F
- GL_R8_SNORM = 0x8F94
- GL_RG8_SNORM = 0x8F95
- GL_RGB8_SNORM = 0x8F96
- GL_RGBA8_SNORM = 0x8F97
- GL_R16_SNORM = 0x8F98
- GL_RG16_SNORM = 0x8F99
- GL_RGB16_SNORM = 0x8F9A
- GL_RGBA16_SNORM = 0x8F9B
- GL_RGB10_A2UI = 0x906F
- GL_COMPRESSED_R11_EAC = 0x9270
- GL_COMPRESSED_SIGNED_R11_EAC = 0x9271
- GL_COMPRESSED_RG11_EAC = 0x9272
- GL_COMPRESSED_SIGNED_RG11_EAC = 0x9273
- GL_COMPRESSED_RGB8_ETC2 = 0x9274
- GL_COMPRESSED_SRGB8_ETC2 = 0x9275
- GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 = 0x9276
- GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 = 0x9277
- GL_COMPRESSED_RGBA8_ETC2_EAC = 0x9278
- GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC = 0x9279
-
-
-class GLTextureFormat:
- def __init__(self, requirements, internalFormat, compressed):
- self.requirements = requirements
- self.internalFormat = internalFormat
- self.compressed = compressed
-
- if internalFormat.name.endswith("UI"):
- self.samplerType = "UI"
- elif internalFormat.name.endswith("I"):
- self.samplerType = "I"
- else:
- self.samplerType = "F"
-
-
-class CompressedGLTextureFormat(GLTextureFormat):
- def __init__(self, requirements, internalFormat):
- super().__init__(requirements, internalFormat, True)
-
-
-class UncompressedGLTextureFormat(GLTextureFormat):
- def __init__(self, requirements, internalFormat, format, type, converter=None):
- super().__init__(requirements, internalFormat, False)
- self.format = format
- self.type = type
- if converter:
- self.converter = converter
- else:
- self.converter = lambda x: x
diff --git a/libs/dds_header/src/DDSPreview.py b/libs/dds_header/src/DDSPreview.py
deleted file mode 100644
index 78ffffc..0000000
--- a/libs/dds_header/src/DDSPreview.py
+++ /dev/null
@@ -1,528 +0,0 @@
-import struct
-import sys
-import threading
-import enum
-
-from PyQt6.QtCore import QCoreApplication, qDebug, Qt, QSize
-from PyQt6.QtGui import QColor, QOpenGLContext, QSurfaceFormat, QMatrix4x4, QVector4D
-from PyQt6.QtOpenGLWidgets import QOpenGLWidget
-from PyQt6.QtWidgets import QGridLayout, QLabel, QPushButton, QWidget, QColorDialog, QComboBox
-from PyQt6.QtOpenGL import QOpenGLBuffer, QOpenGLDebugLogger, QOpenGLShader, QOpenGLShaderProgram, QOpenGLTexture, \
- QOpenGLVersionProfile, QOpenGLVertexArrayObject, QOpenGLFunctions_4_1_Core, QOpenGLVersionFunctionsFactory
-
-from DDS.DDSFile import DDSFile
-
-if "mobase" not in sys.modules:
- import mobase
-
-vertexShader2D = """
-#version 150
-
-uniform float aspectRatioRatio;
-
-in vec4 position;
-in vec2 texCoordIn;
-
-out vec2 texCoord;
-
-void main()
-{
- texCoord = texCoordIn;
- gl_Position = position;
- if (aspectRatioRatio >= 1.0)
- gl_Position.y /= aspectRatioRatio;
- else
- gl_Position.x *= aspectRatioRatio;
-}
-"""
-
-vertexShaderCube = """
-#version 150
-
-uniform float aspectRatioRatio;
-
-in vec4 position;
-in vec2 texCoordIn;
-
-out vec2 texCoord;
-
-void main()
-{
- texCoord = texCoordIn;
- gl_Position = position;
-}
-"""
-
-fragmentShaderFloat = """
-#version 150
-
-uniform sampler2D aTexture;
-uniform mat4 channelMatrix;
-uniform vec4 channelOffset;
-
-in vec2 texCoord;
-
-void main()
-{
- gl_FragData[0] = channelMatrix * texture(aTexture, texCoord) + channelOffset;
-}
-"""
-
-fragmentShaderUInt = """
-#version 150
-
-uniform usampler2D aTexture;
-uniform mat4 channelMatrix;
-uniform vec4 channelOffset;
-
-in vec2 texCoord;
-
-void main()
-{
- // autofilled alpha is 1, so if we have a scaling factor, we need separate ones for luminance and alpha
- gl_FragData[0] = channelMatrix * texture(aTexture, texCoord) + channelOffset;
-}
-"""
-
-fragmentShaderSInt = """
-#version 150
-
-uniform isampler2D aTexture;
-uniform mat4 channelMatrix;
-uniform vec4 channelOffset;
-
-in vec2 texCoord;
-
-void main()
-{
- // autofilled alpha is 1, so if we have a scaling factor and offset, we need separate ones for luminance and alpha
- gl_FragData[0] = channelMatrix * texture(aTexture, texCoord) + channelOffset;
-}
-"""
-
-fragmentShaderCube = """
-#version 150
-
-uniform samplerCube aTexture;
-uniform mat4 channelMatrix;
-uniform vec4 channelOffset;
-
-in vec2 texCoord;
-
-const float PI = 3.1415926535897932384626433832795;
-
-void main()
-{
- float theta = -2.0 * PI * texCoord.x;
- float phi = PI * texCoord.y;
- gl_FragData[0] = channelMatrix * texture(aTexture, vec3(sin(theta) * sin(phi), cos(theta) * sin(phi), cos(phi))) + channelOffset;
-}
-"""
-
-transparencyVS = """
-#version 150
-
-in vec4 position;
-
-void main()
-{
- gl_Position = position;
-}
-"""
-
-transparencyFS = """
-#version 150
-
-uniform vec4 backgroundColour;
-
-void main()
-{
- float x = gl_FragCoord.x;
- float y = gl_FragCoord.y;
- x = mod(x, 16.0);
- y = mod(y, 16.0);
- gl_FragData[0] = x < 8.0 ^^ y < 8.0 ? vec4(vec3(191.0/255.0), 1.0) : vec4(1.0);
- gl_FragData[0].rgb = backgroundColour.rgb * backgroundColour.a + gl_FragData[0].rgb * (1.0 - backgroundColour.a);
-}
-"""
-
-vertices = [
- # vertex coordinates texture coordinates
- -1.0, -1.0, 0.5, 1.0, 0.0, 1.0,
- -1.0, 1.0, 0.5, 1.0, 0.0, 0.0,
- 1.0, 1.0, 0.5, 1.0, 1.0, 0.0,
-
- -1.0, -1.0, 0.5, 1.0, 0.0, 1.0,
- 1.0, 1.0, 0.5, 1.0, 1.0, 0.0,
- 1.0, -1.0, 0.5, 1.0, 1.0, 1.0,
-]
-
-
-class DDSOptions:
- def __init__(self, colour: QColor = QColor(0, 0, 0, 0), channelMatrix: QMatrix4x4 = QMatrix4x4(),
- channelOffset: QVector4D = QVector4D()):
- # QMatrix4x4 with no arguments is the identity matrix, so no channel transformations
- # declare member variables with None values
- self.backgroundColour = None
- self.channelMatrix = None
- self.channelOffset = None
- # initialize member variables with error checks
- self.setBackgroundColour(colour)
- self.setChannelMatrix(channelMatrix)
- self.setChannelOffset(channelOffset)
-
- def setBackgroundColour(self, colour: QColor):
- if isinstance(colour, QColor) and colour.isValid():
- self.backgroundColour = colour
- else:
- raise TypeError(str(colour) + " is not a valid QColor object.")
-
- def getBackgroundColour(self) -> QColor:
- return self.backgroundColour
-
- def getChannelMatrix(self) -> QMatrix4x4:
- return self.channelMatrix
-
- def setChannelMatrix(self, matrix):
- self.channelMatrix = QMatrix4x4(matrix)
-
- def getChannelOffset(self) -> QVector4D:
- return self.channelOffset
-
- def setChannelOffset(self, vector):
- self.channelOffset = QVector4D(vector)
-
-
-glVersionProfile = QOpenGLVersionProfile()
-glVersionProfile.setVersion(2, 1)
-
-
-class DDSWidget(QOpenGLWidget):
- def __init__(self, ddsFile, ddsOptions=DDSOptions(), debugContext=False, parent=None, f=Qt.WindowType(0)):
- super(DDSWidget, self).__init__(parent, f)
-
- self.ddsFile = ddsFile
-
- self.ddsOptions = ddsOptions
-
- self.clean = True
-
- self.logger = None
-
- self.program = None
- self.transparecyProgram = None
- self.texture = None
- self.vbo = None
- self.vao = None
-
- if debugContext:
- format = QSurfaceFormat()
- format.setOption(QSurfaceFormat.FormatOption.DebugContext)
- self.setFormat(format)
- self.logger = QOpenGLDebugLogger(self)
-
- def __del__(self):
- self.cleanup()
-
- def __dtor__(self):
- self.cleanup()
-
- def initializeGL(self):
- if self.logger:
- self.logger.initialize()
- self.logger.messageLogged.connect(
- lambda message: qDebug(self.tr("OpenGL debug message: {0}").format(message.message())))
- self.logger.startLogging()
-
- gl = QOpenGLVersionFunctionsFactory.get(glVersionProfile)
- QOpenGLContext.currentContext().aboutToBeDestroyed.connect(self.cleanup)
-
- self.clean = False
-
- fragmentShader = None
- vertexShader = vertexShader2D
- if self.ddsFile.isCubemap:
- fragmentShader = fragmentShaderCube
- vertexShader = vertexShaderCube
- if QOpenGLContext.currentContext().hasExtension(b"GL_ARB_seamless_cube_map"):
- GL_TEXTURE_CUBE_MAP_SEAMLESS = 0x884F
- gl.glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS)
- elif self.ddsFile.glFormat.samplerType == "F":
- fragmentShader = fragmentShaderFloat
- elif self.ddsFile.glFormat.samplerType == "UI":
- fragmentShader = fragmentShaderUInt
- else:
- fragmentShader = fragmentShaderSInt
-
- self.program = QOpenGLShaderProgram(self)
- self.program.addShaderFromSourceCode(QOpenGLShader.ShaderTypeBit.Vertex, vertexShader)
- self.program.addShaderFromSourceCode(QOpenGLShader.ShaderTypeBit.Fragment, fragmentShader)
- self.program.bindAttributeLocation("position", 0)
- self.program.bindAttributeLocation("texCoordIn", 1)
- self.program.link()
-
- self.transparecyProgram = QOpenGLShaderProgram(self)
- self.transparecyProgram.addShaderFromSourceCode(QOpenGLShader.ShaderTypeBit.Vertex, transparencyVS)
- self.transparecyProgram.addShaderFromSourceCode(QOpenGLShader.ShaderTypeBit.Fragment, transparencyFS)
- self.transparecyProgram.bindAttributeLocation("position", 0)
- self.transparecyProgram.link()
-
- self.vao = QOpenGLVertexArrayObject(self)
- vaoBinder = QOpenGLVertexArrayObject.Binder(self.vao)
-
- self.vbo = QOpenGLBuffer(QOpenGLBuffer.Type.VertexBuffer)
- self.vbo.create()
- self.vbo.bind()
-
- theBytes = struct.pack("%sf" % len(vertices), *vertices)
- self.vbo.allocate(theBytes, len(theBytes))
-
- gl.glEnableVertexAttribArray(0)
- gl.glEnableVertexAttribArray(1)
- gl.glVertexAttribPointer(0, 4, gl.GL_FLOAT, False, 6 * 4, 0)
- gl.glVertexAttribPointer(1, 2, gl.GL_FLOAT, False, 6 * 4, 4 * 4)
-
- self.texture = self.ddsFile.asQOpenGLTexture(gl, QOpenGLContext.currentContext())
-
- def resizeGL(self, w, h):
- aspectRatioTex = self.texture.width() / self.texture.height() if self.texture else 1.0
- aspectRatioWidget = w / h
- ratioRatio = aspectRatioTex / aspectRatioWidget
-
- self.program.bind()
- self.program.setUniformValue("aspectRatioRatio", ratioRatio)
- self.program.release()
-
- def paintGL(self):
- gl = QOpenGLVersionFunctionsFactory.get(glVersionProfile)
-
- vaoBinder = QOpenGLVertexArrayObject.Binder(self.vao)
-
- # Draw checkerboard so transparency is obvious
- self.transparecyProgram.bind()
-
- backgroundColour = self.ddsOptions.getBackgroundColour()
- if backgroundColour and backgroundColour.isValid():
- self.transparecyProgram.setUniformValue("backgroundColour", backgroundColour)
-
- gl.glDrawArrays(gl.GL_TRIANGLES, 0, 6)
-
- self.transparecyProgram.release()
-
- self.program.bind()
-
- if self.texture:
- self.texture.bind()
-
- gl.glEnable(gl.GL_BLEND)
- gl.glBlendFunc(gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA)
-
- self.program.setUniformValue("channelMatrix", self.ddsOptions.getChannelMatrix())
- self.program.setUniformValue("channelOffset", self.ddsOptions.getChannelOffset())
-
- gl.glDrawArrays(gl.GL_TRIANGLES, 0, 6)
-
- if self.texture:
- self.texture.release()
- self.program.release()
-
- def cleanup(self):
- if not self.clean:
- self.makeCurrent()
-
- self.program = None
- self.transparecyProgram = None
- if self.texture:
- self.texture.destroy()
- self.texture = None
- self.vbo.destroy()
- self.vbo = None
- self.vao.destroy()
- self.vao = None
-
- self.doneCurrent()
- self.clean = True
-
- def tr(self, str):
- return QCoreApplication.translate("DDSWidget", str)
-
-
-class ColourChannels(enum.Enum):
- RGBA = "Colour and Alpha"
- RGB = "Colour"
- A = "Alpha"
- R = "Red"
- G = "Green"
- B = "Blue"
-
-
-class DDSChannelManager:
- def __init__(self, channels: ColourChannels):
- self.channels = channels
-
- def setChannels(self, options: DDSOptions, channels: ColourChannels):
- self.channels = channels
-
- def drawColour(alpha: bool):
- colorMatrix = QMatrix4x4()
- colorOffset = QVector4D()
- if not alpha:
- colorMatrix[3, 3] = 0
- colorOffset.setW(1.0)
- options.setChannelMatrix(colorMatrix)
- options.setChannelOffset(colorOffset)
-
- def drawGrayscale(channel: ColourChannels):
- colorOffset = QVector4D(0, 0, 0, 1)
- channelVector = [0, 0, 0, 0]
- if channels == ColourChannels.R:
- channelVector[0] = 1
- elif channel == ColourChannels.G:
- channelVector[1] = 1
- elif channel == ColourChannels.B:
- channelVector[2] = 1
- elif channels == ColourChannels.A:
- channelVector[3] = 1
- else:
- raise ValueError("channel must be a single color channel.")
- alphaVector = [0, 0, 0, 0]
- colorMatrix = channelVector * 3 + alphaVector
- options.setChannelMatrix(colorMatrix)
- options.setChannelOffset(colorOffset)
-
- if channels == ColourChannels.RGBA:
- drawColour(True)
- elif channels == ColourChannels.RGB:
- drawColour(False)
- else:
- drawGrayscale(channels)
-
-
-class DDSPreview(mobase.IPluginPreview):
-
- def __init__(self):
- super().__init__()
- self.__organizer = None
- self.options = None
- self.channelManager = None
-
- def init(self, organizer: mobase.IOrganizer):
- self.__organizer = organizer
- savedColour = QColor(self.pluginSetting("background r"), self.pluginSetting("background g"),
- self.pluginSetting("background b"), self.pluginSetting("background a"))
- try:
- savedChannels = ColourChannels[self.pluginSetting("channels")]
- except KeyError:
- savedChannels = ColourChannels.RGBA
- self.options = DDSOptions(savedColour)
- self.channelManager = DDSChannelManager(savedChannels)
- self.channelManager.setChannels(self.options, savedChannels)
- return True
-
- def pluginSetting(self, name):
- return self.__organizer.pluginSetting(self.name(), name)
-
- def setPluginSetting(self, name, value):
- self.__organizer.setPluginSetting(self.name(), name, value)
-
- def name(self):
- return "DDS Preview Plugin"
-
- def author(self):
- return "AnyOldName3"
-
- def description(self):
- return self.tr("Lets you preview DDS files by actually uploading them to the GPU.")
-
- def version(self):
- return mobase.VersionInfo(1, 0, 1, 0)
-
- def settings(self):
- return [mobase.PluginSetting("log gl errors", self.tr(
- "If enabled, log OpenGL errors and debug messages. May decrease performance."), False),
- mobase.PluginSetting("background r", self.tr("Red channel of background colour"), 0),
- mobase.PluginSetting("background g", self.tr("Green channel of background colour"), 0),
- mobase.PluginSetting("background b", self.tr("Blue channel of background colour"), 0),
- mobase.PluginSetting("background a", self.tr("Alpha channel of background colour"), 0),
- mobase.PluginSetting("channels", self.tr("The colour channels that are displayed."),
- ColourChannels.RGBA.name)]
-
- def supportedExtensions(self) -> set[str]:
- return {"dds"}
-
- def supportsArchives(self) -> bool:
- return True
-
- def genFilePreview(self, fileName: str, maxSize: QSize) -> QWidget:
- return self.previewFromDDSFile(DDSFile.fromFile(fileName))
-
- def genDataPreview(self, fileData: bytes, fileName: str, maxSize: QSize) -> QWidget:
- return self.previewFromDDSFile(DDSFile(fileData, fileName))
-
- def previewFromDDSFile(self, ddsFile: DDSFile) -> QWidget:
- ddsFile.load()
- layout = QGridLayout()
- # Image grows before label and button
- layout.setRowStretch(0, 1)
- # Label grows before button
- layout.setColumnStretch(0, 1)
- layout.addWidget(self.__makeLabel(ddsFile), 1, 0, 1, 1)
-
- ddsWidget = DDSWidget(ddsFile, self.options, self.__organizer.pluginSetting(self.name(), "log gl errors"))
- layout.addWidget(ddsWidget, 0, 0, 1, 3)
-
- layout.addWidget(self.__makeColourButton(ddsWidget), 1, 2, 1, 1)
- layout.addWidget(self.__makeChannelsButton(ddsWidget), 1, 1, 1, 1)
-
- widget = QWidget()
- widget.setLayout(layout)
- return widget
-
- def tr(self, str):
- return QCoreApplication.translate("DDSPreview", str)
-
- def __makeLabel(self, ddsFile):
- label = QLabel(ddsFile.getDescription())
- label.setWordWrap(True)
- label.setTextInteractionFlags(Qt.TextInteractionFlag.TextSelectableByMouse)
- return label
-
- def __makeColourButton(self, ddsWidget):
- button = QPushButton(self.tr("Pick background colour"))
-
- def pickColour(unused):
- newColour = QColorDialog.getColor(self.options.getBackgroundColour(), button, "Background colour",
- QColorDialog.ColorDialogOption.ShowAlphaChannel)
- if newColour.isValid():
- self.setPluginSetting("background r", newColour.red())
- self.setPluginSetting("background g", newColour.green())
- self.setPluginSetting("background b", newColour.blue())
- self.setPluginSetting("background a", newColour.alpha())
- self.options.setBackgroundColour(newColour)
- ddsWidget.update()
-
- button.clicked.connect(pickColour)
- return button
-
- def __makeChannelsButton(self, ddsWidget):
- listwidget = QComboBox()
- channelKeys = [e.name for e in ColourChannels]
- channelNames = [e.value for e in ColourChannels]
-
- listwidget.addItems(channelNames)
- listwidget.setCurrentText(self.channelManager.channels.value)
- listwidget.setToolTip(self.tr("Select what colour channels are displayed."))
-
- listwidget.showEvent = lambda _: listwidget.setCurrentText(self.channelManager.channels.value)
-
- def onChanged(newIndex):
- self.channelManager.setChannels(self.options, ColourChannels[channelKeys[newIndex]])
- self.setPluginSetting("channels", self.channelManager.channels.name)
- ddsWidget.update()
-
- listwidget.currentIndexChanged.connect(onChanged)
- return listwidget
-
-
-def createPlugin():
- return DDSPreview()
diff --git a/libs/dds_header/src/DDSPreview_en.ts b/libs/dds_header/src/DDSPreview_en.ts
deleted file mode 100644
index bc6308a..0000000
--- a/libs/dds_header/src/DDSPreview_en.ts
+++ /dev/null
@@ -1,128 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!DOCTYPE TS>
-<TS version="2.0">
- <context>
- <name>DDSFile</name>
- <message>
- <location filename="DDS\DDSFile.py" line="47" />
- <source>Magic number mismatch.</source>
- <translation type="unfinished" />
- </message>
- <message>
- <location filename="DDS\DDSFile.py" line="101" />
- <source>{0} (equivalent to {1})</source>
- <translation type="unfinished" />
- </message>
- <message>
- <location filename="DDS\DDSFile.py" line="109" />
- <source>Red bitmask {0}, Green bitmask {1}, Blue bitmask {2}</source>
- <translation type="unfinished" />
- </message>
- <message>
- <location filename="DDS\DDSFile.py" line="115" />
- <source>Luminance bitmask {0}</source>
- <translation type="unfinished" />
- </message>
- <message>
- <location filename="DDS\DDSFile.py" line="120" />
- <source>Alpha bitmask {0}</source>
- <translation type="unfinished" />
- </message>
- <message>
- <location filename="DDS\DDSFile.py" line="122" />
- <source>{0}×{1}</source>
- <translation type="unfinished" />
- </message>
- <message>
- <location filename="DDS\DDSFile.py" line="124" />
- <source>Cubemap</source>
- <translation type="unfinished" />
- </message>
- <message>
- <location filename="DDS\DDSFile.py" line="124" />
- <source>2D</source>
- <translation type="unfinished" />
- </message>
- <message>
- <location filename="DDS\DDSFile.py" line="126" />
- <source>Mipmapped</source>
- <translation type="unfinished" />
- </message>
- <message>
- <location filename="DDS\DDSFile.py" line="126" />
- <source>No mipmaps</source>
- <translation type="unfinished" />
- </message>
- <message>
- <location filename="DDS\DDSFile.py" line="128" />
- <source>{0}, {1} {2}, {3}</source>
- <translation type="unfinished" />
- </message>
- <message>
- <location filename="DDS\DDSFile.py" line="150" />
- <source>OpenGL driver incompatible with texture format.</source>
- <translation type="unfinished" />
- </message>
- <message>
- <location filename="DDS\DDSFile.py" line="156" />
- <source>Cubemap faces must be square</source>
- <translation type="unfinished" />
- </message>
- </context>
- <context>
- <name>DDSPreview</name>
- <message>
- <location filename="DDSPreview.py" line="435" />
- <source>Lets you preview DDS files by actually uploading them to the GPU.</source>
- <translation type="unfinished" />
- </message>
- <message>
- <location filename="DDSPreview.py" line="441" />
- <source>If enabled, log OpenGL errors and debug messages. May decrease performance.</source>
- <translation type="unfinished" />
- </message>
- <message>
- <location filename="DDSPreview.py" line="443" />
- <source>Red channel of background colour</source>
- <translation type="unfinished" />
- </message>
- <message>
- <location filename="DDSPreview.py" line="444" />
- <source>Green channel of background colour</source>
- <translation type="unfinished" />
- </message>
- <message>
- <location filename="DDSPreview.py" line="445" />
- <source>Blue channel of background colour</source>
- <translation type="unfinished" />
- </message>
- <message>
- <location filename="DDSPreview.py" line="446" />
- <source>Alpha channel of background colour</source>
- <translation type="unfinished" />
- </message>
- <message>
- <location filename="DDSPreview.py" line="447" />
- <source>The colour channels that are displayed.</source>
- <translation type="unfinished" />
- </message>
- <message>
- <location filename="DDSPreview.py" line="491" />
- <source>Pick background colour</source>
- <translation type="unfinished" />
- </message>
- <message>
- <location filename="DDSPreview.py" line="514" />
- <source>Select what colour channels are displayed.</source>
- <translation type="unfinished" />
- </message>
- </context>
- <context>
- <name>DDSWidget</name>
- <message>
- <location filename="DDSPreview.py" line="234" />
- <source>OpenGL debug message: {0}</source>
- <translation type="unfinished" />
- </message>
- </context>
-</TS>
diff --git a/libs/dds_header/vcpkg.json b/libs/dds_header/vcpkg.json
deleted file mode 100644
index da01ba4..0000000
--- a/libs/dds_header/vcpkg.json
+++ /dev/null
@@ -1,15 +0,0 @@
-{
- "features": {
- "standalone": {
- "description": "Build Standalone.",
- "dependencies": ["mo2-cmake", "mo2-uibase"]
- }
- },
- "vcpkg-configuration": {
- "default-registry": {
- "kind": "git",
- "repository": "https://github.com/ModOrganizer2/vcpkg-registry",
- "baseline": "27d8adbfe9e4ce88a875be3a45fadab69869eb60"
- }
- }
-}
diff --git a/libs/form43_checker_native/CMakeLists.txt b/libs/form43_checker_native/CMakeLists.txt
deleted file mode 100644
index e0f1d08..0000000
--- a/libs/form43_checker_native/CMakeLists.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-cmake_minimum_required(VERSION 3.16)
-
-project(form43_checker_native)
-
-add_subdirectory(src)
diff --git a/libs/form43_checker_native/src/CMakeLists.txt b/libs/form43_checker_native/src/CMakeLists.txt
deleted file mode 100644
index 9e847f6..0000000
--- a/libs/form43_checker_native/src/CMakeLists.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-cmake_minimum_required(VERSION 3.16)
-
-file(GLOB form43_checker_native_SOURCES CONFIGURE_DEPENDS
- ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp
- ${CMAKE_CURRENT_SOURCE_DIR}/*.h
-)
-
-add_library(form43_checker_native SHARED ${form43_checker_native_SOURCES})
-mo2_configure_plugin(form43_checker_native NO_SOURCES WARNINGS OFF)
-target_link_libraries(form43_checker_native PRIVATE mo2::uibase)
-mo2_install_plugin(form43_checker_native)
diff --git a/libs/form43_checker_native/src/form43checker.cpp b/libs/form43_checker_native/src/form43checker.cpp
deleted file mode 100644
index e3cb676..0000000
--- a/libs/form43_checker_native/src/form43checker.cpp
+++ /dev/null
@@ -1,131 +0,0 @@
-#include "form43checker.h"
-
-#include <uibase/ipluginlist.h>
-#include <uibase/pluginrequirements.h>
-
-#include <QCoreApplication>
-#include <QFileInfo>
-
-using namespace MOBase;
-
-Form43Checker::Form43Checker() : m_organizer(nullptr) {}
-
-bool Form43Checker::init(IOrganizer* moInfo)
-{
- m_organizer = moInfo;
- return true;
-}
-
-QString Form43Checker::name() const
-{
- return "Form 43 Plugin Checker (Native)";
-}
-
-QString Form43Checker::localizedName() const
-{
- return tr("Form 43 Plugin Checker (Native)");
-}
-
-QString Form43Checker::author() const
-{
- return "AnyOldName3";
-}
-
-QString Form43Checker::description() const
-{
- return tr("Checks plugins (.ESM/.ESP files) to see if any are lower than "
- "Form 44 (Skyrim SE).");
-}
-
-VersionInfo Form43Checker::version() const
-{
- return VersionInfo(1, 2, 0, VersionInfo::RELEASE_FINAL);
-}
-
-std::vector<std::shared_ptr<const IPluginRequirement>>
-Form43Checker::requirements() const
-{
- return {PluginRequirementFactory::gameDependency("Skyrim Special Edition")};
-}
-
-QList<PluginSetting> Form43Checker::settings() const
-{
- return {};
-}
-
-std::vector<unsigned int> Form43Checker::activeProblems() const
-{
- updateInvalidPlugins();
- if (!m_invalidPlugins.isEmpty()) {
- return {PROBLEM_FORM43};
- }
- return {};
-}
-
-QString Form43Checker::shortDescription(unsigned int key) const
-{
- return tr("Form 43 (or lower) plugin detected");
-}
-
-QString Form43Checker::fullDescription(unsigned int key) const
-{
- QStringList plugins = listPlugins();
- QString pluginListString =
- "<br><br>\u2022 " + plugins.join("<br>\u2022 ");
-
- QString output = tr("You have one or more plugins that are not form 44. "
- "They are:%1")
- .arg(pluginListString);
- output += "<br><br>";
- output += tr(
- "Form 43 (or lower) plugins are modules that were made for Skyrim LE "
- "(Oldrim) and have not been properly ported to Skyrim Special Edition, "
- "which uses form 44 plugins. This usually results in parts of the mod "
- "not working correctly."
- "<br><br>"
- "To be converted, these plugins simply need to be opened and saved with "
- "the SSE Creation Kit but their presence can be an indication that a mod "
- "was not properly ported to SSE and so can potentially have additional "
- "issues."
- "<br><br>"
- "Online guides can have more information on how to correctly convert mods "
- "for Skyrim SE.<br>");
- return output;
-}
-
-bool Form43Checker::hasGuidedFix(unsigned int key) const
-{
- return false;
-}
-
-void Form43Checker::startGuidedFix(unsigned int key) const {}
-
-void Form43Checker::updateInvalidPlugins() const
-{
- m_invalidPlugins.clear();
- QStringList files = m_organizer->findFiles(
- "", QStringList() << "*.esp" << "*.esm");
- for (const QString& file : files) {
- int ver = getFormVersion(file);
- if (ver != -1 && ver < 44) {
- m_invalidPlugins.append(file);
- }
- }
-}
-
-int Form43Checker::getFormVersion(const QString& file) const
-{
- QString pluginName = QFileInfo(file).fileName();
- return m_organizer->pluginList()->formVersion(pluginName);
-}
-
-QStringList Form43Checker::listPlugins() const
-{
- QStringList result;
- for (const QString& file : m_invalidPlugins) {
- QString pluginName = QFileInfo(file).fileName();
- int ver = getFormVersion(file);
- result.append(QString("%1 (form %2)").arg(pluginName).arg(ver));
- }
- return result;
-}
diff --git a/libs/form43_checker_native/src/form43checker.h b/libs/form43_checker_native/src/form43checker.h
deleted file mode 100644
index 583c7fa..0000000
--- a/libs/form43_checker_native/src/form43checker.h
+++ /dev/null
@@ -1,53 +0,0 @@
-#ifndef FORM43CHECKER_H
-#define FORM43CHECKER_H
-
-#include <memory>
-
-#include <QString>
-#include <QStringList>
-
-#include <uibase/iplugin.h>
-#include <uibase/iplugindiagnose.h>
-#include <uibase/pluginrequirements.h>
-
-class Form43Checker : public QObject,
- public MOBase::IPlugin,
- public MOBase::IPluginDiagnose
-{
- Q_OBJECT
- Q_INTERFACES(MOBase::IPlugin MOBase::IPluginDiagnose)
- Q_PLUGIN_METADATA(IID "org.tannin.Form43CheckerNative")
-
-public:
- Form43Checker();
-
-public: // IPlugin
- bool init(MOBase::IOrganizer* moInfo) override;
- QString name() const override;
- QString localizedName() const override;
- QString author() const override;
- QString description() const override;
- MOBase::VersionInfo version() const override;
- std::vector<std::shared_ptr<const MOBase::IPluginRequirement>>
- requirements() const override;
- QList<MOBase::PluginSetting> settings() const override;
-
-public: // IPluginDiagnose
- std::vector<unsigned int> activeProblems() const override;
- QString shortDescription(unsigned int key) const override;
- QString fullDescription(unsigned int key) const override;
- bool hasGuidedFix(unsigned int key) const override;
- void startGuidedFix(unsigned int key) const override;
-
-private:
- static const unsigned int PROBLEM_FORM43 = 0;
-
- void updateInvalidPlugins() const;
- int getFormVersion(const QString& file) const;
- QStringList listPlugins() const;
-
- MOBase::IOrganizer* m_organizer;
- mutable QStringList m_invalidPlugins;
-};
-
-#endif // FORM43CHECKER_H
diff --git a/libs/game_gamebryo/.clang-format b/libs/game_gamebryo/.clang-format
deleted file mode 100644
index 6098e1f..0000000
--- a/libs/game_gamebryo/.clang-format
+++ /dev/null
@@ -1,41 +0,0 @@
----
-# We'll use defaults from the LLVM style, but with 4 columns indentation.
-BasedOnStyle: LLVM
-IndentWidth: 2
----
-Language: Cpp
-DeriveLineEnding: false
-UseCRLF: true
-DerivePointerAlignment: false
-PointerAlignment: Left
-AlignConsecutiveAssignments: true
-AllowShortFunctionsOnASingleLine: Inline
-AllowShortIfStatementsOnASingleLine: Never
-AllowShortLambdasOnASingleLine: Empty
-AlwaysBreakTemplateDeclarations: Yes
-AccessModifierOffset: -2
-AlignTrailingComments: true
-SpacesBeforeTrailingComments: 2
-NamespaceIndentation: Inner
-MaxEmptyLinesToKeep: 1
-BreakBeforeBraces: Custom
-BraceWrapping:
- AfterCaseLabel: false
- AfterClass: true
- AfterControlStatement: false
- AfterEnum: true
- AfterFunction: true
- AfterNamespace: true
- AfterStruct: true
- AfterUnion: true
- AfterExternBlock: true
- BeforeCatch: false
- BeforeElse: false
- BeforeLambdaBody: false
- BeforeWhile: false
- IndentBraces: false
- SplitEmptyFunction: false
- SplitEmptyRecord: false
- SplitEmptyNamespace: true
-ColumnLimit: 88
-ForEachMacros: ['Q_FOREACH', 'foreach']
diff --git a/libs/game_gamebryo/.git-blame-ignore-revs b/libs/game_gamebryo/.git-blame-ignore-revs
deleted file mode 100644
index 7db8755..0000000
--- a/libs/game_gamebryo/.git-blame-ignore-revs
+++ /dev/null
@@ -1 +0,0 @@
-f4c5c5535551ed085bd65cb02058dcab2c9eb110
diff --git a/libs/game_gamebryo/.gitattributes b/libs/game_gamebryo/.gitattributes
deleted file mode 100644
index f869712..0000000
--- a/libs/game_gamebryo/.gitattributes
+++ /dev/null
@@ -1,7 +0,0 @@
-# Set the default behavior, in case people don't have core.autocrlf set.
-* text=auto
-
-# Explicitly declare text files you want to always be normalized and converted
-# to native line endings on checkout.
-*.cpp text eol=crlf
-*.h text eol=crlf
diff --git a/libs/game_gamebryo/.github/workflows/build.yml b/libs/game_gamebryo/.github/workflows/build.yml
deleted file mode 100644
index 7efc43f..0000000
--- a/libs/game_gamebryo/.github/workflows/build.yml
+++ /dev/null
@@ -1,17 +0,0 @@
-name: Build GameBryo Library
-
-on:
- push:
- branches: master
- pull_request:
- types: [opened, synchronize, reopened]
-
-jobs:
- build:
- runs-on: windows-2022
- steps:
- - name: Build GameBryo Library
- uses: ModOrganizer2/build-with-mob-action@master
- with:
- mo2-third-parties: lz4 zlib
- mo2-dependencies: cmake_common uibase
diff --git a/libs/game_gamebryo/.github/workflows/linting.yml b/libs/game_gamebryo/.github/workflows/linting.yml
deleted file mode 100644
index 1b8fe2a..0000000
--- a/libs/game_gamebryo/.github/workflows/linting.yml
+++ /dev/null
@@ -1,16 +0,0 @@
-name: Lint GameBryo Library
-
-on:
- push:
- pull_request:
- types: [opened, synchronize, reopened]
-
-jobs:
- lint:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v3
- - name: Check format
- uses: ModOrganizer2/check-formatting-action@master
- with:
- check-path: "."
diff --git a/libs/game_gamebryo/.gitignore b/libs/game_gamebryo/.gitignore
deleted file mode 100644
index cf71be7..0000000
--- a/libs/game_gamebryo/.gitignore
+++ /dev/null
@@ -1,5 +0,0 @@
-edit
-CMakeLists.txt.user
-/msbuild.log
-/*std*.log
-/*build
diff --git a/libs/game_gamebryo/CMakeLists.txt b/libs/game_gamebryo/CMakeLists.txt
deleted file mode 100644
index 5b7ffba..0000000
--- a/libs/game_gamebryo/CMakeLists.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-cmake_minimum_required(VERSION 3.16)
-
-if(DEFINED DEPENDENCIES_DIR)
- include(${DEPENDENCIES_DIR}/modorganizer_super/cmake_common/mo2.cmake)
-else()
- include(${CMAKE_CURRENT_LIST_DIR}/../cmake_common/mo2.cmake)
-endif()
-
-project(game_gamebryo)
-
-add_subdirectory(src/gamebryo)
-add_subdirectory(src/creation)
diff --git a/libs/game_gamebryo/README.md b/libs/game_gamebryo/README.md
deleted file mode 100644
index 08b5eb4..0000000
--- a/libs/game_gamebryo/README.md
+++ /dev/null
@@ -1,5 +0,0 @@
-# Archived repository - Check [modorganizer-game_bethesda](https://github.com/ModOrganizer2/modorganizer-game_bethesda)
-
-
-ModOrganizer2 plugins for most Bethesda games are now managed in a single repository
-at https://github.com/ModOrganizer2/modorganizer-game_bethesda.
diff --git a/libs/game_gamebryo/src/creation/CMakeLists.txt b/libs/game_gamebryo/src/creation/CMakeLists.txt
deleted file mode 100644
index f2dce97..0000000
--- a/libs/game_gamebryo/src/creation/CMakeLists.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-cmake_minimum_required(VERSION 3.16)
-
-add_library(game_creation STATIC)
-mo2_configure_library(game_creation
- WARNINGS OFF
- TRANSLATIONS ON
- PUBLIC_DEPENDS uibase
- PRIVATE_DEPENDS lz4)
-target_link_libraries(game_creation PUBLIC game_gamebryo)
-mo2_install_target(game_creation)
diff --git a/libs/game_gamebryo/src/creation/creationgameplugins.cpp b/libs/game_gamebryo/src/creation/creationgameplugins.cpp
deleted file mode 100644
index f5cef62..0000000
--- a/libs/game_gamebryo/src/creation/creationgameplugins.cpp
+++ /dev/null
@@ -1,183 +0,0 @@
-#include "creationgameplugins.h"
-#include <ipluginlist.h>
-#include <report.h>
-#include <safewritefile.h>
-#include <scopeguard.h>
-
-#include <QDir>
-#include <QSet>
-#include <QStringEncoder>
-#include <QStringList>
-
-using MOBase::IOrganizer;
-using MOBase::IPluginGame;
-using MOBase::IPluginList;
-using MOBase::reportError;
-using MOBase::SafeWriteFile;
-
-CreationGamePlugins::CreationGamePlugins(IOrganizer* organizer)
- : GamebryoGamePlugins(organizer)
-{}
-
-QStringList CreationGamePlugins::getLoadOrder()
-{
- QString loadOrderPath = organizer()->profile()->absolutePath() + "/loadorder.txt";
- QString pluginsPath = organizer()->profile()->absolutePath() + "/plugins.txt";
-
- bool loadOrderIsNew = !m_LastRead.isValid() || !QFileInfo(loadOrderPath).exists() ||
- QFileInfo(loadOrderPath).lastModified() > m_LastRead;
- bool pluginsIsNew =
- !m_LastRead.isValid() || QFileInfo(pluginsPath).lastModified() > m_LastRead;
-
- if (loadOrderIsNew || !pluginsIsNew) {
- return readLoadOrderList(m_Organizer->pluginList(), loadOrderPath);
- } else {
- return readPluginList(m_Organizer->pluginList());
- }
-}
-
-void CreationGamePlugins::writePluginList(const IPluginList* pluginList,
- const QString& filePath)
-{
- SafeWriteFile file(filePath);
-
- QStringEncoder encoder(QStringConverter::Encoding::System);
-
- file->resize(0);
-
- file->write(
- encoder.encode("# This file was automatically generated by Mod Organizer.\r\n"));
-
- bool invalidFileNames = false;
- int writtenCount = 0;
-
- QStringList plugins = pluginList->pluginNames();
- std::sort(plugins.begin(), plugins.end(),
- [pluginList](const QString& lhs, const QString& rhs) {
- return pluginList->priority(lhs) < pluginList->priority(rhs);
- });
-
- QStringList PrimaryPlugins = organizer()->managedGame()->primaryPlugins();
- QStringList DLCPlugins = organizer()->managedGame()->DLCPlugins();
- QSet<QString> ManagedMods =
- QSet<QString>(PrimaryPlugins.begin(), PrimaryPlugins.end());
- QSet<QString> DLCSet = QSet<QString>(DLCPlugins.begin(), DLCPlugins.end());
- ManagedMods.subtract(DLCSet);
- PrimaryPlugins.append(QList<QString>(ManagedMods.begin(), ManagedMods.end()));
-
- // TODO: do not write plugins in OFFICIAL_FILES container
- for (const QString& pluginName : plugins) {
- if (!PrimaryPlugins.contains(pluginName, Qt::CaseInsensitive)) {
- if (pluginList->state(pluginName) == IPluginList::STATE_ACTIVE) {
- auto result = encoder.encode(pluginName);
- if (encoder.hasError()) {
- invalidFileNames = true;
- qCritical("invalid plugin name %s", qUtf8Printable(pluginName));
- } else {
- file->write("*");
- file->write(result);
- }
- file->write("\r\n");
- ++writtenCount;
- } else {
- auto result = encoder.encode(pluginName);
- if (encoder.hasError()) {
- invalidFileNames = true;
- qCritical("invalid plugin name %s", qUtf8Printable(pluginName));
- } else {
- file->write(result);
- }
- file->write("\r\n");
- ++writtenCount;
- }
- }
- }
-
- if (invalidFileNames) {
- reportError(QObject::tr("Some of your plugins have invalid names! These "
- "plugins can not be loaded by the game. Please see "
- "mo_interface.log for a list of affected plugins "
- "and rename them."));
- }
-
- file->commit();
-}
-
-QStringList CreationGamePlugins::readPluginList(MOBase::IPluginList* pluginList)
-{
- const auto plugins = pluginList->pluginNames();
- const auto primaryPlugins = organizer()->managedGame()->primaryPlugins();
- QStringList loadOrder(primaryPlugins);
-
- for (const QString& pluginName : loadOrder) {
- if (pluginList->state(pluginName) != IPluginList::STATE_MISSING) {
- pluginList->setState(pluginName, IPluginList::STATE_ACTIVE);
- }
- }
-
- QString filePath = organizer()->profile()->absolutePath() + "/plugins.txt";
- QFile file(filePath);
- if (!file.open(QIODevice::ReadOnly)) {
- qWarning("%s not found", qUtf8Printable(filePath));
- return loadOrder;
- }
- ON_BLOCK_EXIT([&]() {
- file.close();
- });
-
- if (file.size() == 0) {
- // MO stores at least a header in the file. if it's completely empty the
- // file is broken
- qWarning("%s empty", qUtf8Printable(filePath));
- return loadOrder;
- }
-
- QStringList pluginsFound;
- while (!file.atEnd()) {
- QByteArray line = file.readLine();
- QString pluginName;
- if ((line.size() > 0) && (line.at(0) != '#')) {
- pluginName = QStringEncoder(QStringConverter::Encoding::System)
- .encode(line.trimmed().constData());
- }
- if (!primaryPlugins.contains(pluginName, Qt::CaseInsensitive)) {
- if (pluginName.startsWith('*')) {
- pluginName.remove(0, 1);
- if (pluginName.size() > 0) {
- pluginList->setState(pluginName, IPluginList::STATE_ACTIVE);
- pluginsFound.append(pluginName);
- if (!loadOrder.contains(pluginName, Qt::CaseInsensitive)) {
- loadOrder.append(pluginName);
- }
- }
- } else {
- if (pluginName.size() > 0) {
- pluginList->setState(pluginName, IPluginList::STATE_INACTIVE);
- pluginsFound.append(pluginName);
- if (!loadOrder.contains(pluginName, Qt::CaseInsensitive)) {
- loadOrder.append(pluginName);
- }
- }
- }
- } else {
- pluginName.remove(0, 1);
- pluginsFound.append(pluginName);
- }
- }
-
- file.close();
-
- // set all plugins not found inactive
- for (const auto& pluginName : plugins) {
- if (!pluginsFound.contains(pluginName, Qt::CaseInsensitive)) {
- pluginList->setState(pluginName, IPluginList::STATE_INACTIVE);
- }
- }
-
- return loadOrder;
-}
-
-bool CreationGamePlugins::lightPluginsAreSupported()
-{
- return true;
-}
diff --git a/libs/game_gamebryo/src/creation/creationgameplugins.h b/libs/game_gamebryo/src/creation/creationgameplugins.h
deleted file mode 100644
index f7afd80..0000000
--- a/libs/game_gamebryo/src/creation/creationgameplugins.h
+++ /dev/null
@@ -1,22 +0,0 @@
-#ifndef CREATIONGAMEPLUGINS_H
-#define CREATIONGAMEPLUGINS_H
-
-#include <gamebryogameplugins.h>
-#include <imoinfo.h>
-#include <iplugingame.h>
-#include <map>
-
-class CreationGamePlugins : public GamebryoGamePlugins
-{
-public:
- CreationGamePlugins(MOBase::IOrganizer* organizer);
-
-protected:
- virtual void writePluginList(const MOBase::IPluginList* pluginList,
- const QString& filePath) override;
- virtual QStringList readPluginList(MOBase::IPluginList* pluginList) override;
- virtual QStringList getLoadOrder() override;
- virtual bool lightPluginsAreSupported() override;
-};
-
-#endif // CREATIONGAMEPLUGINS_H
diff --git a/libs/game_gamebryo/src/creation/game_creation_en.ts b/libs/game_gamebryo/src/creation/game_creation_en.ts
deleted file mode 100644
index f217448..0000000
--- a/libs/game_gamebryo/src/creation/game_creation_en.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!DOCTYPE TS>
-<TS version="2.1" language="en_US">
-<context>
- <name>QObject</name>
- <message>
- <location filename="creationgameplugins.cpp" line="97"/>
- <source>Some of your plugins have invalid names! These plugins can not be loaded by the game. Please see mo_interface.log for a list of affected plugins and rename them.</source>
- <translation type="unfinished"></translation>
- </message>
-</context>
-</TS>
diff --git a/libs/game_gamebryo/src/gamebryo/CMakeLists.txt b/libs/game_gamebryo/src/gamebryo/CMakeLists.txt
deleted file mode 100644
index cff88fc..0000000
--- a/libs/game_gamebryo/src/gamebryo/CMakeLists.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-cmake_minimum_required(VERSION 3.16)
-
-add_library(game_gamebryo STATIC)
-mo2_configure_library(game_gamebryo
- WARNINGS OFF
- TRANSLATIONS ON
- AUTOMOC ON
- PUBLIC_DEPENDS uibase
- PRIVATE_DEPENDS zlib lz4)
-mo2_install_target(game_gamebryo)
diff --git a/libs/game_gamebryo/src/gamebryo/dummybsa.cpp b/libs/game_gamebryo/src/gamebryo/dummybsa.cpp
deleted file mode 100644
index ba6f766..0000000
--- a/libs/game_gamebryo/src/gamebryo/dummybsa.cpp
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
-Copyright (C) 2012 Sebastian Herbord. All rights reserved.
-
-This file is part of Mod Organizer.
-
-Mod Organizer is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-Mod Organizer is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#include "dummybsa.h"
-#include <QFile>
-#define WIN32_LEAN_AND_MEAN
-#include <Windows.h>
-
-static void writeUlong(unsigned char* buffer, int offset, unsigned long value)
-{
- union
- {
- unsigned long ulValue;
- unsigned char cValue[4];
- };
- ulValue = value;
- memcpy(buffer + offset, cValue, 4);
-}
-
-static void writeUlonglong(unsigned char* buffer, int offset, unsigned long long value)
-{
- union
- {
- unsigned long long ullValue;
- unsigned char cValue[8];
- };
- ullValue = value;
- memcpy(buffer + offset, cValue, 8);
-}
-
-static unsigned long genHashInt(const unsigned char* pos, const unsigned char* end)
-{
- unsigned long hash = 0;
- for (; pos < end; ++pos) {
- hash *= 0x1003f;
- hash += *pos;
- }
- return hash;
-}
-
-static unsigned long long genHash(const char* fileName)
-{
- char fileNameLower[MAX_PATH + 1];
- int i = 0;
- for (; i < MAX_PATH && fileName[i] != '\0'; ++i) {
- fileNameLower[i] = static_cast<char>(tolower(fileName[i]));
- if (fileNameLower[i] == '\\') {
- fileNameLower[i] = '/';
- }
- }
- fileNameLower[i] = '\0';
-
- unsigned char* fileNameLowerU = reinterpret_cast<unsigned char*>(fileNameLower);
-
- char* ext = strrchr(fileNameLower, '.');
- if (ext == nullptr) {
- ext = fileNameLower + strlen(fileNameLower);
- }
- unsigned char* extU = reinterpret_cast<unsigned char*>(ext);
-
- int length = ext - fileNameLower;
-
- unsigned long long hash = 0ULL;
-
- if (length > 0) {
- hash = *(extU - 1) | ((length > 2 ? *(ext - 2) : 0) << 8) | (length << 16) |
- (fileNameLowerU[0] << 24);
- }
-
- if (strlen(ext) > 0) {
- if (strcmp(ext + 1, "kf") == 0) {
- hash |= 0x80;
- } else if (strcmp(ext + 1, "nif") == 0) {
- hash |= 0x8000;
- } else if (strcmp(ext + 1, "dds") == 0) {
- hash |= 0x8080;
- } else if (strcmp(ext + 1, "wav") == 0) {
- hash |= 0x80000000;
- }
-
- unsigned long long temp =
- static_cast<unsigned long long>(genHashInt(fileNameLowerU + 1, extU - 2));
- temp += static_cast<unsigned long long>(genHashInt(extU, extU + strlen(ext)));
-
- hash |= (temp & 0xFFFFFFFF) << 32;
- }
- return hash;
-}
-
-DummyBSA::DummyBSA(unsigned long bsaVersion)
- : m_Version(bsaVersion), m_FolderName(""), m_FileName("dummy.dds"),
- m_TotalFileNameLength(0)
-{}
-
-void DummyBSA::writeHeader(QFile& file)
-{
- unsigned char header[] = {
- 'B', 'S', 'A', '\0', // magic string
- 0xDE, 0xAD, 0xBE, 0xEF, // version - insert later
- 0x24, 0x00, 0x00, 0x00, // offset to folder recors. header size is static
- 0xDE, 0xAD, 0xBE, 0xEF, // archive flags - insert later
- 0x01, 0x00, 0x00, 0x00, // folder count
- 0x01, 0x00, 0x00, 0x00, // file count
- 0xDE, 0xAD, 0xBE, 0xEF, // total folder names length - insert later
- 0xDE, 0xAD, 0xBE, 0xEF, // total file names length - insert later
- 0xDE, 0xAD, 0xBE, 0xEF // file flags - insert later
- };
-
- writeUlong(header, 4, m_Version);
- writeUlong(header, 12, 0x01 | 0x02); // has directories and has files.
- writeUlong(header, 24,
- static_cast<unsigned long>(m_FolderName.length()) +
- 1); // empty folder name
- writeUlong(header, 28, m_TotalFileNameLength); // single character file name
-
- writeUlong(header, 32, 2); // has dds
-
- file.write(reinterpret_cast<char*>(header), sizeof(header));
-}
-
-void DummyBSA::writeFolderRecord(QFile& file, const std::string& folderName)
-{
- unsigned char folderRecord[] = {
- 0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF, // folder hash
- 0x01, 0x00, 0x00, 0x00, // file count
- 0xDE, 0xAD, 0xBE, 0xEF, // offset to folder name
- };
- // we'd usually have to sort folders be the hash value generated here
- writeUlonglong(folderRecord, 0, genHash(folderName.c_str()));
- writeUlong(folderRecord, 12,
- 0x34 + m_TotalFileNameLength); // TODO: this should be calculated properly
-
- file.write(reinterpret_cast<char*>(folderRecord), sizeof(folderRecord));
-}
-
-void DummyBSA::writeFileRecord(QFile& file, const std::string& fileName)
-{
- unsigned char fileRecord[] = {
- 0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF, // file name hash
- 0xDE, 0xAD, 0xBE, 0xEF, // size
- 0xDE, 0xAD, 0xBE, 0xEF, // offset to file data
- };
-
- // we'd usually have to sort files by the value generated here
- writeUlonglong(fileRecord, 0, genHash(fileName.c_str()));
- writeUlong(fileRecord, 8, 0);
- writeUlong(
- fileRecord, 12,
- 0x44 + static_cast<unsigned long>(fileName.length() + 1) +
- 4); // after this record we expect the filename and 4 bytes of file size
-
- file.write(reinterpret_cast<char*>(fileRecord), sizeof(fileRecord));
-}
-
-void DummyBSA::writeFileRecordBlocks(QFile& file, const std::string& folderName)
-{
- file.write(folderName.c_str(), folderName.length() + 1);
-
- writeFileRecord(file, m_FileName);
-}
-
-void DummyBSA::write(const QString& fileName)
-{
- QFile file(fileName);
- file.open(QIODevice::WriteOnly);
-
- m_TotalFileNameLength = static_cast<unsigned long>(m_FileName.length() + 1);
-
- writeHeader(file);
- writeFolderRecord(file, m_FolderName);
- writeFileRecordBlocks(file, m_FolderName);
- file.write(m_FileName.c_str(), m_FileName.length() + 1);
- char fileSize[] = {0x00, 0x00, 0x00, 0x00};
- file.write(fileSize, sizeof(fileSize));
- file.close();
-}
diff --git a/libs/game_gamebryo/src/gamebryo/dummybsa.h b/libs/game_gamebryo/src/gamebryo/dummybsa.h
deleted file mode 100644
index 8ea071b..0000000
--- a/libs/game_gamebryo/src/gamebryo/dummybsa.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
-Copyright (C) 2012 Sebastian Herbord. All rights reserved.
-
-This file is part of Mod Organizer.
-
-Mod Organizer is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-Mod Organizer is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#ifndef DUMMYBSA_H
-#define DUMMYBSA_H
-
-#include <QFile>
-#include <QString>
-
-/**
- * @brief Class for creating a dummy bsa used for archive invalidation
- **/
-class DummyBSA
-{
-
-public:
- /**
- * @brief constructor
- *
- **/
- DummyBSA(unsigned long bsaVersion);
-
- /**
- * @brief write to the specified file
- *
- * @param fileName name of the file to write to
- **/
- void write(const QString& fileName);
-
-private:
- void writeHeader(QFile& file);
- void writeFolderRecord(QFile& file, const std::string& folderName);
- void writeFileRecord(QFile& file, const std::string& fileName);
- void writeFileRecordBlocks(QFile& file, const std::string& folderName);
-
-private:
- unsigned long m_Version;
- std::string m_FolderName;
- std::string m_FileName;
- unsigned long m_TotalFileNameLength;
-};
-
-#endif // DUMMYBSA_H
diff --git a/libs/game_gamebryo/src/gamebryo/game_gamebryo_en.ts b/libs/game_gamebryo/src/gamebryo/game_gamebryo_en.ts
deleted file mode 100644
index f924131..0000000
--- a/libs/game_gamebryo/src/gamebryo/game_gamebryo_en.ts
+++ /dev/null
@@ -1,170 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!DOCTYPE TS>
-<TS version="2.1" language="en_US">
-<context>
- <name>GamebryoModDataContent</name>
- <message>
- <location filename="gamebryomoddatacontent.cpp" line="15"/>
- <source>Plugins (ESP/ESM/ESL)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="gamebryomoddatacontent.cpp" line="16"/>
- <source>Optional Plugins</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="gamebryomoddatacontent.cpp" line="17"/>
- <source>Interface</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="gamebryomoddatacontent.cpp" line="18"/>
- <source>Meshes</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="gamebryomoddatacontent.cpp" line="19"/>
- <source>Bethesda Archive</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="gamebryomoddatacontent.cpp" line="20"/>
- <source>Scripts (Papyrus)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="gamebryomoddatacontent.cpp" line="21"/>
- <source>Script Extender Plugin</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="gamebryomoddatacontent.cpp" line="22"/>
- <source>Script Extender Files</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="gamebryomoddatacontent.cpp" line="23"/>
- <source>SkyProc Patcher</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="gamebryomoddatacontent.cpp" line="24"/>
- <source>Sound or Music</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="gamebryomoddatacontent.cpp" line="25"/>
- <source>Textures</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="gamebryomoddatacontent.cpp" line="26"/>
- <source>MCM Configuration</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="gamebryomoddatacontent.cpp" line="27"/>
- <source>INI Files</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="gamebryomoddatacontent.cpp" line="28"/>
- <source>FaceGen Data</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="gamebryomoddatacontent.cpp" line="29"/>
- <source>ModGroup Files</source>
- <translation type="unfinished"></translation>
- </message>
-</context>
-<context>
- <name>GamebryoSaveGameInfoWidget</name>
- <message>
- <location filename="gamebryosavegameinfowidget.ui" line="39"/>
- <source>Save #</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="gamebryosavegameinfowidget.ui" line="51"/>
- <source>Character</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="gamebryosavegameinfowidget.ui" line="63"/>
- <source>Level</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="gamebryosavegameinfowidget.ui" line="75"/>
- <source>Location</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="gamebryosavegameinfowidget.ui" line="87"/>
- <source>Date</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="gamebryosavegameinfowidget.cpp" line="78"/>
- <source>Has Script Extender Data</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="gamebryosavegameinfowidget.cpp" line="83"/>
- <source>Missing ESPs</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="gamebryosavegameinfowidget.cpp" line="116"/>
- <location filename="gamebryosavegameinfowidget.cpp" line="154"/>
- <location filename="gamebryosavegameinfowidget.cpp" line="193"/>
- <source>None</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="gamebryosavegameinfowidget.cpp" line="122"/>
- <source>Missing ESHs</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="gamebryosavegameinfowidget.cpp" line="161"/>
- <source>Missing ESLs</source>
- <translation type="unfinished"></translation>
- </message>
-</context>
-<context>
- <name>QObject</name>
- <message>
- <location filename="gamebryogameplugins.cpp" line="130"/>
- <source>Some of your plugins have invalid names! These plugins can not be loaded by the game. Please see mo_interface.log for a list of affected plugins and rename them.</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="gamebryosavegame.cpp" line="48"/>
- <source>%1, #%2, Level %3, %4</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="gamebryosavegame.cpp" line="102"/>
- <source>failed to open %1</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="gamebryosavegame.cpp" line="112"/>
- <source>wrong file format - expected %1 got &apos;%2&apos; for %3</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="gamegamebryo.cpp" line="318"/>
- <source>failed to query registry path (preflight): %1</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="gamegamebryo.cpp" line="326"/>
- <source>failed to query registry path (read): %1</source>
- <translation type="unfinished"></translation>
- </message>
-</context>
-</TS>
diff --git a/libs/game_gamebryo/src/gamebryo/gamebryobsainvalidation.cpp b/libs/game_gamebryo/src/gamebryo/gamebryobsainvalidation.cpp
deleted file mode 100644
index 1b7144b..0000000
--- a/libs/game_gamebryo/src/gamebryo/gamebryobsainvalidation.cpp
+++ /dev/null
@@ -1,133 +0,0 @@
-#include "gamebryobsainvalidation.h"
-
-#include "dummybsa.h"
-#include "iplugingame.h"
-#include "iprofile.h"
-#include "registry.h"
-#include <imoinfo.h>
-#include <utility.h>
-
-#include <QDir>
-#include <QStringList>
-
-#include <Windows.h>
-
-GamebryoBSAInvalidation::GamebryoBSAInvalidation(MOBase::DataArchives* dataArchives,
- const QString& iniFilename,
- MOBase::IPluginGame const* game)
- : m_DataArchives(dataArchives), m_IniFileName(iniFilename), m_Game(game)
-{}
-
-bool GamebryoBSAInvalidation::isInvalidationBSA(const QString& bsaName)
-{
- static QStringList invalidation{invalidationBSAName()};
-
- for (const QString& file : invalidation) {
- if (file.compare(bsaName, Qt::CaseInsensitive) == 0) {
- return true;
- }
- }
- return false;
-}
-
-void GamebryoBSAInvalidation::deactivate(MOBase::IProfile* profile)
-{
- prepareProfile(profile);
-}
-
-void GamebryoBSAInvalidation::activate(MOBase::IProfile* profile)
-{
- prepareProfile(profile);
-}
-
-bool GamebryoBSAInvalidation::prepareProfile(MOBase::IProfile* profile)
-{
- bool dirty = false;
- QString basePath = profile->localSettingsEnabled()
- ? profile->absolutePath()
- : m_Game->documentsDirectory().absolutePath();
- QString iniFilePath = basePath + "/" + m_IniFileName;
- WCHAR setting[MAX_PATH];
-
- // write bInvalidateOlderFiles = 1, if needed
- if (!::GetPrivateProfileStringW(L"Archive", L"bInvalidateOlderFiles", L"0", setting,
- MAX_PATH, iniFilePath.toStdWString().c_str()) ||
- wcstol(setting, nullptr, 10) != 1) {
- dirty = true;
- if (!MOBase::WriteRegistryValue(L"Archive", L"bInvalidateOlderFiles", L"1",
- iniFilePath.toStdWString().c_str())) {
- qWarning("failed to activate BSA invalidation in \"%s\"",
- qUtf8Printable(m_IniFileName));
- }
- }
-
- if (profile->invalidationActive(nullptr)) {
-
- // add the dummy bsa to the archive string, if needed
- QStringList archives = m_DataArchives->archives(profile);
- bool bsaInstalled = false;
- for (const QString& archive : archives) {
- if (isInvalidationBSA(archive)) {
- bsaInstalled = true;
- break;
- }
- }
- if (!bsaInstalled) {
- m_DataArchives->addArchive(profile, 0, invalidationBSAName());
- dirty = true;
- }
-
- // create the dummy bsa if necessary
- QString bsaFile = m_Game->dataDirectory().absoluteFilePath(invalidationBSAName());
- if (!QFile::exists(bsaFile)) {
- DummyBSA bsa(bsaVersion());
- bsa.write(bsaFile);
- dirty = true;
- }
-
- // write SInvalidationFile = "", if needed
- if (::GetPrivateProfileStringW(L"Archive", L"SInvalidationFile",
- L"ArchiveInvalidation.txt", setting, MAX_PATH,
- iniFilePath.toStdWString().c_str()) ||
- wcscmp(setting, L"") != 0) {
- dirty = true;
- if (!MOBase::WriteRegistryValue(L"Archive", L"SInvalidationFile", L"",
- iniFilePath.toStdWString().c_str())) {
- qWarning("failed to activate BSA invalidation in \"%s\"",
- qUtf8Printable(m_IniFileName));
- }
- }
- } else {
-
- // remove the dummy bsa from the archive string, if needed
- QStringList archivesBefore = m_DataArchives->archives(profile);
- for (const QString& archive : archivesBefore) {
- if (isInvalidationBSA(archive)) {
- m_DataArchives->removeArchive(profile, archive);
- dirty = true;
- }
- }
-
- // delete the dummy bsa, if needed
- QString bsaFile = m_Game->dataDirectory().absoluteFilePath(invalidationBSAName());
- if (QFile::exists(bsaFile)) {
- MOBase::shellDeleteQuiet(bsaFile);
- dirty = true;
- }
-
- // write SInvalidationFile = "ArchiveInvalidation.txt", if needed
- if (!::GetPrivateProfileStringW(L"Archive", L"SInvalidationFile", L"", setting,
- MAX_PATH, iniFilePath.toStdWString().c_str()) ||
- wcscmp(setting, L"ArchiveInvalidation.txt") != 0) {
- dirty = true;
- if (!MOBase::WriteRegistryValue(L"Archive", L"SInvalidationFile",
- L"ArchiveInvalidation.txt",
- iniFilePath.toStdWString().c_str())) {
- qWarning("failed to activate BSA invalidation in \"%s\"",
- qUtf8Printable(m_IniFileName));
- }
- }
- }
-
- return dirty;
-}
diff --git a/libs/game_gamebryo/src/gamebryo/gamebryobsainvalidation.h b/libs/game_gamebryo/src/gamebryo/gamebryobsainvalidation.h
deleted file mode 100644
index efd3d7b..0000000
--- a/libs/game_gamebryo/src/gamebryo/gamebryobsainvalidation.h
+++ /dev/null
@@ -1,36 +0,0 @@
-#ifndef GAMEBRYOBSAINVALIDATION_H
-#define GAMEBRYOBSAINVALIDATION_H
-
-#include <QString>
-#include <bsainvalidation.h>
-#include <dataarchives.h>
-#include <memory>
-
-namespace MOBase
-{
-class IPluginGame;
-}
-
-class GamebryoBSAInvalidation : public MOBase::BSAInvalidation
-{
-public:
- GamebryoBSAInvalidation(MOBase::DataArchives* dataArchives,
- const QString& iniFilename, MOBase::IPluginGame const* game);
-
- virtual bool isInvalidationBSA(const QString& bsaName) override;
- virtual void deactivate(MOBase::IProfile* profile) override;
- virtual void activate(MOBase::IProfile* profile) override;
- virtual bool prepareProfile(MOBase::IProfile* profile) override;
-
-private:
- virtual QString invalidationBSAName() const = 0;
- virtual unsigned long
- bsaVersion() const = 0; // 0x67 for oblivion, 0x68 for everything else
-
-private:
- MOBase::DataArchives* m_DataArchives;
- QString m_IniFileName;
- MOBase::IPluginGame const* m_Game;
-};
-
-#endif // GAMEBRYOBSAINVALIDATION_H
diff --git a/libs/game_gamebryo/src/gamebryo/gamebryodataarchives.cpp b/libs/game_gamebryo/src/gamebryo/gamebryodataarchives.cpp
deleted file mode 100644
index 3dcba8a..0000000
--- a/libs/game_gamebryo/src/gamebryo/gamebryodataarchives.cpp
+++ /dev/null
@@ -1,80 +0,0 @@
-#include "gamebryodataarchives.h"
-
-#include <Windows.h>
-
-#include <registry.h>
-#include <utility.h>
-
-#include "gamegamebryo.h"
-
-GamebryoDataArchives::GamebryoDataArchives(const GameGamebryo* game) : m_Game{game} {}
-
-QDir GamebryoDataArchives::gameDirectory() const
-{
- return QDir(m_Game->gameDirectory()).absolutePath();
-}
-
-QDir GamebryoDataArchives::localGameDirectory() const
-{
- return QDir(m_Game->myGamesPath()).absolutePath();
-}
-
-QStringList GamebryoDataArchives::getArchivesFromKey(const QString& iniFile,
- const QString& key,
- const int size) const
-{
- wchar_t* buffer = new wchar_t[size];
- QStringList result;
- std::wstring iniFileW = QDir::toNativeSeparators(iniFile).toStdWString();
-
- // epic ms fail: GetPrivateProfileString uses errno (for whatever reason) to signal a
- // fail since the return value has a different meaning (number of bytes copied).
- // HOWEVER, it will not set errno to 0 if NO error occured
- errno = 0;
-
- if (::GetPrivateProfileStringW(L"Archive", key.toStdWString().c_str(), L"", buffer,
- size, iniFileW.c_str()) != 0) {
- result.append(QString::fromStdWString(buffer).split(','));
- }
-
- for (int i = 0; i < result.count(); ++i) {
- result[i] = result[i].trimmed();
- }
- delete[] buffer;
- return result;
-}
-
-void GamebryoDataArchives::setArchivesToKey(const QString& iniFile, const QString& key,
- const QString& value)
-{
- if (!MOBase::WriteRegistryValue(L"Archive", key.toStdWString().c_str(),
- value.toStdWString().c_str(),
- iniFile.toStdWString().c_str())) {
- qWarning("failed to set archives in \"%s\"", qUtf8Printable(iniFile));
- }
-}
-
-void GamebryoDataArchives::addArchive(MOBase::IProfile* profile, int index,
- const QString& archiveName)
-{
- QStringList current = archives(profile);
- if (current.contains(archiveName, Qt::CaseInsensitive)) {
- return;
- }
-
- current.insert(index != INT_MAX ? index : current.size(), archiveName);
-
- writeArchiveList(profile, current);
-}
-
-void GamebryoDataArchives::removeArchive(MOBase::IProfile* profile,
- const QString& archiveName)
-{
- QStringList current = archives(profile);
- if (!current.contains(archiveName, Qt::CaseInsensitive)) {
- return;
- }
- current.removeAll(archiveName);
-
- writeArchiveList(profile, current);
-}
diff --git a/libs/game_gamebryo/src/gamebryo/gamebryodataarchives.h b/libs/game_gamebryo/src/gamebryo/gamebryodataarchives.h
deleted file mode 100644
index 13f4130..0000000
--- a/libs/game_gamebryo/src/gamebryo/gamebryodataarchives.h
+++ /dev/null
@@ -1,37 +0,0 @@
-#ifndef GAMEBRYODATAARCHIVES_H
-#define GAMEBRYODATAARCHIVES_H
-
-#include <QDir>
-
-#include "dataarchives.h"
-
-class GameGamebryo;
-
-class GamebryoDataArchives : public MOBase::DataArchives
-{
-
-public:
- GamebryoDataArchives(const GameGamebryo* game);
-
- virtual void addArchive(MOBase::IProfile* profile, int index,
- const QString& archiveName) override;
- virtual void removeArchive(MOBase::IProfile* profile,
- const QString& archiveName) override;
-
-protected:
- QDir gameDirectory() const;
- QDir localGameDirectory() const;
-
- QStringList getArchivesFromKey(const QString& iniFile, const QString& key,
- int size = 256) const;
- void setArchivesToKey(const QString& iniFile, const QString& key,
- const QString& value);
-
-private:
- const GameGamebryo* m_Game;
-
- virtual void writeArchiveList(MOBase::IProfile* profile,
- const QStringList& before) = 0;
-};
-
-#endif // GAMEBRYODATAARCHIVES_H
diff --git a/libs/game_gamebryo/src/gamebryo/gamebryogameplugins.cpp b/libs/game_gamebryo/src/gamebryo/gamebryogameplugins.cpp
deleted file mode 100644
index 83ad50f..0000000
--- a/libs/game_gamebryo/src/gamebryo/gamebryogameplugins.cpp
+++ /dev/null
@@ -1,248 +0,0 @@
-#include "gamebryogameplugins.h"
-#include <imodinterface.h>
-#include <iplugingame.h>
-#include <ipluginlist.h>
-#include <report.h>
-#include <safewritefile.h>
-#include <scopeguard.h>
-#include <utility.h>
-
-#include <QDateTime>
-#include <QDir>
-#include <QString>
-#include <QStringEncoder>
-#include <QStringList>
-
-using MOBase::IOrganizer;
-using MOBase::IPluginList;
-using MOBase::reportError;
-using MOBase::SafeWriteFile;
-
-GamebryoGamePlugins::GamebryoGamePlugins(IOrganizer* organizer) : m_Organizer(organizer)
-{}
-
-void GamebryoGamePlugins::writePluginLists(const IPluginList* pluginList)
-{
- if (!m_LastRead.isValid()) {
- // attempt to write uninitialized plugin lists
- return;
- }
-
- writePluginList(pluginList, m_Organizer->profile()->absolutePath() + "/plugins.txt");
- writeLoadOrderList(pluginList,
- m_Organizer->profile()->absolutePath() + "/loadorder.txt");
-
- m_LastRead = QDateTime::currentDateTime();
-}
-
-void GamebryoGamePlugins::readPluginLists(MOBase::IPluginList* pluginList)
-{
- QString loadOrderPath = organizer()->profile()->absolutePath() + "/loadorder.txt";
- QString pluginsPath = organizer()->profile()->absolutePath() + "/plugins.txt";
-
- bool loadOrderIsNew = !m_LastRead.isValid() || !QFileInfo(loadOrderPath).exists() ||
- QFileInfo(loadOrderPath).lastModified() > m_LastRead;
- bool pluginsIsNew =
- !m_LastRead.isValid() || QFileInfo(pluginsPath).lastModified() > m_LastRead;
-
- if (loadOrderIsNew || !pluginsIsNew) {
- // read both files if they are both new or both older than the last read
- QStringList loadOrder = readLoadOrderList(pluginList, loadOrderPath);
- pluginList->setLoadOrder(loadOrder);
- readPluginList(pluginList);
- } else {
- // If the plugins is new but not loadorder, we must reparse the load order from the
- // plugin files
- QStringList loadOrder = readPluginList(pluginList);
- pluginList->setLoadOrder(loadOrder);
- }
-
- m_LastRead = QDateTime::currentDateTime();
-}
-
-QStringList GamebryoGamePlugins::getLoadOrder()
-{
- QString loadOrderPath = organizer()->profile()->absolutePath() + "/loadorder.txt";
- QString pluginsPath = organizer()->profile()->absolutePath() + "/plugins.txt";
-
- bool loadOrderIsNew = !m_LastRead.isValid() || !QFileInfo(loadOrderPath).exists() ||
- QFileInfo(loadOrderPath).lastModified() > m_LastRead;
- bool pluginsIsNew =
- !m_LastRead.isValid() || QFileInfo(pluginsPath).lastModified() > m_LastRead;
-
- if (loadOrderIsNew || !pluginsIsNew) {
- return readLoadOrderList(m_Organizer->pluginList(), loadOrderPath);
- } else {
- return readPluginList(m_Organizer->pluginList());
- }
-}
-
-void GamebryoGamePlugins::writePluginList(const MOBase::IPluginList* pluginList,
- const QString& filePath)
-{
- return writeList(pluginList, filePath, false);
-}
-
-void GamebryoGamePlugins::writeLoadOrderList(const MOBase::IPluginList* pluginList,
- const QString& filePath)
-{
- return writeList(pluginList, filePath, true);
-}
-
-void GamebryoGamePlugins::writeList(const IPluginList* pluginList,
- const QString& filePath, bool loadOrder)
-{
- SafeWriteFile file(filePath);
-
- QStringEncoder encoder = loadOrder
- ? QStringEncoder(QStringConverter::Encoding::Utf8)
- : QStringEncoder(QStringConverter::Encoding::System);
-
- file->resize(0);
-
- file->write(
- encoder.encode("# This file was automatically generated by Mod Organizer.\r\n"));
-
- bool invalidFileNames = false;
- int writtenCount = 0;
-
- QStringList plugins = pluginList->pluginNames();
- std::sort(plugins.begin(), plugins.end(),
- [pluginList](const QString& lhs, const QString& rhs) {
- return pluginList->priority(lhs) < pluginList->priority(rhs);
- });
-
- for (const QString& pluginName : plugins) {
- if (loadOrder || (pluginList->state(pluginName) == IPluginList::STATE_ACTIVE)) {
- auto result = encoder.encode(pluginName);
- if (encoder.hasError()) {
- invalidFileNames = true;
- qCritical("invalid plugin name %s", qUtf8Printable(pluginName));
- } else {
- file->write(result);
- }
- file->write("\r\n");
- ++writtenCount;
- }
- }
-
- if (invalidFileNames) {
- reportError(QObject::tr("Some of your plugins have invalid names! These "
- "plugins can not be loaded by the game. Please see "
- "mo_interface.log for a list of affected plugins "
- "and rename them."));
- }
-
- if (writtenCount == 0) {
- qWarning("plugin list would be empty, this is almost certainly wrong. Not "
- "saving.");
- } else {
- file->commit();
- }
-}
-
-QStringList GamebryoGamePlugins::readLoadOrderList(MOBase::IPluginList* pluginList,
- const QString& filePath)
-{
- QStringList pluginNames = organizer()->managedGame()->primaryPlugins();
-
- std::set<QString> pluginLookup;
- for (auto&& name : pluginNames) {
- pluginLookup.insert(name.toLower());
- }
-
- const auto b = MOBase::forEachLineInFile(filePath, [&](QString s) {
- if (!pluginLookup.contains(s.toLower())) {
- pluginLookup.insert(s);
- pluginNames.push_back(std::move(s));
- }
- });
-
- if (!b) {
- return readPluginList(pluginList);
- }
-
- return pluginNames;
-}
-
-QStringList GamebryoGamePlugins::readPluginList(MOBase::IPluginList* pluginList)
-{
- QStringList primary = organizer()->managedGame()->primaryPlugins();
- for (const QString& pluginName : primary) {
- if (pluginList->state(pluginName) != IPluginList::STATE_MISSING) {
- pluginList->setState(pluginName, IPluginList::STATE_ACTIVE);
- }
- }
- QStringList plugins = pluginList->pluginNames();
- QStringList pluginsClone(plugins);
- // Do not sort the primary plugins. Their load order should be locked as defined in
- // "primaryPlugins".
- for (const auto& plugin : pluginsClone) {
- if (primary.contains(plugin, Qt::CaseInsensitive))
- plugins.removeAll(plugin);
- }
-
- // Always use filetime loadorder to get the actual load order
- std::sort(plugins.begin(), plugins.end(),
- [&](const QString& lhs, const QString& rhs) {
- MOBase::IModInterface* lhm =
- organizer()->modList()->getMod(pluginList->origin(lhs));
- MOBase::IModInterface* rhm =
- organizer()->modList()->getMod(pluginList->origin(rhs));
- QDir lhd = organizer()->managedGame()->dataDirectory();
- QDir rhd = organizer()->managedGame()->dataDirectory();
- if (lhm != nullptr)
- lhd = lhm->absolutePath();
- if (rhm != nullptr)
- rhd = rhm->absolutePath();
- QString lhp = lhd.absoluteFilePath(lhs);
- QString rhp = rhd.absoluteFilePath(rhs);
- return QFileInfo(lhp).lastModified() < QFileInfo(rhp).lastModified();
- });
-
- // Determine plugin active state by the plugins.txt file.
- bool pluginsTxtExists = true;
- QString filePath = organizer()->profile()->absolutePath() + "/plugins.txt";
- QFile file(filePath);
- if (!file.open(QIODevice::ReadOnly)) {
- pluginsTxtExists = false;
- }
- ON_BLOCK_EXIT([&]() {
- file.close();
- });
-
- if (file.size() == 0) {
- // MO stores at least a header in the file. if it's completely empty the
- // file is broken
- pluginsTxtExists = false;
- }
-
- QStringList activePlugins;
- QStringList inactivePlugins;
- if (pluginsTxtExists) {
- while (!file.atEnd()) {
- QByteArray line = file.readLine();
- QString pluginName;
- if ((line.size() > 0) && (line.at(0) != '#')) {
- QStringEncoder encoder(QStringConverter::Encoding::System);
- pluginName = encoder.encode(line.trimmed().constData());
- }
- if (pluginName.size() > 0) {
- pluginList->setState(pluginName, IPluginList::STATE_ACTIVE);
- activePlugins.push_back(pluginName);
- }
- }
-
- for (const auto& pluginName : plugins) {
- if (!activePlugins.contains(pluginName, Qt::CaseInsensitive)) {
- pluginList->setState(pluginName, IPluginList::STATE_INACTIVE);
- }
- }
- } else {
- for (const QString& pluginName : plugins) {
- pluginList->setState(pluginName, IPluginList::STATE_INACTIVE);
- }
- }
-
- return primary + plugins;
-}
diff --git a/libs/game_gamebryo/src/gamebryo/gamebryogameplugins.h b/libs/game_gamebryo/src/gamebryo/gamebryogameplugins.h
deleted file mode 100644
index ca70c75..0000000
--- a/libs/game_gamebryo/src/gamebryo/gamebryogameplugins.h
+++ /dev/null
@@ -1,38 +0,0 @@
-#ifndef GAMEBRYOGAMEPLUGINS_H
-#define GAMEBRYOGAMEPLUGINS_H
-
-#include <QDateTime>
-#include <QStringList>
-#include <gameplugins.h>
-#include <imoinfo.h>
-
-class GamebryoGamePlugins : public MOBase::GamePlugins
-{
-public:
- GamebryoGamePlugins(MOBase::IOrganizer* organizer);
-
- virtual void writePluginLists(const MOBase::IPluginList* pluginList) override;
- virtual void readPluginLists(MOBase::IPluginList* pluginList) override;
- virtual QStringList getLoadOrder() override;
-
-protected:
- MOBase::IOrganizer* organizer() const { return m_Organizer; }
-
- virtual void writePluginList(const MOBase::IPluginList* pluginList,
- const QString& filePath);
- virtual void writeLoadOrderList(const MOBase::IPluginList* pluginList,
- const QString& filePath);
- virtual QStringList readLoadOrderList(MOBase::IPluginList* pluginList,
- const QString& filePath);
- virtual QStringList readPluginList(MOBase::IPluginList* pluginList);
-
-protected:
- MOBase::IOrganizer* m_Organizer;
- QDateTime m_LastRead;
-
-private:
- void writeList(const MOBase::IPluginList* pluginList, const QString& filePath,
- bool loadOrder);
-};
-
-#endif // GAMEBRYOGAMEPLUGINS_H
diff --git a/libs/game_gamebryo/src/gamebryo/gamebryolocalsavegames.cpp b/libs/game_gamebryo/src/gamebryo/gamebryolocalsavegames.cpp
deleted file mode 100644
index d0cc2ef..0000000
--- a/libs/game_gamebryo/src/gamebryo/gamebryolocalsavegames.cpp
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
-Copyright (C) 2015 Sebastian Herbord. All rights reserved.
-
-This library is free software; you can redistribute it and/or
-modify it under the terms of the GNU Lesser General Public
-License as published by the Free Software Foundation; either
-version 3 of the License, or (at your option) any later version.
-
-This library is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public
-License along with this library; if not, write to the Free Software
-Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-*/
-
-#include "gamebryolocalsavegames.h"
-#include "registry.h"
-#include <QtDebug>
-#include <iprofile.h>
-#include <stddef.h>
-#include <string>
-#include <windows.h>
-
-#include "gamegamebryo.h"
-
-GamebryoLocalSavegames::GamebryoLocalSavegames(const GameGamebryo* game,
- const QString& iniFileName)
- : m_Game{game}, m_IniFileName(iniFileName)
-{}
-
-MappingType GamebryoLocalSavegames::mappings(const QDir& profileSaveDir) const
-{
- return {{profileSaveDir.absolutePath(), localSavesDirectory().absolutePath(), true,
- true}};
-}
-
-QString GamebryoLocalSavegames::localSavesDummy() const
-{
- return "__MO_Saves\\";
-}
-
-QDir GamebryoLocalSavegames::localSavesDirectory() const
-{
- QString dummy = localSavesDummy();
- dummy.replace("\\", "/");
- return QDir(m_Game->myGamesPath()).absoluteFilePath(dummy);
-}
-
-QDir GamebryoLocalSavegames::localGameDirectory() const
-{
- return QDir(m_Game->myGamesPath()).absolutePath();
-}
-
-bool GamebryoLocalSavegames::prepareProfile(MOBase::IProfile* profile)
-{
- bool enable = profile->localSavesEnabled();
-
- QString basePath = profile->localSettingsEnabled()
- ? profile->absolutePath()
- : localGameDirectory().absolutePath();
- QString iniFilePath = basePath + "/" + m_IniFileName;
- QString saveIni = profile->absolutePath() + "/" + "savepath.ini";
-
- // Get the current sLocalSavePath
- WCHAR currentPath[MAX_PATH];
- GetPrivateProfileStringW(L"General", L"sLocalSavePath", L"SKIP_ME", currentPath,
- MAX_PATH, iniFilePath.toStdWString().c_str());
- bool alreadyEnabled =
- wcscmp(currentPath, localSavesDummy().toStdWString().c_str()) == 0;
-
- // Get the current bUseMyGamesDirectory
- WCHAR currentMyGames[MAX_PATH];
- GetPrivateProfileStringW(L"General", L"bUseMyGamesDirectory", L"SKIP_ME",
- currentMyGames, MAX_PATH,
- iniFilePath.toStdWString().c_str());
-
- // Create the __MO_Saves directory if local saves are enabled and it doesn't exist
- if (enable) {
- QDir saves = localSavesDirectory();
- if (!saves.exists()) {
- saves.mkdir(".");
- }
- }
-
- // Set the path to __MO_Saves if it's not already
- if (enable && !alreadyEnabled) {
- // If the path is not blank, save it to savepath.ini
- if (wcscmp(currentPath, L"SKIP_ME") != 0) {
- MOBase::WriteRegistryValue(L"General", L"sLocalSavePath", currentPath,
- saveIni.toStdWString().c_str());
- }
- if (wcscmp(currentMyGames, L"SKIP_ME") != 0) {
- MOBase::WriteRegistryValue(L"General", L"bUseMyGamesDirectory", currentMyGames,
- saveIni.toStdWString().c_str());
- }
- MOBase::WriteRegistryValue(L"General", L"sLocalSavePath",
- localSavesDummy().toStdWString().c_str(),
- iniFilePath.toStdWString().c_str());
- MOBase::WriteRegistryValue(L"General", L"bUseMyGamesDirectory", L"1",
- iniFilePath.toStdWString().c_str());
- }
-
- // Get rid of the local saves setting if it's still there
- if (!enable && alreadyEnabled) {
- // If savepath.ini exists, use it and delete it
- if (QFile::exists(saveIni)) {
- WCHAR savedPath[MAX_PATH];
- WCHAR savedMyGames[MAX_PATH];
- GetPrivateProfileStringW(L"General", L"sLocalSavePath", L"DELETE_ME", savedPath,
- MAX_PATH, saveIni.toStdWString().c_str());
- GetPrivateProfileStringW(L"General", L"bUseMyGamesDirectory", L"DELETE_ME",
- savedMyGames, MAX_PATH, saveIni.toStdWString().c_str());
- if (wcscmp(savedPath, L"DELETE_ME") != 0) {
- MOBase::WriteRegistryValue(L"General", L"sLocalSavePath", savedPath,
- iniFilePath.toStdWString().c_str());
- } else {
- MOBase::WriteRegistryValue(L"General", L"sLocalSavePath", NULL,
- iniFilePath.toStdWString().c_str());
- }
- if (wcscmp(savedMyGames, L"DELETE_ME") != 0) {
- MOBase::WriteRegistryValue(L"General", L"bUseMyGamesDirectory", savedMyGames,
- iniFilePath.toStdWString().c_str());
- } else {
- MOBase::WriteRegistryValue(L"General", L"bUseMyGamesDirectory", NULL,
- iniFilePath.toStdWString().c_str());
- }
- QFile::remove(saveIni);
- }
- // Otherwise just delete the setting
- else {
- MOBase::WriteRegistryValue(L"General", L"sLocalSavePath", NULL,
- iniFilePath.toStdWString().c_str());
- MOBase::WriteRegistryValue(L"General", L"bUseMyGamesDirectory", NULL,
- iniFilePath.toStdWString().c_str());
- }
- }
-
- return enable != alreadyEnabled;
-}
diff --git a/libs/game_gamebryo/src/gamebryo/gamebryolocalsavegames.h b/libs/game_gamebryo/src/gamebryo/gamebryolocalsavegames.h
deleted file mode 100644
index e5f2a92..0000000
--- a/libs/game_gamebryo/src/gamebryo/gamebryolocalsavegames.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
-Copyright (C) 2015 Sebastian Herbord. All rights reserved.
-
-This library is free software; you can redistribute it and/or
-modify it under the terms of the GNU Lesser General Public
-License as published by the Free Software Foundation; either
-version 3 of the License, or (at your option) any later version.
-
-This library is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public
-License along with this library; if not, write to the Free Software
-Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-*/
-
-#ifndef GAMEBRYOLOCALSAVEGAMES_H
-#define GAMEBRYOLOCALSAVEGAMES_H
-
-#include <localsavegames.h>
-
-#include <QDir>
-#include <QString>
-
-class GameGamebryo;
-
-class GamebryoLocalSavegames : public MOBase::LocalSavegames
-{
-
-public:
- GamebryoLocalSavegames(const GameGamebryo* game, const QString& iniFileName);
-
- virtual MappingType mappings(const QDir& profileSaveDir) const override;
- virtual bool prepareProfile(MOBase::IProfile* profile) override;
-
-protected:
- // return the path from the local game directory to the local saves folder
- //
- // this is virtual so game plugins for complete game overhauld (Enderal, Nehrim, etc.)
- // can override it properly
- //
- virtual QString localSavesDummy() const;
-
- QDir localSavesDirectory() const;
- QDir localGameDirectory() const;
-
-private:
- const GameGamebryo* m_Game;
- QString m_IniFileName;
-};
-
-#endif // GAMEBRYOLOCALSAVEGAMES_H
diff --git a/libs/game_gamebryo/src/gamebryo/gamebryomoddatachecker.cpp b/libs/game_gamebryo/src/gamebryo/gamebryomoddatachecker.cpp
deleted file mode 100644
index b5bace6..0000000
--- a/libs/game_gamebryo/src/gamebryo/gamebryomoddatachecker.cpp
+++ /dev/null
@@ -1,76 +0,0 @@
-#include <ifiletree.h>
-
-#include "gamebryomoddatachecker.h"
-
-/**
- * @return the list of possible folder names in data.
- */
-auto GamebryoModDataChecker::possibleFolderNames() const -> const FileNameSet&
-{
- static FileNameSet result{"fonts",
- "interface",
- "menus",
- "meshes",
- "music",
- "scripts",
- "shaders",
- "sound",
- "strings",
- "textures",
- "trees",
- "video",
- "facegen",
- "materials",
- "skse",
- "obse",
- "mwse",
- "nvse",
- "fose",
- "f4se",
- "distantlod",
- "asi",
- "SkyProc Patchers",
- "Tools",
- "MCM",
- "icons",
- "bookart",
- "distantland",
- "mits",
- "splash",
- "dllplugins",
- "CalienteTools",
- "NetScriptFramework",
- "shadersfx"};
- return result;
-}
-
-/**
- * @return the extensions of possible files in data.
- */
-auto GamebryoModDataChecker::possibleFileExtensions() const -> const FileNameSet&
-{
- static FileNameSet result{"esp", "esm", "esl", "bsa", "ba2", "modgroups", "ini"};
- return result;
-}
-
-GamebryoModDataChecker::GamebryoModDataChecker(const GameGamebryo* game) : m_Game(game)
-{}
-
-GamebryoModDataChecker::CheckReturn GamebryoModDataChecker::dataLooksValid(
- std::shared_ptr<const MOBase::IFileTree> fileTree) const
-{
- auto& folders = possibleFolderNames();
- auto& suffixes = possibleFileExtensions();
- for (auto entry : *fileTree) {
- if (entry->isDir()) {
- if (folders.count(entry->name()) > 0) {
- return CheckReturn::VALID;
- }
- } else {
- if (suffixes.count(entry->suffix()) > 0) {
- return CheckReturn::VALID;
- }
- }
- }
- return CheckReturn::INVALID;
-}
diff --git a/libs/game_gamebryo/src/gamebryo/gamebryomoddatachecker.h b/libs/game_gamebryo/src/gamebryo/gamebryomoddatachecker.h
deleted file mode 100644
index e0b0733..0000000
--- a/libs/game_gamebryo/src/gamebryo/gamebryomoddatachecker.h
+++ /dev/null
@@ -1,46 +0,0 @@
-#ifndef GAMEBRYO_MODATACHECKER_H
-#define GAMEBRYO_MODATACHECKER_H
-
-#include <ifiletree.h>
-#include <moddatachecker.h>
-
-class GameGamebryo;
-
-/**
- * @brief ModDataChecker for GameBryo games that look at folder and files in the "data"
- * directory.
- *
- * The default implementation is game-agnostic and uses the list of folders and file
- * extensions that were used before the ModDataChecker feature was added. It is possible
- * to inherit the class to provide custom list of folders or filenames.
- */
-class GamebryoModDataChecker : public MOBase::ModDataChecker
-{
-public:
- /**
- * @brief Construct a new mod-data checker for GameBryo games.
- */
- GamebryoModDataChecker(const GameGamebryo* game);
-
- virtual CheckReturn
- dataLooksValid(std::shared_ptr<const MOBase::IFileTree> fileTree) const override;
-
-protected:
- GameGamebryo const* const m_Game;
-
- using FileNameSet = std::set<QString, MOBase::FileNameComparator>;
-
- const GameGamebryo* game() const { return m_Game; }
-
- /**
- * @return the list of possible folder names in data.
- */
- virtual const FileNameSet& possibleFolderNames() const;
-
- /**
- * @return the extensions of possible files in data.
- */
- virtual const FileNameSet& possibleFileExtensions() const;
-};
-
-#endif // GAMEBRYO_MODATACHECKER_H
diff --git a/libs/game_gamebryo/src/gamebryo/gamebryomoddatacontent.cpp b/libs/game_gamebryo/src/gamebryo/gamebryomoddatacontent.cpp
deleted file mode 100644
index 85b66b0..0000000
--- a/libs/game_gamebryo/src/gamebryo/gamebryomoddatacontent.cpp
+++ /dev/null
@@ -1,116 +0,0 @@
-#include "gamebryomoddatacontent.h"
-
-#include <igamefeatures.h>
-#include <scriptextender.h>
-
-GamebryoModDataContent::GamebryoModDataContent(
- MOBase::IGameFeatures const* gameFeatures)
- : m_GameFeatures(gameFeatures), m_Enabled(CONTENT_MODGROUP + 1, true)
-{}
-
-std::vector<GamebryoModDataContent::Content>
-GamebryoModDataContent::getAllContents() const
-{
- static std::vector<Content> GAMEBRYO_CONTENTS{
- {CONTENT_PLUGIN, QT_TR_NOOP("Plugins (ESP/ESM/ESL)"), ":/MO/gui/content/plugin"},
- {CONTENT_OPTIONAL, QT_TR_NOOP("Optional Plugins"), "", true},
- {CONTENT_INTERFACE, QT_TR_NOOP("Interface"), ":/MO/gui/content/interface"},
- {CONTENT_MESH, QT_TR_NOOP("Meshes"), ":/MO/gui/content/mesh"},
- {CONTENT_BSA, QT_TR_NOOP("Bethesda Archive"), ":/MO/gui/content/bsa"},
- {CONTENT_SCRIPT, QT_TR_NOOP("Scripts (Papyrus)"), ":/MO/gui/content/script"},
- {CONTENT_SKSE, QT_TR_NOOP("Script Extender Plugin"), ":/MO/gui/content/skse"},
- {CONTENT_SKSE_FILES, QT_TR_NOOP("Script Extender Files"), "", true},
- {CONTENT_SKYPROC, QT_TR_NOOP("SkyProc Patcher"), ":/MO/gui/content/skyproc"},
- {CONTENT_SOUND, QT_TR_NOOP("Sound or Music"), ":/MO/gui/content/sound"},
- {CONTENT_TEXTURE, QT_TR_NOOP("Textures"), ":/MO/gui/content/texture"},
- {CONTENT_MCM, QT_TR_NOOP("MCM Configuration"), ":/MO/gui/content/menu"},
- {CONTENT_INI, QT_TR_NOOP("INI Files"), ":/MO/gui/content/inifile"},
- {CONTENT_FACEGEN, QT_TR_NOOP("FaceGen Data"), ":/MO/gui/content/facegen"},
- {CONTENT_MODGROUP, QT_TR_NOOP("ModGroup Files"), ":/MO/gui/content/modgroup"}};
-
- // Copy the list of enabled contents:
- std::vector<Content> contents;
- std::copy_if(std::begin(GAMEBRYO_CONTENTS), std::end(GAMEBRYO_CONTENTS),
- std::back_inserter(contents), [this](auto e) {
- return m_Enabled[e.id()];
- });
- return contents;
-}
-
-std::vector<int> GamebryoModDataContent::getContentsFor(
- std::shared_ptr<const MOBase::IFileTree> fileTree) const
-{
- std::vector<int> contents;
-
- for (auto e : *fileTree) {
- if (e->isFile()) {
- auto suffix = e->suffix().toLower();
- if (m_Enabled[CONTENT_PLUGIN] &&
- (suffix == "esp" || suffix == "esm" || suffix == "esl")) {
- contents.push_back(CONTENT_PLUGIN);
- } else if (m_Enabled[CONTENT_BSA] && (suffix == "bsa" || suffix == "ba2")) {
- contents.push_back(CONTENT_BSA);
- } else if (m_Enabled[CONTENT_INI] && suffix == "ini" &&
- e->compare("meta.ini") != 0) {
- contents.push_back(CONTENT_INI);
- } else if (m_Enabled[CONTENT_MODGROUP] && suffix == "modgroups") {
- contents.push_back(CONTENT_MODGROUP);
- }
- } else {
- if (m_Enabled[CONTENT_TEXTURE] &&
- (e->compare("textures") == 0 || e->compare("icons") == 0 ||
- e->compare("bookart") == 0)) {
- contents.push_back(CONTENT_TEXTURE);
- } else if (m_Enabled[CONTENT_MESH] && e->compare("meshes") == 0) {
- contents.push_back(CONTENT_MESH);
- } else if (m_Enabled[CONTENT_INTERFACE] &&
- (e->compare("interface") == 0 || e->compare("menus") == 0)) {
- contents.push_back(CONTENT_INTERFACE);
- } else if (m_Enabled[CONTENT_SOUND] && e->compare("music") == 0 ||
- e->compare("sound") == 0) {
- contents.push_back(CONTENT_SOUND);
- } else if (m_Enabled[CONTENT_SCRIPT] && e->compare("scripts") == 0) {
- contents.push_back(CONTENT_SCRIPT);
- } else if (m_Enabled[CONTENT_SKYPROC] && e->compare("SkyProc Patchers") == 0) {
- contents.push_back(CONTENT_SKYPROC);
- } else if (m_Enabled[CONTENT_MCM] && e->compare("MCM") == 0) {
- contents.push_back(CONTENT_MCM);
- } else if (m_Enabled[CONTENT_OPTIONAL] && e->compare("Optional") == 0 &&
- e->astree()->size() > 0) {
- contents.push_back(CONTENT_OPTIONAL);
- }
- }
- }
-
- if (m_Enabled[CONTENT_FACEGEN]) {
- auto e1 = fileTree->findDirectory("meshes/actors/character/facegendata");
- if (e1) {
- contents.push_back(CONTENT_FACEGEN);
- } else {
- auto e2 = fileTree->findDirectory("textures/actors/character/facegendata");
- if (e2) {
- contents.push_back(CONTENT_FACEGEN);
- }
- }
- }
-
- auto extender = m_GameFeatures->gameFeature<MOBase::ScriptExtender>();
- if (extender != nullptr) {
- auto e = fileTree->findDirectory(extender->PluginPath());
- if (e) {
- if (m_Enabled[CONTENT_SKSE_FILES]) {
- contents.push_back(CONTENT_SKSE_FILES);
- }
- if (m_Enabled[CONTENT_SKSE]) {
- for (auto f : *e) {
- if (f->hasSuffix("dll")) {
- contents.push_back(CONTENT_SKSE);
- break;
- }
- }
- }
- }
- }
-
- return contents;
-}
diff --git a/libs/game_gamebryo/src/gamebryo/gamebryomoddatacontent.h b/libs/game_gamebryo/src/gamebryo/gamebryomoddatacontent.h
deleted file mode 100644
index be02331..0000000
--- a/libs/game_gamebryo/src/gamebryo/gamebryomoddatacontent.h
+++ /dev/null
@@ -1,75 +0,0 @@
-#ifndef GAMEBRYO_MODDATACONTENT_H
-#define GAMEBRYO_MODDATACONTENT_H
-
-#include <ifiletree.h>
-#include <moddatacontent.h>
-
-namespace MOBase
-{
-class IGameFeatures;
-}
-
-/**
- * @brief ModDataContent for GameBryo games.
- *
- */
-class GamebryoModDataContent : public MOBase::ModDataContent
-{
-protected:
- /**
- * Note: These are used to index m_Enabled so should have standard
- * enum values, not custom ones.
- */
- enum EContent
- {
- CONTENT_PLUGIN,
- CONTENT_OPTIONAL,
- CONTENT_TEXTURE,
- CONTENT_MESH,
- CONTENT_BSA,
- CONTENT_INTERFACE,
- CONTENT_SOUND,
- CONTENT_SCRIPT,
- CONTENT_SKSE,
- CONTENT_SKSE_FILES,
- CONTENT_SKYPROC,
- CONTENT_MCM,
- CONTENT_INI,
- CONTENT_FACEGEN,
- CONTENT_MODGROUP
- };
-
- /**
- * This is the first value that can be used for game-specific contents.
- */
- constexpr static auto CONTENT_NEXT_VALUE = CONTENT_MODGROUP + 1;
-
-public:
- /**
- *
- */
- GamebryoModDataContent(const MOBase::IGameFeatures* gameFeatures);
-
- /**
- * @return the list of all possible contents for the corresponding game.
- */
- virtual std::vector<Content> getAllContents() const override;
-
- /**
- * @brief Retrieve the list of contents in the given tree.
- *
- * @param fileTree The tree corresponding to the mod to retrieve contents for.
- *
- * @return the IDs of the content in the given tree.
- */
- virtual std::vector<int>
- getContentsFor(std::shared_ptr<const MOBase::IFileTree> fileTree) const override;
-
-protected:
- MOBase::IGameFeatures const* const m_GameFeatures;
-
- // List of enabled contents:
- std::vector<bool> m_Enabled;
-};
-
-#endif // GAMEBRYO_MODDATACONTENT_H
diff --git a/libs/game_gamebryo/src/gamebryo/gamebryosavegame.cpp b/libs/game_gamebryo/src/gamebryo/gamebryosavegame.cpp
deleted file mode 100644
index 596b19b..0000000
--- a/libs/game_gamebryo/src/gamebryo/gamebryosavegame.cpp
+++ /dev/null
@@ -1,606 +0,0 @@
-#include "gamebryosavegame.h"
-
-#include "iplugingame.h"
-#include "log.h"
-#include "scriptextender.h"
-
-#include <QDate>
-#include <QFile>
-#include <QFileInfo>
-#include <QScopedArrayPointer>
-#include <QTime>
-
-#include <Windows.h>
-#include <lz4.h>
-#include <zlib.h>
-
-#include <stdexcept>
-#include <vector>
-
-#include "gamegamebryo.h"
-#include "imoinfo.h"
-
-#define CHUNK 16384
-
-GamebryoSaveGame::GamebryoSaveGame(QString const& file, GameGamebryo const* game,
- bool const lightEnabled, bool const mediumEnabled)
- : m_FileName(file), m_CreationTime(QFileInfo(file).lastModified()), m_Game(game),
- m_MediumEnabled(mediumEnabled), m_LightEnabled(lightEnabled),
- m_DataFields([this]() {
- return fetchDataFields();
- })
-{}
-
-GamebryoSaveGame::~GamebryoSaveGame() {}
-
-QString GamebryoSaveGame::getFilepath() const
-{
- return m_FileName;
-}
-
-QDateTime GamebryoSaveGame::getCreationTime() const
-{
- return m_CreationTime;
-}
-
-QString GamebryoSaveGame::getName() const
-{
- return QObject::tr("%1, #%2, Level %3, %4")
- .arg(m_PCName)
- .arg(m_SaveNumber)
- .arg(m_PCLevel)
- .arg(m_PCLocation);
-}
-
-QString GamebryoSaveGame::getSaveGroupIdentifier() const
-{
- return m_PCName;
-}
-
-QStringList GamebryoSaveGame::allFiles() const
-{
- // This returns all valid files associated with this game
- QStringList res = {m_FileName};
- auto e = m_Game->m_Organizer->gameFeatures()->gameFeature<MOBase::ScriptExtender>();
- if (e != nullptr) {
- QFileInfo file(m_FileName);
- QFileInfo SEfile(file.absolutePath() + "/" + file.completeBaseName() + "." +
- m_Game->savegameSEExtension());
- if (SEfile.exists()) {
- res.push_back(SEfile.absoluteFilePath());
- }
- }
- return res;
-}
-
-bool GamebryoSaveGame::hasScriptExtenderFile() const
-{
- QFileInfo file(m_FileName);
- QFileInfo SEfile(file.absolutePath() + "/" + file.completeBaseName() + "." +
- m_Game->savegameSEExtension());
- return SEfile.exists();
-}
-
-void GamebryoSaveGame::setCreationTime(_SYSTEMTIME const& ctime)
-{
- QDate date;
- date.setDate(ctime.wYear, ctime.wMonth, ctime.wDay);
- QTime time;
- time.setHMS(ctime.wHour, ctime.wMinute, ctime.wSecond, ctime.wMilliseconds);
-
- m_CreationTime = QDateTime(date, time, Qt::UTC);
-}
-
-GamebryoSaveGame::FileWrapper::FileWrapper(QString const& filepath,
- QString const& expected)
- : m_File(filepath), m_HasFieldMarkers(false),
- m_PluginString(StringType::TYPE_WSTRING),
- m_PluginStringFormat(StringFormat::UTF8), m_NextChunk(0)
-{
- if (!m_File.open(QIODevice::ReadOnly)) {
- throw std::runtime_error(
- QObject::tr("failed to open %1").arg(filepath).toUtf8().constData());
- }
-
- std::vector<char> fileID(expected.length() + 1);
- m_File.read(fileID.data(), expected.length());
- fileID[expected.length()] = '\0';
-
- QString id(fileID.data());
- if (expected != id) {
- throw std::runtime_error(
- QObject::tr("wrong file format - expected %1 got \'%2\' for %3")
- .arg(expected)
- .arg(id)
- .arg(filepath)
- .toUtf8()
- .constData());
- }
-}
-
-void GamebryoSaveGame::FileWrapper::setHasFieldMarkers(bool state)
-{
- m_HasFieldMarkers = state;
-}
-
-void GamebryoSaveGame::FileWrapper::setPluginString(StringType type)
-{
- m_PluginString = type;
-}
-
-void GamebryoSaveGame::FileWrapper::setPluginStringFormat(StringFormat type)
-{
- m_PluginStringFormat = type;
-}
-
-void GamebryoSaveGame::FileWrapper::readQDataStream(QDataStream& data, void* buff,
- std::size_t length)
-{
- int read = data.readRawData(static_cast<char*>(buff), static_cast<int>(length));
- bool result = true;
- if (read != length && m_CompressionType == 1) {
- bool result = readNextChunk();
- if (result) {
- read += data.readRawData(static_cast<char*>(buff) + read,
- static_cast<int>(length - read));
- }
- }
- if (read != length || !result) {
- throw std::runtime_error("unexpected end of file");
- }
-}
-
-template <typename T>
-void GamebryoSaveGame::FileWrapper::readQDataStream(QDataStream& data, T& value)
-{
- static_assert(std::is_trivial_v<T> && std::is_standard_layout_v<T>);
- readQDataStream(data, &value, sizeof(T));
-}
-
-void GamebryoSaveGame::FileWrapper::skipQDataStream(QDataStream& data,
- std::size_t length)
-{
- int skip = data.skipRawData(static_cast<int>(length));
- bool result = true;
- if (skip != length && m_CompressionType == 1) {
- result = readNextChunk();
- if (result) {
- skip += data.skipRawData(static_cast<int>(length - skip));
- }
- }
- if (skip != length || !result) {
- throw std::runtime_error("unexpected end of file");
- }
-}
-
-template <>
-void GamebryoSaveGame::FileWrapper::read<QString>(QString& value)
-{
- if (m_CompressionType == 0) {
- unsigned short length;
- if (m_PluginString == StringType::TYPE_BSTRING ||
- m_PluginString == StringType::TYPE_BZSTRING) {
- unsigned char len;
- read(len);
- length = m_PluginString == StringType::TYPE_BZSTRING ? len + 1 : len;
- } else {
- read(length);
- }
-
- if (m_HasFieldMarkers) {
- skip<char>();
- }
-
- QByteArray buffer;
- buffer.resize(length);
-
- read(buffer.data(),
- m_PluginString == StringType::TYPE_BZSTRING ? length - 1 : length);
-
- if (m_PluginString == StringType::TYPE_BZSTRING)
- buffer[length - 1] = '\0';
-
- if (m_HasFieldMarkers) {
- skip<char>();
- }
-
- if (m_PluginStringFormat == StringFormat::UTF8)
- value = QString::fromUtf8(buffer.constData());
- else
- value = QString::fromLocal8Bit(buffer.constData());
- } else if (m_CompressionType == 1 || m_CompressionType == 2) {
- unsigned short length;
- if (m_PluginString == StringType::TYPE_BSTRING ||
- m_PluginString == StringType::TYPE_BZSTRING) {
- unsigned char len;
- readQDataStream(*m_Data, len);
- length = m_PluginString == StringType::TYPE_BZSTRING ? len + 1 : len;
- } else {
- readQDataStream(*m_Data, length);
- }
-
- if (m_HasFieldMarkers) {
- skip<char>();
- }
-
- QByteArray buffer;
- buffer.resize(length);
-
- readQDataStream(*m_Data, buffer.data(),
- m_PluginString == StringType::TYPE_BZSTRING ? length - 1 : length);
-
- if (m_PluginString == StringType::TYPE_BZSTRING)
- buffer[length - 1] = '\0';
-
- if (m_HasFieldMarkers) {
- m_Data->skipRawData(1);
- }
-
- if (m_PluginStringFormat == StringFormat::UTF8)
- value = QString::fromUtf8(buffer.constData());
- else
- value = QString::fromLocal8Bit(buffer.constData());
- } else {
- MOBase::log::warn("Please create an issue on the MO github labeled \"Found unknown "
- "Compressed\" with your savefile attached");
- }
-}
-
-void GamebryoSaveGame::FileWrapper::read(void* buff, std::size_t length)
-{
- int read = m_File.read(static_cast<char*>(buff), length);
- if (read != length) {
- throw std::runtime_error("unexpected end of file");
- }
-}
-
-QImage GamebryoSaveGame::FileWrapper::readImage(int scale, bool alpha)
-{
- unsigned long width;
- read(width);
- unsigned long height;
- read(height);
- return readImage(width, height, scale, alpha);
-}
-
-QImage GamebryoSaveGame::FileWrapper::readImage(unsigned long width,
- unsigned long height, int scale,
- bool alpha)
-{
- int bpp = alpha ? 4 : 3;
- QScopedArrayPointer<unsigned char> buffer(new unsigned char[width * height * bpp]);
- read(buffer.data(), width * height * bpp);
- QImage image(buffer.data(), width, height,
- alpha ? QImage::Format_RGBA8888_Premultiplied : QImage::Format_RGB888);
-
- // We need to copy the image here because QImage does not make a copy of the
- // buffer when constructed.
- if (scale != 0) {
- return image.copy().scaledToWidth(scale);
- } else {
- return image.copy();
- }
-}
-
-void GamebryoSaveGame::FileWrapper::setCompressionType(uint16_t compressionType)
-{
- m_CompressionType = compressionType;
-}
-
-void GamebryoSaveGame::FileWrapper::closeCompressedData()
-{
- if (m_CompressionType == 0) {
- } else if (m_CompressionType == 1 || m_CompressionType == 2) {
- m_NextChunk = 0;
- m_UncompressedSize = 0;
- m_Data->device()->close();
- delete m_Data;
- } else
- MOBase::log::warn("Please create an issue on the MO github labeled \"Found unknown "
- "Compressed\" with your savefile attached");
-}
-
-bool GamebryoSaveGame::FileWrapper::openCompressedData(int bytesToIgnore)
-{
- if (m_CompressionType == 0) {
- if (bytesToIgnore > 0) // Just to make certain
- skip<char>(bytesToIgnore);
- return false;
- } else if (m_CompressionType == 1) {
- read(m_NextChunk);
- read(m_UncompressedSize);
- QByteArray placeholder;
- m_Data = new QDataStream(placeholder);
- bool result = readNextChunk();
- if (result)
- skipQDataStream(*m_Data, bytesToIgnore);
- return result;
- } else if (m_CompressionType == 2) {
- uint32_t uncompressedSize;
- read(uncompressedSize);
- uint32_t compressedSize;
- read(compressedSize);
- QByteArray compressed;
- compressed.resize(compressedSize);
- read(compressed.data(), compressedSize);
- QByteArray decompressed;
- decompressed.resize(uncompressedSize);
- LZ4_decompress_safe_partial(compressed.data(), decompressed.data(), compressedSize,
- uncompressedSize, uncompressedSize);
- compressed.clear();
-
- m_Data = new QDataStream(decompressed);
- skipQDataStream(*m_Data, bytesToIgnore);
-
- return true;
- } else {
- MOBase::log::warn("Please create an issue on the MO github labeled \"Found unknown "
- "Compressed\" with your savefile attached");
- return false;
- }
-}
-
-bool GamebryoSaveGame::FileWrapper::readNextChunk()
-{
- uint32_t have;
- uint64_t read = 0;
- std::unique_ptr<char[]> inBuffer = std::make_unique<char[]>(CHUNK);
- std::unique_ptr<char[]> outBuffer = std::make_unique<char[]>(CHUNK);
- QByteArray finalData;
- m_Data->device()->close();
- delete m_Data;
- z_stream stream{};
- try {
- stream.zalloc = Z_NULL;
- stream.zfree = Z_NULL;
- stream.opaque = Z_NULL;
- stream.avail_in = 0;
- stream.next_in = Z_NULL;
- if (m_NextChunk >= m_File.size() || finalData.size() == m_UncompressedSize)
- return false;
- m_File.seek(m_NextChunk);
- int zlibRet = inflateInit2(&stream, 15 + 32);
- if (zlibRet != Z_OK) {
- return false;
- }
- do {
- stream.avail_in = m_File.read(inBuffer.get(), CHUNK);
- read += stream.avail_in;
- if (!m_File.isReadable()) {
- (void)inflateEnd(&stream);
- return false;
- }
- if (stream.avail_in == 0)
- break;
- stream.next_in = reinterpret_cast<Bytef*>(inBuffer.get());
- do {
- stream.avail_out = CHUNK;
- stream.next_out = reinterpret_cast<Bytef*>(outBuffer.get());
- zlibRet = inflate(&stream, Z_NO_FLUSH);
- if ((zlibRet != Z_OK) && (zlibRet != Z_STREAM_END) &&
- (zlibRet != Z_BUF_ERROR)) {
- return false;
- }
- have = CHUNK - stream.avail_out;
- finalData += QByteArray::fromRawData(outBuffer.get(), have);
- } while (stream.avail_out == 0);
- read -= stream.avail_in;
- } while (zlibRet != Z_STREAM_END);
- inflateEnd(&stream);
- uint64_t remainder = (m_NextChunk + read) % 16;
- uint64_t next = m_NextChunk + read + 16 - (remainder == 0 ? 16 : remainder);
- m_NextChunk = next;
- } catch (const std::exception&) {
- inflateEnd(&stream);
- return false;
- }
- m_Data = new QDataStream(finalData);
- return true;
-}
-
-uint8_t GamebryoSaveGame::FileWrapper::readChar(int bytesToIgnore)
-{
- if (m_CompressionType == 0) {
- if (bytesToIgnore > 0) // Just to make certain
- skip<char>(bytesToIgnore);
- uint8_t version;
- read(version);
- return version;
- } else if (m_CompressionType == 1 || m_CompressionType == 2) {
- // decompression already done by readSaveGameVersion
- skipQDataStream(*m_Data, bytesToIgnore);
-
- uint8_t version;
- readQDataStream(*m_Data, version);
- return version;
-
- } else {
- MOBase::log::warn("Please create an issue on the MO github labeled \"Found unknown "
- "Compressed\" with your savefile attached");
- return 0;
- }
-}
-
-uint16_t GamebryoSaveGame::FileWrapper::readShort(int bytesToIgnore)
-{
- if (m_CompressionType == 0) {
- if (bytesToIgnore > 0) // Just to make certain
- skip<char>(bytesToIgnore);
- uint16_t size;
- read(size);
- return size;
- } else if (m_CompressionType == 1 || m_CompressionType == 2) {
- // decompression already done by readSaveGameVersion
- skipQDataStream(*m_Data, bytesToIgnore);
-
- uint16_t size;
- readQDataStream(*m_Data, size);
- return size;
- } else {
- MOBase::log::warn("Please create an issue on the MO github labeled \"Found unknown "
- "Compressed\" with your savefile attached");
- return 0;
- }
-}
-
-uint32_t GamebryoSaveGame::FileWrapper::readInt(int bytesToIgnore)
-{
- if (m_CompressionType == 0) {
- if (bytesToIgnore > 0) // Just to make certain
- skip<char>(bytesToIgnore);
- uint32_t size;
- read(size);
- return size;
- } else if (m_CompressionType == 1 || m_CompressionType == 2) {
- // decompression already done by readSaveGameVersion
- skipQDataStream(*m_Data, bytesToIgnore);
-
- uint32_t size;
- readQDataStream(*m_Data, size);
- return size;
- } else {
- MOBase::log::warn("Please create an issue on the MO github labeled \"Found unknown "
- "Compressed\" with your savefile attached");
- return 0;
- }
-}
-
-uint64_t GamebryoSaveGame::FileWrapper::readLong(int bytesToIgnore)
-{
- if (m_CompressionType == 0) {
- if (bytesToIgnore > 0) // Just to make certain
- skip<char>(bytesToIgnore);
- uint64_t size;
- read(size);
- return size;
- } else if (m_CompressionType == 1 || m_CompressionType == 2) {
- // decompression already done by readSaveGameVersion
- skipQDataStream(*m_Data, bytesToIgnore);
-
- uint64_t size;
- readQDataStream(*m_Data, size);
- return size;
- } else {
- MOBase::log::warn("Please create an issue on the MO github labeled \"Found unknown "
- "Compressed\" with your savefile attached");
- return 0;
- }
-}
-
-float_t GamebryoSaveGame::FileWrapper::readFloat(int bytesToIgnore)
-{
- if (m_CompressionType == 0) {
- if (bytesToIgnore > 0) // Just to make certain
- skip<char>(bytesToIgnore);
- float_t value;
- read(value);
- return value;
- } else if (m_CompressionType == 1 || m_CompressionType == 2) {
- // decompression already done by readSaveGameVersion
- skipQDataStream(*m_Data, bytesToIgnore);
-
- float_t value;
- readQDataStream(*m_Data, value);
- return value;
- } else {
- MOBase::log::warn("Please create an issue on the MO github labeled \"Found unknown "
- "Compressed\" with your savefile attached");
- return 0;
- }
-}
-
-QStringList GamebryoSaveGame::FileWrapper::readPlugins(int bytesToIgnore, int extraData,
- const QStringList& corePlugins)
-{
- if (m_CompressionType == 0) {
- if (bytesToIgnore > 0) // Just to make certain
- skip<char>(bytesToIgnore);
- uint8_t count;
- read(count);
- return readPluginData(count, extraData, corePlugins);
- } else if (m_CompressionType == 1 || m_CompressionType == 2) {
- skipQDataStream(*m_Data, bytesToIgnore);
- uint8_t count;
- readQDataStream(*m_Data, count);
- return readPluginData(count, extraData, corePlugins);
- }
- return {};
-}
-
-QStringList
-GamebryoSaveGame::FileWrapper::readLightPlugins(int bytesToIgnore, int extraData,
- const QStringList& corePlugins)
-{
- if (m_CompressionType == 0) {
- if (bytesToIgnore > 0) // Just to make certain
- skip<char>(bytesToIgnore);
- uint16_t count;
- read(count);
- return readPluginData(count, extraData, corePlugins);
- } else if (m_CompressionType == 1 || m_CompressionType == 2) {
- skipQDataStream(*m_Data, bytesToIgnore);
- uint16_t count;
- readQDataStream(*m_Data, count);
- return readPluginData(count, extraData, corePlugins);
- }
- return {};
-}
-
-QStringList
-GamebryoSaveGame::FileWrapper::readMediumPlugins(int bytesToIgnore, int extraData,
- const QStringList& corePlugins)
-{
- if (m_CompressionType != 1) {
- return {};
- } else {
- skipQDataStream(*m_Data, bytesToIgnore);
- uint32_t count;
- readQDataStream(*m_Data, count);
- return readPluginData(count, extraData, corePlugins);
- }
-}
-
-QStringList GamebryoSaveGame::FileWrapper::readPluginData(uint32_t count, int extraData,
- const QStringList corePlugins)
-{
- QStringList plugins;
- plugins.reserve(count);
- if (m_CompressionType == 0) {
- for (std::size_t i = 0; i < count; ++i) {
- QString name;
- read(name);
- plugins.push_back(name);
- }
- } else {
- for (std::size_t i = 0; i < count; ++i) {
- QString name;
- read(name);
- plugins.push_back(name);
- bool isCustomPlugin;
- if (extraData) {
- if (extraData > 1) {
- readQDataStream(*m_Data, isCustomPlugin);
- } else {
- isCustomPlugin = !corePlugins.contains(name);
- }
- if (isCustomPlugin) {
- QString creationName;
- QString creationId;
- uint16_t flagsSize;
- uint8_t isCreation;
- read(creationName);
- read(creationId);
- readQDataStream(*m_Data, flagsSize);
- skipQDataStream(*m_Data, flagsSize);
- readQDataStream(*m_Data, isCreation);
- }
- }
- }
- }
- return plugins;
-}
-
-void GamebryoSaveGame::FileWrapper::close()
-{
- m_File.close();
-}
diff --git a/libs/game_gamebryo/src/gamebryo/gamebryosavegame.h b/libs/game_gamebryo/src/gamebryo/gamebryosavegame.h
deleted file mode 100644
index 44d0584..0000000
--- a/libs/game_gamebryo/src/gamebryo/gamebryosavegame.h
+++ /dev/null
@@ -1,240 +0,0 @@
-#ifndef GAMEBRYOSAVEGAME_H
-#define GAMEBRYOSAVEGAME_H
-
-#include "isavegame.h"
-#include "memoizedlock.h"
-
-#include <QDateTime>
-#include <QFile>
-#include <QImage>
-#include <QString>
-#include <QStringList>
-
-#include <stddef.h>
-#include <stdexcept>
-
-struct _SYSTEMTIME;
-
-namespace MOBase
-{
-class IPluginGame;
-} // namespace MOBase
-
-class GameGamebryo;
-
-class GamebryoSaveGame : public MOBase::ISaveGame
-{
-public:
- GamebryoSaveGame(QString const& file, GameGamebryo const* game,
- bool const lightEnabled = false, bool const mediumEnabled = false);
-
- virtual ~GamebryoSaveGame();
-
-public: // ISaveGame interface
- virtual QString getFilepath() const override;
- virtual QDateTime getCreationTime() const override;
- virtual QString getName() const override;
- virtual QString getSaveGroupIdentifier() const override;
- virtual QStringList allFiles() const override;
-
-public:
- bool hasScriptExtenderFile() const;
-
- // Simple getters
- virtual QString getPCName() const { return m_PCName; }
- virtual unsigned short getPCLevel() const { return m_PCLevel; }
- virtual QString getPCLocation() const { return m_PCLocation; }
- virtual unsigned long getSaveNumber() const { return m_SaveNumber; }
-
- QStringList const& getPlugins() const { return m_DataFields.value()->Plugins; }
- QStringList const& getMediumPlugins() const
- {
- return m_DataFields.value()->MediumPlugins;
- }
- QStringList const& getLightPlugins() const
- {
- return m_DataFields.value()->LightPlugins;
- }
- QImage const& getScreenshot() const { return m_DataFields.value()->Screenshot; }
-
- bool isMediumEnabled() const { return m_MediumEnabled; }
-
- bool isLightEnabled() const { return m_LightEnabled; }
-
- enum class StringType
- {
- TYPE_BZSTRING,
- TYPE_BSTRING,
- TYPE_WSTRING
- };
-
- enum class StringFormat
- {
- UTF8,
- LOCAL8BIT
- };
-
-protected:
- friend class FileWrapper;
-
- class FileWrapper
- {
- public:
- /**
- * @brief Construct the save file information.
- *
- * @param filepath The path to the save file.
- * @params expected Expecte bytes at start of file.
- *
- **/
- FileWrapper(QString const& filepath, QString const& expected);
-
- /** Set this for save games that have a marker at the end of each
- * field. Specifically fallout
- **/
- void setHasFieldMarkers(bool);
-
- /** Set bz string mode (1 byte length, null terminated)
- **/
- void setPluginString(StringType);
-
- /** Set string format (utf-8, windows local 8 bit strings)
- **/
- void setPluginStringFormat(StringFormat);
-
- template <typename T>
- void skip(int count = 1)
- {
- if (!m_File.seek(m_File.pos() + count * sizeof(T))) {
- throw std::runtime_error("unexpected end of file");
- }
- }
-
- template <typename T>
- void read(T& value)
- {
- int read = m_File.read(reinterpret_cast<char*>(&value), sizeof(T));
- if (read != sizeof(T)) {
- throw std::runtime_error("unexpected end of file");
- }
- if (m_HasFieldMarkers) {
- skip<char>();
- }
- }
-
- template <>
- void read<QString>(QString& value);
-
- void seek(unsigned long pos)
- {
- if (!m_File.seek(pos)) {
- throw std::runtime_error("unexpected end of file");
- }
- }
-
- void read(void* buff, std::size_t length);
-
- /* Reads RGB image from save
- * Assumes picture dimentions come immediately before the save
- */
- QImage readImage(int scale = 0, bool alpha = false);
-
- /* Reads RGB image from save */
- QImage readImage(unsigned long width, unsigned long height, int scale = 0,
- bool alpha = false);
-
- /* Sets the compression type. */
- void setCompressionType(uint16_t type);
-
- /* uncompress the begining of the compressed block */
- bool openCompressedData(int bytesToIgnore = 0);
-
- /* read the next compressed block */
- bool readNextChunk();
-
- /* frees the uncompressed block */
- void closeCompressedData();
-
- /* Read the save game version in the compressed block */
- uint8_t readChar(int bytesToIgnore = 0);
-
- uint16_t readShort(int bytesToIgnore = 0);
-
- uint32_t readInt(int bytesToIgnore = 0);
-
- uint64_t readLong(int bytesToIgnore = 0);
-
- float_t readFloat(int bytesToIgnore = 0);
-
- /* Read the plugin list */
- QStringList readPlugins(int bytesToIgnore = 0, int extraData = 0,
- const QStringList& corePlugins = {});
-
- /* Read the light plugin list */
- QStringList readLightPlugins(int bytesToIgnore = 0, int extraData = 0,
- const QStringList& corePlugins = {});
-
- /* Read the medium plugin list */
- QStringList readMediumPlugins(int bytesToIgnore = 0, int extraData = 0,
- const QStringList& corePlugins = {});
-
- void close();
-
- private:
- QFile m_File;
- uint64_t m_NextChunk;
- uint64_t m_UncompressedSize;
- bool m_HasFieldMarkers;
- StringType m_PluginString;
- StringFormat m_PluginStringFormat;
- QDataStream* m_Data;
- uint16_t m_CompressionType = 0;
-
- private:
- template <typename T>
- void readQDataStream(QDataStream& data, T& value);
-
- void readQDataStream(QDataStream& data, void* buff, std::size_t length);
-
- void skipQDataStream(QDataStream& data, std::size_t length);
-
- QStringList readPluginData(uint32_t count, int extraData,
- const QStringList corePlugins);
- };
-
- void setCreationTime(_SYSTEMTIME const& time);
-
- GameGamebryo const* m_Game;
- bool m_MediumEnabled;
- bool m_LightEnabled;
-
- QString m_FileName;
- QString m_PCName;
- unsigned short m_PCLevel;
- QString m_PCLocation;
- unsigned long m_SaveNumber;
- QDateTime m_CreationTime;
-
- // Those three fields are usually much slower to fetch than
- // the other, so we do not fetch them if not needed.
- //
- // This is virtual so child class can add fields if those are
- // hard to access.
- struct DataFields
- {
- QStringList Plugins;
- QStringList LightPlugins;
- QStringList MediumPlugins;
- QImage Screenshot;
-
- // We need this constructor.
- DataFields() {}
- virtual ~DataFields() {}
- };
- MOBase::MemoizedLocked<std::unique_ptr<DataFields>> m_DataFields;
-
- // Fetch the field.
- virtual std::unique_ptr<DataFields> fetchDataFields() const = 0;
-};
-
-#endif // GAMEBRYOSAVEGAME_H
diff --git a/libs/game_gamebryo/src/gamebryo/gamebryosavegameinfo.cpp b/libs/game_gamebryo/src/gamebryo/gamebryosavegameinfo.cpp
deleted file mode 100644
index 6d1289a..0000000
--- a/libs/game_gamebryo/src/gamebryo/gamebryosavegameinfo.cpp
+++ /dev/null
@@ -1,100 +0,0 @@
-#include "gamebryosavegameinfo.h"
-
-#include "gamebryosavegame.h"
-#include "gamebryosavegameinfowidget.h"
-#include "gamegamebryo.h"
-#include "imodinterface.h"
-#include "imoinfo.h"
-#include "iplugingame.h"
-#include "ipluginlist.h"
-
-#include <QDir>
-#include <QString>
-#include <QStringList>
-
-GamebryoSaveGameInfo::GamebryoSaveGameInfo(GameGamebryo const* game) : m_Game(game) {}
-
-GamebryoSaveGameInfo::~GamebryoSaveGameInfo() {}
-
-GamebryoSaveGameInfo::MissingAssets
-GamebryoSaveGameInfo::getMissingAssets(MOBase::ISaveGame const& save) const
-{
- GamebryoSaveGame const& gamebryoSave = dynamic_cast<GamebryoSaveGame const&>(save);
- MOBase::IOrganizer* organizerCore = m_Game->m_Organizer;
-
- // collect the list of missing plugins
- MissingAssets missingAssets;
-
- for (QString const& pluginName : gamebryoSave.getPlugins()) {
- switch (organizerCore->pluginList()->state(pluginName)) {
- case MOBase::IPluginList::STATE_INACTIVE:
- missingAssets[pluginName] =
- ProvidingModules{organizerCore->pluginList()->origin(pluginName)};
- break;
- case MOBase::IPluginList::STATE_MISSING:
- missingAssets[pluginName] = ProvidingModules();
- break;
- }
- }
-
- for (QString const& pluginName : gamebryoSave.getLightPlugins()) {
- switch (organizerCore->pluginList()->state(pluginName)) {
- case MOBase::IPluginList::STATE_INACTIVE:
- missingAssets[pluginName] =
- ProvidingModules{organizerCore->pluginList()->origin(pluginName)};
- break;
- case MOBase::IPluginList::STATE_MISSING:
- missingAssets[pluginName] = ProvidingModules();
- break;
- }
- }
-
- // Find out any other mods that might contain the esp/esm
- QStringList espFilter({"*.esp", "*.esl", "*.esm"});
-
- QString dataDir(organizerCore->managedGame()->dataDirectory().absolutePath());
-
- // Search normal mods. A note: This will also find mods in data.
- for (QString const& mod : organizerCore->modList()->allModsByProfilePriority()) {
- MOBase::IModInterface* modInfo = organizerCore->modList()->getMod(mod);
- QStringList esps = QDir(modInfo->absolutePath()).entryList(espFilter);
- for (QString const& esp : esps) {
- MissingAssets::iterator iter = missingAssets.find(esp);
- if (modInfo->absolutePath() == dataDir) {
- // We have to prune esps that reside in the data directory, otherwise
- // you get all the unmanaged mods listed as potential candidates for
- // enabling
- if (modInfo->name() != organizerCore->pluginList()->origin(esp)) {
- continue;
- }
- }
- if (iter != missingAssets.end()) {
- if (!iter->contains(modInfo->name())) {
- iter->push_back(modInfo->name());
- }
- }
- }
- }
-
- // search in overwrite
- {
- QDir overwriteDir(organizerCore->overwritePath());
- QStringList esps = overwriteDir.entryList(espFilter);
- for (const QString& esp : esps) {
- MissingAssets::iterator iter = missingAssets.find(esp);
- if (iter != missingAssets.end()) {
- if (!iter->contains("<overwrite>")) {
- iter->push_back("<overwrite>");
- }
- }
- }
- }
-
- return missingAssets;
-}
-
-MOBase::ISaveGameInfoWidget*
-GamebryoSaveGameInfo::getSaveGameWidget(QWidget* parent) const
-{
- return new GamebryoSaveGameInfoWidget(this, parent);
-}
diff --git a/libs/game_gamebryo/src/gamebryo/gamebryosavegameinfo.h b/libs/game_gamebryo/src/gamebryo/gamebryosavegameinfo.h
deleted file mode 100644
index e214293..0000000
--- a/libs/game_gamebryo/src/gamebryo/gamebryosavegameinfo.h
+++ /dev/null
@@ -1,23 +0,0 @@
-#ifndef GAMEBRYOSAVEGAMEINFO_H
-#define GAMEBRYOSAVEGAMEINFO_H
-
-#include "savegameinfo.h"
-
-class GameGamebryo;
-
-class GamebryoSaveGameInfo : public MOBase::SaveGameInfo
-{
-public:
- GamebryoSaveGameInfo(GameGamebryo const* game);
- ~GamebryoSaveGameInfo();
-
- virtual MissingAssets getMissingAssets(MOBase::ISaveGame const& save) const override;
-
- virtual MOBase::ISaveGameInfoWidget* getSaveGameWidget(QWidget*) const override;
-
-protected:
- friend class GamebryoSaveGameInfoWidget;
- GameGamebryo const* m_Game;
-};
-
-#endif // GAMEBRYOSAVEGAMEINFO_H
diff --git a/libs/game_gamebryo/src/gamebryo/gamebryosavegameinfowidget.cpp b/libs/game_gamebryo/src/gamebryo/gamebryosavegameinfowidget.cpp
deleted file mode 100644
index 8c95dad..0000000
--- a/libs/game_gamebryo/src/gamebryo/gamebryosavegameinfowidget.cpp
+++ /dev/null
@@ -1,199 +0,0 @@
-#include "gamebryosavegameinfowidget.h"
-#include "ui_gamebryosavegameinfowidget.h"
-
-#include "gamebryosavegame.h"
-#include "gamebryosavegameinfo.h"
-#include "gamegamebryo.h"
-#include "imoinfo.h"
-#include "ipluginlist.h"
-
-#include <QDate>
-#include <QDateTime>
-#include <QFont>
-#include <QFrame>
-#include <QLabel>
-#include <QLayout>
-#include <QLayoutItem>
-#include <QPixmap>
-#include <QString>
-#include <QStyle>
-#include <QTime>
-#include <QVBoxLayout>
-
-#include <Qt>
-#include <QtGlobal>
-
-#include <memory>
-
-GamebryoSaveGameInfoWidget::GamebryoSaveGameInfoWidget(GamebryoSaveGameInfo const* info,
- QWidget* parent)
- : MOBase::ISaveGameInfoWidget(parent), ui(new Ui::GamebryoSaveGameInfoWidget),
- m_Info(info)
-{
- ui->setupUi(this);
- this->setWindowFlags(Qt::ToolTip | Qt::BypassGraphicsProxyWidget);
- setWindowOpacity(style()->styleHint(QStyle::SH_ToolTipLabel_Opacity, 0, this) /
- qreal(255.0));
- ui->gameFrame->setStyleSheet("background-color: transparent;");
-
- QVBoxLayout* gameLayout = new QVBoxLayout();
- gameLayout->setContentsMargins(0, 0, 0, 0);
- gameLayout->setSpacing(2);
- ui->gameFrame->setLayout(gameLayout);
-}
-
-GamebryoSaveGameInfoWidget::~GamebryoSaveGameInfoWidget()
-{
- delete ui;
-}
-
-void GamebryoSaveGameInfoWidget::setSave(MOBase::ISaveGame const& save)
-{
- auto& gamebryoSave = dynamic_cast<GamebryoSaveGame const&>(save);
- ui->saveNumLabel->setText(QString("%1").arg(gamebryoSave.getSaveNumber()));
- ui->characterLabel->setText(gamebryoSave.getPCName());
- ui->locationLabel->setText(gamebryoSave.getPCLocation());
- ui->levelLabel->setText(QString("%1").arg(gamebryoSave.getPCLevel()));
- // This somewhat contorted code is because on my system at least, the
- // old way of doing this appears to give short date and long time.
- QDateTime t = gamebryoSave.getCreationTime().toLocalTime();
- ui->dateLabel->setText(
- QLocale::system().toString(t.date(), QLocale::FormatType::ShortFormat) + " " +
- QLocale::system().toString(t.time()));
- ui->screenshotLabel->setPixmap(QPixmap::fromImage(gamebryoSave.getScreenshot()));
- if (ui->gameFrame->layout() != nullptr) {
- QLayoutItem* item = nullptr;
- while ((item = ui->gameFrame->layout()->takeAt(0)) != nullptr) {
- delete item->widget();
- delete item;
- }
- ui->gameFrame->layout()->setSizeConstraint(QLayout::SetFixedSize);
- }
-
- // Resize box to new content
- this->resize(0, 0);
-
- QLayout* layout = ui->gameFrame->layout();
- if (gamebryoSave.hasScriptExtenderFile()) {
- QLabel* scriptExtender = new QLabel(tr("Has Script Extender Data"));
- QFont headerFont = scriptExtender->font();
- headerFont.setBold(true);
- layout->addWidget(scriptExtender);
- }
- QLabel* header = new QLabel(tr("Missing ESPs"));
- QFont headerFont = header->font();
- QFont contentFont = headerFont;
- headerFont.setItalic(true);
- contentFont.setBold(true);
- contentFont.setPointSize(7);
- header->setFont(headerFont);
- layout->addWidget(header);
- int count = 0;
- MOBase::IPluginList* pluginList = m_Info->m_Game->m_Organizer->pluginList();
- for (QString const& pluginName : gamebryoSave.getPlugins()) {
- if (pluginList->state(pluginName) == MOBase::IPluginList::STATE_ACTIVE) {
- continue;
- }
-
- ++count;
-
- if (count > 7) {
- break;
- }
-
- QLabel* pluginLabel = new QLabel(pluginName);
- pluginLabel->setIndent(10);
- pluginLabel->setFont(contentFont);
- layout->addWidget(pluginLabel);
- }
- if (count > 7) {
- QLabel* dotDotLabel = new QLabel("...");
- dotDotLabel->setIndent(10);
- dotDotLabel->setFont(contentFont);
- layout->addWidget(dotDotLabel);
- }
- if (count == 0) {
- QLabel* dotDotLabel = new QLabel(tr("None"));
- dotDotLabel->setIndent(10);
- dotDotLabel->setFont(contentFont);
- layout->addWidget(dotDotLabel);
- }
- if (gamebryoSave.isMediumEnabled()) {
- QLabel* headerEsh = new QLabel(tr("Missing ESHs"));
- QFont headerEshFont = headerEsh->font();
- QFont contentEshFont = headerEshFont;
- headerEshFont.setItalic(true);
- contentEshFont.setBold(true);
- contentEshFont.setPointSize(7);
- headerEsh->setFont(headerEshFont);
- layout->addWidget(headerEsh);
- int countEsh = 0;
- for (QString const& pluginName : gamebryoSave.getMediumPlugins()) {
- if (pluginList->state(pluginName) == MOBase::IPluginList::STATE_ACTIVE) {
- continue;
- }
-
- ++countEsh;
-
- if (countEsh > 7) {
- break;
- }
-
- QLabel* pluginLabel = new QLabel(pluginName);
- pluginLabel->setIndent(10);
- pluginLabel->setFont(contentFont);
- layout->addWidget(pluginLabel);
- }
- if (countEsh > 7) {
- QLabel* dotDotLabel = new QLabel("...");
- dotDotLabel->setIndent(10);
- dotDotLabel->setFont(contentFont);
- layout->addWidget(dotDotLabel);
- }
- if (countEsh == 0) {
- QLabel* dotDotLabel = new QLabel(tr("None"));
- dotDotLabel->setIndent(10);
- dotDotLabel->setFont(contentFont);
- layout->addWidget(dotDotLabel);
- }
- }
- if (gamebryoSave.isLightEnabled()) {
- QLabel* headerEsl = new QLabel(tr("Missing ESLs"));
- QFont headerEslFont = headerEsl->font();
- QFont contentEslFont = headerEslFont;
- headerEslFont.setItalic(true);
- contentEslFont.setBold(true);
- contentEslFont.setPointSize(7);
- headerEsl->setFont(headerEslFont);
- layout->addWidget(headerEsl);
- int countEsl = 0;
- for (QString const& pluginName : gamebryoSave.getLightPlugins()) {
- if (pluginList->state(pluginName) == MOBase::IPluginList::STATE_ACTIVE) {
- continue;
- }
-
- ++countEsl;
-
- if (countEsl > 7) {
- break;
- }
-
- QLabel* pluginLabel = new QLabel(pluginName);
- pluginLabel->setIndent(10);
- pluginLabel->setFont(contentFont);
- layout->addWidget(pluginLabel);
- }
- if (countEsl > 7) {
- QLabel* dotDotLabel = new QLabel("...");
- dotDotLabel->setIndent(10);
- dotDotLabel->setFont(contentFont);
- layout->addWidget(dotDotLabel);
- }
- if (countEsl == 0) {
- QLabel* dotDotLabel = new QLabel(tr("None"));
- dotDotLabel->setIndent(10);
- dotDotLabel->setFont(contentFont);
- layout->addWidget(dotDotLabel);
- }
- }
-}
diff --git a/libs/game_gamebryo/src/gamebryo/gamebryosavegameinfowidget.h b/libs/game_gamebryo/src/gamebryo/gamebryosavegameinfowidget.h
deleted file mode 100644
index 6642863..0000000
--- a/libs/game_gamebryo/src/gamebryo/gamebryosavegameinfowidget.h
+++ /dev/null
@@ -1,30 +0,0 @@
-#ifndef GAMEBRYOSAVEGAMEINFOWIDGET_H
-#define GAMEBRYOSAVEGAMEINFOWIDGET_H
-
-#include "isavegameinfowidget.h"
-
-#include <QObject>
-
-class GamebryoSaveGameInfo;
-
-namespace Ui
-{
-class GamebryoSaveGameInfoWidget;
-}
-
-class GamebryoSaveGameInfoWidget : public MOBase::ISaveGameInfoWidget
-{
- Q_OBJECT
-
-public:
- GamebryoSaveGameInfoWidget(GamebryoSaveGameInfo const* info, QWidget* parent);
- ~GamebryoSaveGameInfoWidget();
-
- virtual void setSave(MOBase::ISaveGame const&) override;
-
-private:
- Ui::GamebryoSaveGameInfoWidget* ui;
- GamebryoSaveGameInfo const* m_Info;
-};
-
-#endif // GAMEBRYOSAVEGAMEINFOWIDGET_H
diff --git a/libs/game_gamebryo/src/gamebryo/gamebryosavegameinfowidget.ui b/libs/game_gamebryo/src/gamebryo/gamebryosavegameinfowidget.ui
deleted file mode 100644
index ea45d10..0000000
--- a/libs/game_gamebryo/src/gamebryo/gamebryosavegameinfowidget.ui
+++ /dev/null
@@ -1,209 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>GamebryoSaveGameInfoWidget</class>
- <widget class="QWidget" name="GamebryoSaveGameInfoWidget">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>400</width>
- <height>300</height>
- </rect>
- </property>
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Minimum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="windowTitle">
- <string/>
- </property>
- <property name="toolTip">
- <string notr="true"/>
- </property>
- <layout class="QVBoxLayout" name="verticalLayout">
- <item>
- <layout class="QFormLayout" name="formLayout">
- <property name="fieldGrowthPolicy">
- <enum>QFormLayout::AllNonFixedFieldsGrow</enum>
- </property>
- <item row="0" column="0">
- <widget class="QLabel" name="label">
- <property name="font">
- <font>
- <italic>true</italic>
- </font>
- </property>
- <property name="text">
- <string>Save #</string>
- </property>
- </widget>
- </item>
- <item row="1" column="0">
- <widget class="QLabel" name="label_2">
- <property name="font">
- <font>
- <italic>true</italic>
- </font>
- </property>
- <property name="text">
- <string>Character</string>
- </property>
- </widget>
- </item>
- <item row="2" column="0">
- <widget class="QLabel" name="label_4">
- <property name="font">
- <font>
- <italic>true</italic>
- </font>
- </property>
- <property name="text">
- <string>Level</string>
- </property>
- </widget>
- </item>
- <item row="3" column="0">
- <widget class="QLabel" name="label_3">
- <property name="font">
- <font>
- <italic>true</italic>
- </font>
- </property>
- <property name="text">
- <string>Location</string>
- </property>
- </widget>
- </item>
- <item row="4" column="0">
- <widget class="QLabel" name="label_5">
- <property name="font">
- <font>
- <italic>true</italic>
- </font>
- </property>
- <property name="text">
- <string>Date</string>
- </property>
- </widget>
- </item>
- <item row="0" column="1">
- <widget class="QLabel" name="saveNumLabel">
- <property name="font">
- <font>
- <weight>75</weight>
- <bold>true</bold>
- </font>
- </property>
- <property name="text">
- <string notr="true"/>
- </property>
- </widget>
- </item>
- <item row="1" column="1">
- <widget class="QLabel" name="characterLabel">
- <property name="font">
- <font>
- <weight>75</weight>
- <bold>true</bold>
- </font>
- </property>
- <property name="text">
- <string notr="true"/>
- </property>
- </widget>
- </item>
- <item row="2" column="1">
- <widget class="QLabel" name="levelLabel">
- <property name="font">
- <font>
- <weight>75</weight>
- <bold>true</bold>
- </font>
- </property>
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- <item row="3" column="1">
- <widget class="QLabel" name="locationLabel">
- <property name="font">
- <font>
- <weight>75</weight>
- <bold>true</bold>
- </font>
- </property>
- <property name="text">
- <string notr="true"/>
- </property>
- </widget>
- </item>
- <item row="4" column="1">
- <widget class="QLabel" name="dateLabel">
- <property name="font">
- <font>
- <weight>75</weight>
- <bold>true</bold>
- </font>
- </property>
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <widget class="QFrame" name="gameFrame">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Expanding" vsizetype="Minimum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>0</width>
- <height>0</height>
- </size>
- </property>
- <property name="frameShape">
- <enum>QFrame::StyledPanel</enum>
- </property>
- <property name="frameShadow">
- <enum>QFrame::Raised</enum>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QLabel" name="screenshotLabel">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="maximumSize">
- <size>
- <width>16777215</width>
- <height>16777215</height>
- </size>
- </property>
- <property name="autoFillBackground">
- <bool>false</bool>
- </property>
- <property name="text">
- <string notr="true"/>
- </property>
- <property name="alignment">
- <set>Qt::AlignCenter</set>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- <resources/>
- <connections/>
-</ui>
diff --git a/libs/game_gamebryo/src/gamebryo/gamebryoscriptextender.cpp b/libs/game_gamebryo/src/gamebryo/gamebryoscriptextender.cpp
deleted file mode 100644
index e3f709a..0000000
--- a/libs/game_gamebryo/src/gamebryo/gamebryoscriptextender.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-#include "gamebryoscriptextender.h"
-
-#include "gamegamebryo.h"
-
-#include "utility.h"
-
-#include <QDir>
-#include <QString>
-
-GamebryoScriptExtender::GamebryoScriptExtender(const GameGamebryo* game) : m_Game(game)
-{}
-
-GamebryoScriptExtender::~GamebryoScriptExtender() {}
-
-QString GamebryoScriptExtender::loaderName() const
-{
- return BinaryName();
-}
-
-QString GamebryoScriptExtender::loaderPath() const
-{
- return m_Game->gameDirectory().absoluteFilePath(loaderName());
-}
-
-QString GamebryoScriptExtender::savegameExtension() const
-{
- return m_Game->savegameSEExtension();
-}
-
-bool GamebryoScriptExtender::isInstalled() const
-{
- // A note: It is possibly also OK if xxse_steam_loader.dll exists, but it's
- // not clear why that would exist and the exe not if you'd installed it per
- // instructions, and it'd mess up NCC installs a treat.
- return m_Game->gameDirectory().exists(loaderName());
-}
-
-QString GamebryoScriptExtender::getExtenderVersion() const
-{
- return MOBase::getFileVersion(m_Game->gameDirectory().absoluteFilePath(loaderName()));
-}
-
-WORD GamebryoScriptExtender::getArch() const
-{
- return m_Game->getArch(loaderName());
-}
diff --git a/libs/game_gamebryo/src/gamebryo/gamebryoscriptextender.h b/libs/game_gamebryo/src/gamebryo/gamebryoscriptextender.h
deleted file mode 100644
index 7ab4f1d..0000000
--- a/libs/game_gamebryo/src/gamebryo/gamebryoscriptextender.h
+++ /dev/null
@@ -1,31 +0,0 @@
-#ifndef GAMEBRYOSCRIPTEXTENDER_H
-#define GAMEBRYOSCRIPTEXTENDER_H
-
-#include "scriptextender.h"
-
-class GameGamebryo;
-
-class GamebryoScriptExtender : public MOBase::ScriptExtender
-{
-public:
- GamebryoScriptExtender(GameGamebryo const* game);
-
- virtual ~GamebryoScriptExtender();
-
- virtual QString loaderName() const override;
-
- virtual QString loaderPath() const override;
-
- virtual QString savegameExtension() const override;
-
- virtual bool isInstalled() const override;
-
- virtual QString getExtenderVersion() const override;
-
- virtual WORD getArch() const override;
-
-protected:
- GameGamebryo const* const m_Game;
-};
-
-#endif // GAMEBRYOSCRIPTEXTENDER_H
diff --git a/libs/game_gamebryo/src/gamebryo/gamebryounmanagedmods.cpp b/libs/game_gamebryo/src/gamebryo/gamebryounmanagedmods.cpp
deleted file mode 100644
index e5c7608..0000000
--- a/libs/game_gamebryo/src/gamebryo/gamebryounmanagedmods.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-#include "gamebryounmanagedmods.h"
-#include "gamegamebryo.h"
-#include <pluginsetting.h>
-
-GamebryoUnmangedMods::GamebryoUnmangedMods(const GameGamebryo* game) : m_Game(game) {}
-
-GamebryoUnmangedMods::~GamebryoUnmangedMods() {}
-
-QStringList GamebryoUnmangedMods::mods(bool onlyOfficial) const
-{
- QStringList result;
-
- QStringList dlcPlugins = m_Game->DLCPlugins();
- QStringList mainPlugins = m_Game->primaryPlugins();
-
- QDir dataDir(m_Game->dataDirectory());
- for (const QString& fileName : dataDir.entryList({"*.esp", "*.esl", "*.esm"})) {
- if (!mainPlugins.contains(fileName, Qt::CaseInsensitive) &&
- (!onlyOfficial || dlcPlugins.contains(fileName, Qt::CaseInsensitive))) {
- result.append(fileName.chopped(4)); // trims the extension off
- }
- }
-
- return result;
-}
-
-QString GamebryoUnmangedMods::displayName(const QString& modName) const
-{
- return modName;
-}
-
-QFileInfo GamebryoUnmangedMods::referenceFile(const QString& modName) const
-{
- QFileInfoList files =
- m_Game->dataDirectory().entryInfoList(QStringList() << modName + ".es*");
- if (files.size() > 0) {
- return files.at(0);
- } else {
- return QFileInfo();
- }
-}
-
-QStringList GamebryoUnmangedMods::secondaryFiles(const QString& modName) const
-{
- QStringList archives;
- QDir dataDir = m_Game->dataDirectory();
- for (const QString& archiveName : dataDir.entryList({modName + "*.bsa"})) {
- archives.append(dataDir.absoluteFilePath(archiveName));
- }
- return archives;
-}
diff --git a/libs/game_gamebryo/src/gamebryo/gamebryounmanagedmods.h b/libs/game_gamebryo/src/gamebryo/gamebryounmanagedmods.h
deleted file mode 100644
index 38e2ba5..0000000
--- a/libs/game_gamebryo/src/gamebryo/gamebryounmanagedmods.h
+++ /dev/null
@@ -1,26 +0,0 @@
-#ifndef GAMEBRYOUNMANAGEDMODS_H
-#define GAMEBRYOUNMANAGEDMODS_H
-
-#include <unmanagedmods.h>
-
-class GameGamebryo;
-
-class GamebryoUnmangedMods : public MOBase::UnmanagedMods
-{
-public:
- GamebryoUnmangedMods(const GameGamebryo* game);
- ~GamebryoUnmangedMods();
-
- virtual QStringList mods(bool onlyOfficial) const override;
- virtual QString displayName(const QString& modName) const override;
- virtual QFileInfo referenceFile(const QString& modName) const override;
- virtual QStringList secondaryFiles(const QString& modName) const override;
-
-protected:
- const GameGamebryo* game() const { return m_Game; }
-
-private:
- const GameGamebryo* m_Game;
-};
-
-#endif // GAMEBRYOUNMANAGEDMODS_H
diff --git a/libs/game_gamebryo/src/gamebryo/gamegamebryo.cpp b/libs/game_gamebryo/src/gamebryo/gamegamebryo.cpp
deleted file mode 100644
index 347c2d8..0000000
--- a/libs/game_gamebryo/src/gamebryo/gamegamebryo.cpp
+++ /dev/null
@@ -1,478 +0,0 @@
-#include "gamegamebryo.h"
-
-#include "bsainvalidation.h"
-#include "dataarchives.h"
-#include "gamebryomoddatacontent.h"
-#include "gamebryosavegame.h"
-#include "gameplugins.h"
-#include "iprofile.h"
-#include "log.h"
-#include "registry.h"
-#include "savegameinfo.h"
-#include "scopeguard.h"
-#include "scriptextender.h"
-#include "utility.h"
-#include "vdf_parser.h"
-
-#include <QDir>
-#include <QDirIterator>
-#include <QFile>
-#include <QFileInfo>
-#include <QIcon>
-#include <QJsonDocument>
-#include <QJsonValue>
-
-#include <QtDebug>
-#include <QtGlobal>
-
-#include <Knownfolders.h>
-#include <Shlobj.h>
-#include <Windows.h>
-#include <winreg.h>
-#include <winver.h>
-
-#include <optional>
-#include <string>
-#include <vector>
-
-GameGamebryo::GameGamebryo() {}
-
-void GameGamebryo::detectGame()
-{
- m_GamePath = identifyGamePath();
- m_MyGamesPath = determineMyGamesPath(gameName());
-}
-
-bool GameGamebryo::init(MOBase::IOrganizer* moInfo)
-{
- m_Organizer = moInfo;
- m_Organizer->onAboutToRun([this](const auto& binary) {
- return prepareIni(binary);
- });
- return true;
-}
-
-bool GameGamebryo::isInstalled() const
-{
- return !m_GamePath.isEmpty();
-}
-
-QIcon GameGamebryo::gameIcon() const
-{
- return MOBase::iconForExecutable(gameDirectory().absoluteFilePath(binaryName()));
-}
-
-QDir GameGamebryo::gameDirectory() const
-{
- return QDir(m_GamePath);
-}
-
-QDir GameGamebryo::dataDirectory() const
-{
- return gameDirectory().absoluteFilePath("data");
-}
-
-void GameGamebryo::setGamePath(const QString& path)
-{
- m_GamePath = path;
-}
-
-QDir GameGamebryo::documentsDirectory() const
-{
- return m_MyGamesPath;
-}
-
-QDir GameGamebryo::savesDirectory() const
-{
- return QDir(myGamesPath() + "/Saves");
-}
-
-std::vector<std::shared_ptr<const MOBase::ISaveGame>>
-GameGamebryo::listSaves(QDir folder) const
-{
- QStringList filters;
- filters << QString("*.") + savegameExtension();
-
- std::vector<std::shared_ptr<const MOBase::ISaveGame>> saves;
- for (auto info : folder.entryInfoList(filters, QDir::Files)) {
- try {
- saves.push_back(makeSaveGame(info.filePath()));
- } catch (std::exception& e) {
- MOBase::log::error("{}", e.what());
- continue;
- }
- }
-
- return saves;
-}
-
-void GameGamebryo::setGameVariant(const QString& variant)
-{
- m_GameVariant = variant;
-}
-
-QString GameGamebryo::binaryName() const
-{
- return gameShortName() + ".exe";
-}
-
-MOBase::IPluginGame::LoadOrderMechanism GameGamebryo::loadOrderMechanism() const
-{
- return LoadOrderMechanism::FileTime;
-}
-
-MOBase::IPluginGame::SortMechanism GameGamebryo::sortMechanism() const
-{
- return SortMechanism::LOOT;
-}
-
-bool GameGamebryo::looksValid(QDir const& path) const
-{
- // Check for <prog>.exe for now.
- return path.exists(binaryName());
-}
-
-QString GameGamebryo::gameVersion() const
-{
- // We try the file version, but if it looks invalid (starts with the fallback
- // version), we look the product version instead. If the product version is
- // not empty, we use it.
- QString binaryAbsPath = gameDirectory().absoluteFilePath(binaryName());
- QString version = MOBase::getFileVersion(binaryAbsPath);
- if (version.startsWith(FALLBACK_GAME_VERSION)) {
- QString pversion = MOBase::getProductVersion(binaryAbsPath);
- if (!pversion.isEmpty()) {
- version = pversion;
- }
- }
- return version;
-}
-
-QString GameGamebryo::getLauncherName() const
-{
- return gameShortName() + "Launcher.exe";
-}
-
-WORD GameGamebryo::getArch(QString const& program) const
-{
- WORD arch = 0;
- // This *really* needs to be factored out
- std::wstring app_name =
- L"\\\\?\\" +
- QDir::toNativeSeparators(this->gameDirectory().absoluteFilePath(program))
- .toStdWString();
-
- WIN32_FIND_DATAW FindFileData;
- HANDLE hFind = ::FindFirstFileW(app_name.c_str(), &FindFileData);
-
- // exit if the binary was not found
- if (hFind == INVALID_HANDLE_VALUE)
- return arch;
-
- HANDLE hFile = INVALID_HANDLE_VALUE;
- HANDLE hMapping = INVALID_HANDLE_VALUE;
- LPVOID addrHeader = nullptr;
- PIMAGE_NT_HEADERS peHdr = nullptr;
-
- hFile = CreateFileW(app_name.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL,
- OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);
- if (hFile == INVALID_HANDLE_VALUE)
- goto cleanup;
-
- hMapping = CreateFileMappingW(hFile, NULL, PAGE_READONLY | SEC_IMAGE, 0, 0,
- program.toStdWString().c_str());
- if (hMapping == INVALID_HANDLE_VALUE)
- goto cleanup;
-
- addrHeader = MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0);
- if (addrHeader == NULL)
- goto cleanup; // couldn't memory map the file
-
- peHdr = ImageNtHeader(addrHeader);
- if (peHdr == NULL)
- goto cleanup; // couldn't read the header
-
- arch = peHdr->FileHeader.Machine;
-
-cleanup: // release all of our handles
- FindClose(hFind);
- if (hFile != INVALID_HANDLE_VALUE)
- CloseHandle(hFile);
- if (hMapping != INVALID_HANDLE_VALUE)
- CloseHandle(hMapping);
- return arch;
-}
-
-QFileInfo GameGamebryo::findInGameFolder(const QString& relativePath) const
-{
- return QFileInfo(m_GamePath + "/" + relativePath);
-}
-
-QString GameGamebryo::identifyGamePath() const
-{
- QString path = "Software\\Bethesda Softworks\\" + gameShortName();
- return findInRegistry(HKEY_LOCAL_MACHINE, path.toStdWString().c_str(),
- L"Installed Path");
-}
-
-bool GameGamebryo::prepareIni(const QString& exec)
-{
- MOBase::IProfile* profile = m_Organizer->profile();
-
- QString basePath = profile->localSettingsEnabled()
- ? profile->absolutePath()
- : documentsDirectory().absolutePath();
-
- if (!iniFiles().isEmpty()) {
-
- QString profileIni = basePath + "/" + iniFiles()[0];
-
- WCHAR setting[512];
- if (!GetPrivateProfileStringW(L"Launcher", L"bEnableFileSelection", L"0", setting,
- 512, profileIni.toStdWString().c_str()) ||
- wcstol(setting, nullptr, 10) != 1) {
- MOBase::WriteRegistryValue(L"Launcher", L"bEnableFileSelection", L"1",
- profileIni.toStdWString().c_str());
- }
- }
-
- return true;
-}
-
-QString GameGamebryo::selectedVariant() const
-{
- return m_GameVariant;
-}
-
-QString GameGamebryo::myGamesPath() const
-{
- return m_MyGamesPath;
-}
-
-/*static*/ QString GameGamebryo::getLootPath()
-{
- return findInRegistry(HKEY_LOCAL_MACHINE, L"Software\\LOOT", L"Installed Path") +
- "/Loot.exe";
-}
-
-QString GameGamebryo::localAppFolder()
-{
- QString result = getKnownFolderPath(FOLDERID_LocalAppData, false);
- if (result.isEmpty()) {
- // fallback: try the registry
- result = getSpecialPath("Local AppData");
- }
- return result;
-}
-
-void GameGamebryo::copyToProfile(QString const& sourcePath,
- QDir const& destinationDirectory,
- QString const& sourceFileName)
-{
- copyToProfile(sourcePath, destinationDirectory, sourceFileName, sourceFileName);
-}
-
-void GameGamebryo::copyToProfile(QString const& sourcePath,
- QDir const& destinationDirectory,
- QString const& sourceFileName,
- QString const& destinationFileName)
-{
- QString filePath = destinationDirectory.absoluteFilePath(destinationFileName);
- if (!QFileInfo(filePath).exists()) {
- if (!MOBase::shellCopy(sourcePath + "/" + sourceFileName, filePath)) {
- // if copy file fails, create the file empty
- QFile(filePath).open(QIODevice::WriteOnly);
- }
- }
-}
-
-MappingType GameGamebryo::mappings() const
-{
- MappingType result;
-
- for (const QString& profileFile : {"plugins.txt", "loadorder.txt"}) {
- result.push_back({m_Organizer->profilePath() + "/" + profileFile,
- localAppFolder() + "/" + gameShortName() + "/" + profileFile,
- false});
- }
-
- return result;
-}
-
-std::unique_ptr<BYTE[]> GameGamebryo::getRegValue(HKEY key, LPCWSTR path, LPCWSTR value,
- DWORD flags, LPDWORD type = nullptr)
-{
- DWORD size = 0;
- HKEY subKey;
- LONG res = ::RegOpenKeyExW(key, path, 0, KEY_QUERY_VALUE | KEY_WOW64_32KEY, &subKey);
- if (res != ERROR_SUCCESS) {
- res = ::RegOpenKeyExW(key, path, 0, KEY_QUERY_VALUE | KEY_WOW64_64KEY, &subKey);
- if (res != ERROR_SUCCESS)
- return std::unique_ptr<BYTE[]>();
- }
- res = ::RegGetValueW(subKey, L"", value, flags, type, nullptr, &size);
- if (res == ERROR_FILE_NOT_FOUND || res == ERROR_UNSUPPORTED_TYPE) {
- return std::unique_ptr<BYTE[]>();
- }
- if (res != ERROR_SUCCESS && res != ERROR_MORE_DATA) {
- throw MOBase::MyException(
- QObject::tr("failed to query registry path (preflight): %1").arg(res, 0, 16));
- }
-
- std::unique_ptr<BYTE[]> result(new BYTE[size]);
- res = ::RegGetValueW(subKey, L"", value, flags, type, result.get(), &size);
-
- if (res != ERROR_SUCCESS) {
- throw MOBase::MyException(
- QObject::tr("failed to query registry path (read): %1").arg(res, 0, 16));
- }
-
- return result;
-}
-
-QString GameGamebryo::findInRegistry(HKEY baseKey, LPCWSTR path, LPCWSTR value)
-{
- std::unique_ptr<BYTE[]> buffer =
- getRegValue(baseKey, path, value, RRF_RT_REG_SZ | RRF_NOEXPAND);
-
- return QString::fromUtf16(reinterpret_cast<const ushort*>(buffer.get()));
-}
-
-QString GameGamebryo::getKnownFolderPath(REFKNOWNFOLDERID folderId, bool useDefault)
-{
- PWSTR path = nullptr;
- ON_BLOCK_EXIT([&]() {
- if (path != nullptr)
- ::CoTaskMemFree(path);
- });
-
- if (::SHGetKnownFolderPath(folderId, useDefault ? KF_FLAG_DEFAULT_PATH : 0, NULL,
- &path) == S_OK) {
- return QDir::fromNativeSeparators(QString::fromWCharArray(path));
- } else {
- return QString();
- }
-}
-
-QString GameGamebryo::getSpecialPath(const QString& name)
-{
- QString base = findInRegistry(
- HKEY_CURRENT_USER,
- L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders",
- name.toStdWString().c_str());
-
- WCHAR temp[MAX_PATH];
- if (::ExpandEnvironmentStringsW(base.toStdWString().c_str(), temp, MAX_PATH) != 0) {
- return QString::fromWCharArray(temp);
- } else {
- return base;
- }
-}
-
-QString GameGamebryo::determineMyGamesPath(const QString& gameName)
-{
- const QString pattern = "%1/My Games/" + gameName;
-
- auto tryDir = [&](const QString& dir) -> std::optional<QString> {
- if (dir.isEmpty()) {
- return {};
- }
-
- const auto path = pattern.arg(dir);
- if (!QFileInfo(path).exists()) {
- return {};
- }
-
- return path;
- };
-
- // a) this is the way it should work. get the configured My Documents directory
- if (auto d = tryDir(getKnownFolderPath(FOLDERID_Documents, false))) {
- return *d;
- }
-
- // b) if there is no <game> directory there, look in the default directory
- if (auto d = tryDir(getKnownFolderPath(FOLDERID_Documents, true))) {
- return *d;
- }
-
- // c) finally, look in the registry. This is discouraged
- if (auto d = tryDir(getSpecialPath("Personal"))) {
- return *d;
- }
-
- return {};
-}
-
-QString GameGamebryo::parseEpicGamesLocation(const QStringList& manifests)
-{
- // Use the registry entry to find the EGL Data dir first, just in case something
- // changes
- QString manifestDir = findInRegistry(
- HKEY_LOCAL_MACHINE, L"Software\\Epic Games\\EpicGamesLauncher", L"AppDataPath");
- if (manifestDir.isEmpty())
- manifestDir = getKnownFolderPath(FOLDERID_ProgramData, false) +
- "\\Epic\\EpicGamesLauncher\\Data\\";
- manifestDir += "Manifests";
- QDir epicManifests(manifestDir, "*.item",
- QDir::SortFlags(QDir::Name | QDir::IgnoreCase), QDir::Files);
- if (epicManifests.exists()) {
- QDirIterator it(epicManifests);
- while (it.hasNext()) {
- QString manifestFile = it.next();
- QFile manifest(manifestFile);
-
- if (!manifest.open(QIODevice::ReadOnly)) {
- qWarning("Couldn't open Epic Games manifest file.");
- continue;
- }
-
- QByteArray manifestData = manifest.readAll();
-
- QJsonDocument manifestJson(QJsonDocument::fromJson(manifestData));
-
- if (manifests.contains(manifestJson["AppName"].toString())) {
- return manifestJson["InstallLocation"].toString();
- }
- }
- }
- return "";
-}
-
-QString GameGamebryo::parseSteamLocation(const QString& appid,
- const QString& directoryName)
-{
- QString path = "Software\\Valve\\Steam";
- QString steamLocation =
- findInRegistry(HKEY_CURRENT_USER, path.toStdWString().c_str(), L"SteamPath");
- if (!steamLocation.isEmpty()) {
- QString steamLibraryLocation;
- QString steamLibraries(steamLocation + "\\" + "config" + "\\" +
- "libraryfolders.vdf");
- if (QFile(steamLibraries).exists()) {
- std::ifstream file(steamLibraries.toStdString());
- auto root = tyti::vdf::read(file);
- for (auto child : root.childs) {
- tyti::vdf::object* library = child.second.get();
- auto apps = library->childs["apps"];
- if (apps->attribs.contains(appid.toStdString())) {
- steamLibraryLocation = QString::fromStdString(library->attribs["path"]);
- break;
- }
- }
- }
- if (!steamLibraryLocation.isEmpty()) {
- QString gameLocation = steamLibraryLocation + "\\" + "steamapps" + "\\" +
- "common" + "\\" + directoryName;
- if (QDir(gameLocation).exists())
- return gameLocation;
- }
- }
- return "";
-}
-
-void GameGamebryo::registerFeature(std::shared_ptr<MOBase::GameFeature> feature)
-{
- // priority does not matter, this is a game plugin so will get lowest priority in MO2
- m_Organizer->gameFeatures()->registerFeature(this, feature, 0, true);
-}
diff --git a/libs/game_gamebryo/src/gamebryo/gamegamebryo.h b/libs/game_gamebryo/src/gamebryo/gamegamebryo.h
deleted file mode 100644
index 2ff07db..0000000
--- a/libs/game_gamebryo/src/gamebryo/gamegamebryo.h
+++ /dev/null
@@ -1,151 +0,0 @@
-#ifndef GAMEGAMEBRYO_H
-#define GAMEGAMEBRYO_H
-
-#include "iplugingame.h"
-
-class BSAInvalidation;
-class DataArchives;
-class LocalSavegames;
-class SaveGameInfo;
-class BSAInvalidation;
-class LocalSavegames;
-class ScriptExtender;
-class GamePlugins;
-class UnmanagedMods;
-
-#include <QObject>
-#include <QString>
-#include <ShlObj.h>
-#include <dbghelp.h>
-#include <ipluginfilemapper.h>
-#include <iplugingame.h>
-#include <memory>
-
-#include "gamebryosavegame.h"
-#include "igamefeatures.h"
-
-class GameGamebryo : public MOBase::IPluginGame, public MOBase::IPluginFileMapper
-{
- Q_OBJECT
- Q_INTERFACES(MOBase::IPlugin MOBase::IPluginGame MOBase::IPluginFileMapper)
-
- friend class GamebryoScriptExtender;
- friend class GamebryoSaveGameInfo;
- friend class GamebryoSaveGameInfoWidget;
- friend class GamebryoSaveGame;
-
- /**
- * Some Bethesda games do not have a valid file version but a valid product
- * version. If the file version starts with FALLBACK_GAME_VERSION, the product
- * version will be tried.
- */
- static constexpr const char* FALLBACK_GAME_VERSION = "1.0.0";
-
-public:
- GameGamebryo();
-
- void detectGame() override;
- bool init(MOBase::IOrganizer* moInfo) override;
-
-public: // IPluginGame interface
- // getName
- // initializeProfile
- virtual std::vector<std::shared_ptr<const MOBase::ISaveGame>>
- listSaves(QDir folder) const override;
-
- virtual bool isInstalled() const override;
- virtual QIcon gameIcon() const override;
- virtual QDir gameDirectory() const override;
- virtual QDir dataDirectory() const override;
- // secondaryDataDirectories
- virtual void setGamePath(const QString& path) override;
- virtual QDir documentsDirectory() const override;
- virtual QDir savesDirectory() const override;
- // executables
- // steamAPPId
- // primaryPlugins
- // enabledPlugins
- // gameVariants
- virtual void setGameVariant(const QString& variant) override;
- virtual QString binaryName() const override;
- // gameShortName
- // primarySources
- // validShortNames
- // iniFiles
- // DLCPlugins
- // CCPlugins
- virtual LoadOrderMechanism loadOrderMechanism() const override;
- virtual SortMechanism sortMechanism() const override;
- // nexusModOrganizerID
- // nexusGameID
- virtual bool looksValid(QDir const&) const override;
- virtual QString gameVersion() const override;
- virtual QString getLauncherName() const override;
-
-public: // IPluginFileMapper interface
- virtual MappingType mappings() const;
-
-public: // Other (e.g. for game features)
- QString myGamesPath() const;
-
-protected:
- // Retrieve the saves extension for the game.
- virtual QString savegameExtension() const = 0;
- virtual QString savegameSEExtension() const = 0;
-
- // Create a save game.
- virtual std::shared_ptr<const GamebryoSaveGame>
- makeSaveGame(QString filepath) const = 0;
-
- QFileInfo findInGameFolder(const QString& relativePath) const;
- QString selectedVariant() const;
- WORD getArch(QString const& program) const;
-
- static QString localAppFolder();
- // Arguably this shouldn't really be here but every gamebryo program seems to
- // use it
- static QString getLootPath();
-
- // This function is not terribly well named as it copies exactly where it's told
- // to, irrespective of whether it's in the profile...
- static void copyToProfile(const QString& sourcePath, const QDir& destinationDirectory,
- const QString& sourceFileName);
-
- static void copyToProfile(const QString& sourcePath, const QDir& destinationDirectory,
- const QString& sourceFileName,
- const QString& destinationFileName);
-
- virtual QString identifyGamePath() const;
-
- virtual bool prepareIni(const QString& exec);
-
- static std::unique_ptr<BYTE[]> getRegValue(HKEY key, LPCWSTR path, LPCWSTR value,
- DWORD flags, LPDWORD type);
-
- static QString findInRegistry(HKEY baseKey, LPCWSTR path, LPCWSTR value);
-
- static QString getKnownFolderPath(REFKNOWNFOLDERID folderId, bool useDefault);
-
- static QString getSpecialPath(const QString& name);
-
- static QString determineMyGamesPath(const QString& gameName);
-
- static QString parseEpicGamesLocation(const QStringList& manifests);
-
- static QString parseSteamLocation(const QString& appid, const QString& directoryName);
-
-protected:
- void registerFeature(std::shared_ptr<MOBase::GameFeature> feature);
-
-protected:
- // to access organizer for game features, avoid having to pass it to all saves since
- // we already pass the game
- friend class GamebryoSaveGame;
-
- QString m_GamePath;
- QString m_MyGamesPath;
- QString m_GameVariant;
- MOBase::IOrganizer* m_Organizer;
-};
-
-#endif // GAMEGAMEBRYO_H
diff --git a/libs/game_gamebryo/src/gamebryo/vdf_parser.h b/libs/game_gamebryo/src/gamebryo/vdf_parser.h
deleted file mode 100644
index 32d4d27..0000000
--- a/libs/game_gamebryo/src/gamebryo/vdf_parser.h
+++ /dev/null
@@ -1,739 +0,0 @@
-// MIT License
-//
-// Copyright(c) 2016 Matthias Moeller
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files(the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions :
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-
-#ifndef __TYTI_STEAM_VDF_PARSER_H__
-#define __TYTI_STEAM_VDF_PARSER_H__
-
-#include <algorithm>
-#include <fstream>
-#include <functional>
-#include <iterator>
-#include <map>
-#include <memory>
-#include <unordered_map>
-#include <unordered_set>
-#include <utility>
-#include <vector>
-
-#include <exception>
-#include <system_error>
-
-// for wstring support
-#include <locale>
-#include <string>
-
-// internal
-#include <stack>
-
-// VS < 2015 has only partial C++11 support
-#if defined(_MSC_VER) && _MSC_VER < 1900
-#ifndef CONSTEXPR
-#define CONSTEXPR
-#endif
-
-#ifndef NOEXCEPT
-#define NOEXCEPT
-#endif
-#else
-#ifndef CONSTEXPR
-#define CONSTEXPR constexpr
-#define TYTI_UNDEF_CONSTEXPR
-#endif
-
-#ifndef NOEXCEPT
-#define NOEXCEPT noexcept
-#define TYTI_UNDEF_NOEXCEPT
-#endif
-
-#endif
-
-namespace tyti
-{
-namespace vdf
-{
- namespace detail
- {
- ///////////////////////////////////////////////////////////////////////////
- // Helper functions selecting the right encoding (char/wchar_T)
- ///////////////////////////////////////////////////////////////////////////
-
- template <typename T>
- struct literal_macro_help
- {
- static CONSTEXPR const char* result(const char* c, const wchar_t*) NOEXCEPT
- {
- return c;
- }
- static CONSTEXPR const char result(const char c, const wchar_t) NOEXCEPT
- {
- return c;
- }
- };
-
- template <>
- struct literal_macro_help<wchar_t>
- {
- static CONSTEXPR const wchar_t* result(const char*, const wchar_t* wc) NOEXCEPT
- {
- return wc;
- }
- static CONSTEXPR const wchar_t result(const char, const wchar_t wc) NOEXCEPT
- {
- return wc;
- }
- };
-#define TYTI_L(type, text) vdf::detail::literal_macro_help<type>::result(text, L##text)
-
- inline std::string string_converter(const std::string& w) NOEXCEPT
- {
- return w;
- }
-
- // utility wrapper to adapt locale-bound facets for wstring/wbuffer convert
- // from cppreference
- template <class Facet>
- struct deletable_facet : Facet
- {
- template <class... Args>
- deletable_facet(Args&&... args) : Facet(std::forward<Args>(args)...)
- {}
- ~deletable_facet() {}
- };
-
- inline std::string string_converter(const std::wstring& w) // todo: use us-locale
- {
- std::wstring_convert<deletable_facet<std::codecvt<wchar_t, char, std::mbstate_t>>>
- conv1;
- return conv1.to_bytes(w);
- }
-
- ///////////////////////////////////////////////////////////////////////////
- // Writer helper functions
- ///////////////////////////////////////////////////////////////////////////
-
- template <typename charT>
- class tabs
- {
- const size_t t;
-
- public:
- explicit CONSTEXPR tabs(size_t i) NOEXCEPT : t(i) {}
- std::basic_string<charT> print() const
- {
- return std::basic_string<charT>(t, TYTI_L(charT, '\t'));
- }
- inline CONSTEXPR tabs operator+(size_t i) const NOEXCEPT { return tabs(t + i); }
- };
-
- template <typename oStreamT>
- oStreamT& operator<<(oStreamT& s, const tabs<typename oStreamT::char_type> t)
- {
- s << t.print();
- return s;
- }
- } // end namespace detail
-
- ///////////////////////////////////////////////////////////////////////////
- // Interface
- ///////////////////////////////////////////////////////////////////////////
-
- /// custom objects and their corresponding write functions
-
- /// basic object node. Every object has a name and can contains attributes saved as
- /// key_value pairs or childrens
- template <typename CharT>
- struct basic_object
- {
- typedef CharT char_type;
- std::basic_string<char_type> name;
- std::unordered_map<std::basic_string<char_type>, std::basic_string<char_type>>
- attribs;
- std::unordered_map<std::basic_string<char_type>,
- std::shared_ptr<basic_object<char_type>>>
- childs;
-
- void add_attribute(std::basic_string<char_type> key,
- std::basic_string<char_type> value)
- {
- attribs.emplace(std::move(key), std::move(value));
- }
- void add_child(std::unique_ptr<basic_object<char_type>> child)
- {
- std::shared_ptr<basic_object<char_type>> obj{child.release()};
- childs.emplace(obj->name, obj);
- }
- void set_name(std::basic_string<char_type> n) { name = std::move(n); }
- };
-
- template <typename CharT>
- struct basic_multikey_object
- {
- typedef CharT char_type;
- std::basic_string<char_type> name;
- std::unordered_multimap<std::basic_string<char_type>, std::basic_string<char_type>>
- attribs;
- std::unordered_multimap<std::basic_string<char_type>,
- std::shared_ptr<basic_multikey_object<char_type>>>
- childs;
-
- void add_attribute(std::basic_string<char_type> key,
- std::basic_string<char_type> value)
- {
- attribs.emplace(std::move(key), std::move(value));
- }
- void add_child(std::unique_ptr<basic_multikey_object<char_type>> child)
- {
- std::shared_ptr<basic_multikey_object<char_type>> obj{child.release()};
- childs.emplace(obj->name, obj);
- }
- void set_name(std::basic_string<char_type> n) { name = std::move(n); }
- };
-
- typedef basic_object<char> object;
- typedef basic_object<wchar_t> wobject;
- typedef basic_multikey_object<char> multikey_object;
- typedef basic_multikey_object<wchar_t> wmultikey_object;
-
- struct Options
- {
- bool strip_escape_symbols;
- bool ignore_all_platform_conditionals;
- bool ignore_includes;
-
- Options()
- : strip_escape_symbols(true), ignore_all_platform_conditionals(false),
- ignore_includes(false)
- {}
- };
-
- // forward decls
- // forward decl
- template <typename OutputT, typename iStreamT>
- OutputT read(iStreamT& inStream, const Options& opt = Options{});
-
- /** \brief writes given object tree in vdf format to given stream.
- Output is prettyfied, using tabs
- */
- template <typename oStreamT, typename T>
- void write(oStreamT& s, const T& r,
- const detail::tabs<typename oStreamT::char_type> tab =
- detail::tabs<typename oStreamT::char_type>(0))
- {
- typedef typename oStreamT::char_type charT;
- using namespace detail;
- s << tab << TYTI_L(charT, '"') << r.name << TYTI_L(charT, "\"\n") << tab
- << TYTI_L(charT, "{\n");
- for (const auto& i : r.attribs)
- s << tab + 1 << TYTI_L(charT, '"') << i.first << TYTI_L(charT, "\"\t\t\"")
- << i.second << TYTI_L(charT, "\"\n");
- for (const auto& i : r.childs)
- if (i.second)
- write(s, *i.second, tab + 1);
- s << tab << TYTI_L(charT, "}\n");
- }
-
- namespace detail
- {
- template <typename iStreamT>
- std::basic_string<typename iStreamT::char_type> read_file(iStreamT& inStream)
- {
- // cache the file
- typedef typename iStreamT::char_type charT;
- std::basic_string<charT> str;
- inStream.seekg(0, std::ios::end);
- str.resize(static_cast<size_t>(inStream.tellg()));
- if (str.empty())
- return str;
-
- inStream.seekg(0, std::ios::beg);
- inStream.read(&str[0], str.size());
- return str;
- }
-
- /** \brief Read VDF formatted sequences defined by the range [first, last).
- If the file is mailformatted, parser will try to read it until it can.
- @param first begin iterator
- @param end end iterator
- @param exclude_files list of files which cant be included anymore.
- prevents circular includes
-
- can thow:
- - "std::runtime_error" if a parsing error occured
- - "std::bad_alloc" if not enough memory coup be allocated
- */
- template <typename OutputT, typename IterT>
- std::vector<std::unique_ptr<OutputT>>
- read_internal(IterT first, const IterT last,
- std::unordered_set<std::basic_string<
- typename std::iterator_traits<IterT>::value_type>>& exclude_files,
- const Options& opt)
- {
- static_assert(std::is_default_constructible<OutputT>::value,
- "Output Type must be default constructible (provide constructor "
- "without arguments)");
- static_assert(std::is_move_constructible<OutputT>::value,
- "Output Type must be move constructible");
-
- typedef typename std::iterator_traits<IterT>::value_type charT;
-
- const std::basic_string<charT> comment_end_str = TYTI_L(charT, "*/");
- const std::basic_string<charT> whitespaces = TYTI_L(charT, " \n\v\f\r\t");
-
-#ifdef WIN32
- std::function<bool(const std::basic_string<charT>&)> is_platform_str =
- [](const std::basic_string<charT>& in) {
- return in == TYTI_L(charT, "$WIN32") || in == TYTI_L(charT, "$WINDOWS");
- };
-#elif __APPLE__
- // WIN32 stands for pc in general
- std::function<bool(const std::basic_string<charT>&)> is_platform_str =
- [](const std::basic_string<charT>& in) {
- return in == TYTI_L(charT, "$WIN32") || in == TYTI_L(charT, "$POSIX") ||
- in == TYTI_L(charT, "$OSX");
- };
-
-#elif __linux__
- // WIN32 stands for pc in general
- std::function<bool(const std::basic_string<charT>&)> is_platform_str =
- [](const std::basic_string<charT>& in) {
- return in == TYTI_L(charT, "$WIN32") || in == TYTI_L(charT, "$POSIX") ||
- in == TYTI_L(charT, "$LINUX");
- };
-#else
- std::function<bool(const std::basic_string<charT>&)> is_platform_str =
- [](const std::basic_string<charT>& in) {
- return false;
- };
-#endif
-
- if (opt.ignore_all_platform_conditionals)
- is_platform_str = [](const std::basic_string<charT>&) {
- return false;
- };
-
- // function for skipping a comment block
- // iter: iterator poition to the position after a '/'
- auto skip_comments = [&comment_end_str](IterT iter, const IterT& last) -> IterT {
- ++iter;
- if (iter != last) {
- if (*iter == TYTI_L(charT, '/')) {
- // line comment, skip whole line
- iter = std::find(iter + 1, last, TYTI_L(charT, '\n'));
- }
-
- if (*iter == '*') {
- // block comment, skip until next occurance of "*\"
- iter = std::search(iter + 1, last, std::begin(comment_end_str),
- std::end(comment_end_str));
- iter += 2;
- }
- }
- return iter;
- };
-
- auto end_quote = [](IterT iter, const IterT& last) -> IterT {
- const auto begin = iter;
- auto last_esc = iter;
- do {
- ++iter;
- iter = std::find(iter, last, TYTI_L(charT, '\"'));
- if (iter == last)
- break;
-
- last_esc = std::prev(iter);
- while (last_esc != begin && *last_esc == '\\')
- --last_esc;
- } while (!(std::distance(last_esc, iter) % 2));
- if (iter == last)
- throw std::runtime_error{"quote was opened but not closed."};
- return iter;
- };
-
- auto end_word = [&whitespaces](IterT iter, const IterT& last) -> IterT {
- const auto begin = iter;
- auto last_esc = iter;
- do {
- ++iter;
- iter = std::find_first_of(iter, last, std::begin(whitespaces),
- std::end(whitespaces));
- if (iter == last)
- break;
-
- last_esc = std::prev(iter);
- while (last_esc != begin && *last_esc == '\\')
- --last_esc;
- } while (!(std::distance(last_esc, iter) % 2));
- // if (iter == last)
- // throw std::runtime_error{ "word wasnt properly ended" };
- return iter;
- };
-
- auto skip_whitespaces = [&whitespaces](IterT iter, const IterT& last) -> IterT {
- iter = std::find_if_not(iter, last, [&whitespaces](charT c) {
- // return true if whitespace
- return std::any_of(std::begin(whitespaces), std::end(whitespaces),
- [c](charT pc) {
- return pc == c;
- });
- });
- return iter;
- };
-
- std::function<void(std::basic_string<charT>&)> strip_escape_symbols =
- [](std::basic_string<charT>& s) {
- auto quote_searcher = [&s](size_t pos) {
- return s.find(TYTI_L(charT, "\\\""), pos);
- };
- auto p = quote_searcher(0);
- while (p != s.npos) {
- s.replace(p, 2, TYTI_L(charT, "\""));
- p = quote_searcher(p);
- }
- auto searcher = [&s](size_t pos) {
- return s.find(TYTI_L(charT, "\\\\"), pos);
- };
- p = searcher(0);
- while (p != s.npos) {
- s.replace(p, 2, TYTI_L(charT, "\\"));
- p = searcher(p);
- }
- };
-
- if (!opt.strip_escape_symbols)
- strip_escape_symbols = [](std::basic_string<charT>&) {};
-
- auto conditional_fullfilled = [&skip_whitespaces,
- &is_platform_str](IterT& iter, const IterT& last) {
- iter = skip_whitespaces(iter, last);
- if (*iter == '[') {
- ++iter;
- const auto end = std::find(iter, last, ']');
- const bool negate = *iter == '!';
- if (negate)
- ++iter;
- auto conditional = std::basic_string<charT>(iter, end);
-
- const bool is_platform = is_platform_str(conditional);
- iter = end + 1;
-
- return static_cast<bool>(is_platform ^ negate);
- }
- return true;
- };
-
- // read header
- // first, quoted name
- std::unique_ptr<OutputT> curObj = nullptr;
- std::vector<std::unique_ptr<OutputT>> roots;
- std::stack<std::unique_ptr<OutputT>> lvls;
- auto curIter = first;
-
- while (curIter != last && *curIter != '\0') {
- // find first starting attrib/child, or ending
- curIter = skip_whitespaces(curIter, last);
- if (curIter == last || *curIter == '\0')
- break;
- if (*curIter == TYTI_L(charT, '/')) {
- curIter = skip_comments(curIter, last);
- } else if (*curIter != TYTI_L(charT, '}')) {
-
- // get key
- const auto keyEnd = (*curIter == TYTI_L(charT, '\"'))
- ? end_quote(curIter, last)
- : end_word(curIter, last);
- if (*curIter == TYTI_L(charT, '\"'))
- ++curIter;
- std::basic_string<charT> key(curIter, keyEnd);
- strip_escape_symbols(key);
- curIter = keyEnd + ((*keyEnd == TYTI_L(charT, '\"')) ? 1 : 0);
-
- curIter = skip_whitespaces(curIter, last);
-
- auto conditional = conditional_fullfilled(curIter, last);
- if (!conditional)
- continue;
-
- while (*curIter == TYTI_L(charT, '/')) {
-
- curIter = skip_comments(curIter, last);
- if (curIter == last || *curIter == '}')
- throw std::runtime_error{"key declared, but no value"};
- curIter = skip_whitespaces(curIter, last);
- if (curIter == last || *curIter == '}')
- throw std::runtime_error{"key declared, but no value"};
- }
- // get value
- if (*curIter != '{') {
- const auto valueEnd = (*curIter == TYTI_L(charT, '\"'))
- ? end_quote(curIter, last)
- : end_word(curIter, last);
- if (*curIter == TYTI_L(charT, '\"'))
- ++curIter;
-
- auto value = std::basic_string<charT>(curIter, valueEnd);
- strip_escape_symbols(value);
- curIter = valueEnd + ((*valueEnd == TYTI_L(charT, '\"')) ? 1 : 0);
-
- auto conditional = conditional_fullfilled(curIter, last);
- if (!conditional)
- continue;
-
- // process value
- if (key != TYTI_L(charT, "#include") && key != TYTI_L(charT, "#base")) {
- if (curObj) {
- curObj->add_attribute(std::move(key), std::move(value));
- } else {
- throw std::runtime_error{"unexpected key without object"};
- }
- } else {
- if (!opt.ignore_includes &&
- exclude_files.find(value) == exclude_files.end()) {
- exclude_files.insert(value);
- std::basic_ifstream<charT> i(detail::string_converter(value));
- auto str = read_file(i);
- auto file_objs =
- read_internal<OutputT>(str.begin(), str.end(), exclude_files, opt);
- for (auto& n : file_objs) {
- if (curObj)
- curObj->add_child(std::move(n));
- else
- roots.push_back(std::move(n));
- }
- exclude_files.erase(value);
- }
- }
- } else if (*curIter == '{') {
- if (curObj)
- lvls.push(std::move(curObj));
- curObj = std::make_unique<OutputT>();
- curObj->set_name(std::move(key));
- ++curIter;
- }
- }
- // end of new object
- else if (curObj && *curIter == TYTI_L(charT, '}')) {
- if (!lvls.empty()) {
- // get object before
- std::unique_ptr<OutputT> prev{std::move(lvls.top())};
- lvls.pop();
-
- // add finished obj to obj before and release it from processing
- prev->add_child(std::move(curObj));
- curObj = std::move(prev);
- } else {
- roots.push_back(std::move(curObj));
- curObj.reset();
- }
- ++curIter;
- } else {
- throw std::runtime_error{"unexpected '}'"};
- }
- }
- if (curObj != nullptr || !lvls.empty()) {
- throw std::runtime_error{"object is not closed with '}'"};
- }
-
- return roots;
- }
-
- } // namespace detail
-
- /** \brief Read VDF formatted sequences defined by the range [first, last).
- If the file is mailformatted, parser will try to read it until it can.
- @param first begin iterator
- @param end end iterator
-
- can thow:
- - "std::runtime_error" if a parsing error occured
- - "std::bad_alloc" if not enough memory coup be allocated
- */
- template <typename OutputT, typename IterT>
- OutputT read(IterT first, const IterT last, const Options& opt = Options{})
- {
- auto exclude_files = std::unordered_set<
- std::basic_string<typename std::iterator_traits<IterT>::value_type>>{};
- auto roots = detail::read_internal<OutputT>(first, last, exclude_files, opt);
-
- OutputT result;
- if (roots.size() > 1) {
- for (auto& i : roots)
- result.add_child(std::move(i));
- } else if (roots.size() == 1)
- result = std::move(*roots[0]);
-
- return result;
- }
-
- /** \brief Read VDF formatted sequences defined by the range [first, last).
- If the file is mailformatted, parser will try to read it until it can.
- @param first begin iterator
- @param end end iterator
- @param ec output bool. 0 if ok, otherwise, holds an system error code
-
- Possible error codes:
- std::errc::protocol_error: file is mailformatted
- std::errc::not_enough_memory: not enough space
- std::errc::invalid_argument: iterators throws e.g. out of range
- */
- template <typename OutputT, typename IterT>
- OutputT read(IterT first, IterT last, std::error_code& ec,
- const Options& opt = Options{}) NOEXCEPT
-
- {
- ec.clear();
- OutputT r{};
- try {
- r = read<OutputT>(first, last, opt);
- } catch (std::runtime_error&) {
- ec = std::make_error_code(std::errc::protocol_error);
- } catch (std::bad_alloc&) {
- ec = std::make_error_code(std::errc::not_enough_memory);
- } catch (...) {
- ec = std::make_error_code(std::errc::invalid_argument);
- }
- return r;
- }
-
- /** \brief Read VDF formatted sequences defined by the range [first, last).
- If the file is mailformatted, parser will try to read it until it can.
- @param first begin iterator
- @param end end iterator
- @param ok output bool. true, if parser successed, false, if parser failed
- */
- template <typename OutputT, typename IterT>
- OutputT read(IterT first, const IterT last, bool* ok,
- const Options& opt = Options{}) NOEXCEPT
- {
- std::error_code ec;
- auto r = read<OutputT>(first, last, ec, opt);
- if (ok)
- *ok = !ec;
- return r;
- }
-
- template <typename IterT>
- inline auto read(IterT first, const IterT last, bool* ok,
- const Options& opt = Options{}) NOEXCEPT
- ->basic_object<typename std::iterator_traits<IterT>::value_type>
- {
- return read<basic_object<typename std::iterator_traits<IterT>::value_type>>(
- first, last, ok, opt);
- }
-
- template <typename IterT>
- inline auto read(IterT first, IterT last, std::error_code& ec,
- const Options& opt = Options{}) NOEXCEPT
- ->basic_object<typename std::iterator_traits<IterT>::value_type>
- {
- return read<basic_object<typename std::iterator_traits<IterT>::value_type>>(
- first, last, ec, opt);
- }
-
- template <typename IterT>
- inline auto read(IterT first, const IterT last, const Options& opt = Options{})
- -> basic_object<typename std::iterator_traits<IterT>::value_type>
- {
- return read<basic_object<typename std::iterator_traits<IterT>::value_type>>(
- first, last, opt);
- }
-
- /** \brief Loads a stream (e.g. filestream) into the memory and parses the vdf
- formatted data. throws "std::bad_alloc" if file buffer could not be allocated
- */
- template <typename OutputT, typename iStreamT>
- OutputT read(iStreamT& inStream, std::error_code& ec, const Options& opt = Options{})
- {
- // cache the file
- typedef typename iStreamT::char_type charT;
- std::basic_string<charT> str = detail::read_file(inStream);
-
- // parse it
- return read<OutputT>(str.begin(), str.end(), ec, opt);
- }
-
- template <typename iStreamT>
- inline basic_object<typename iStreamT::char_type>
- read(iStreamT& inStream, std::error_code& ec, const Options& opt = Options{})
- {
- return read<basic_object<typename iStreamT::char_type>>(inStream, ec, opt);
- }
-
- /** \brief Loads a stream (e.g. filestream) into the memory and parses the vdf
- formatted data. throws "std::bad_alloc" if file buffer could not be allocated ok ==
- false, if a parsing error occured
- */
- template <typename OutputT, typename iStreamT>
- OutputT read(iStreamT& inStream, bool* ok, const Options& opt = Options{})
- {
- std::error_code ec;
- const auto r = read<OutputT>(inStream, ec, opt);
- if (ok)
- *ok = !ec;
- return r;
- }
-
- template <typename iStreamT>
- inline basic_object<typename iStreamT::char_type> read(iStreamT& inStream, bool* ok,
- const Options& opt = Options{})
- {
- return read<basic_object<typename iStreamT::char_type>>(inStream, ok, opt);
- }
-
- /** \brief Loads a stream (e.g. filestream) into the memory and parses the vdf
- formatted data. throws "std::bad_alloc" if file buffer could not be allocated
- throws "std::runtime_error" if a parsing error occured
- */
- template <typename OutputT, typename iStreamT>
- OutputT read(iStreamT& inStream, const Options& opt)
- {
-
- // cache the file
- typedef typename iStreamT::char_type charT;
- std::basic_string<charT> str = detail::read_file(inStream);
- // parse it
- return read<OutputT>(str.begin(), str.end(), opt);
- }
-
- template <typename iStreamT>
- inline basic_object<typename iStreamT::char_type> read(iStreamT& inStream,
- const Options& opt = Options{})
- {
- return read<basic_object<typename iStreamT::char_type>>(inStream, opt);
- }
-
-} // namespace vdf
-} // namespace tyti
-#ifndef TYTI_NO_L_UNDEF
-#undef TYTI_L
-#endif
-
-#ifdef TYTI_UNDEF_CONSTEXPR
-#undef CONSTEXPR
-#undef TYTI_NO_L_UNDEF
-#endif
-
-#ifdef TYTI_UNDEF_NOTHROW
-#undef NOTHROW
-#undef TYTI_UNDEF_NOTHROW
-#endif
-
-#endif //__TYTI_STEAM_VDF_PARSER_H__
diff --git a/libs/installer_omod_native/CMakeLists.txt b/libs/installer_omod_native/CMakeLists.txt
deleted file mode 100644
index 920de28..0000000
--- a/libs/installer_omod_native/CMakeLists.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-cmake_minimum_required(VERSION 3.16)
-
-project(installer_omod_native)
-
-add_subdirectory(src)
diff --git a/libs/installer_omod_native/src/CMakeLists.txt b/libs/installer_omod_native/src/CMakeLists.txt
deleted file mode 100644
index 6c7e01e..0000000
--- a/libs/installer_omod_native/src/CMakeLists.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-cmake_minimum_required(VERSION 3.16)
-
-find_package(Qt6 REQUIRED COMPONENTS Widgets)
-qt_standard_project_setup()
-
-file(GLOB installer_omod_native_SOURCES CONFIGURE_DEPENDS
- ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp
- ${CMAKE_CURRENT_SOURCE_DIR}/*.h
-)
-
-add_library(installer_omod_native SHARED ${installer_omod_native_SOURCES})
-mo2_configure_plugin(installer_omod_native NO_SOURCES WARNINGS OFF)
-target_link_libraries(installer_omod_native PRIVATE mo2::uibase Qt6::Widgets z lzma)
-mo2_install_plugin(installer_omod_native)
diff --git a/libs/installer_omod_native/src/omodinstaller.cpp b/libs/installer_omod_native/src/omodinstaller.cpp
deleted file mode 100644
index 592111a..0000000
--- a/libs/installer_omod_native/src/omodinstaller.cpp
+++ /dev/null
@@ -1,472 +0,0 @@
-#include "omodinstaller.h"
-
-#include <uibase/guessedvalue.h>
-#include <uibase/iinstallationmanager.h>
-#include <uibase/log.h>
-
-#include <QDir>
-#include <QDirIterator>
-#include <QFile>
-#include <QFileInfo>
-#include <QProcess>
-#include <QTemporaryDir>
-
-#include <zlib.h>
-#include <lzma.h>
-
-#include <cstring>
-#include <stdexcept>
-
-// ============================================================================
-// BinaryReader
-// ============================================================================
-
-OmodInstaller::BinaryReader::BinaryReader(const QByteArray& data) : m_data(data) {}
-
-uint8_t OmodInstaller::BinaryReader::readByte()
-{
- if (m_pos >= m_data.size())
- throw std::runtime_error("BinaryReader: read past end");
- return static_cast<uint8_t>(m_data[m_pos++]);
-}
-
-int32_t OmodInstaller::BinaryReader::readInt32LE()
-{
- if (m_pos + 4 > m_data.size())
- throw std::runtime_error("BinaryReader: read past end");
- int32_t val;
- std::memcpy(&val, m_data.constData() + m_pos, 4);
- m_pos += 4;
- return val;
-}
-
-uint32_t OmodInstaller::BinaryReader::readUInt32LE()
-{
- if (m_pos + 4 > m_data.size())
- throw std::runtime_error("BinaryReader: read past end");
- uint32_t val;
- std::memcpy(&val, m_data.constData() + m_pos, 4);
- m_pos += 4;
- return val;
-}
-
-int64_t OmodInstaller::BinaryReader::readInt64LE()
-{
- if (m_pos + 8 > m_data.size())
- throw std::runtime_error("BinaryReader: read past end");
- int64_t val;
- std::memcpy(&val, m_data.constData() + m_pos, 8);
- m_pos += 8;
- return val;
-}
-
-void OmodInstaller::BinaryReader::skip(int bytes)
-{
- m_pos += bytes;
- if (m_pos > m_data.size())
- throw std::runtime_error("BinaryReader: skip past end");
-}
-
-int OmodInstaller::BinaryReader::read7BitEncodedInt()
-{
- int result = 0;
- int shift = 0;
- while (true) {
- uint8_t b = readByte();
- result |= (b & 0x7F) << shift;
- shift += 7;
- if ((b & 0x80) == 0)
- break;
- }
- return result;
-}
-
-QString OmodInstaller::BinaryReader::readNetString()
-{
- int length = read7BitEncodedInt();
- if (length == 0)
- return {};
- QByteArray raw = readBytes(length);
- return QString::fromUtf8(raw);
-}
-
-QByteArray OmodInstaller::BinaryReader::readBytes(int count)
-{
- if (m_pos + count > m_data.size())
- throw std::runtime_error("BinaryReader: read past end");
- QByteArray result = m_data.mid(m_pos, count);
- m_pos += count;
- return result;
-}
-
-bool OmodInstaller::BinaryReader::atEnd() const
-{
- return m_pos >= m_data.size();
-}
-
-// ============================================================================
-// OmodInstaller
-// ============================================================================
-
-OmodInstaller::OmodInstaller() = default;
-
-bool OmodInstaller::init(MOBase::IOrganizer* organizer)
-{
- m_organizer = organizer;
- return true;
-}
-
-QString OmodInstaller::name() const
-{
- return "OMOD Installer (Native)";
-}
-
-QString OmodInstaller::localizedName() const
-{
- return "OMOD Installer (Native)";
-}
-
-QString OmodInstaller::author() const
-{
- return "Fluorine Manager";
-}
-
-QString OmodInstaller::description() const
-{
- return "Installer for .omod archives (Oblivion Mod Manager format)";
-}
-
-MOBase::VersionInfo OmodInstaller::version() const
-{
- return MOBase::VersionInfo(1, 0, 0);
-}
-
-QList<MOBase::PluginSetting> OmodInstaller::settings() const
-{
- return {};
-}
-
-unsigned int OmodInstaller::priority() const
-{
- return 500;
-}
-
-bool OmodInstaller::isManualInstaller() const
-{
- return false;
-}
-
-bool OmodInstaller::isArchiveSupported(
- std::shared_ptr<const MOBase::IFileTree> tree) const
-{
- if (!tree)
- return false;
-
- // An OMOD zip always contains a "config" entry.
- for (auto it = tree->begin(); it != tree->end(); ++it) {
- auto entry = *it;
- if (entry && !entry->isDir() && entry->name().compare("config", Qt::CaseInsensitive) == 0)
- return true;
- }
- return false;
-}
-
-bool OmodInstaller::isArchiveSupported(const QString& archiveName) const
-{
- return archiveName.toLower().endsWith(".omod");
-}
-
-std::set<QString> OmodInstaller::supportedExtensions() const
-{
- return {"omod"};
-}
-
-OmodInstaller::EInstallResult OmodInstaller::install(
- MOBase::GuessedValue<QString>& modName, QString /*gameName*/,
- const QString& archiveName, const QString& /*version*/, int /*nexusID*/)
-{
- try {
- return doInstall(modName, archiveName);
- } catch (const std::exception& e) {
- MOBase::log::error("OMOD install failed: {}", e.what());
- return RESULT_FAILED;
- }
-}
-
-OmodInstaller::EInstallResult OmodInstaller::doInstall(
- MOBase::GuessedValue<QString>& modName, const QString& archiveName)
-{
- // Extract the .omod (zip) to a temp directory using unzip.
- QTemporaryDir omodTmp;
- if (!omodTmp.isValid()) {
- MOBase::log::error("OMOD: failed to create temp directory");
- return RESULT_FAILED;
- }
-
- {
- QProcess unzip;
- unzip.setWorkingDirectory(omodTmp.path());
- unzip.start("unzip", {"-o", archiveName, "-d", omodTmp.path()});
- unzip.waitForFinished(60000);
- if (unzip.exitCode() != 0) {
- MOBase::log::error("OMOD: unzip failed: {}",
- unzip.readAllStandardError().toStdString());
- return RESULT_FAILED;
- }
- }
-
- // Read and parse the config entry.
- QString configPath = QDir(omodTmp.path()).filePath("config");
- if (!QFile::exists(configPath)) {
- MOBase::log::warn("OMOD: no config entry found");
- return RESULT_NOTATTEMPTED;
- }
-
- QFile configFile(configPath);
- if (!configFile.open(QIODevice::ReadOnly)) {
- MOBase::log::error("OMOD: could not read config");
- return RESULT_FAILED;
- }
- QByteArray configData = configFile.readAll();
- configFile.close();
-
- OmodConfig config = parseConfig(configData);
- if (!config.modName.isEmpty()) {
- modName.update(config.modName, MOBase::GUESS_META);
- }
-
- // Create a separate temp directory for the extracted files.
- QTemporaryDir extractTmp;
- if (!extractTmp.isValid()) {
- MOBase::log::error("OMOD: failed to create extraction temp directory");
- return RESULT_FAILED;
- }
-
- // Extract data and plugins streams.
- extractStream(omodTmp.path(), "data", "data.crc", config.compressionType,
- extractTmp.path());
- extractStream(omodTmp.path(), "plugins", "plugins.crc", config.compressionType,
- extractTmp.path());
-
- // Copy readme files if present.
- QDir omodDir(omodTmp.path());
- QStringList entries = omodDir.entryList(QDir::Files);
- for (const QString& entry : entries) {
- QString lower = entry.toLower();
- if (lower == "readme" || lower.startsWith("readme.")) {
- QFile::copy(omodDir.filePath(entry),
- QDir(extractTmp.path()).filePath(entry));
- }
- }
-
- // Verify we have files to install.
- QDirIterator dirIt(extractTmp.path(), QDir::Files, QDirIterator::Subdirectories);
- if (!dirIt.hasNext()) {
- MOBase::log::warn("OMOD: no files extracted");
- return RESULT_FAILED;
- }
-
- // Repackage extracted files as a standard zip for MO2's installer.
- QString repackPath = QDir(extractTmp.path()).filePath("_repack.zip");
- {
- QProcess zip;
- zip.setWorkingDirectory(extractTmp.path());
- // Collect all files relative to extractTmp, excluding the repack zip itself.
- QStringList zipArgs;
- zipArgs << "-r" << repackPath << ".";
- zipArgs << "-x" << "./_repack.zip";
- zip.start("zip", zipArgs);
- zip.waitForFinished(120000);
- if (zip.exitCode() != 0) {
- MOBase::log::error("OMOD: zip repackaging failed: {}",
- zip.readAllStandardError().toStdString());
- return RESULT_FAILED;
- }
- }
-
- return manager()->installArchive(modName, repackPath);
-}
-
-// ============================================================================
-// OMOD binary format parsing
-// ============================================================================
-
-OmodInstaller::OmodConfig OmodInstaller::parseConfig(const QByteArray& data)
-{
- BinaryReader reader(data);
- OmodConfig config;
-
- config.fileVersion = reader.readByte();
- config.modName = reader.readNetString();
- config.major = reader.readInt32LE();
- config.minor = reader.readInt32LE();
- config.authorName = reader.readNetString();
- config.email = reader.readNetString();
- config.website = reader.readNetString();
- config.desc = reader.readNetString();
-
- // Windows FILETIME (8 bytes) - skip.
- reader.skip(8);
-
- // Compression type: 0 = deflate, 1 = lzma.
- config.compressionType = reader.readByte();
-
- return config;
-}
-
-std::vector<OmodInstaller::CrcEntry>
-OmodInstaller::parseCrcFile(const QByteArray& data)
-{
- BinaryReader reader(data);
- int count = reader.read7BitEncodedInt();
-
- std::vector<CrcEntry> entries;
- entries.reserve(count);
-
- for (int i = 0; i < count; ++i) {
- CrcEntry entry;
- entry.path = reader.readNetString();
- entry.crc = reader.readUInt32LE();
- entry.size = reader.readInt64LE();
- entries.push_back(std::move(entry));
- }
-
- return entries;
-}
-
-QByteArray OmodInstaller::decompressStream(const QByteArray& data,
- uint8_t compressionType)
-{
- if (compressionType == 0) {
- // Raw deflate (zlib with wbits = -15, no header).
- z_stream strm{};
- if (inflateInit2(&strm, -15) != Z_OK) {
- throw std::runtime_error("inflateInit2 failed");
- }
-
- strm.next_in = reinterpret_cast<Bytef*>(const_cast<char*>(data.constData()));
- strm.avail_in = static_cast<uInt>(data.size());
-
- QByteArray result;
- char buffer[65536];
-
- int ret;
- do {
- strm.next_out = reinterpret_cast<Bytef*>(buffer);
- strm.avail_out = sizeof(buffer);
- ret = inflate(&strm, Z_NO_FLUSH);
- if (ret == Z_STREAM_ERROR || ret == Z_DATA_ERROR || ret == Z_MEM_ERROR) {
- inflateEnd(&strm);
- throw std::runtime_error("zlib inflate failed");
- }
- int have = sizeof(buffer) - strm.avail_out;
- result.append(buffer, have);
- } while (ret != Z_STREAM_END);
-
- inflateEnd(&strm);
- return result;
-
- } else if (compressionType == 1) {
- // LZMA decompression.
- lzma_stream strm = LZMA_STREAM_INIT;
- lzma_ret ret = lzma_alone_decoder(&strm, UINT64_MAX);
- if (ret != LZMA_OK) {
- throw std::runtime_error("lzma_alone_decoder init failed");
- }
-
- strm.next_in = reinterpret_cast<const uint8_t*>(data.constData());
- strm.avail_in = data.size();
-
- QByteArray result;
- uint8_t buffer[65536];
-
- do {
- strm.next_out = buffer;
- strm.avail_out = sizeof(buffer);
- ret = lzma_code(&strm, LZMA_FINISH);
- if (ret != LZMA_OK && ret != LZMA_STREAM_END) {
- lzma_end(&strm);
- throw std::runtime_error("lzma decompression failed");
- }
- size_t have = sizeof(buffer) - strm.avail_out;
- result.append(reinterpret_cast<const char*>(buffer), static_cast<int>(have));
- } while (ret != LZMA_STREAM_END);
-
- lzma_end(&strm);
- return result;
-
- } else {
- throw std::runtime_error(
- std::string("Unknown OMOD compression type: ") +
- std::to_string(compressionType));
- }
-}
-
-void OmodInstaller::extractStream(const QString& omodDir, const QString& streamName,
- const QString& crcName, uint8_t compressionType,
- const QString& outDir)
-{
- QDir dir(omodDir);
- QString streamPath = dir.filePath(streamName);
- QString crcPath = dir.filePath(crcName);
-
- if (!QFile::exists(streamPath))
- return;
-
- if (!QFile::exists(crcPath)) {
- MOBase::log::warn("OMOD: {} present but {} missing", streamName.toStdString(),
- crcName.toStdString());
- return;
- }
-
- // Read CRC file.
- QFile crcFile(crcPath);
- if (!crcFile.open(QIODevice::ReadOnly))
- return;
- QByteArray crcData = crcFile.readAll();
- crcFile.close();
-
- std::vector<CrcEntry> fileList = parseCrcFile(crcData);
- if (fileList.empty())
- return;
-
- // Read and decompress the stream.
- QFile streamFile(streamPath);
- if (!streamFile.open(QIODevice::ReadOnly))
- return;
- QByteArray rawData = streamFile.readAll();
- streamFile.close();
-
- QByteArray decompressed = decompressStream(rawData, compressionType);
-
- // Split the decompressed data into individual files.
- int offset = 0;
- for (const CrcEntry& entry : fileList) {
- if (offset + entry.size > decompressed.size()) {
- MOBase::log::warn(
- "OMOD: truncated stream for {} (need {} bytes at offset {}, have {})",
- entry.path.toStdString(), entry.size, offset, decompressed.size());
- break;
- }
-
- QByteArray fileData = decompressed.mid(offset, static_cast<int>(entry.size));
- offset += static_cast<int>(entry.size);
-
- // Normalise Windows path separators.
- QString normalPath = entry.path;
- normalPath.replace('\\', '/');
-
- QString destPath = QDir(outDir).filePath(normalPath);
-
- // Ensure parent directory exists.
- QDir().mkpath(QFileInfo(destPath).absolutePath());
-
- QFile outFile(destPath);
- if (outFile.open(QIODevice::WriteOnly)) {
- outFile.write(fileData);
- outFile.close();
- } else {
- MOBase::log::warn("OMOD: could not write {}", destPath.toStdString());
- }
- }
-}
diff --git a/libs/installer_omod_native/src/omodinstaller.h b/libs/installer_omod_native/src/omodinstaller.h
deleted file mode 100644
index de48b77..0000000
--- a/libs/installer_omod_native/src/omodinstaller.h
+++ /dev/null
@@ -1,101 +0,0 @@
-#ifndef OMODINSTALLER_H
-#define OMODINSTALLER_H
-
-#include <uibase/iplugininstallercustom.h>
-
-#include <QByteArray>
-#include <QList>
-#include <QString>
-
-#include <cstdint>
-#include <vector>
-
-class OmodInstaller : public MOBase::IPluginInstallerCustom
-{
- Q_OBJECT
- Q_INTERFACES(MOBase::IPlugin MOBase::IPluginInstaller MOBase::IPluginInstallerCustom)
- Q_PLUGIN_METADATA(IID "com.tannin.ModOrganizer.PluginInstallerCustom/1.0")
-
-public:
- OmodInstaller();
-
- // 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;
-
- // IPluginInstaller
- unsigned int priority() const override;
- bool isManualInstaller() const override;
- bool isArchiveSupported(std::shared_ptr<const MOBase::IFileTree> tree) const override;
-
- // IPluginInstallerCustom
- bool isArchiveSupported(const QString& archiveName) const override;
- std::set<QString> supportedExtensions() const override;
- EInstallResult install(MOBase::GuessedValue<QString>& modName, QString gameName,
- const QString& archiveName, const QString& version,
- int nexusID) override;
-
-private:
- MOBase::IOrganizer* m_organizer = nullptr;
-
- // OMOD config parsed from the binary "config" entry.
- struct OmodConfig
- {
- uint8_t fileVersion = 0;
- QString modName;
- int32_t major = 0;
- int32_t minor = 0;
- QString authorName;
- QString email;
- QString website;
- QString desc;
- uint8_t compressionType = 0; // 0 = deflate, 1 = lzma
- };
-
- // Entry from data.crc / plugins.crc.
- struct CrcEntry
- {
- QString path;
- uint32_t crc = 0;
- int64_t size = 0;
- };
-
- // Binary reader helper over a QByteArray.
- class BinaryReader
- {
- public:
- explicit BinaryReader(const QByteArray& data);
-
- uint8_t readByte();
- int32_t readInt32LE();
- uint32_t readUInt32LE();
- int64_t readInt64LE();
- void skip(int bytes);
- int read7BitEncodedInt();
- QString readNetString();
- QByteArray readBytes(int count);
- bool atEnd() const;
-
- private:
- const QByteArray& m_data;
- int m_pos = 0;
- };
-
- OmodConfig parseConfig(const QByteArray& data);
- std::vector<CrcEntry> parseCrcFile(const QByteArray& data);
- QByteArray decompressStream(const QByteArray& data, uint8_t compressionType);
-
- EInstallResult doInstall(MOBase::GuessedValue<QString>& modName,
- const QString& archiveName);
-
- void extractStream(const QString& omodDir, const QString& streamName,
- const QString& crcName, uint8_t compressionType,
- const QString& outDir);
-};
-
-#endif // OMODINSTALLER_H
diff --git a/libs/preview_dds_native/CMakeLists.txt b/libs/preview_dds_native/CMakeLists.txt
deleted file mode 100644
index 2e50105..0000000
--- a/libs/preview_dds_native/CMakeLists.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-cmake_minimum_required(VERSION 3.16)
-
-project(preview_dds_native)
-
-add_subdirectory(src)
diff --git a/libs/preview_dds_native/src/CMakeLists.txt b/libs/preview_dds_native/src/CMakeLists.txt
deleted file mode 100644
index c920d49..0000000
--- a/libs/preview_dds_native/src/CMakeLists.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-cmake_minimum_required(VERSION 3.16)
-
-find_package(Qt6 REQUIRED COMPONENTS OpenGL OpenGLWidgets)
-
-file(GLOB preview_dds_native_SOURCES CONFIGURE_DEPENDS
- ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp
- ${CMAKE_CURRENT_SOURCE_DIR}/*.h
-)
-
-add_library(preview_dds_native SHARED ${preview_dds_native_SOURCES})
-mo2_configure_plugin(preview_dds_native NO_SOURCES WARNINGS OFF)
-target_link_libraries(preview_dds_native PRIVATE
- mo2::uibase
- Qt6::OpenGL
- Qt6::OpenGLWidgets
-)
-mo2_install_plugin(preview_dds_native)
diff --git a/libs/preview_dds_native/src/ddsfile.cpp b/libs/preview_dds_native/src/ddsfile.cpp
deleted file mode 100644
index 91dc627..0000000
--- a/libs/preview_dds_native/src/ddsfile.cpp
+++ /dev/null
@@ -1,132 +0,0 @@
-#include "ddsfile.h"
-
-#include <QFile>
-
-#include <algorithm>
-#include <cstring>
-
-bool DDSFile::loadFromFile(const QString& path)
-{
- QFile file(path);
- if (!file.open(QIODevice::ReadOnly)) {
- return false;
- }
- return parse(file.readAll());
-}
-
-bool DDSFile::loadFromData(const QByteArray& data)
-{
- return parse(data);
-}
-
-bool DDSFile::parse(const QByteArray& data)
-{
- const char* ptr = data.constData();
- int offset = 0;
- int size = data.size();
-
- // Magic number
- if (size < 4)
- return false;
- uint32_t magic;
- std::memcpy(&magic, ptr + offset, 4);
- offset += 4;
- if (magic != DDS_MAGIC)
- return false;
-
- // DDS header
- if (size - offset < static_cast<int>(sizeof(DDSHeader)))
- return false;
- DDSHeader header;
- std::memcpy(&header, ptr + offset, sizeof(DDSHeader));
- offset += sizeof(DDSHeader);
-
- if (header.dwSize != 124)
- return false;
-
- // DXT10 extended header
- DDSHeaderDXT10 dxt10{};
- bool hasDXT10 = false;
- if ((header.ddspf.dwFlags & DDPF_FOURCC) &&
- header.ddspf.dwFourCC == makeFourCC('D', 'X', '1', '0')) {
- if (size - offset < static_cast<int>(sizeof(DDSHeaderDXT10)))
- return false;
- std::memcpy(&dxt10, ptr + offset, sizeof(DDSHeaderDXT10));
- offset += sizeof(DDSHeaderDXT10);
- hasDXT10 = true;
- }
-
- m_width = header.dwWidth;
- m_height = header.dwHeight;
-
- // Determine GL format
- m_glFormat = getGLFormat(header.ddspf, hasDXT10 ? &dxt10 : nullptr);
- if (!m_glFormat.valid) {
- return false;
- }
-
- // Determine DXGI format for size calculations
- if (hasDXT10) {
- m_dxgiFormat = dxt10.dxgiFormat;
- } else if (header.ddspf.dwFlags & DDPF_FOURCC) {
- m_dxgiFormat = fourCCToDXGI(header.ddspf.dwFourCC);
- } else {
- m_dxgiFormat = DXGIFormat::UNKNOWN;
- }
-
- // Description
- m_description = formatDescription(header.ddspf, hasDXT10 ? &dxt10 : nullptr);
- m_description +=
- QString(" | %1x%2").arg(m_width).arg(m_height);
-
- // Cubemap detection
- m_cubemap = false;
- int layers = 1;
- if (header.dwCaps2 & DDSCAPS2_CUBEMAP) {
- m_cubemap = true;
- layers = 0;
- if (header.dwCaps2 & DDSCAPS2_CUBEMAP_POSITIVEX) layers++;
- if (header.dwCaps2 & DDSCAPS2_CUBEMAP_NEGATIVEX) layers++;
- if (header.dwCaps2 & DDSCAPS2_CUBEMAP_POSITIVEY) layers++;
- if (header.dwCaps2 & DDSCAPS2_CUBEMAP_NEGATIVEY) layers++;
- if (header.dwCaps2 & DDSCAPS2_CUBEMAP_POSITIVEZ) layers++;
- if (header.dwCaps2 & DDSCAPS2_CUBEMAP_NEGATIVEZ) layers++;
- m_description += " | Cubemap";
- } else {
- m_description += " | 2D";
- }
-
- // Mipmap count
- int mipCount = 1;
- if (header.dwFlags & DDSD_MIPMAPCOUNT) {
- mipCount = std::max(1u, header.dwMipMapCount);
- }
- if (mipCount > 1) {
- m_description += QString(" | %1 mips").arg(mipCount);
- }
-
- // Read pixel data for each face and mip level
- m_faces.resize(layers);
- for (int face = 0; face < layers; ++face) {
- m_faces[face].mips.resize(mipCount);
- uint32_t w = m_width;
- uint32_t h = m_height;
- for (int mip = 0; mip < mipCount; ++mip) {
- uint32_t dataSize = mipDataSize(m_dxgiFormat, header.ddspf, w, h);
- if (offset + static_cast<int>(dataSize) > size) {
- // Truncated file, keep what we have
- m_faces[face].mips.resize(mip);
- break;
- }
- m_faces[face].mips[mip].width = w;
- m_faces[face].mips[mip].height = h;
- m_faces[face].mips[mip].data =
- QByteArray(ptr + offset, static_cast<int>(dataSize));
- offset += dataSize;
- w = std::max(1u, w / 2);
- h = std::max(1u, h / 2);
- }
- }
-
- return !m_faces.isEmpty() && !m_faces[0].mips.isEmpty();
-}
diff --git a/libs/preview_dds_native/src/ddsfile.h b/libs/preview_dds_native/src/ddsfile.h
deleted file mode 100644
index 4242c61..0000000
--- a/libs/preview_dds_native/src/ddsfile.h
+++ /dev/null
@@ -1,47 +0,0 @@
-#ifndef DDSFILE_H
-#define DDSFILE_H
-
-#include "ddsformat.h"
-
-#include <QByteArray>
-#include <QString>
-#include <QVector>
-
-struct DDSMipLevel {
- uint32_t width;
- uint32_t height;
- QByteArray data;
-};
-
-struct DDSFace {
- QVector<DDSMipLevel> mips;
-};
-
-class DDSFile
-{
-public:
- bool loadFromFile(const QString& path);
- bool loadFromData(const QByteArray& data);
-
- uint32_t width() const { return m_width; }
- uint32_t height() const { return m_height; }
- bool isCubemap() const { return m_cubemap; }
- int faceCount() const { return m_faces.size(); }
- int mipCount() const { return m_faces.isEmpty() ? 0 : m_faces[0].mips.size(); }
- const DDSFace& face(int i) const { return m_faces[i]; }
- const GLFormatInfo& glFormat() const { return m_glFormat; }
- QString description() const { return m_description; }
-
-private:
- bool parse(const QByteArray& data);
-
- uint32_t m_width = 0;
- uint32_t m_height = 0;
- bool m_cubemap = false;
- GLFormatInfo m_glFormat;
- DXGIFormat m_dxgiFormat = DXGIFormat::UNKNOWN;
- QString m_description;
- QVector<DDSFace> m_faces;
-};
-
-#endif // DDSFILE_H
diff --git a/libs/preview_dds_native/src/ddsformat.cpp b/libs/preview_dds_native/src/ddsformat.cpp
deleted file mode 100644
index b56db91..0000000
--- a/libs/preview_dds_native/src/ddsformat.cpp
+++ /dev/null
@@ -1,619 +0,0 @@
-#include "ddsformat.h"
-
-#include <QOpenGLFunctions>
-#include <QString>
-
-#include <algorithm>
-#include <cstring>
-
-// GL compressed format constants not always in headers
-#ifndef GL_COMPRESSED_RGB_S3TC_DXT1_EXT
-#define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0
-#endif
-#ifndef GL_COMPRESSED_RGBA_S3TC_DXT1_EXT
-#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1
-#endif
-#ifndef GL_COMPRESSED_RGBA_S3TC_DXT3_EXT
-#define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2
-#endif
-#ifndef GL_COMPRESSED_RGBA_S3TC_DXT5_EXT
-#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3
-#endif
-#ifndef GL_COMPRESSED_RED_RGTC1
-#define GL_COMPRESSED_RED_RGTC1 0x8DBB
-#endif
-#ifndef GL_COMPRESSED_SIGNED_RED_RGTC1
-#define GL_COMPRESSED_SIGNED_RED_RGTC1 0x8DBC
-#endif
-#ifndef GL_COMPRESSED_RG_RGTC2
-#define GL_COMPRESSED_RG_RGTC2 0x8DBD
-#endif
-#ifndef GL_COMPRESSED_SIGNED_RG_RGTC2
-#define GL_COMPRESSED_SIGNED_RG_RGTC2 0x8DBE
-#endif
-#ifndef GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT
-#define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT 0x8E8F
-#endif
-#ifndef GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT
-#define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT 0x8E8E
-#endif
-#ifndef GL_COMPRESSED_RGBA_BPTC_UNORM
-#define GL_COMPRESSED_RGBA_BPTC_UNORM 0x8E8C
-#endif
-#ifndef GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM
-#define GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM 0x8E8D
-#endif
-#ifndef GL_COMPRESSED_SRGB_S3TC_DXT1_EXT
-#define GL_COMPRESSED_SRGB_S3TC_DXT1_EXT 0x8C4C
-#endif
-#ifndef GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT
-#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT 0x8C4D
-#endif
-#ifndef GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT
-#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT 0x8C4E
-#endif
-#ifndef GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT
-#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT 0x8C4F
-#endif
-
-static GLFormatInfo dxgiToGL(DXGIFormat fmt)
-{
- GLFormatInfo info;
- info.valid = true;
-
- switch (fmt) {
- // BC1 (DXT1)
- case DXGIFormat::BC1_UNORM:
- info.compressed = true;
- info.internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
- break;
- case DXGIFormat::BC1_UNORM_SRGB:
- info.compressed = true;
- info.internalFormat = GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT;
- break;
- // BC2 (DXT3)
- case DXGIFormat::BC2_UNORM:
- info.compressed = true;
- info.internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
- break;
- case DXGIFormat::BC2_UNORM_SRGB:
- info.compressed = true;
- info.internalFormat = GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT;
- break;
- // BC3 (DXT5)
- case DXGIFormat::BC3_UNORM:
- info.compressed = true;
- info.internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
- break;
- case DXGIFormat::BC3_UNORM_SRGB:
- info.compressed = true;
- info.internalFormat = GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT;
- break;
- // BC4
- case DXGIFormat::BC4_UNORM:
- info.compressed = true;
- info.internalFormat = GL_COMPRESSED_RED_RGTC1;
- break;
- case DXGIFormat::BC4_SNORM:
- info.compressed = true;
- info.internalFormat = GL_COMPRESSED_SIGNED_RED_RGTC1;
- break;
- // BC5
- case DXGIFormat::BC5_UNORM:
- info.compressed = true;
- info.internalFormat = GL_COMPRESSED_RG_RGTC2;
- break;
- case DXGIFormat::BC5_SNORM:
- info.compressed = true;
- info.internalFormat = GL_COMPRESSED_SIGNED_RG_RGTC2;
- break;
- // BC6H
- case DXGIFormat::BC6H_UF16:
- info.compressed = true;
- info.internalFormat = GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT;
- break;
- case DXGIFormat::BC6H_SF16:
- info.compressed = true;
- info.internalFormat = GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT;
- break;
- // BC7
- case DXGIFormat::BC7_UNORM:
- info.compressed = true;
- info.internalFormat = GL_COMPRESSED_RGBA_BPTC_UNORM;
- break;
- case DXGIFormat::BC7_UNORM_SRGB:
- info.compressed = true;
- info.internalFormat = GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM;
- break;
-
- // Uncompressed RGBA
- case DXGIFormat::R8G8B8A8_UNORM:
- case DXGIFormat::R8G8B8A8_UNORM_SRGB:
- info.internalFormat = GL_RGBA8;
- info.format = GL_RGBA;
- info.type = GL_UNSIGNED_BYTE;
- break;
- case DXGIFormat::R8G8B8A8_UINT:
- info.internalFormat = GL_RGBA8UI;
- info.format = GL_RGBA_INTEGER;
- info.type = GL_UNSIGNED_BYTE;
- info.sampler = SamplerType::UInt;
- break;
- case DXGIFormat::R8G8B8A8_SNORM:
- info.internalFormat = GL_RGBA8_SNORM;
- info.format = GL_RGBA;
- info.type = GL_BYTE;
- break;
- case DXGIFormat::R8G8B8A8_SINT:
- info.internalFormat = GL_RGBA8I;
- info.format = GL_RGBA_INTEGER;
- info.type = GL_BYTE;
- info.sampler = SamplerType::SInt;
- break;
-
- // BGRA
- case DXGIFormat::B8G8R8A8_UNORM:
- case DXGIFormat::B8G8R8A8_UNORM_SRGB:
- info.internalFormat = GL_RGBA8;
- info.format = GL_BGRA;
- info.type = GL_UNSIGNED_BYTE;
- break;
- case DXGIFormat::B8G8R8X8_UNORM:
- case DXGIFormat::B8G8R8X8_UNORM_SRGB:
- info.internalFormat = GL_RGBA8;
- info.format = GL_BGRA;
- info.type = GL_UNSIGNED_BYTE;
- break;
-
- // 16-bit float
- case DXGIFormat::R16G16B16A16_FLOAT:
- info.internalFormat = GL_RGBA16F;
- info.format = GL_RGBA;
- info.type = GL_HALF_FLOAT;
- break;
- case DXGIFormat::R16G16B16A16_UNORM:
- info.internalFormat = GL_RGBA16;
- info.format = GL_RGBA;
- info.type = GL_UNSIGNED_SHORT;
- break;
- case DXGIFormat::R16G16B16A16_UINT:
- info.internalFormat = GL_RGBA16UI;
- info.format = GL_RGBA_INTEGER;
- info.type = GL_UNSIGNED_SHORT;
- info.sampler = SamplerType::UInt;
- break;
- case DXGIFormat::R16G16B16A16_SNORM:
- info.internalFormat = GL_RGBA16_SNORM;
- info.format = GL_RGBA;
- info.type = GL_SHORT;
- break;
- case DXGIFormat::R16G16B16A16_SINT:
- info.internalFormat = GL_RGBA16I;
- info.format = GL_RGBA_INTEGER;
- info.type = GL_SHORT;
- info.sampler = SamplerType::SInt;
- break;
-
- // 32-bit float
- case DXGIFormat::R32G32B32A32_FLOAT:
- info.internalFormat = GL_RGBA32F;
- info.format = GL_RGBA;
- info.type = GL_FLOAT;
- break;
- case DXGIFormat::R32G32B32_FLOAT:
- info.internalFormat = GL_RGB32F;
- info.format = GL_RGB;
- info.type = GL_FLOAT;
- break;
-
- // RG formats
- case DXGIFormat::R8G8_UNORM:
- info.internalFormat = GL_RG8;
- info.format = GL_RG;
- info.type = GL_UNSIGNED_BYTE;
- break;
- case DXGIFormat::R16G16_FLOAT:
- info.internalFormat = GL_RG16F;
- info.format = GL_RG;
- info.type = GL_HALF_FLOAT;
- break;
- case DXGIFormat::R16G16_UNORM:
- info.internalFormat = GL_RG16;
- info.format = GL_RG;
- info.type = GL_UNSIGNED_SHORT;
- break;
- case DXGIFormat::R32G32_FLOAT:
- info.internalFormat = GL_RG32F;
- info.format = GL_RG;
- info.type = GL_FLOAT;
- break;
-
- // R formats
- case DXGIFormat::R8_UNORM:
- info.internalFormat = GL_R8;
- info.format = GL_RED;
- info.type = GL_UNSIGNED_BYTE;
- break;
- case DXGIFormat::R16_FLOAT:
- info.internalFormat = GL_R16F;
- info.format = GL_RED;
- info.type = GL_HALF_FLOAT;
- break;
- case DXGIFormat::R16_UNORM:
- info.internalFormat = GL_R16;
- info.format = GL_RED;
- info.type = GL_UNSIGNED_SHORT;
- break;
- case DXGIFormat::R32_FLOAT:
- info.internalFormat = GL_R32F;
- info.format = GL_RED;
- info.type = GL_FLOAT;
- break;
- case DXGIFormat::A8_UNORM:
- info.internalFormat = GL_R8;
- info.format = GL_RED;
- info.type = GL_UNSIGNED_BYTE;
- break;
-
- // Packed formats
- case DXGIFormat::R10G10B10A2_UNORM:
- info.internalFormat = GL_RGB10_A2;
- info.format = GL_RGBA;
- info.type = GL_UNSIGNED_INT_2_10_10_10_REV;
- break;
- case DXGIFormat::R11G11B10_FLOAT:
- info.internalFormat = GL_R11F_G11F_B10F;
- info.format = GL_RGB;
- info.type = GL_UNSIGNED_INT_10F_11F_11F_REV;
- break;
- case DXGIFormat::B5G6R5_UNORM:
- info.internalFormat = GL_RGB565;
- info.format = GL_RGB;
- info.type = GL_UNSIGNED_SHORT_5_6_5;
- break;
- case DXGIFormat::B5G5R5A1_UNORM:
- info.internalFormat = GL_RGB5_A1;
- info.format = GL_BGRA;
- info.type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
- break;
- case DXGIFormat::B4G4R4A4_UNORM:
- info.internalFormat = GL_RGBA4;
- info.format = GL_BGRA;
- info.type = GL_UNSIGNED_SHORT_4_4_4_4_REV;
- break;
-
- default:
- info.valid = false;
- break;
- }
-
- return info;
-}
-
-DXGIFormat fourCCToDXGI(uint32_t fourCC)
-{
- if (fourCC == makeFourCC('D', 'X', 'T', '1'))
- return DXGIFormat::BC1_UNORM;
- if (fourCC == makeFourCC('D', 'X', 'T', '3'))
- return DXGIFormat::BC2_UNORM;
- if (fourCC == makeFourCC('D', 'X', 'T', '5'))
- return DXGIFormat::BC3_UNORM;
- if (fourCC == makeFourCC('B', 'C', '4', 'U') ||
- fourCC == makeFourCC('A', 'T', 'I', '1'))
- return DXGIFormat::BC4_UNORM;
- if (fourCC == makeFourCC('B', 'C', '4', 'S'))
- return DXGIFormat::BC4_SNORM;
- if (fourCC == makeFourCC('A', 'T', 'I', '2') ||
- fourCC == makeFourCC('B', 'C', '5', 'U'))
- return DXGIFormat::BC5_UNORM;
- if (fourCC == makeFourCC('B', 'C', '5', 'S'))
- return DXGIFormat::BC5_SNORM;
-
- // Numeric FourCC codes for float/half-float formats
- switch (fourCC) {
- case 36:
- return DXGIFormat::R16G16B16A16_UNORM;
- case 110:
- return DXGIFormat::R16G16B16A16_SNORM;
- case 111:
- return DXGIFormat::R16_FLOAT;
- case 112:
- return DXGIFormat::R16G16_FLOAT;
- case 113:
- return DXGIFormat::R16G16B16A16_FLOAT;
- case 114:
- return DXGIFormat::R32_FLOAT;
- case 115:
- return DXGIFormat::R32G32_FLOAT;
- case 116:
- return DXGIFormat::R32G32B32A32_FLOAT;
- default:
- return DXGIFormat::UNKNOWN;
- }
-}
-
-// Helper: count trailing zeros / bit shift for a mask
-static int maskShift(uint32_t mask)
-{
- if (mask == 0)
- return 0;
- int shift = 0;
- while ((mask & 1) == 0) {
- mask >>= 1;
- ++shift;
- }
- return shift;
-}
-
-static int maskBits(uint32_t mask)
-{
- int count = 0;
- while (mask) {
- count += mask & 1;
- mask >>= 1;
- }
- return count;
-}
-
-// Build a converter for arbitrary bitmask pixel formats
-static GLFormatInfo buildBitmaskFormat(const DDSPixelFormat& pf)
-{
- GLFormatInfo info;
- info.valid = true;
-
- int rShift = maskShift(pf.dwRBitMask);
- int gShift = maskShift(pf.dwGBitMask);
- int bShift = maskShift(pf.dwBBitMask);
- int aShift = maskShift(pf.dwABitMask);
- int rBits = maskBits(pf.dwRBitMask);
- int gBits = maskBits(pf.dwGBitMask);
- int bBits = maskBits(pf.dwBBitMask);
- int aBits = maskBits(pf.dwABitMask);
- int bpp = pf.dwRGBBitCount;
-
- // Try to match common uncompressed formats directly
- if (bpp == 32 && pf.dwRBitMask == 0x000000FF && pf.dwGBitMask == 0x0000FF00 &&
- pf.dwBBitMask == 0x00FF0000 &&
- (pf.dwABitMask == 0xFF000000 || pf.dwABitMask == 0)) {
- info.internalFormat = GL_RGBA8;
- info.format = GL_RGBA;
- info.type = GL_UNSIGNED_BYTE;
- return info;
- }
- if (bpp == 32 && pf.dwRBitMask == 0x00FF0000 && pf.dwGBitMask == 0x0000FF00 &&
- pf.dwBBitMask == 0x000000FF) {
- info.internalFormat = GL_RGBA8;
- info.format = GL_BGRA;
- info.type = GL_UNSIGNED_BYTE;
- return info;
- }
-
- // Generic converter: extract channels and pack into RGBA8
- uint32_t rMask = pf.dwRBitMask;
- uint32_t gMask = pf.dwGBitMask;
- uint32_t bMask = pf.dwBBitMask;
- uint32_t aMask = pf.dwABitMask;
- bool hasAlpha = (pf.dwFlags & DDPF_ALPHAPIXELS) && aMask != 0;
- int bytesPerPixel = bpp / 8;
-
- info.internalFormat = GL_RGBA8;
- info.format = GL_RGBA;
- info.type = GL_UNSIGNED_BYTE;
- info.converter = [=](const QByteArray& data, int w, int h) -> QByteArray {
- QByteArray result(w * h * 4, '\0');
- const uint8_t* src = reinterpret_cast<const uint8_t*>(data.constData());
- uint8_t* dst = reinterpret_cast<uint8_t*>(result.data());
-
- for (int i = 0; i < w * h; ++i) {
- uint32_t pixel = 0;
- std::memcpy(&pixel, src + i * bytesPerPixel,
- std::min(bytesPerPixel, 4));
-
- int r = rBits > 0 ? ((pixel & rMask) >> rShift) * 255 / ((1 << rBits) - 1) : 0;
- int g = gBits > 0 ? ((pixel & gMask) >> gShift) * 255 / ((1 << gBits) - 1) : 0;
- int b = bBits > 0 ? ((pixel & bMask) >> bShift) * 255 / ((1 << bBits) - 1) : 0;
- int a = hasAlpha && aBits > 0
- ? ((pixel & aMask) >> aShift) * 255 / ((1 << aBits) - 1)
- : 255;
-
- dst[i * 4 + 0] = static_cast<uint8_t>(r);
- dst[i * 4 + 1] = static_cast<uint8_t>(g);
- dst[i * 4 + 2] = static_cast<uint8_t>(b);
- dst[i * 4 + 3] = static_cast<uint8_t>(a);
- }
- return result;
- };
-
- return info;
-}
-
-GLFormatInfo getGLFormat(const DDSPixelFormat& pf, const DDSHeaderDXT10* dxt10)
-{
- // DX10 extended header takes priority
- if (dxt10) {
- return dxgiToGL(dxt10->dxgiFormat);
- }
-
- // FourCC compressed or float formats
- if (pf.dwFlags & DDPF_FOURCC) {
- DXGIFormat dxgi = fourCCToDXGI(pf.dwFourCC);
- if (dxgi != DXGIFormat::UNKNOWN) {
- return dxgiToGL(dxgi);
- }
- GLFormatInfo info;
- info.valid = false;
- return info;
- }
-
- // Uncompressed with bitmasks
- if (pf.dwFlags & (DDPF_RGB | DDPF_LUMINANCE | DDPF_YUV | DDPF_ALPHA)) {
- return buildBitmaskFormat(pf);
- }
-
- GLFormatInfo info;
- info.valid = false;
- return info;
-}
-
-static bool isBlockCompressed(DXGIFormat fmt)
-{
- switch (fmt) {
- case DXGIFormat::BC1_UNORM:
- case DXGIFormat::BC1_UNORM_SRGB:
- case DXGIFormat::BC2_UNORM:
- case DXGIFormat::BC2_UNORM_SRGB:
- case DXGIFormat::BC3_UNORM:
- case DXGIFormat::BC3_UNORM_SRGB:
- case DXGIFormat::BC4_UNORM:
- case DXGIFormat::BC4_SNORM:
- case DXGIFormat::BC5_UNORM:
- case DXGIFormat::BC5_SNORM:
- case DXGIFormat::BC6H_UF16:
- case DXGIFormat::BC6H_SF16:
- case DXGIFormat::BC7_UNORM:
- case DXGIFormat::BC7_UNORM_SRGB:
- return true;
- default:
- return false;
- }
-}
-
-static int blockSize(DXGIFormat fmt)
-{
- switch (fmt) {
- case DXGIFormat::BC1_UNORM:
- case DXGIFormat::BC1_UNORM_SRGB:
- case DXGIFormat::BC4_UNORM:
- case DXGIFormat::BC4_SNORM:
- return 8;
- default:
- return 16;
- }
-}
-
-uint32_t mipDataSize(DXGIFormat fmt, const DDSPixelFormat& pf,
- uint32_t width, uint32_t height)
-{
- if (isBlockCompressed(fmt)) {
- uint32_t blocksW = std::max(1u, (width + 3) / 4);
- uint32_t blocksH = std::max(1u, (height + 3) / 4);
- return blocksW * blocksH * blockSize(fmt);
- }
-
- // Uncompressed: use bits per pixel
- uint32_t bpp = pf.dwRGBBitCount;
- if (bpp == 0) {
- // Estimate from DXGI format for non-bitmask formats
- switch (fmt) {
- case DXGIFormat::R32G32B32A32_FLOAT:
- bpp = 128;
- break;
- case DXGIFormat::R32G32B32_FLOAT:
- bpp = 96;
- break;
- case DXGIFormat::R16G16B16A16_FLOAT:
- case DXGIFormat::R16G16B16A16_UNORM:
- case DXGIFormat::R16G16B16A16_SNORM:
- case DXGIFormat::R32G32_FLOAT:
- bpp = 64;
- break;
- case DXGIFormat::R8G8B8A8_UNORM:
- case DXGIFormat::R8G8B8A8_UNORM_SRGB:
- case DXGIFormat::B8G8R8A8_UNORM:
- case DXGIFormat::B8G8R8X8_UNORM:
- case DXGIFormat::R16G16_FLOAT:
- case DXGIFormat::R16G16_UNORM:
- case DXGIFormat::R32_FLOAT:
- case DXGIFormat::R10G10B10A2_UNORM:
- case DXGIFormat::R11G11B10_FLOAT:
- bpp = 32;
- break;
- case DXGIFormat::R8G8_UNORM:
- case DXGIFormat::R16_FLOAT:
- case DXGIFormat::R16_UNORM:
- case DXGIFormat::B5G6R5_UNORM:
- case DXGIFormat::B5G5R5A1_UNORM:
- case DXGIFormat::B4G4R4A4_UNORM:
- bpp = 16;
- break;
- case DXGIFormat::R8_UNORM:
- case DXGIFormat::A8_UNORM:
- bpp = 8;
- break;
- default:
- bpp = 32;
- break;
- }
- }
- return width * height * bpp / 8;
-}
-
-static const char* dxgiFormatName(DXGIFormat fmt)
-{
- switch (fmt) {
- case DXGIFormat::BC1_UNORM:
- return "BC1_UNORM (DXT1)";
- case DXGIFormat::BC1_UNORM_SRGB:
- return "BC1_UNORM_SRGB";
- case DXGIFormat::BC2_UNORM:
- return "BC2_UNORM (DXT3)";
- case DXGIFormat::BC2_UNORM_SRGB:
- return "BC2_UNORM_SRGB";
- case DXGIFormat::BC3_UNORM:
- return "BC3_UNORM (DXT5)";
- case DXGIFormat::BC3_UNORM_SRGB:
- return "BC3_UNORM_SRGB";
- case DXGIFormat::BC4_UNORM:
- return "BC4_UNORM";
- case DXGIFormat::BC4_SNORM:
- return "BC4_SNORM";
- case DXGIFormat::BC5_UNORM:
- return "BC5_UNORM";
- case DXGIFormat::BC5_SNORM:
- return "BC5_SNORM";
- case DXGIFormat::BC6H_UF16:
- return "BC6H_UF16";
- case DXGIFormat::BC6H_SF16:
- return "BC6H_SF16";
- case DXGIFormat::BC7_UNORM:
- return "BC7_UNORM";
- case DXGIFormat::BC7_UNORM_SRGB:
- return "BC7_UNORM_SRGB";
- case DXGIFormat::R8G8B8A8_UNORM:
- return "R8G8B8A8_UNORM";
- case DXGIFormat::R8G8B8A8_UNORM_SRGB:
- return "R8G8B8A8_UNORM_SRGB";
- case DXGIFormat::B8G8R8A8_UNORM:
- return "B8G8R8A8_UNORM";
- case DXGIFormat::R16G16B16A16_FLOAT:
- return "R16G16B16A16_FLOAT";
- case DXGIFormat::R32G32B32A32_FLOAT:
- return "R32G32B32A32_FLOAT";
- default:
- return "Unknown";
- }
-}
-
-QString formatDescription(const DDSPixelFormat& pf, const DDSHeaderDXT10* dxt10)
-{
- if (dxt10) {
- return QString("DXGI: %1").arg(dxgiFormatName(dxt10->dxgiFormat));
- }
-
- if (pf.dwFlags & DDPF_FOURCC) {
- char cc[5] = {0};
- std::memcpy(cc, &pf.dwFourCC, 4);
- DXGIFormat dxgi = fourCCToDXGI(pf.dwFourCC);
- if (dxgi != DXGIFormat::UNKNOWN) {
- return QString("FourCC: %1 (%2)").arg(cc).arg(dxgiFormatName(dxgi));
- }
- return QString("FourCC: %1").arg(cc);
- }
-
- return QString("%1-bit R:0x%2 G:0x%3 B:0x%4 A:0x%5")
- .arg(pf.dwRGBBitCount)
- .arg(pf.dwRBitMask, 0, 16)
- .arg(pf.dwGBitMask, 0, 16)
- .arg(pf.dwBBitMask, 0, 16)
- .arg(pf.dwABitMask, 0, 16);
-}
diff --git a/libs/preview_dds_native/src/ddsformat.h b/libs/preview_dds_native/src/ddsformat.h
deleted file mode 100644
index 22d3565..0000000
--- a/libs/preview_dds_native/src/ddsformat.h
+++ /dev/null
@@ -1,200 +0,0 @@
-#ifndef DDSFORMAT_H
-#define DDSFORMAT_H
-
-#include <cstdint>
-#include <functional>
-#include <vector>
-
-#include <QByteArray>
-
-// DDS file magic number
-constexpr uint32_t DDS_MAGIC = 0x20534444; // "DDS "
-
-// DDS_PIXELFORMAT flags
-constexpr uint32_t DDPF_ALPHAPIXELS = 0x1;
-constexpr uint32_t DDPF_ALPHA = 0x2;
-constexpr uint32_t DDPF_FOURCC = 0x4;
-constexpr uint32_t DDPF_RGB = 0x40;
-constexpr uint32_t DDPF_YUV = 0x200;
-constexpr uint32_t DDPF_LUMINANCE = 0x20000;
-
-// DDS_HEADER flags
-constexpr uint32_t DDSD_CAPS = 0x1;
-constexpr uint32_t DDSD_HEIGHT = 0x2;
-constexpr uint32_t DDSD_WIDTH = 0x4;
-constexpr uint32_t DDSD_PITCH = 0x8;
-constexpr uint32_t DDSD_PIXELFORMAT = 0x1000;
-constexpr uint32_t DDSD_MIPMAPCOUNT = 0x20000;
-constexpr uint32_t DDSD_LINEARSIZE = 0x80000;
-constexpr uint32_t DDSD_DEPTH = 0x800000;
-
-// DDS_HEADER caps2
-constexpr uint32_t DDSCAPS2_CUBEMAP = 0x200;
-constexpr uint32_t DDSCAPS2_CUBEMAP_POSITIVEX = 0x400;
-constexpr uint32_t DDSCAPS2_CUBEMAP_NEGATIVEX = 0x800;
-constexpr uint32_t DDSCAPS2_CUBEMAP_POSITIVEY = 0x1000;
-constexpr uint32_t DDSCAPS2_CUBEMAP_NEGATIVEY = 0x2000;
-constexpr uint32_t DDSCAPS2_CUBEMAP_POSITIVEZ = 0x4000;
-constexpr uint32_t DDSCAPS2_CUBEMAP_NEGATIVEZ = 0x8000;
-constexpr uint32_t DDSCAPS2_VOLUME = 0x200000;
-
-constexpr uint32_t DDSCAPS2_CUBEMAP_ALLFACES =
- DDSCAPS2_CUBEMAP_POSITIVEX | DDSCAPS2_CUBEMAP_NEGATIVEX |
- DDSCAPS2_CUBEMAP_POSITIVEY | DDSCAPS2_CUBEMAP_NEGATIVEY |
- DDSCAPS2_CUBEMAP_POSITIVEZ | DDSCAPS2_CUBEMAP_NEGATIVEZ;
-
-// DXGI formats (subset covering common DDS textures)
-enum class DXGIFormat : uint32_t {
- UNKNOWN = 0,
- R32G32B32A32_FLOAT = 2,
- R32G32B32A32_UINT = 3,
- R32G32B32A32_SINT = 4,
- R32G32B32_FLOAT = 6,
- R32G32B32_UINT = 7,
- R32G32B32_SINT = 8,
- R16G16B16A16_FLOAT = 10,
- R16G16B16A16_UNORM = 11,
- R16G16B16A16_UINT = 12,
- R16G16B16A16_SNORM = 13,
- R16G16B16A16_SINT = 14,
- R32G32_FLOAT = 16,
- R32G32_UINT = 17,
- R32G32_SINT = 18,
- R10G10B10A2_UNORM = 24,
- R10G10B10A2_UINT = 25,
- R11G11B10_FLOAT = 26,
- R8G8B8A8_UNORM = 28,
- R8G8B8A8_UNORM_SRGB = 29,
- R8G8B8A8_UINT = 30,
- R8G8B8A8_SNORM = 31,
- R8G8B8A8_SINT = 32,
- R16G16_FLOAT = 34,
- R16G16_UNORM = 35,
- R16G16_UINT = 36,
- R16G16_SNORM = 37,
- R16G16_SINT = 38,
- R32_FLOAT = 41,
- R32_UINT = 42,
- R32_SINT = 43,
- R8G8_UNORM = 49,
- R8G8_UINT = 50,
- R8G8_SNORM = 51,
- R8G8_SINT = 52,
- R16_FLOAT = 54,
- R16_UNORM = 56,
- R16_UINT = 57,
- R16_SNORM = 58,
- R16_SINT = 59,
- R8_UNORM = 61,
- R8_UINT = 62,
- R8_SNORM = 63,
- R8_SINT = 64,
- A8_UNORM = 65,
- BC1_UNORM = 71,
- BC1_UNORM_SRGB = 72,
- BC2_UNORM = 74,
- BC2_UNORM_SRGB = 75,
- BC3_UNORM = 77,
- BC3_UNORM_SRGB = 78,
- BC4_UNORM = 80,
- BC4_SNORM = 81,
- BC5_UNORM = 83,
- BC5_SNORM = 84,
- B5G6R5_UNORM = 85,
- B5G5R5A1_UNORM = 86,
- B8G8R8A8_UNORM = 87,
- B8G8R8X8_UNORM = 88,
- B8G8R8A8_UNORM_SRGB = 91,
- B8G8R8X8_UNORM_SRGB = 93,
- BC6H_UF16 = 95,
- BC6H_SF16 = 96,
- BC7_UNORM = 98,
- BC7_UNORM_SRGB = 99,
- B4G4R4A4_UNORM = 115,
-};
-
-// D3D10 resource dimension
-enum class D3D10ResourceDimension : uint32_t {
- Unknown = 0,
- Buffer = 1,
- Texture1D = 2,
- Texture2D = 3,
- Texture3D = 4,
-};
-
-#pragma pack(push, 1)
-
-struct DDSPixelFormat {
- uint32_t dwSize;
- uint32_t dwFlags;
- uint32_t dwFourCC;
- uint32_t dwRGBBitCount;
- uint32_t dwRBitMask;
- uint32_t dwGBitMask;
- uint32_t dwBBitMask;
- uint32_t dwABitMask;
-};
-
-struct DDSHeader {
- uint32_t dwSize;
- uint32_t dwFlags;
- uint32_t dwHeight;
- uint32_t dwWidth;
- uint32_t dwPitchOrLinearSize;
- uint32_t dwDepth;
- uint32_t dwMipMapCount;
- uint32_t dwReserved1[11];
- DDSPixelFormat ddspf;
- uint32_t dwCaps;
- uint32_t dwCaps2;
- uint32_t dwCaps3;
- uint32_t dwCaps4;
- uint32_t dwReserved2;
-};
-
-struct DDSHeaderDXT10 {
- DXGIFormat dxgiFormat;
- D3D10ResourceDimension resourceDimension;
- uint32_t miscFlag;
- uint32_t arraySize;
- uint32_t miscFlags2;
-};
-
-#pragma pack(pop)
-
-// Sampler type for shader selection
-enum class SamplerType { Float, UInt, SInt };
-
-// OpenGL format info
-struct GLFormatInfo {
- bool valid = false;
- bool compressed = false;
- uint32_t internalFormat = 0;
- uint32_t format = 0; // only for uncompressed
- uint32_t type = 0; // only for uncompressed
- SamplerType sampler = SamplerType::Float;
- // Optional converter for non-standard bitmask formats
- std::function<QByteArray(const QByteArray&, int, int)> converter;
-};
-
-// Inline helper: make a FourCC from 4 chars
-constexpr uint32_t makeFourCC(char a, char b, char c, char d)
-{
- return static_cast<uint32_t>(a) | (static_cast<uint32_t>(b) << 8) |
- (static_cast<uint32_t>(c) << 16) | (static_cast<uint32_t>(d) << 24);
-}
-
-// Convert a FourCC code to DXGI format
-DXGIFormat fourCCToDXGI(uint32_t fourCC);
-
-// Resolve the OpenGL format from a DDS pixel format + optional DXT10 header
-GLFormatInfo getGLFormat(const DDSPixelFormat& pf, const DDSHeaderDXT10* dxt10);
-
-// Calculate mip level data size in bytes
-uint32_t mipDataSize(DXGIFormat fmt, const DDSPixelFormat& pf,
- uint32_t width, uint32_t height);
-
-// Get a human-readable format description
-QString formatDescription(const DDSPixelFormat& pf, const DDSHeaderDXT10* dxt10);
-
-#endif // DDSFORMAT_H
diff --git a/libs/preview_dds_native/src/ddspreview.cpp b/libs/preview_dds_native/src/ddspreview.cpp
deleted file mode 100644
index 2bb95be..0000000
--- a/libs/preview_dds_native/src/ddspreview.cpp
+++ /dev/null
@@ -1,126 +0,0 @@
-#include "ddspreview.h"
-#include "ddsfile.h"
-#include "ddswidget.h"
-
-#include <QLabel>
-#include <QVBoxLayout>
-#include <QWidget>
-
-using namespace MOBase;
-
-DDSPreview::DDSPreview() {}
-
-bool DDSPreview::init(IOrganizer* moInfo)
-{
- m_organizer = moInfo;
- return true;
-}
-
-QString DDSPreview::name() const
-{
- return "DDS Preview (Native)";
-}
-
-QString DDSPreview::localizedName() const
-{
- return tr("DDS Preview (Native)");
-}
-
-QString DDSPreview::author() const
-{
- return "AnyOldName3";
-}
-
-QString DDSPreview::description() const
-{
- return tr("Displays DDS texture files using OpenGL.");
-}
-
-VersionInfo DDSPreview::version() const
-{
- return VersionInfo(1, 0, 0, VersionInfo::RELEASE_FINAL);
-}
-
-QList<PluginSetting> DDSPreview::settings() const
-{
- return {};
-}
-
-std::set<QString> DDSPreview::supportedExtensions() const
-{
- return {"dds"};
-}
-
-bool DDSPreview::supportsArchives() const
-{
- return true;
-}
-
-QWidget* DDSPreview::genFilePreview(const QString& fileName,
- const QSize& maxSize) const
-{
- DDSFile dds;
- if (!dds.loadFromFile(fileName)) {
- QLabel* label = new QLabel(tr("Failed to load DDS file."));
- label->setAlignment(Qt::AlignCenter);
- return label;
- }
- return buildPreview(dds, maxSize);
-}
-
-QWidget* DDSPreview::genDataPreview(const QByteArray& fileData,
- const QString& fileName,
- const QSize& maxSize) const
-{
- DDSFile dds;
- if (!dds.loadFromData(fileData)) {
- QLabel* label = new QLabel(tr("Failed to load DDS data."));
- label->setAlignment(Qt::AlignCenter);
- return label;
- }
- return buildPreview(dds, maxSize);
-}
-
-QWidget* DDSPreview::buildPreview(DDSFile& dds, const QSize& maxSize) const
-{
- QWidget* container = new QWidget();
- QVBoxLayout* layout = new QVBoxLayout(container);
-
- // Description label
- QLabel* descLabel = new QLabel(dds.description());
- descLabel->setAlignment(Qt::AlignCenter);
- layout->addWidget(descLabel);
-
- // OpenGL preview widget — we need to keep the DDSFile alive, so store
- // it in the container. We use a shared_ptr stored as a property.
- auto* ddsPtr = new DDSFile(std::move(dds));
- DDSWidget* widget = new DDSWidget(*ddsPtr, container);
-
- // Clean up DDSFile when container is destroyed
- QObject::connect(container, &QObject::destroyed, [ddsPtr]() {
- delete ddsPtr;
- });
-
- // Size the widget proportionally
- int previewW = maxSize.width();
- int previewH = maxSize.height() - 30; // leave room for label
- if (ddsPtr->width() > 0 && ddsPtr->height() > 0) {
- float texAspect =
- static_cast<float>(ddsPtr->width()) / ddsPtr->height();
- float availAspect =
- static_cast<float>(previewW) / std::max(1, previewH);
- if (texAspect > availAspect) {
- previewH = static_cast<int>(previewW / texAspect);
- } else {
- previewW = static_cast<int>(previewH * texAspect);
- }
- }
- widget->setMinimumSize(std::min(previewW, static_cast<int>(ddsPtr->width())),
- std::min(previewH, static_cast<int>(ddsPtr->height())));
- widget->setMaximumSize(previewW, previewH);
-
- layout->addWidget(widget);
- container->setLayout(layout);
-
- return container;
-}
diff --git a/libs/preview_dds_native/src/ddspreview.h b/libs/preview_dds_native/src/ddspreview.h
deleted file mode 100644
index 36f2bec..0000000
--- a/libs/preview_dds_native/src/ddspreview.h
+++ /dev/null
@@ -1,44 +0,0 @@
-#ifndef DDSPREVIEW_H
-#define DDSPREVIEW_H
-
-#include <set>
-
-#include <QString>
-
-#include <uibase/iplugin.h>
-#include <uibase/ipluginpreview.h>
-
-class DDSPreview : public MOBase::IPluginPreview
-{
- Q_OBJECT
- Q_INTERFACES(MOBase::IPlugin MOBase::IPluginPreview)
- Q_PLUGIN_METADATA(IID "org.tannin.DDSPreviewNative")
-
-public:
- DDSPreview();
-
-public: // IPlugin
- bool init(MOBase::IOrganizer* moInfo) 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;
-
-public: // IPluginPreview
- std::set<QString> supportedExtensions() const override;
- QWidget* genFilePreview(const QString& fileName,
- const QSize& maxSize) const override;
- bool supportsArchives() const override;
- QWidget* genDataPreview(const QByteArray& fileData,
- const QString& fileName,
- const QSize& maxSize) const override;
-
-private:
- QWidget* buildPreview(class DDSFile& dds, const QSize& maxSize) const;
-
- MOBase::IOrganizer* m_organizer = nullptr;
-};
-
-#endif // DDSPREVIEW_H
diff --git a/libs/preview_dds_native/src/ddswidget.cpp b/libs/preview_dds_native/src/ddswidget.cpp
deleted file mode 100644
index 43eb586..0000000
--- a/libs/preview_dds_native/src/ddswidget.cpp
+++ /dev/null
@@ -1,252 +0,0 @@
-#include "ddswidget.h"
-
-#include <QDebug>
-
-#include <algorithm>
-#include <cstring>
-
-// Vertex data: position (x,y) + texcoord (u,v)
-static const float quadVertices[] = {
- // pos // tex
- -1.0f, -1.0f, 0.0f, 1.0f,
- 1.0f, -1.0f, 1.0f, 1.0f,
- -1.0f, 1.0f, 0.0f, 0.0f,
- 1.0f, 1.0f, 1.0f, 0.0f,
-};
-
-static const char* vertexShaderSrc = R"(
-#version 330 core
-layout(location = 0) in vec2 aPos;
-layout(location = 1) in vec2 aTexCoord;
-out vec2 vTexCoord;
-uniform float uAspect;
-uniform float uWidgetAspect;
-void main() {
- vec2 pos = aPos;
- float ratio = uAspect / uWidgetAspect;
- if (ratio > 1.0)
- pos.y *= 1.0 / ratio;
- else
- pos.x *= ratio;
- gl_Position = vec4(pos, 0.0, 1.0);
- vTexCoord = aTexCoord;
-}
-)";
-
-static const char* fragmentShaderSrc = R"(
-#version 330 core
-in vec2 vTexCoord;
-out vec4 fragColor;
-uniform sampler2D uTexture;
-void main() {
- vec4 texel = texture(uTexture, vTexCoord);
- // Checkerboard for transparency
- vec2 checker = floor(vTexCoord * 16.0);
- float c = mod(checker.x + checker.y, 2.0);
- vec3 bg = mix(vec3(0.6), vec3(0.4), c);
- fragColor = vec4(mix(bg, texel.rgb, texel.a), 1.0);
-}
-)";
-
-static const char* cubemapFragSrc = R"(
-#version 330 core
-in vec2 vTexCoord;
-out vec4 fragColor;
-uniform samplerCube uTexture;
-void main() {
- // Spherical projection for cubemap preview
- float theta = vTexCoord.x * 6.28318530718;
- float phi = vTexCoord.y * 3.14159265359;
- vec3 dir = vec3(sin(phi) * cos(theta), cos(phi), sin(phi) * sin(theta));
- vec4 texel = texture(uTexture, dir);
- vec2 checker = floor(vTexCoord * 16.0);
- float c = mod(checker.x + checker.y, 2.0);
- vec3 bg = mix(vec3(0.6), vec3(0.4), c);
- fragColor = vec4(mix(bg, texel.rgb, texel.a), 1.0);
-}
-)";
-
-DDSWidget::DDSWidget(const DDSFile& dds, QWidget* parent)
- : QOpenGLWidget(parent), m_dds(dds), m_vbo(QOpenGLBuffer::VertexBuffer)
-{
- if (dds.height() > 0) {
- m_aspectRatio =
- static_cast<float>(dds.width()) / static_cast<float>(dds.height());
- }
-}
-
-DDSWidget::~DDSWidget()
-{
- makeCurrent();
- delete m_texture;
- delete m_shader;
- m_vbo.destroy();
- doneCurrent();
-}
-
-void DDSWidget::initializeGL()
-{
- initializeOpenGLFunctions();
-
- glClearColor(0.2f, 0.2f, 0.2f, 1.0f);
-
- // VBO
- m_vbo.create();
- m_vbo.bind();
- m_vbo.allocate(quadVertices, sizeof(quadVertices));
-
- setupShaders();
- uploadTexture();
-}
-
-void DDSWidget::setupShaders()
-{
- m_shader = new QOpenGLShaderProgram(this);
- m_shader->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSrc);
-
- if (m_dds.isCubemap()) {
- m_shader->addShaderFromSourceCode(QOpenGLShader::Fragment, cubemapFragSrc);
- } else {
- m_shader->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSrc);
- }
-
- m_shader->link();
-}
-
-void DDSWidget::uploadTexture()
-{
- if (m_dds.faceCount() == 0 || m_dds.mipCount() == 0)
- return;
-
- const auto& fmt = m_dds.glFormat();
-
- if (m_dds.isCubemap()) {
- m_texture = new QOpenGLTexture(QOpenGLTexture::TargetCubeMap);
- } else {
- m_texture = new QOpenGLTexture(QOpenGLTexture::Target2D);
- }
-
- m_texture->setAutoMipMapGenerationEnabled(false);
- m_texture->setMipLevels(m_dds.mipCount());
-
- if (m_dds.isCubemap()) {
- m_texture->setSize(m_dds.width(), m_dds.height());
- // Set format for allocation
- if (fmt.compressed) {
- m_texture->setFormat(
- static_cast<QOpenGLTexture::TextureFormat>(fmt.internalFormat));
- } else {
- m_texture->setFormat(
- static_cast<QOpenGLTexture::TextureFormat>(fmt.internalFormat));
- }
- m_texture->allocateStorage();
-
- static const QOpenGLTexture::CubeMapFace cubeMapFaces[] = {
- QOpenGLTexture::CubeMapPositiveX, QOpenGLTexture::CubeMapNegativeX,
- QOpenGLTexture::CubeMapPositiveY, QOpenGLTexture::CubeMapNegativeY,
- QOpenGLTexture::CubeMapPositiveZ, QOpenGLTexture::CubeMapNegativeZ,
- };
-
- int numFaces = std::min(m_dds.faceCount(), 6);
- for (int f = 0; f < numFaces; ++f) {
- const auto& face = m_dds.face(f);
- for (int m = 0; m < face.mips.size(); ++m) {
- const auto& mip = face.mips[m];
- QByteArray pixelData = mip.data;
- if (fmt.converter) {
- pixelData = fmt.converter(mip.data, mip.width, mip.height);
- }
- if (fmt.compressed) {
- m_texture->setCompressedData(
- m, 0, cubeMapFaces[f],
- pixelData.size(),
- pixelData.constData());
- } else {
- m_texture->setData(
- m, 0, cubeMapFaces[f],
- static_cast<QOpenGLTexture::PixelFormat>(fmt.format),
- static_cast<QOpenGLTexture::PixelType>(fmt.type),
- pixelData.constData());
- }
- }
- }
- } else {
- m_texture->setSize(m_dds.width(), m_dds.height());
- if (fmt.compressed) {
- m_texture->setFormat(
- static_cast<QOpenGLTexture::TextureFormat>(fmt.internalFormat));
- } else {
- m_texture->setFormat(
- static_cast<QOpenGLTexture::TextureFormat>(fmt.internalFormat));
- }
- m_texture->allocateStorage();
-
- const auto& face = m_dds.face(0);
- for (int m = 0; m < face.mips.size(); ++m) {
- const auto& mip = face.mips[m];
- QByteArray pixelData = mip.data;
- if (fmt.converter) {
- pixelData = fmt.converter(mip.data, mip.width, mip.height);
- }
- if (fmt.compressed) {
- m_texture->setCompressedData(
- m, 0,
- pixelData.size(),
- pixelData.constData());
- } else {
- m_texture->setData(
- m, 0,
- static_cast<QOpenGLTexture::PixelFormat>(fmt.format),
- static_cast<QOpenGLTexture::PixelType>(fmt.type),
- pixelData.constData());
- }
- }
- }
-
- m_texture->setWrapMode(QOpenGLTexture::ClampToEdge);
- if (fmt.sampler != SamplerType::Float) {
- m_texture->setMinMagFilters(QOpenGLTexture::Nearest,
- QOpenGLTexture::Nearest);
- } else {
- m_texture->setMinMagFilters(QOpenGLTexture::LinearMipMapLinear,
- QOpenGLTexture::Linear);
- }
-}
-
-void DDSWidget::resizeGL(int w, int h)
-{
- glViewport(0, 0, w, h);
-}
-
-void DDSWidget::paintGL()
-{
- glClear(GL_COLOR_BUFFER_BIT);
-
- if (!m_texture || !m_shader)
- return;
-
- m_shader->bind();
- m_texture->bind();
-
- float widgetAspect = width() > 0 && height() > 0
- ? static_cast<float>(width()) / height()
- : 1.0f;
- m_shader->setUniformValue("uAspect", m_aspectRatio);
- m_shader->setUniformValue("uWidgetAspect", widgetAspect);
- m_shader->setUniformValue("uTexture", 0);
-
- m_vbo.bind();
- m_shader->enableAttributeArray(0);
- m_shader->enableAttributeArray(1);
- m_shader->setAttributeBuffer(0, GL_FLOAT, 0, 2, 4 * sizeof(float));
- m_shader->setAttributeBuffer(1, GL_FLOAT, 2 * sizeof(float), 2,
- 4 * sizeof(float));
-
- glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
-
- m_shader->disableAttributeArray(0);
- m_shader->disableAttributeArray(1);
- m_vbo.release();
- m_texture->release();
- m_shader->release();
-}
diff --git a/libs/preview_dds_native/src/ddswidget.h b/libs/preview_dds_native/src/ddswidget.h
deleted file mode 100644
index 968e253..0000000
--- a/libs/preview_dds_native/src/ddswidget.h
+++ /dev/null
@@ -1,36 +0,0 @@
-#ifndef DDSWIDGET_H
-#define DDSWIDGET_H
-
-#include "ddsfile.h"
-
-#include <QOpenGLBuffer>
-#include <QOpenGLFunctions>
-#include <QOpenGLShaderProgram>
-#include <QOpenGLTexture>
-#include <QOpenGLWidget>
-
-class DDSWidget : public QOpenGLWidget, protected QOpenGLFunctions
-{
- Q_OBJECT
-
-public:
- explicit DDSWidget(const DDSFile& dds, QWidget* parent = nullptr);
- ~DDSWidget() override;
-
-protected:
- void initializeGL() override;
- void resizeGL(int w, int h) override;
- void paintGL() override;
-
-private:
- void uploadTexture();
- void setupShaders();
-
- const DDSFile& m_dds;
- QOpenGLTexture* m_texture = nullptr;
- QOpenGLShaderProgram* m_shader = nullptr;
- QOpenGLBuffer m_vbo;
- float m_aspectRatio = 1.0f;
-};
-
-#endif // DDSWIDGET_H
diff --git a/libs/rootbuilder_native/CMakeLists.txt b/libs/rootbuilder_native/CMakeLists.txt
deleted file mode 100644
index 583c0ac..0000000
--- a/libs/rootbuilder_native/CMakeLists.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-cmake_minimum_required(VERSION 3.16)
-project(rootbuilder_native)
-add_subdirectory(src)
diff --git a/libs/rootbuilder_native/src/CMakeLists.txt b/libs/rootbuilder_native/src/CMakeLists.txt
deleted file mode 100644
index 438c068..0000000
--- a/libs/rootbuilder_native/src/CMakeLists.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-cmake_minimum_required(VERSION 3.16)
-
-find_package(Qt6 REQUIRED COMPONENTS Widgets)
-qt_standard_project_setup()
-
-file(GLOB SOURCES *.cpp *.h)
-
-add_library(rootbuilder_native SHARED ${SOURCES})
-
-set_target_properties(rootbuilder_native PROPERTIES
- AUTOMOC ON
- CXX_STANDARD 23
- CXX_STANDARD_REQUIRED ON
- PREFIX "lib"
-)
-
-target_link_libraries(rootbuilder_native PRIVATE mo2::uibase Qt6::Widgets)
-install(TARGETS rootbuilder_native LIBRARY DESTINATION bin/plugins)
diff --git a/libs/rootbuilder_native/src/rootbuilder.cpp b/libs/rootbuilder_native/src/rootbuilder.cpp
deleted file mode 100644
index 6ee2e1c..0000000
--- a/libs/rootbuilder_native/src/rootbuilder.cpp
+++ /dev/null
@@ -1,674 +0,0 @@
-#include "rootbuilder.h"
-
-#include <uibase/imodinterface.h>
-#include <uibase/imodlist.h>
-#include <uibase/iplugingame.h>
-#include <uibase/versioninfo.h>
-
-#include <QCoreApplication>
-#include <QCheckBox>
-#include <QComboBox>
-#include <QDialog>
-#include <QDir>
-#include <QDirIterator>
-#include <QFile>
-#include <QFileInfo>
-#include <QHBoxLayout>
-#include <QJsonArray>
-#include <QJsonDocument>
-#include <QJsonObject>
-#include <QLabel>
-#include <QProcess>
-#include <QPushButton>
-#include <QVBoxLayout>
-
-#include <algorithm>
-#include <set>
-
-// Storage constants
-static const QString STORAGE_SUBDIR = QStringLiteral("rootbuilder");
-static const QString MANIFEST_NAME = QStringLiteral("manifest.json");
-static const QString SETTINGS_NAME = QStringLiteral("settings.json");
-static const QString BACKUP_SUBDIR = QStringLiteral("backup");
-static const QString LEGACY_MANIFEST = QStringLiteral(".rootbuilder_manifest.json");
-static const QString LEGACY_BACKUP = QStringLiteral(".rootbuilder_backup");
-
-// ============================================================================
-// Construction / IPlugin
-// ============================================================================
-
-RootBuilderNative::RootBuilderNative() = default;
-
-bool RootBuilderNative::init(MOBase::IOrganizer* organizer)
-{
- m_organizer = organizer;
- migrateLegacy();
- checkThirdPartyRootBuilder();
-
- organizer->onAboutToRun([this](const QString& exe) {
- return onAboutToRun(exe);
- });
- organizer->onFinishedRun([this](const QString& exe, unsigned int code) {
- onFinishedRun(exe, code);
- });
-
- return true;
-}
-
-QString RootBuilderNative::name() const
-{
- return QStringLiteral("Root Builder (Native)");
-}
-
-QString RootBuilderNative::localizedName() const
-{
- return QStringLiteral("Root Builder (Native)");
-}
-
-QString RootBuilderNative::author() const
-{
- return QStringLiteral("Fluorine Manager");
-}
-
-QString RootBuilderNative::description() const
-{
- return QStringLiteral(
- "Deploys mod files from Root/ subdirectories to the game's root directory. "
- "Supports copy and symlink modes with auto-deploy on launch.");
-}
-
-MOBase::VersionInfo RootBuilderNative::version() const
-{
- return MOBase::VersionInfo(1, 0, 0);
-}
-
-QList<MOBase::PluginSetting> RootBuilderNative::settings() const
-{
- return {};
-}
-
-bool RootBuilderNative::enabledByDefault() const
-{
- return true;
-}
-
-// ============================================================================
-// IPluginTool
-// ============================================================================
-
-QString RootBuilderNative::displayName() const
-{
- return QStringLiteral("Root Builder");
-}
-
-QString RootBuilderNative::tooltip() const
-{
- return QStringLiteral("Deploy mod Root/ files to the game directory");
-}
-
-QIcon RootBuilderNative::icon() const
-{
- return QIcon();
-}
-
-void RootBuilderNative::display() const
-{
- QJsonObject settings = loadSettings();
-
- // We need non-const this for build/clear — display() is const in the
- // interface, but build/clear mutate the filesystem (not object state).
- auto* self = const_cast<RootBuilderNative*>(this);
-
- auto* dialog = new QDialog(parentWidget());
- dialog->setAttribute(Qt::WA_DeleteOnClose);
- dialog->setWindowTitle(QStringLiteral("Root Builder"));
- dialog->resize(350, 220);
-
- auto* layout = new QVBoxLayout(dialog);
-
- auto* desc = new QLabel(
- QStringLiteral("Deploys files from mod Root/ folders to the game directory."));
- desc->setWordWrap(true);
- layout->addWidget(desc);
-
- // Enable checkbox
- auto* enableCheck = new QCheckBox(QStringLiteral("Auto-deploy on game launch"));
- enableCheck->setChecked(settings.value(QStringLiteral("enabled")).toBool(false));
- layout->addWidget(enableCheck);
-
- // Mode selector
- auto* modeLayout = new QHBoxLayout();
- modeLayout->addWidget(new QLabel(QStringLiteral("Deploy mode:")));
- auto* modeCombo = new QComboBox();
- modeCombo->addItems({QStringLiteral("copy"), QStringLiteral("link")});
- modeCombo->setCurrentText(
- settings.value(QStringLiteral("mode")).toString(QStringLiteral("copy")));
- modeLayout->addWidget(modeCombo);
- layout->addLayout(modeLayout);
-
- // Manual build/clear buttons
- auto* btnLayout = new QHBoxLayout();
- auto* buildBtn = new QPushButton(QStringLiteral("Build Now"));
- auto* clearBtn = new QPushButton(QStringLiteral("Clear Now"));
- btnLayout->addWidget(buildBtn);
- btnLayout->addWidget(clearBtn);
- layout->addLayout(btnLayout);
-
- // Status label
- auto* statusLabel = new QLabel();
- layout->addWidget(statusLabel);
-
- // Close button
- auto* closeBtn = new QPushButton(QStringLiteral("Close"));
- layout->addWidget(closeBtn);
-
- // Save helper — captures enableCheck and modeCombo
- auto doSave = [this, enableCheck, modeCombo]() {
- QJsonObject s;
- s[QStringLiteral("enabled")] = enableCheck->isChecked();
- s[QStringLiteral("mode")] = modeCombo->currentText();
- saveSettings(s);
- };
-
- QObject::connect(enableCheck, &QCheckBox::stateChanged, dialog, [doSave](int) {
- doSave();
- });
- QObject::connect(modeCombo, &QComboBox::currentTextChanged, dialog,
- [doSave](const QString&) { doSave(); });
-
- QObject::connect(buildBtn, &QPushButton::clicked, dialog,
- [self, statusLabel]() {
- int count = self->build();
- statusLabel->setText(
- QStringLiteral("Deployed %1 file(s).").arg(count));
- });
-
- QObject::connect(clearBtn, &QPushButton::clicked, dialog,
- [self, statusLabel]() {
- int count = self->clear();
- statusLabel->setText(
- QStringLiteral("Cleared %1 file(s).").arg(count));
- });
-
- QObject::connect(closeBtn, &QPushButton::clicked, dialog, &QDialog::accept);
-
- dialog->exec();
-}
-
-// ============================================================================
-// Hooks
-// ============================================================================
-
-bool RootBuilderNative::onAboutToRun(const QString& /*executable*/)
-{
- if (isAutoEnabled())
- build();
- return true;
-}
-
-void RootBuilderNative::onFinishedRun(const QString& /*executable*/,
- unsigned int /*exitCode*/)
-{
- if (isAutoEnabled())
- clear();
-}
-
-// ============================================================================
-// Storage paths
-// ============================================================================
-
-QString RootBuilderNative::storageDir() const
-{
- QString d = m_organizer->basePath() + QStringLiteral("/") + STORAGE_SUBDIR;
- QDir().mkpath(d);
- return d;
-}
-
-QString RootBuilderNative::backupDir() const
-{
- return storageDir() + QStringLiteral("/") + BACKUP_SUBDIR;
-}
-
-QString RootBuilderNative::manifestPath() const
-{
- return storageDir() + QStringLiteral("/") + MANIFEST_NAME;
-}
-
-QString RootBuilderNative::settingsPath() const
-{
- return storageDir() + QStringLiteral("/") + SETTINGS_NAME;
-}
-
-// ============================================================================
-// Settings (own JSON)
-// ============================================================================
-
-QJsonObject RootBuilderNative::loadSettings() const
-{
- QFile f(settingsPath());
- if (!f.open(QIODevice::ReadOnly))
- return QJsonObject{
- {QStringLiteral("enabled"), false},
- {QStringLiteral("mode"), QStringLiteral("copy")}};
-
- QJsonParseError err;
- auto doc = QJsonDocument::fromJson(f.readAll(), &err);
- if (err.error != QJsonParseError::NoError || !doc.isObject())
- return QJsonObject{
- {QStringLiteral("enabled"), false},
- {QStringLiteral("mode"), QStringLiteral("copy")}};
-
- return doc.object();
-}
-
-void RootBuilderNative::saveSettings(const QJsonObject& settings) const
-{
- QDir().mkpath(storageDir());
- QFile f(settingsPath());
- if (f.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
- f.write(QJsonDocument(settings).toJson(QJsonDocument::Indented));
- }
-}
-
-bool RootBuilderNative::isAutoEnabled() const
-{
- return loadSettings().value(QStringLiteral("enabled")).toBool(false);
-}
-
-// ============================================================================
-// Manifest
-// ============================================================================
-
-QJsonObject RootBuilderNative::loadManifest() const
-{
- QFile f(manifestPath());
- if (!f.open(QIODevice::ReadOnly))
- return QJsonObject();
-
- QJsonParseError err;
- auto doc = QJsonDocument::fromJson(f.readAll(), &err);
- if (err.error != QJsonParseError::NoError || !doc.isObject())
- return QJsonObject();
-
- return doc.object();
-}
-
-void RootBuilderNative::saveManifest(const QJsonObject& manifest) const
-{
- QDir().mkpath(storageDir());
- QFile f(manifestPath());
- if (f.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
- f.write(QJsonDocument(manifest).toJson(QJsonDocument::Indented));
- }
-}
-
-void RootBuilderNative::removeManifest() const
-{
- QFile::remove(manifestPath());
-}
-
-// ============================================================================
-// Legacy migration
-// ============================================================================
-
-void RootBuilderNative::migrateLegacy() const
-{
- auto* game = m_organizer->managedGame();
- if (!game)
- return;
-
- QString gameDir = game->gameDirectory().absolutePath();
- QString storage = storageDir();
-
- // Migrate manifest
- QString oldManifest = gameDir + QStringLiteral("/") + LEGACY_MANIFEST;
- if (QFileInfo::exists(oldManifest)) {
- QString newPath = storage + QStringLiteral("/") + MANIFEST_NAME;
- if (!QFileInfo::exists(newPath))
- QFile::copy(oldManifest, newPath);
- forceRemove(oldManifest);
- }
-
- // Migrate backup directory
- QString oldBackup = gameDir + QStringLiteral("/") + LEGACY_BACKUP;
- QFileInfo oldBackupInfo(oldBackup);
- if (oldBackupInfo.isDir()) {
- QString newBackup = storage + QStringLiteral("/") + BACKUP_SUBDIR;
- if (!QFileInfo(newBackup).isDir()) {
- // Copy tree
- QDir().mkpath(newBackup);
- QDirIterator it(oldBackup, QDir::Files, QDirIterator::Subdirectories);
- while (it.hasNext()) {
- it.next();
- QString rel = QDir(oldBackup).relativeFilePath(it.filePath());
- QString dst = newBackup + QStringLiteral("/") + rel;
- QDir().mkpath(QFileInfo(dst).absolutePath());
- QFile::copy(it.filePath(), dst);
- }
- }
- // Remove old backup tree
- QDir(oldBackup).removeRecursively();
- }
-}
-
-// ============================================================================
-// Third-party conflict detection
-// ============================================================================
-
-void RootBuilderNative::checkThirdPartyRootBuilder() const
-{
- // Locate the plugins directory (where .py/.so plugins live, next to the binary)
- QString pluginsDir = QCoreApplication::applicationDirPath() + QStringLiteral("/plugins");
- if (!QDir(pluginsDir).exists())
- return;
-
- QString disabledDir =
- QCoreApplication::applicationDirPath() + QStringLiteral("/DisabledPlugins");
-
- QDir dir(pluginsDir);
- const auto entries = dir.entryInfoList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
-
- for (const auto& entry : entries) {
- bool conflict = false;
- QString entryName = entry.fileName();
-
- if (entry.isDir() && entryName.compare(QStringLiteral("rootbuilder"),
- Qt::CaseInsensitive) == 0) {
- conflict = true;
- } else if (entry.isFile() &&
- entryName.toLower().startsWith(QStringLiteral("rootbuilder")) &&
- entryName.toLower().endsWith(QStringLiteral(".py"))) {
- // Don't move our own bundled rootbuilder.py
- if (entryName == QStringLiteral("rootbuilder.py"))
- continue;
- conflict = true;
- }
-
- if (conflict) {
- QDir().mkpath(disabledDir);
- QString dst = disabledDir + QStringLiteral("/") + entryName;
- if (QFile::rename(entry.absoluteFilePath(), dst)) {
- qInfo("Root Builder: moved incompatible third-party plugin "
- "'%s' to DisabledPlugins/.",
- qUtf8Printable(entryName));
- } else {
- qWarning("Root Builder: failed to move third-party plugin "
- "'%s' to DisabledPlugins/.",
- qUtf8Printable(entryName));
- }
- }
- }
-}
-
-// ============================================================================
-// File helpers
-// ============================================================================
-
-QString RootBuilderNative::findRootDir(const QString& modPath)
-{
- QDir dir(modPath);
- const auto entries =
- dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
- for (const auto& entry : entries) {
- if (entry.fileName().compare(QStringLiteral("root"),
- Qt::CaseInsensitive) == 0) {
- return entry.absoluteFilePath();
- }
- }
- return QString();
-}
-
-void RootBuilderNative::ensureReadable(const QString& path)
-{
- QFileInfo fi(path);
- if (fi.exists() && !fi.isReadable()) {
- QFile::setPermissions(path, fi.permissions() | QFileDevice::ReadOwner);
- }
-}
-
-void RootBuilderNative::ensureWritable(const QString& path)
-{
- QFileInfo fi(path);
- if (fi.exists()) {
- QFile::setPermissions(path, fi.permissions() | QFileDevice::WriteOwner);
- }
-}
-
-void RootBuilderNative::reflinkCopy(const QString& src, const QString& dst)
-{
- ensureReadable(src);
-
- // Try cp --reflink=auto first
- QProcess proc;
- proc.setProgram(QStringLiteral("cp"));
- proc.setArguments({QStringLiteral("--reflink=auto"), QStringLiteral("-f"),
- QStringLiteral("--"), src, dst});
- proc.start();
- if (proc.waitForFinished(10000) && proc.exitCode() == 0)
- return;
-
- // Fallback to QFile::copy (remove dst first since QFile::copy won't overwrite)
- QFile::remove(dst);
- if (QFile::copy(src, dst))
- return;
-
- qWarning("Root Builder: failed to copy %s -> %s",
- qUtf8Printable(src), qUtf8Printable(dst));
-}
-
-bool RootBuilderNative::forceRemove(const QString& path)
-{
- if (QFile::remove(path))
- return true;
-
- // Fix permissions and retry
- ensureWritable(QFileInfo(path).absolutePath());
- ensureWritable(path);
- if (QFile::remove(path))
- return true;
-
- qWarning("Root Builder: could not remove %s", qUtf8Printable(path));
- return false;
-}
-
-void RootBuilderNative::cleanupEmptyDirs(const QString& baseDir,
- const QStringList& paths)
-{
- std::set<QString> dirsToCheck;
- for (const auto& path : paths) {
- QString parent = QFileInfo(path).absolutePath();
- while (!parent.isEmpty() && parent != baseDir) {
- // Check samefile
- QFileInfo parentInfo(parent);
- QFileInfo baseInfo(baseDir);
- if (parentInfo.absoluteFilePath() == baseInfo.absoluteFilePath())
- break;
- dirsToCheck.insert(parent);
- parent = QFileInfo(parent).absolutePath();
- }
- }
-
- // Sort by length descending (deepest first)
- QStringList sorted(dirsToCheck.begin(), dirsToCheck.end());
- std::sort(sorted.begin(), sorted.end(),
- [](const QString& a, const QString& b) {
- return a.length() > b.length();
- });
-
- for (const auto& d : sorted) {
- QDir dir(d);
- if (dir.exists() && dir.isEmpty())
- dir.rmdir(QStringLiteral("."));
- }
-}
-
-// ============================================================================
-// Build
-// ============================================================================
-
-int RootBuilderNative::build() const
-{
- auto* game = m_organizer->managedGame();
- if (!game)
- return 0;
-
- QString gameDir = game->gameDirectory().absolutePath();
- QString storage = storageDir();
- auto* modList = m_organizer->modList();
- QString mode = loadSettings().value(QStringLiteral("mode"))
- .toString(QStringLiteral("copy"));
-
- // Clear any previous deployment first
- QJsonObject existingManifest = loadManifest();
- if (!existingManifest.isEmpty())
- const_cast<RootBuilderNative*>(this)->clear();
-
- QJsonArray deployed;
- QJsonObject backups;
- QSet<QString> deployedSet;
-
- QStringList mods = modList->allModsByProfilePriority();
- for (const auto& modName : mods) {
- if (!(modList->state(modName) & MOBase::IModList::STATE_ACTIVE))
- continue;
-
- auto* mod = modList->getMod(modName);
- if (!mod)
- continue;
- if (mod->isSeparator() || mod->isBackup() || mod->isForeign())
- continue;
-
- QString modPath = mod->absolutePath();
- QString rootDir = findRootDir(modPath);
- if (rootDir.isEmpty())
- continue;
-
- QDirIterator it(rootDir, QDir::Files, QDirIterator::Subdirectories);
- while (it.hasNext()) {
- it.next();
- QString srcFile = it.filePath();
- QString rel = QDir(rootDir).relativeFilePath(srcFile);
- QString dst = gameDir + QStringLiteral("/") + rel;
-
- // Backup existing file if not already deployed by us
- if (QFile::exists(dst) && !deployedSet.contains(dst)) {
- QString bak =
- backupDir() + QStringLiteral("/") + rel;
- QDir().mkpath(QFileInfo(bak).absolutePath());
- ensureWritable(dst);
- QFile::copy(dst, bak);
- backups[dst] = bak;
- }
-
- QDir().mkpath(QFileInfo(dst).absolutePath());
-
- // Remove existing file/symlink
- if (QFileInfo::exists(dst) || QFileInfo(dst).isSymLink())
- forceRemove(dst);
-
- // In link mode, .exe and .dll must be copied (Wine resolves
- // symlinked exe paths to the target, breaking sibling lookups)
- QString ext = QFileInfo(srcFile).suffix().toLower();
- if (mode == QStringLiteral("link") && ext != QStringLiteral("exe") &&
- ext != QStringLiteral("dll")) {
- QFile::link(srcFile, dst);
- } else {
- reflinkCopy(srcFile, dst);
- }
-
- if (!deployedSet.contains(dst)) {
- deployed.append(dst);
- deployedSet.insert(dst);
- }
- }
- }
-
- QJsonObject manifest;
- manifest[QStringLiteral("deployed")] = deployed;
- manifest[QStringLiteral("backups")] = backups;
- saveManifest(manifest);
-
- return deployed.count();
-}
-
-// ============================================================================
-// Clear
-// ============================================================================
-
-int RootBuilderNative::clear() const
-{
- auto* game = m_organizer->managedGame();
- if (!game)
- return 0;
-
- QString gameDir = game->gameDirectory().absolutePath();
- QString storage = storageDir();
-
- QJsonObject manifest = loadManifest();
- if (manifest.isEmpty())
- return 0;
-
- int count = 0;
- QJsonArray deployedArr = manifest.value(QStringLiteral("deployed")).toArray();
- QJsonObject backupsObj = manifest.value(QStringLiteral("backups")).toObject();
-
- QStringList failed;
- QStringList cleared;
-
- // Remove deployed files
- for (const auto& val : deployedArr) {
- QString path = val.toString();
- if (QFileInfo::exists(path) || QFileInfo(path).isSymLink()) {
- if (forceRemove(path)) {
- ++count;
- cleared.append(path);
- } else {
- failed.append(path);
- }
- } else {
- cleared.append(path);
- }
- }
-
- // Restore backups
- for (auto it = backupsObj.begin(); it != backupsObj.end(); ++it) {
- QString dst = it.key();
- QString bak = it.value().toString();
- if (QFile::exists(bak)) {
- QDir().mkpath(QFileInfo(dst).absolutePath());
- ensureWritable(QFileInfo(dst).absolutePath());
- if (QFileInfo::exists(dst) || QFileInfo(dst).isSymLink())
- forceRemove(dst);
- if (!QFile::rename(bak, dst)) {
- qWarning("Root Builder: could not restore backup %s -> %s",
- qUtf8Printable(bak), qUtf8Printable(dst));
- }
- }
- }
-
- // Clean up backup dir
- QString bDir = backupDir();
- if (QFileInfo(bDir).isDir())
- QDir(bDir).removeRecursively();
-
- if (!failed.isEmpty()) {
- // Update manifest to only contain files we couldn't remove
- QJsonArray failedArr;
- for (const auto& f : failed)
- failedArr.append(f);
-
- QJsonObject retryManifest;
- retryManifest[QStringLiteral("deployed")] = failedArr;
- retryManifest[QStringLiteral("backups")] = QJsonObject();
- saveManifest(retryManifest);
-
- qWarning("Root Builder: %d file(s) could not be removed. "
- "They will be retried on next clear.",
- static_cast<int>(failed.size()));
- } else {
- removeManifest();
- }
-
- cleanupEmptyDirs(gameDir, cleared);
- return count;
-}
diff --git a/libs/rootbuilder_native/src/rootbuilder.h b/libs/rootbuilder_native/src/rootbuilder.h
deleted file mode 100644
index 121bcc1..0000000
--- a/libs/rootbuilder_native/src/rootbuilder.h
+++ /dev/null
@@ -1,79 +0,0 @@
-#ifndef ROOTBUILDER_NATIVE_H
-#define ROOTBUILDER_NATIVE_H
-
-#include <uibase/iplugintool.h>
-
-#include <QJsonObject>
-#include <QString>
-
-class RootBuilderNative : public MOBase::IPluginTool
-{
- Q_OBJECT
- Q_INTERFACES(MOBase::IPlugin MOBase::IPluginTool)
- Q_PLUGIN_METADATA(IID "com.tannin.ModOrganizer.PluginTool/1.0")
-
-public:
- RootBuilderNative();
-
- // 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;
- bool enabledByDefault() const override;
-
- // IPluginTool
- QString displayName() const override;
- QString tooltip() const override;
- QIcon icon() const override;
-
-public slots:
- void display() const override;
-
-private:
- // Storage paths
- QString storageDir() const;
- QString backupDir() const;
- QString manifestPath() const;
- QString settingsPath() const;
-
- // Settings (own JSON, not pluginSetting)
- QJsonObject loadSettings() const;
- void saveSettings(const QJsonObject& settings) const;
- bool isAutoEnabled() const;
-
- // Manifest
- QJsonObject loadManifest() const;
- void saveManifest(const QJsonObject& manifest) const;
- void removeManifest() const;
-
- // Legacy migration
- void migrateLegacy() const;
-
- // Third-party conflict detection
- void checkThirdPartyRootBuilder() const;
-
- // Build / Clear
- int build() const;
- int clear() const;
-
- // File operations
- static QString findRootDir(const QString& modPath);
- static void reflinkCopy(const QString& src, const QString& dst);
- static void ensureReadable(const QString& path);
- static void ensureWritable(const QString& path);
- static bool forceRemove(const QString& path);
- static void cleanupEmptyDirs(const QString& baseDir,
- const QStringList& paths);
-
- // Hooks
- bool onAboutToRun(const QString& executable);
- void onFinishedRun(const QString& executable, unsigned int exitCode);
-
- MOBase::IOrganizer* m_organizer = nullptr;
-};
-
-#endif // ROOTBUILDER_NATIVE_H
diff --git a/libs/script_extender_checker_native/CMakeLists.txt b/libs/script_extender_checker_native/CMakeLists.txt
deleted file mode 100644
index 60ea2ec..0000000
--- a/libs/script_extender_checker_native/CMakeLists.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-cmake_minimum_required(VERSION 3.16)
-
-project(script_extender_checker_native)
-
-add_subdirectory(src)
diff --git a/libs/script_extender_checker_native/src/CMakeLists.txt b/libs/script_extender_checker_native/src/CMakeLists.txt
deleted file mode 100644
index 7d63643..0000000
--- a/libs/script_extender_checker_native/src/CMakeLists.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-cmake_minimum_required(VERSION 3.16)
-
-file(GLOB script_extender_checker_native_SOURCES CONFIGURE_DEPENDS
- ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp
- ${CMAKE_CURRENT_SOURCE_DIR}/*.h
-)
-
-add_library(script_extender_checker_native SHARED ${script_extender_checker_native_SOURCES})
-mo2_configure_plugin(script_extender_checker_native NO_SOURCES WARNINGS OFF)
-target_link_libraries(script_extender_checker_native PRIVATE mo2::uibase)
-mo2_install_plugin(script_extender_checker_native)
diff --git a/libs/script_extender_checker_native/src/scriptextenderchecker.cpp b/libs/script_extender_checker_native/src/scriptextenderchecker.cpp
deleted file mode 100644
index 65276ad..0000000
--- a/libs/script_extender_checker_native/src/scriptextenderchecker.cpp
+++ /dev/null
@@ -1,363 +0,0 @@
-#include "scriptextenderchecker.h"
-
-#include <uibase/iplugingame.h>
-#include <uibase/pluginrequirements.h>
-
-#include <QCoreApplication>
-#include <QDir>
-#include <QFile>
-#include <QFileInfo>
-#include <QMap>
-#include <QRegularExpression>
-#include <QStringConverter>
-#include <QTextStream>
-
-using namespace MOBase;
-
-// Regex patterns matching SKSE/F4SE/etc log formats
-static const QRegularExpression RE_NORMAL(
- R"(plugin (?P<pluginPath>.+) \((?P<infoVersion>[\dA-Fa-f]{8}) (?P<name>.*) (?P<version>[\dA-Fa-f]{8})\) (?P<loadStatus>.+?)(?P<errorCode> \d+)?( \(handle \d+\))?\s*$)");
-
-static const QRegularExpression RE_COULDNT_LOAD(
- R"(couldn't load plugin (?P<pluginPath>.+) \(Error (?:code )?(?P<lastError>[-+]?\d+)(?::\s*(?P<seDetails>.*))?\)\s*)");
-
-static const QRegularExpression RE_NOT_PLUGIN(
- R"(plugin (?P<pluginPath>.+) does not appear to be an (?:SK|F4|NV|FO|OB)SE plugin\s*)");
-
-ScriptExtenderChecker::ScriptExtenderChecker() : m_organizer(nullptr) {}
-
-const QMap<QString, ScriptExtenderChecker::GameType>&
-ScriptExtenderChecker::supportedGames()
-{
- static const QMap<QString, GameType> games = {
- {"Skyrim",
- {LogLocation::Docs, "SKSE/skse.log", "SKSE/skse_editor.log"}},
- {"Skyrim Special Edition",
- {LogLocation::Docs, "SKSE/skse64.log", ""}},
- {"Skyrim VR",
- {LogLocation::Docs, "SKSE/sksevr.log", ""}},
- {"Fallout 4",
- {LogLocation::Docs, "F4SE/f4se.log", ""}},
- {"Oblivion",
- {LogLocation::Install, "obse.log", "obse_editor.log"}},
- {"New Vegas",
- {LogLocation::Install, "nvse.log", "nvse_editor.log"}},
- {"TTW",
- {LogLocation::Install, "nvse.log", "nvse_editor.log"}},
- {"Fallout 3",
- {LogLocation::Install, "fose.log", "fose_editor.log"}},
- };
- return games;
-}
-
-bool ScriptExtenderChecker::init(IOrganizer* moInfo)
-{
- m_organizer = moInfo;
- m_organizer->onFinishedRun(
- [this](const QString&, unsigned int) { invalidate(); });
- return true;
-}
-
-QString ScriptExtenderChecker::name() const
-{
- return "Script Extender Plugin Load Checker (Native)";
-}
-
-QString ScriptExtenderChecker::localizedName() const
-{
- return tr("Script Extender Plugin Load Checker (Native)");
-}
-
-QString ScriptExtenderChecker::author() const
-{
- return "AnyOldName3";
-}
-
-QString ScriptExtenderChecker::description() const
-{
- return tr("Checks script extender log to see if any plugins failed to load.");
-}
-
-VersionInfo ScriptExtenderChecker::version() const
-{
- return VersionInfo(1, 2, 0, VersionInfo::RELEASE_FINAL);
-}
-
-std::vector<std::shared_ptr<const IPluginRequirement>>
-ScriptExtenderChecker::requirements() const
-{
- const auto& games = supportedGames();
- return {PluginRequirementFactory::gameDependency(QStringList(games.keys()))};
-}
-
-QList<PluginSetting> ScriptExtenderChecker::settings() const
-{
- return {};
-}
-
-std::vector<unsigned int> ScriptExtenderChecker::activeProblems() const
-{
- if (!listBadPluginMessages().isEmpty()) {
- return {PROBLEM_PLUGIN_LOAD};
- }
- return {};
-}
-
-QString ScriptExtenderChecker::shortDescription(unsigned int key) const
-{
- return tr("Script extender log reports incompatible plugins.");
-}
-
-QString ScriptExtenderChecker::fullDescription(unsigned int key) const
-{
- QStringList plugins = listBadPluginMessages();
- QString pluginListString = "\n \u2022 " + plugins.join("\n \u2022 ");
- return tr("You have one or more script extender plugins which failed to "
- "load!\n\n"
- "If you want this notification to go away, here are some steps you "
- "can take:\n"
- " \u2022 Look for updates to the mod or the specific plugin "
- "included in the mod.\n"
- " \u2022 Disable the mod containing the plugin.\n"
- " \u2022 Hide or delete the plugin from the mod.\n\n"
- "To refresh the script extender logs, you will need to run the game "
- "and/or editor again!\n\n"
- "The failed plugins are:%1")
- .arg(pluginListString);
-}
-
-bool ScriptExtenderChecker::hasGuidedFix(unsigned int key) const
-{
- return false;
-}
-
-void ScriptExtenderChecker::startGuidedFix(unsigned int key) const {}
-
-QString ScriptExtenderChecker::resolveOrigin(const QString& pluginPath) const
-{
- try {
- QString dataDir =
- m_organizer->managedGame()->dataDirectory().absolutePath();
- QString relativePath = QDir(dataDir).relativeFilePath(pluginPath);
- QStringList origins = m_organizer->getFileOrigins(relativePath);
- if (!origins.isEmpty()) {
- return origins.first();
- }
- } catch (...) {
- }
- return QString();
-}
-
-ScriptExtenderChecker::PluginMessage
-ScriptExtenderChecker::parseNormalLine(
- const QRegularExpressionMatch& match) const
-{
- PluginMessage msg;
- msg.pluginPath = match.captured("pluginPath");
- QString name = match.captured("name");
- QString version = match.captured("version");
- QString status = match.captured("loadStatus");
- msg.origin = resolveOrigin(msg.pluginPath);
- msg.success =
- msg.origin.isEmpty() || status == "loaded correctly" || status == "no version data";
-
- if (!msg.success) {
- QString trStatus = status;
- // Translate known statuses
- if (status == "disabled, address library needs to be updated")
- trStatus = tr("disabled, address library needs to be updated");
- else if (status == "disabled, fatal error occurred while loading plugin")
- trStatus = tr("disabled, fatal error occurred while loading plugin");
- else if (status == "disabled, bad version data")
- trStatus = tr("disabled, bad version data");
- else if (status == "disabled, no name specified")
- trStatus = tr("disabled, no name specified");
- else if (status == "disabled, unsupported version independence method")
- trStatus = tr("disabled, unsupported version independence method");
- else if (status == "disabled, incompatible with current runtime version")
- trStatus = tr("disabled, incompatible with current runtime version");
- else if (status == "disabled, requires newer script extender")
- trStatus = tr("disabled, requires newer script extender");
- else if (status == "reported as incompatible during query")
- trStatus = tr("reported as incompatible during query");
- else if (status == "reported as incompatible during load")
- trStatus = tr("reported as incompatible during load");
- else if (status ==
- "disabled, fatal error occurred while checking plugin compatibility")
- trStatus = tr("disabled, fatal error occurred while checking plugin "
- "compatibility");
- else if (status == "disabled, fatal error occurred while querying plugin")
- trStatus = tr("disabled, fatal error occurred while querying plugin");
-
- msg.message = tr("%1 version %2 (%3, %4) %5.")
- .arg(name)
- .arg(version)
- .arg(QFileInfo(msg.pluginPath).fileName())
- .arg(msg.origin)
- .arg(trStatus);
- }
- return msg;
-}
-
-ScriptExtenderChecker::PluginMessage
-ScriptExtenderChecker::parseCouldntLoadLine(
- const QRegularExpressionMatch& match) const
-{
- PluginMessage msg;
- msg.pluginPath = match.captured("pluginPath");
- int lastError = match.captured("lastError").toInt();
- QString details = match.captured("seDetails").trimmed();
- msg.origin = resolveOrigin(msg.pluginPath);
- msg.success = msg.origin.isEmpty();
-
- if (!msg.success) {
- QString fileName = QFileInfo(msg.pluginPath).fileName();
- if (lastError == 126) {
- msg.message =
- tr("Couldn't load %1 (%2). A dependency DLL could not be found "
- "(code %3). %4")
- .arg(fileName)
- .arg(msg.origin)
- .arg(lastError)
- .arg(details);
- } else if (lastError == 193) {
- msg.message = tr("Couldn't load %1 (%2). A DLL is invalid (code %3).")
- .arg(fileName)
- .arg(msg.origin)
- .arg(lastError);
- } else {
- msg.message =
- tr("Couldn't load %1 (%2). The last error code was %3.")
- .arg(fileName)
- .arg(msg.origin)
- .arg(lastError);
- }
- }
- return msg;
-}
-
-ScriptExtenderChecker::PluginMessage
-ScriptExtenderChecker::parseNotAPluginLine(
- const QRegularExpressionMatch& match) const
-{
- PluginMessage msg;
- msg.pluginPath = match.captured("pluginPath");
- msg.origin = resolveOrigin(msg.pluginPath);
- msg.success = msg.origin.isEmpty();
-
- if (!msg.success) {
- msg.message =
- tr("%1 (%2) does not appear to be a script extender plugin.")
- .arg(QFileInfo(msg.pluginPath).fileName())
- .arg(msg.origin);
- }
- return msg;
-}
-
-QList<ScriptExtenderChecker::PluginMessage>
-ScriptExtenderChecker::parseLog(const QString& logPath) const
-{
- QList<PluginMessage> messages;
-
- QFile file(logPath);
- if (!file.exists() || !file.open(QIODevice::ReadOnly | QIODevice::Text)) {
- return messages;
- }
-
- // Script extender logs use cp1252 encoding
- QTextStream stream(&file);
-#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
- stream.setCodec("Windows-1252");
-#else
- stream.setEncoding(QStringConverter::Latin1);
-#endif
-
- while (!stream.atEnd()) {
- QString line = stream.readLine() + "\n";
-
- QRegularExpressionMatch match = RE_NORMAL.match(line);
- if (match.hasMatch()) {
- messages.append(parseNormalLine(match));
- continue;
- }
-
- match = RE_COULDNT_LOAD.match(line);
- if (match.hasMatch()) {
- messages.append(parseCouldntLoadLine(match));
- continue;
- }
-
- match = RE_NOT_PLUGIN.match(line);
- if (match.hasMatch()) {
- messages.append(parseNotAPluginLine(match));
- continue;
- }
- }
-
- return messages;
-}
-
-QStringList ScriptExtenderChecker::listBadPluginMessages() const
-{
- const auto& games = supportedGames();
- QString gameName = m_organizer->managedGame()->gameName();
-
- if (!games.contains(gameName)) {
- return {};
- }
-
- const GameType& gameType = games[gameName];
-
- QString baseDir;
- if (gameType.base == LogLocation::Docs) {
- baseDir = m_organizer->managedGame()->documentsDirectory().absolutePath();
- } else {
- baseDir = m_organizer->managedGame()->gameDirectory().absolutePath();
- }
-
- QList<PluginMessage> gameMessages;
- QList<PluginMessage> editorMessages;
-
- if (!gameType.gameSuffix.isEmpty()) {
- gameMessages = parseLog(QDir(baseDir).filePath(gameType.gameSuffix));
- }
- if (!gameType.editorSuffix.isEmpty()) {
- editorMessages = parseLog(QDir(baseDir).filePath(gameType.editorSuffix));
- }
-
- QStringList result;
-
- // Report game log failures that aren't successful in editor log
- for (const auto& gameMsg : gameMessages) {
- if (!gameMsg.success) {
- bool editorOk = false;
- for (const auto& editorMsg : editorMessages) {
- if (gameMsg.pluginPath == editorMsg.pluginPath && editorMsg.success) {
- editorOk = true;
- break;
- }
- }
- if (!editorOk && !result.contains(gameMsg.message)) {
- result.append(gameMsg.message);
- }
- }
- }
-
- // Report editor log failures that aren't successful in game log
- for (const auto& editorMsg : editorMessages) {
- if (!editorMsg.success) {
- bool gameOk = false;
- for (const auto& gameMsg : gameMessages) {
- if (editorMsg.pluginPath == gameMsg.pluginPath && gameMsg.success) {
- gameOk = true;
- break;
- }
- }
- if (!gameOk && !result.contains(editorMsg.message)) {
- result.append(editorMsg.message);
- }
- }
- }
-
- return result;
-}
diff --git a/libs/script_extender_checker_native/src/scriptextenderchecker.h b/libs/script_extender_checker_native/src/scriptextenderchecker.h
deleted file mode 100644
index 8dbadcb..0000000
--- a/libs/script_extender_checker_native/src/scriptextenderchecker.h
+++ /dev/null
@@ -1,72 +0,0 @@
-#ifndef SCRIPTEXTENDERCHECKER_H
-#define SCRIPTEXTENDERCHECKER_H
-
-#include <memory>
-
-#include <QString>
-#include <QStringList>
-
-#include <uibase/iplugin.h>
-#include <uibase/iplugindiagnose.h>
-#include <uibase/pluginrequirements.h>
-
-class ScriptExtenderChecker : public QObject,
- public MOBase::IPlugin,
- public MOBase::IPluginDiagnose
-{
- Q_OBJECT
- Q_INTERFACES(MOBase::IPlugin MOBase::IPluginDiagnose)
- Q_PLUGIN_METADATA(IID "org.tannin.ScriptExtenderCheckerNative")
-
-public:
- ScriptExtenderChecker();
-
-public: // IPlugin
- bool init(MOBase::IOrganizer* moInfo) override;
- QString name() const override;
- QString localizedName() const override;
- QString author() const override;
- QString description() const override;
- MOBase::VersionInfo version() const override;
- std::vector<std::shared_ptr<const MOBase::IPluginRequirement>>
- requirements() const override;
- QList<MOBase::PluginSetting> settings() const override;
-
-public: // IPluginDiagnose
- std::vector<unsigned int> activeProblems() const override;
- QString shortDescription(unsigned int key) const override;
- QString fullDescription(unsigned int key) const override;
- bool hasGuidedFix(unsigned int key) const override;
- void startGuidedFix(unsigned int key) const override;
-
-private:
- static const unsigned int PROBLEM_PLUGIN_LOAD = 0;
-
- enum class LogLocation { Docs, Install };
-
- struct GameType {
- LogLocation base;
- QString gameSuffix;
- QString editorSuffix; // empty if no editor log
- };
-
- struct PluginMessage {
- QString pluginPath;
- QString origin;
- QString message;
- bool success;
- };
-
- QStringList listBadPluginMessages() const;
- QList<PluginMessage> parseLog(const QString& logPath) const;
- PluginMessage parseNormalLine(const QRegularExpressionMatch& match) const;
- PluginMessage parseCouldntLoadLine(const QRegularExpressionMatch& match) const;
- PluginMessage parseNotAPluginLine(const QRegularExpressionMatch& match) const;
- QString resolveOrigin(const QString& pluginPath) const;
-
- static const QMap<QString, GameType>& supportedGames();
-
- MOBase::IOrganizer* m_organizer;
-};
-
-#endif // SCRIPTEXTENDERCHECKER_H