diff options
| author | SulfurNitride <SulfurNitride@users.noreply.github.com> | 2026-04-12 02:40:00 -0500 |
|---|---|---|
| committer | SulfurNitride <SulfurNitride@users.noreply.github.com> | 2026-04-12 02:40:00 -0500 |
| commit | e0fb66428a9b78b4c326df8d7e30429a5b271d74 (patch) | |
| tree | 0fd83fe790ecb7d5cd3cfb36535e45d1b7118098 /libs | |
| parent | e985dc340097831d4e5625941bded217abaa57b8 (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')
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 '%2' 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 |
