aboutsummaryrefslogtreecommitdiff
path: root/libs/installer_fomod_plus/installer/lib
diff options
context:
space:
mode:
Diffstat (limited to 'libs/installer_fomod_plus/installer/lib')
-rw-r--r--libs/installer_fomod_plus/installer/lib/ConditionTester.cpp176
-rw-r--r--libs/installer_fomod_plus/installer/lib/ConditionTester.h42
-rw-r--r--libs/installer_fomod_plus/installer/lib/CrashHandler.cpp122
-rw-r--r--libs/installer_fomod_plus/installer/lib/CrashHandler.h20
-rw-r--r--libs/installer_fomod_plus/installer/lib/FileInstaller.cpp274
-rw-r--r--libs/installer_fomod_plus/installer/lib/FileInstaller.h94
-rw-r--r--libs/installer_fomod_plus/installer/lib/FlagMap.h113
-rw-r--r--libs/installer_fomod_plus/installer/lib/Logger.h116
-rw-r--r--libs/installer_fomod_plus/installer/lib/ViewModels.h120
9 files changed, 0 insertions, 1077 deletions
diff --git a/libs/installer_fomod_plus/installer/lib/ConditionTester.cpp b/libs/installer_fomod_plus/installer/lib/ConditionTester.cpp
deleted file mode 100644
index c35bf47..0000000
--- a/libs/installer_fomod_plus/installer/lib/ConditionTester.cpp
+++ /dev/null
@@ -1,176 +0,0 @@
-#include "ConditionTester.h"
-
-#include "ui/FomodViewModel.h"
-
-#include <iplugingame.h>
-#include <ipluginlist.h>
-
-std::string setToString(const std::set<int> &set)
-{
- std::string str;
- for (const auto& i : set) {
- str += std::to_string(i) + ", ";
- }
- return str;
-}
-
-bool ConditionTester::isStepVisible(const std::shared_ptr<FlagMap>& flags,
- const CompositeDependency& compositeDependency,
- const int stepIndex,
- const std::vector<std::shared_ptr<StepViewModel>>& steps) const
-{
-
- // first things first: is it visible?
- if (!testCompositeDependency(flags, compositeDependency)) {
- return false;
- }
-
- const auto flagDependencies = compositeDependency.flagDependencies;
- if (flagDependencies.empty()) {
- return true;
- }
-
- std::set<int> stepsThatSetThisFlag;
-
- for (const auto& flagDependency : flagDependencies) {
- // for this flag, find the plugins that set it
- for (int i = stepIndex - 1; i >= 0; --i) {
- for (const auto& group : steps[i]->getGroups()) {
- for (const auto& plugin : group->getPlugins()) {
- if (std::ranges::any_of(plugin->getPlugin()->conditionFlags.flags,
- [&flagDependency](const ConditionFlag& flag) {
- return flag.name == flagDependency.flag && flag.value == flagDependency.value;
- })) {
- stepsThatSetThisFlag.insert(i);
- }
- }
- }
- }
- }
- const auto anyVisible = std::ranges::any_of(stepsThatSetThisFlag, [this, &steps, &flags](const int index) {
- return isStepVisible(flags, steps[index]->getVisibilityConditions(), index, steps);
- });
- if (!anyVisible) {
- log.logMessage(DEBUG, "Step " + steps[stepIndex]->getName() + " has no dependent steps that are visible.");
- log.logMessage(DEBUG, "Steps that set this flag: " + setToString(stepsThatSetThisFlag));
- }
- return anyVisible;
-
-}
-
-bool ConditionTester::testCompositeDependency(const std::shared_ptr<FlagMap>& flags,
- const CompositeDependency& compositeDependency) const
-{
- const auto fileDependencies = compositeDependency.fileDependencies;
- const auto flagDependencies = compositeDependency.flagDependencies;
- const auto gameDependencies = compositeDependency.gameDependencies;
- const auto nestedDependencies = compositeDependency.nestedDependencies;
- const auto globalOperatorType = compositeDependency.operatorType;
-
- // For the globalOperatorType
- // Evaluate all conditions and store the results in a vector<bool>, then return based on operator.
- // These aren't expensive to calculate so rather than do some fancy logic to short-circuit, just calculate all of 'em.
- std::vector<bool> results;
- for (const auto& fileDependency : fileDependencies) {
- results.emplace_back(testFileDependency(fileDependency));
- }
- for (const auto& flagDependency : flagDependencies) {
- results.emplace_back(testFlagDependency(flags, flagDependency));
- }
- for (const auto& gameDependency : gameDependencies) {
- results.emplace_back(testGameDependency(gameDependency));
- }
- for (const auto& nestedDependency : nestedDependencies) {
- results.emplace_back(testCompositeDependency(flags, nestedDependency));
- }
-
- if (globalOperatorType == OperatorTypeEnum::AND) {
- return std::ranges::all_of(results, [](const bool result) { return result; });
- }
- return std::ranges::any_of(results, [](const bool result) { return result; });
-}
-
-
-bool ConditionTester::testFlagDependency(const std::shared_ptr<FlagMap>& flags, const FlagDependency& flagDependency)
-{
- // Every instance of this flag being set in the map.
- const auto flagList = flags->getFlagsByKey(flagDependency.flag);
-
- // Find the first instance of this flag being set (in the order specified by getFlagsByKey)
- if (flagList.empty()) {
- // If the dependency value is an empty string, it means this flag should be unset.
- // So if we don't have any value for this flag, the result is true.
- return flagDependency.value.empty();
- }
-
- return flagList.front().second == flagDependency.value;
-}
-
-bool ConditionTester::testFileDependency(const FileDependency& fileDependency) const
-{
- const std::string& pluginName = fileDependency.file;
- const auto pluginState = getFileDependencyStateForPlugin(pluginName);
- return pluginState == fileDependency.state;
-}
-
-bool ConditionTester::testGameDependency(const GameDependency& gameDependency) const
-{
- const auto gameVersion = mOrganizer->managedGame()->gameVersion().toStdString();
- log.logMessage(DEBUG, "Comparing condition version " + gameDependency.version + " against " + gameVersion);
- if ( gameDependency.version <= gameVersion) {
- log.logMessage(DEBUG, "Version matches!");
- }
- return gameDependency.version <= gameVersion;
-}
-
-FileDependencyTypeEnum ConditionTester::getFileDependencyStateForPlugin(const std::string& pluginName) const
-{
- if (const auto it = pluginStateCache.find(pluginName); it != pluginStateCache.end()) {
- return it->second;
- }
-
- const QFlags<MOBase::IPluginList::PluginState> pluginState = mOrganizer->pluginList()->state(
- QString::fromStdString(pluginName));
-
- FileDependencyTypeEnum state;
-
- if (pluginState == MOBase::IPluginList::STATE_MISSING) {
- state = FileDependencyTypeEnum::Missing;
- } else if (pluginState == MOBase::IPluginList::STATE_INACTIVE) {
- state = FileDependencyTypeEnum::Inactive;
- } else if (pluginState == MOBase::IPluginList::STATE_ACTIVE) {
- state = FileDependencyTypeEnum::Active;
- } else {
- state = FileDependencyTypeEnum::UNKNOWN_STATE;
- }
-
- pluginStateCache[pluginName] = state;
- return state;
-}
-
-PluginTypeEnum ConditionTester::getPluginTypeDescriptorState(const std::shared_ptr<Plugin>& plugin,
- const std::shared_ptr<FlagMap>& flags) const
-{
- // NOTE: A plugin's ConditionFlags aren't the same thing as a step visibility one.
- // A plugin's ConditionFlags are toggled based on the selection state of the plugin
- // We only evaluate the typeDescriptor here.
-
- // We will return the 'winning' type or the default. If multiple conditions are met,
- // ...well, I'm not sure.
- // ReSharper disable once CppTooWideScopeInitStatement
- const auto& dependencyType = plugin->typeDescriptor.dependencyType;
- for (const auto& pattern : dependencyType.patterns.patterns) {
- if (testCompositeDependency(flags, pattern.dependencies)) {
- return pattern.type;
- }
- }
-
- // Sometimes authors do this.
- if (plugin->typeDescriptor.type != PluginTypeEnum::Optional) {
- return plugin->typeDescriptor.type;
- }
- if (plugin->typeDescriptor.dependencyType.defaultType.has_value()) {
- return plugin->typeDescriptor.dependencyType.defaultType.value();
- }
- return PluginTypeEnum::Optional;
-} \ No newline at end of file
diff --git a/libs/installer_fomod_plus/installer/lib/ConditionTester.h b/libs/installer_fomod_plus/installer/lib/ConditionTester.h
deleted file mode 100644
index 09aa26e..0000000
--- a/libs/installer_fomod_plus/installer/lib/ConditionTester.h
+++ /dev/null
@@ -1,42 +0,0 @@
-#pragma once
-
-#include <imoinfo.h>
-
-#include "FlagMap.h"
-#include "Logger.h"
-#include "xml/ModuleConfiguration.h"
-
-class CompositeDependency;
-class StepViewModel;
-
-class ConditionTester {
-public:
- explicit ConditionTester(MOBase::IOrganizer* organizer) : mOrganizer(organizer) {}
-
- bool isStepVisible(const std::shared_ptr<FlagMap>& flags, const CompositeDependency& compositeDependency,
- int stepIndex,
- const std::vector<std::shared_ptr<StepViewModel>>& steps) const;
-
- bool testCompositeDependency(const std::shared_ptr<FlagMap>& flags,
- const CompositeDependency& compositeDependency) const;
-
- static bool testFlagDependency(const std::shared_ptr<FlagMap>& flags, const FlagDependency& flagDependency);
-
- [[nodiscard]] bool testFileDependency(const FileDependency& fileDependency) const;
-
- bool testGameDependency(const GameDependency& gameDependency) const;
-
-private:
- Logger& log = Logger::getInstance();
- MOBase::IOrganizer* mOrganizer;
-
- friend class FomodViewModel;
-
- [[nodiscard]] FileDependencyTypeEnum getFileDependencyStateForPlugin(const std::string& pluginName) const;
-
- PluginTypeEnum getPluginTypeDescriptorState(const std::shared_ptr<Plugin>& plugin,
- const std::shared_ptr<FlagMap>& flags) const;
-
- mutable std::unordered_map<std::string, FileDependencyTypeEnum> pluginStateCache;
-
-};
diff --git a/libs/installer_fomod_plus/installer/lib/CrashHandler.cpp b/libs/installer_fomod_plus/installer/lib/CrashHandler.cpp
deleted file mode 100644
index 98ed1f3..0000000
--- a/libs/installer_fomod_plus/installer/lib/CrashHandler.cpp
+++ /dev/null
@@ -1,122 +0,0 @@
-#include "CrashHandler.h"
-#include <iostream>
-
-#ifdef _WIN32
-#include <windows.h>
-#include <dbghelp.h>
-#include <sstream>
-#include <fstream>
-
-LPTOP_LEVEL_EXCEPTION_FILTER CrashHandler::previousFilter = nullptr;
-PVOID CrashHandler::vectoredHandler = nullptr;
-#endif
-
-void CrashHandler::initialize() {
-#ifdef _WIN32
- std::cout << "CrashHandler initializing..." << std::endl;
- vectoredHandler = AddVectoredExceptionHandler(1, vectoredExceptionHandler);
- previousFilter = SetUnhandledExceptionFilter(unhandledExceptionFilter);
- std::cout << "CrashHandler initialized successfully" << std::endl;
-#else
- // No-op on Linux — Windows crash handler not needed.
-#endif
-}
-
-void CrashHandler::cleanup() {
-#ifdef _WIN32
- if (vectoredHandler) {
- RemoveVectoredExceptionHandler(vectoredHandler);
- vectoredHandler = nullptr;
- }
- if (previousFilter) {
- SetUnhandledExceptionFilter(previousFilter);
- previousFilter = nullptr;
- }
-#endif
-}
-
-#ifdef _WIN32
-LONG WINAPI CrashHandler::vectoredExceptionHandler(EXCEPTION_POINTERS* exceptionInfo) {
- // Log all exceptions but only create dumps for serious ones
- std::cout << "Exception caught: 0x" << std::hex << exceptionInfo->ExceptionRecord->ExceptionCode << std::endl;
-
- // Only create dumps for serious crashes, not benign exceptions
- if (exceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION ||
- exceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_STACK_OVERFLOW ||
- exceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_ILLEGAL_INSTRUCTION) {
-
- // For stack overflow, we need to handle it on a different thread with its own stack
- if (exceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_STACK_OVERFLOW) {
- // Create a new thread to handle the crash dump since our stack is corrupted
- HANDLE dumpThread = CreateThread(nullptr, 0, [](LPVOID param) -> DWORD {
- auto* exceptionInfo = static_cast<EXCEPTION_POINTERS*>(param);
- logCrashInfo(exceptionInfo);
-
- HANDLE file = CreateFileA("fomod_plus_stack_overflow.dmp", GENERIC_WRITE, 0, nullptr,
- CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
-
- if (file != INVALID_HANDLE_VALUE) {
- MINIDUMP_EXCEPTION_INFORMATION dumpInfo = {0};
- dumpInfo.ThreadId = GetCurrentThreadId();
- dumpInfo.ExceptionPointers = exceptionInfo;
- dumpInfo.ClientPointers = FALSE;
-
- MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), file,
- MiniDumpNormal, &dumpInfo, nullptr, nullptr);
- CloseHandle(file);
-
- std::cout << "Stack overflow crash dump written to fomod_plus_stack_overflow.dmp" << std::endl;
- }
- return 0;
- }, exceptionInfo, 0, nullptr);
-
- if (dumpThread) {
- WaitForSingleObject(dumpThread, 5000); // Wait up to 5 seconds
- CloseHandle(dumpThread);
- }
- } else {
- logCrashInfo(exceptionInfo);
-
- // Generate minidump
- HANDLE file = CreateFileA("fomod_plus_crash.dmp", GENERIC_WRITE, 0, nullptr,
- CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
-
- if (file != INVALID_HANDLE_VALUE) {
- MINIDUMP_EXCEPTION_INFORMATION dumpInfo = {0};
- dumpInfo.ThreadId = GetCurrentThreadId();
- dumpInfo.ExceptionPointers = exceptionInfo;
- dumpInfo.ClientPointers = FALSE;
-
- MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), file,
- MiniDumpNormal, &dumpInfo, nullptr, nullptr);
- CloseHandle(file);
-
- std::cout << "Crash dump written to fomod_plus_crash.dmp" << std::endl;
- }
- }
- }
-
- return EXCEPTION_CONTINUE_SEARCH; // Let other handlers process it
-}
-
-LONG WINAPI CrashHandler::unhandledExceptionFilter(EXCEPTION_POINTERS* exceptionInfo) {
- logCrashInfo(exceptionInfo);
- return EXCEPTION_EXECUTE_HANDLER;
-}
-
-void CrashHandler::logCrashInfo(const EXCEPTION_POINTERS* exceptionInfo) {
- std::ostringstream oss;
- oss << "FOMOD PLUS CRASH DETECTED!" << std::endl;
- oss << "Exception Code: 0x" << std::hex << exceptionInfo->ExceptionRecord->ExceptionCode << std::endl;
- oss << "Exception Address: 0x" << std::hex << exceptionInfo->ExceptionRecord->ExceptionAddress << std::endl;
-
- // Write to stdout immediately
- std::cout << oss.str() << std::endl;
-
- // Also write to crash log file
- std::ofstream crashLog("fomod_plus_crash.log", std::ios::app);
- if (crashLog.is_open()) {
- crashLog << oss.str() << std::endl;
- }
-}
-#endif
diff --git a/libs/installer_fomod_plus/installer/lib/CrashHandler.h b/libs/installer_fomod_plus/installer/lib/CrashHandler.h
deleted file mode 100644
index 78c8552..0000000
--- a/libs/installer_fomod_plus/installer/lib/CrashHandler.h
+++ /dev/null
@@ -1,20 +0,0 @@
-#pragma once
-
-#ifdef _WIN32
-#include <windows.h>
-#endif
-
-class CrashHandler {
-public:
- static void initialize();
- static void cleanup();
-
-private:
-#ifdef _WIN32
- static LONG WINAPI unhandledExceptionFilter(EXCEPTION_POINTERS* exceptionInfo);
- static LONG WINAPI vectoredExceptionHandler(EXCEPTION_POINTERS* exceptionInfo);
- static void logCrashInfo(const EXCEPTION_POINTERS* exceptionInfo);
- static LPTOP_LEVEL_EXCEPTION_FILTER previousFilter;
- static PVOID vectoredHandler;
-#endif
-};
diff --git a/libs/installer_fomod_plus/installer/lib/FileInstaller.cpp b/libs/installer_fomod_plus/installer/lib/FileInstaller.cpp
deleted file mode 100644
index e47e088..0000000
--- a/libs/installer_fomod_plus/installer/lib/FileInstaller.cpp
+++ /dev/null
@@ -1,274 +0,0 @@
-#include "FileInstaller.h"
-
-#include <utility>
-
-#include "ui/FomodViewModel.h"
-
-using namespace MOBase;
-
-FileInstaller::FileInstaller(
- IOrganizer* organizer,
- QString fomodPath,
- const std::shared_ptr<IFileTree>& fileTree,
- std::unique_ptr<ModuleConfiguration> fomodFile,
- const std::shared_ptr<FlagMap>& flagMap,
- const std::vector<std::shared_ptr<StepViewModel> >& steps) : mOrganizer(organizer),
- mFomodPath(std::move(fomodPath)),
- mFileTree(fileTree),
- mFomodFile(std::move(fomodFile)),
- mFlagMap(flagMap),
- mConditionTester(organizer), mSteps(steps)
-{
- const auto requiredCount =
- (mFomodFile != nullptr) ? mFomodFile->requiredInstallFiles.files.size() : 0;
- const auto stepCount = mSteps.size();
- logMessage(DEBUG,
- "FileInstaller constructed. steps=" + std::to_string(stepCount) + ", required files=" +
- std::to_string(requiredCount) + ", fomodPath=" + mFomodPath.toStdString());
-}
-
-std::shared_ptr<IFileTree> FileInstaller::install() const
-{
- logMessage(DEBUG, "Starting FileInstaller::install()");
- const auto filesToInstall = collectFilesToInstall();
- logMessage(INFO, "Installing " + std::to_string(filesToInstall.size()) + " files");
- logMessage(INFO, "FlagMap");
- logMessage(INFO, mFlagMap->toString());
-
- // update the file tree with the new files
- const std::shared_ptr<IFileTree> installTree = mFileTree->createOrphanTree();
-
- for (const auto& file : filesToInstall) {
-
- logMessage(DEBUG,
- "Processing install entry source=" + file.source + ", destination=" +
- (file.destination.has_value() ? file.destination.value() : "<default>") +
- ", priority=" + std::to_string(file.priority));
- const auto sourcePath = getQualifiedFilePath(file.source);
- const auto sourceNode = mFileTree->find(QString::fromStdString(sourcePath));
- if (sourceNode == nullptr) {
- logMessage(ERR, "Could not find source: " + file.source);
- continue;
- }
- const auto targetPath = file.destination.has_value()
- ? QString::fromStdString(file.destination.value())
- : QString::fromStdString(sourcePath);
-
- // If it's a folder, copy the contents of the folder, not the folder itself.
- if (sourceNode->isDir()) {
- logMessage(DEBUG,
- "Copying directory '" + file.source + "' into target '" + targetPath.toStdString() +
- "'");
- // TODO: Check if target path is literally undefined/null
- const auto& tree = sourceNode->astree();
- for (auto it = tree->begin(); it != tree->end(); ++it) {
- const auto& entry = *it;
- const auto path = targetPath.isEmpty() ? entry->name() : targetPath + "/" + entry->name();
- logMessage(DEBUG,
- " Copying entry '" + entry->name().toStdString() + "' to '" +
- path.toStdString() + "'");
- installTree->copy(entry, path, IFileTree::InsertPolicy::MERGE);
- }
- } else {
- logMessage(DEBUG,
- "Copying file '" + file.source + "' to '" + targetPath.toStdString() + "'");
- installTree->copy(sourceNode, targetPath, IFileTree::InsertPolicy::MERGE);
- }
- }
-
- // This file will be written by the InstallationManager later.
- const auto jsonFilePath = "fomod.json";
- installTree->addFile(QString::fromStdString(jsonFilePath), true);
- logMessage(DEBUG, "Added fomod.json placeholder file to install tree.");
-
- logMessage(DEBUG, "FileInstaller::install completed.");
- return installTree;
-}
-
-nlohmann::json FileInstaller::generateFomodJson() const
-{
- nlohmann::json fomodJson;
- logMessage(DEBUG, "Generating fomod.json representation for " + std::to_string(mSteps.size()) +
- " steps.");
-
- fomodJson["steps"] = nlohmann::json::array();
- for (const auto& stepViewModel : mSteps) {
- auto stepJson = nlohmann::json::object();
- stepJson["name"] = stepViewModel->getName();
- stepJson["groups"] = nlohmann::json::array();
- logMessage(DEBUG, "Serializing step '" + stepViewModel->getName() + "'");
-
- for (const auto& groupViewModel : stepViewModel->getGroups()) {
- auto groupJson = nlohmann::json::object();
- groupJson["name"] = groupViewModel->getName();
- auto pluginArray = nlohmann::json::array();
- auto deselectedArray = nlohmann::json::array();
- logMessage(DEBUG,
- " Serializing group '" + groupViewModel->getName() +
- "' with " + std::to_string(groupViewModel->getPlugins().size()) + " plugins");
-
- for (const auto& pluginViewModel : groupViewModel->getPlugins()) {
- logMessage(DEBUG,
- " Plugin '" + pluginViewModel->getName() + "' selected=" +
- (pluginViewModel->isSelected() ? "true" : "false") + ", manually-set=" +
- (pluginViewModel->wasManuallySet() ? "true" : "false"));
- if (pluginViewModel->isSelected()) {
- pluginArray.emplace_back(pluginViewModel->getName());
- }
- // Add deselected plugins here. (TODO: This will be replaced with an embedded db.)
- if (!pluginViewModel->isSelected() && pluginViewModel->wasManuallySet()) {
- deselectedArray.emplace_back(pluginViewModel->getName());
- }
- }
- groupJson["plugins"] = pluginArray;
- groupJson["deselected"] = deselectedArray;
- stepJson["groups"].emplace_back(groupJson);
- }
- fomodJson["steps"].emplace_back(stepJson);
- }
- return fomodJson;
-}
-
-std::string FileInstaller::getQualifiedFilePath(const std::string& treePath) const
-{
- // We need to prepend the fomod path to whatever source we reference. Guess we're passing that path around.
- const auto qualifiedPath = mFomodPath.toStdString() + "/" + treePath;
- logMessage(DEBUG, "Qualifying tree path '" + treePath + "' to '" + qualifiedPath + "'");
- return qualifiedPath;
-}
-
-std::vector<std::string> FileInstaller::collectPositiveFileNamesFromDependencyPatterns(
- const std::vector<DependencyPattern>& patterns)
-{
- std::vector<std::string> usableFileDependencyPluginNames = {};
- logMessage(DEBUG,
- "Collecting positive file names from " + std::to_string(patterns.size()) + " patterns.");
-
- for (const auto& pattern : patterns) {
- if (pattern.type == PluginTypeEnum::NotUsable) {
- logMessage(DEBUG, "Skipping pattern marked as NotUsable.");
- continue;
- }
-
- if (pattern.dependencies.fileDependencies.empty() && pattern.dependencies.nestedDependencies.empty()) {
- logMessage(DEBUG, "Skipping pattern with no dependencies.");
- continue;
- }
-
- const auto fileDependencies = pattern.dependencies.fileDependencies;
- const auto nestedDependencies = pattern.dependencies.nestedDependencies;
-
- for (const auto& fileDependency : fileDependencies) {
- if (fileDependency.state != FileDependencyTypeEnum::Active) {
- continue;
- }
- usableFileDependencyPluginNames.emplace_back(fileDependency.file);
- logMessage(DEBUG, "Adding active file dependency: " + fileDependency.file);
- }
-
- for (const auto& nestedDependency : nestedDependencies) {
- for (const auto& fileDependency : nestedDependency.fileDependencies) {
- if (fileDependency.state != FileDependencyTypeEnum::Active) {
- continue;
- }
- usableFileDependencyPluginNames.emplace_back(fileDependency.file);
- logMessage(DEBUG, "Adding nested active file dependency: " + fileDependency.file);
- }
- }
- // Not handling twice-nested dependencies now. IDK if that's even feasible.
- }
-
- logMessage(DEBUG,
- "CollectPositiveFileNamesFromDependencyPatterns found " +
- std::to_string(usableFileDependencyPluginNames.size()) + " entries.");
- return usableFileDependencyPluginNames;
-}
-
-// Generic vector appender
-void FileInstaller::addFiles(std::vector<File>& main, std::vector<File> toAdd) const
-{
- for (const auto& add : toAdd) {
- logMessage(INFO, "Adding file with source: " + add.source);
- }
- main.insert(main.end(), toAdd.begin(), toAdd.end());
- logMessage(DEBUG,
- "addFiles merged " + std::to_string(toAdd.size()) + " files. Total is now " +
- std::to_string(main.size()));
-}
-
-// TODO: Unclear if we're copying. oh well.
-// TODO: Rebuild flagmap and step indeces before installing
-std::vector<File> FileInstaller::collectFilesToInstall() const
-{
- logMessage(DEBUG, "collectFilesToInstall started.");
- std::vector<File> allFiles;
-
- // Required files from FOMOD
- const FileList requiredInstallFiles = mFomodFile->requiredInstallFiles;
- logMessage(DEBUG,
- "Adding " + std::to_string(requiredInstallFiles.files.size()) +
- " required install files from fomod.");
- addFiles(allFiles, requiredInstallFiles.files);
-
- // Selected files from visible steps
- for (const auto& stepViewModel : mSteps) {
- if (!mConditionTester.testCompositeDependency(mFlagMap, stepViewModel->getVisibilityConditions())) {
- logMessage(DEBUG,
- "Skipping invisible step '" + stepViewModel->getName() + "'");
- continue;
- }
- logMessage(DEBUG,
- "Processing visible step '" + stepViewModel->getName() + "' with " +
- std::to_string(stepViewModel->getGroups().size()) + " groups.");
- for (const auto& groupViewModel : stepViewModel->getGroups()) {
- logMessage(DEBUG,
- " Processing group '" + groupViewModel->getName() + "' with " +
- std::to_string(groupViewModel->getPlugins().size()) + " plugins.");
- for (const auto& pluginViewModel : groupViewModel->getPlugins()) {
- if (pluginViewModel->isSelected()) {
- logMessage(DEBUG,
- " Adding selected plugin '" + pluginViewModel->getName() +
- "' with " +
- std::to_string(pluginViewModel->getPlugin()->files.files.size()) +
- " files.");
- addFiles(allFiles, pluginViewModel->getPlugin()->files.files);
- } else {
- logMessage(DEBUG,
- " Skipping plugin '" + pluginViewModel->getName() +
- "' (not selected).");
- }
- }
- }
- }
-
- // ConditionalInstall files
- for (const auto conditionals = mFomodFile->conditionalFileInstalls; const auto& pattern : conditionals.patterns) {
- //<folder source="CR\Dagi-Raht LL\VLrn_Custom Race - Dagi-Raht LL" destination="" priority="2" />
-
- if (mConditionTester.testCompositeDependency(mFlagMap, pattern.dependencies)) {
- // also check if the plugins setting these flags are visible. at least one
-
- addFiles(allFiles, pattern.files.files);
- logMessage(DEBUG,
- "Conditional install pattern matched; added " +
- std::to_string(pattern.files.files.size()) + " files.");
- } else {
- logMessage(DEBUG, "Conditional install pattern did not match.");
- }
- }
-
- // Files will all have a default priority of 0 if not specified, so the order should also be informed by the
- // order they appear within XML. That's why we put conditionalFileInstalls after.
- logMessage(DEBUG, "Sorting " + std::to_string(allFiles.size()) + " files by priority.");
- std::ranges::sort(allFiles, [](const auto& a, const auto& b) {
- return a.priority < b.priority;
- });
-
- for (const auto& toInstall : allFiles) {
- logMessage(DEBUG, "File to install: " + toInstall.source);
- }
- logMessage(DEBUG,
- "collectFilesToInstall completed with " + std::to_string(allFiles.size()) + " files.");
-
- return allFiles;
-}
diff --git a/libs/installer_fomod_plus/installer/lib/FileInstaller.h b/libs/installer_fomod_plus/installer/lib/FileInstaller.h
deleted file mode 100644
index 1c122a7..0000000
--- a/libs/installer_fomod_plus/installer/lib/FileInstaller.h
+++ /dev/null
@@ -1,94 +0,0 @@
-#pragma once
-
-#include <ifiletree.h>
-#include <nlohmann/json.hpp>
-
-#include "ConditionTester.h"
-#include "FlagMap.h"
-#include "Logger.h"
-#include "xml/ModuleConfiguration.h"
-
-
-/* This is what legacy fomodInstaller does:
- modName.update(dialog.getName(), GUESS_USER);
- return dialog.updateTree(tree);
-
- This is a link to the legacy updateTree method:
- https://github.com/ModOrganizer2/modorganizer-installer_fomod/blob/fc263f2d923c704b4853c11ed4f8b8cf3920f30d/src/fomodinstallerdialog.cpp#L546
-
-*/
-
-// Things this should do:
-// - Copy all requiredInstall files
-// - Copy all conditionalInstall files
-// - Copy all selected files from steps that were visible to the user at the point of install
-
-using namespace MOBase;
-
-using FileGlobalIndex = int;
-using FileDescriptor = std::pair<File, FileGlobalIndex>;
-
-class StepViewModel;
-
-class FileInstaller {
-public:
- FileInstaller(
- IOrganizer* organizer,
- QString fomodPath,
- const std::shared_ptr<IFileTree>& fileTree,
- std::unique_ptr<ModuleConfiguration> fomodFile,
- const std::shared_ptr<FlagMap>& flagMap,
- const std::vector<std::shared_ptr<StepViewModel> >& steps);
-
- std::shared_ptr<IFileTree> install() const;
-
- /**
- * @brief Create a 'fomod.json' file to add to the base of the installTree. Functionally similar to MO2's meta.ini.
- *
- * Until there's more utility in the JSON structure itself, it will simply be of this format:
- * @code
- * {
- * "steps": [
- * {
- * "name": "Step 1",
- * "groups": [
- * {
- * "name": "Group 1",
- * "plugins: [
- * "Plugin1.esp",
- * "Plugin2.esp"
- * ]
- * }
- * ]
- * }
- * ]
- * }
- * @endcode
- *
- * @return nhlohmann::json
- */
- nlohmann::json generateFomodJson() const;
-
- std::string getQualifiedFilePath(const std::string& treePath) const;
-
- std::vector<std::string> collectPositiveFileNamesFromDependencyPatterns(const std::vector<DependencyPattern> &patterns);
-
- void addFiles(std::vector<File>& main, std::vector<File> toAdd) const;
-
-private:
- IOrganizer* mOrganizer;
- Logger& log = Logger::getInstance();
- QString mFomodPath;
- std::shared_ptr<IFileTree> mFileTree;
- std::unique_ptr<ModuleConfiguration> mFomodFile;
- std::shared_ptr<FlagMap> mFlagMap;
- ConditionTester mConditionTester;
- std::vector<std::shared_ptr<StepViewModel> > mSteps; // TODO: Maybe this is nasty. Idk.
-
- std::vector<File> collectFilesToInstall() const;
-
- void logMessage(LogLevel level, const std::string& message) const
- {
- log.logMessage(level, "[INSTALLER] " + message);
- }
-};
diff --git a/libs/installer_fomod_plus/installer/lib/FlagMap.h b/libs/installer_fomod_plus/installer/lib/FlagMap.h
deleted file mode 100644
index 4959cec..0000000
--- a/libs/installer_fomod_plus/installer/lib/FlagMap.h
+++ /dev/null
@@ -1,113 +0,0 @@
-#pragma once
-
-#include "ViewModels.h"
-#include "stringutil.h"
-
-#include <ranges>
-#include <string>
-#include <unordered_map>
-
-using Flag = std::pair<std::string, std::string>;
-using FlagList = std::vector<Flag>;
-
-class FlagMap {
-public:
-
- [[nodiscard]] std::vector<std::shared_ptr<PluginViewModel>> getPluginsSettingFlag(const std::string& key, const std::string& value) const
- {
- std::vector<std::shared_ptr<PluginViewModel>> result;
- for (const auto& [plugin, theseFlags] : flags) {
- for (const auto& [fst, snd] : theseFlags) {
- if (toLower(fst) == toLower(key) && snd == value) {
- result.emplace_back(plugin);
- }
- }
- }
- return result;
- }
-
- /**
- *
- * @param key The flag key
- * @return A list of flags currently set in this map with the given key. The list is ordered by step descending, then plugin ascending.
- * So if steps 1, 2, and 3 set flag X in their first two plugins, it'll be ordered [3:1, 3:2, 2:1, 2:2, 1:2, 1:1]
- */
- [[nodiscard]] FlagList getFlagsByKey(const std::string& key) const
- {
- FlagList result;
- std::vector<std::pair<int, std::shared_ptr<PluginViewModel>>> orderedPlugins;
-
- // Collect all plugins with their stepIndex and ownIndex
- for (const auto& plugin : flags | std::views::keys) {
- orderedPlugins.emplace_back(plugin->getStepIndex(), plugin);
- }
-
- // Sort plugins by stepIndex and ownIndex, stepIndex descending and ownIndex ascending
- // TODO: Might need group sorting too. How can I just do the natural order??
- std::ranges::sort(orderedPlugins, [](const auto& a, const auto& b) {
- return a.first > b.first || (a.first == b.first && a.second->getOwnIndex() < b.second->getOwnIndex());
- });
-
- // Collect flags in the sorted order
- for (const auto& plugin : orderedPlugins | std::views::values) {
- for (const auto& flag : flags.at(plugin)) {
- if (toLower(flag.first) == toLower(key)) {
- result.emplace_back(flag);
- }
- }
- }
- return result;
- }
-
- void setFlagsForPlugin(PluginRef plugin)
- {
- // Don't clutter the map with empty key-vals
- if (plugin->getConditionFlags().empty()) {
- return;
- }
- unsetFlagsForPlugin(plugin);
-
- FlagList flagList;
- for (const auto& conditionFlag : plugin->getConditionFlags()) {
- flagList.emplace_back(toLower(conditionFlag.name), conditionFlag.value);
- }
- flags[plugin] = flagList;
- }
-
- void unsetFlagsForPlugin(PluginRef plugin)
- {
- if (flags.contains(plugin)) {
- flags.erase(plugin);
- }
- }
-
- std::string toString()
- {
- auto result = std::string();
- result += "FlagMap:\n";
-
- for (const auto& [plugin, theseFlags] : flags) {
- result += plugin->getName() + " [";
- for (const auto& [fst, snd] : theseFlags) {
- result += fst + ": " + snd + ", ";
- }
- result.erase(result.size() - 2);
- result += "]\n";
- }
- return result;
- }
-
- void clearAll()
- {
- flags.clear();
- }
-
- [[nodiscard]] size_t getFlagCount() const
- {
- return flags.size();
- }
-
-
-private:
- std::unordered_map<std::shared_ptr<PluginViewModel>, FlagList> flags;
-};
diff --git a/libs/installer_fomod_plus/installer/lib/Logger.h b/libs/installer_fomod_plus/installer/lib/Logger.h
deleted file mode 100644
index 53fb7d9..0000000
--- a/libs/installer_fomod_plus/installer/lib/Logger.h
+++ /dev/null
@@ -1,116 +0,0 @@
-#pragma once
-#include <fstream>
-#include <iostream>
-#include <mutex>
-#include <regex>
-
-
-// Log levels
-enum LogLevel {
- DEBUG = 0,
- INFO = 1,
- WARN = 2,
- ERR = 3
-};
-
-// Convert LogLevel to string
-inline const char* logLevelToString(const LogLevel level) {
- switch (level) {
- case DEBUG: return "DEBUG";
- case INFO: return "INFO";
- case WARN: return "WARN";
- case ERR: return "ERROR";
- default: return "UNKNOWN";
- }
-}
-
-
-class Logger {
-public:
- static Logger& getInstance()
- {
- static Logger instance;
- return instance;
- }
-
- void setLogFilePath(const std::string& filePath)
- {
- std::lock_guard lock(mMutex);
- if (mLogFile.is_open()) {
- mLogFile.close();
- }
- mLogFile.open(filePath, std::ios::out);
- // std::ios::app is an option for appending but dont wanna grow it forever.
- }
-
- void setDebugMode(bool debug)
- {
- std::lock_guard lock(mMutex);
- mDebugMode = debug;
- }
-
- void logMessage(const LogLevel level, const std::string& message)
- {
-
-#if defined(__GNUC__) || defined(__clang__)
- std::string functionName = __PRETTY_FUNCTION__;
-#elif defined(_MSC_VER)
- std::string functionName = __FUNCSIG__;
-#else
- std::string functionName = "UnknownFunction";
-#endif
-
- std::regex classNameRegex(R"((\w+)::\w+\()");
- std::smatch match;
- std::string className = "UnknownClass";
-
- if (std::regex_search(functionName, match, classNameRegex) && match.size() > 1) {
- className = match.str(1);
- }
-
- std::lock_guard lock(mMutex);
-
- auto writeLog = [&](std::ostream& stream) {
- switch (level) {
- case DEBUG:
- stream << "[DEBUG] " << message << std::endl;
- break;
- case INFO:
- stream << "[INFO] " << message << std::endl;
- break;
- case WARN:
- stream << "[WARN] " << message << std::endl;
- break;
- case ERR:
- stream << "[ERROR] " << message << std::endl;
- break;
- }
- };
-
- if (mLogFile.is_open()) {
- writeLog(mLogFile);
- }
- }
-
- Logger& operator=(const Logger&) = delete;
-
-private:
- Logger() = default;
-
- ~Logger()
- {
- if (mLogFile.is_open()) {
- mLogFile.close();
- }
- }
-
- Logger(const Logger&) = delete;
-
- std::ofstream mLogFile;
- std::mutex mMutex;
-#if !defined(NDEBUG) || defined(CMAKE_BUILD_TYPE_RELWITHDEBINFO)
- bool mDebugMode = true; // Auto-enable in debug/RelWithDebInfo builds
-#else
- bool mDebugMode = false; // Disable in release builds
-#endif
-}; \ No newline at end of file
diff --git a/libs/installer_fomod_plus/installer/lib/ViewModels.h b/libs/installer_fomod_plus/installer/lib/ViewModels.h
deleted file mode 100644
index 062445a..0000000
--- a/libs/installer_fomod_plus/installer/lib/ViewModels.h
+++ /dev/null
@@ -1,120 +0,0 @@
-#pragma once
-
-#include <memory>
-#include <vector>
-
-#include "xml/ModuleConfiguration.h"
-
-
-template <typename T>
-using shared_ptr_list = std::vector<std::shared_ptr<T> >;
-
-
-/*
---------------------------------------------------------------------------------
- Plugins
---------------------------------------------------------------------------------
-*/
-class PluginViewModel {
-public:
- PluginViewModel(const std::shared_ptr<Plugin>& plugin_, const bool selected, bool, const int index)
- : ownIndex(index), selected(selected), enabled(true), manuallySet(false), plugin(plugin_) {}
-
- void setSelected(const bool selected) { this->selected = selected; }
- void setEnabled(const bool enabled) { this->enabled = enabled; }
- [[nodiscard]] std::string getName() const { return plugin ? plugin->name : std::string(); }
- [[nodiscard]] std::string getDescription() const { return plugin ? plugin->description : std::string(); }
- [[nodiscard]] std::string getImagePath() const { return plugin ? plugin->image.path : std::string(); }
- [[nodiscard]] bool isSelected() const { return selected; }
- [[nodiscard]] bool isEnabled() const { return enabled; }
- [[nodiscard]] int getOwnIndex() const { return ownIndex; }
- [[nodiscard]] std::vector<ConditionFlag> getConditionFlags() const { return plugin->conditionFlags.flags; }
- [[nodiscard]] PluginTypeEnum getCurrentPluginType() const { return currentPluginType; }
- [[nodiscard]] bool wasManuallySet() const { return manuallySet; }
-
- void setCurrentPluginType(const PluginTypeEnum type) { currentPluginType = type; }
- void setStepIndex(const int stepIndex) { this->stepIndex = stepIndex; }
- void setGroupIndex(const int groupIndex) { this->groupIndex = groupIndex; }
-
- [[nodiscard]] int getStepIndex() const { return stepIndex; }
- [[nodiscard]] int getGroupIndex() const { return groupIndex; }
-
- friend class FomodViewModel;
- friend class FileInstaller;
- friend class ConditionTester;
-
-protected:
- [[nodiscard]] std::shared_ptr<Plugin> getPlugin() const { return plugin; }
-
-private:
- int ownIndex;
- bool selected;
- bool enabled;
- bool manuallySet;
- PluginTypeEnum currentPluginType = PluginTypeEnum::UNKNOWN;
- std::shared_ptr<Plugin> plugin;
-
- int stepIndex{ -1 };
- int groupIndex{ -1 };
-};
-
-/*
---------------------------------------------------------------------------------
- Groups
---------------------------------------------------------------------------------
-*/
-class GroupViewModel {
-public:
- GroupViewModel(const std::shared_ptr<Group>& group_, const shared_ptr_list<PluginViewModel>& plugins,
- const int index, const int stepIndex)
- : plugins(plugins), group(group_), ownIndex(index), stepIndex(stepIndex) {}
-
- void addPlugin(const std::shared_ptr<PluginViewModel>& plugin) { plugins.emplace_back(plugin); }
-
- [[nodiscard]] std::string getName() const { return group->name; }
- [[nodiscard]] GroupTypeEnum getType() const { return group->type; }
- [[nodiscard]] const shared_ptr_list<PluginViewModel>& getPlugins() const { return plugins; }
- [[nodiscard]] int getOwnIndex() const { return ownIndex; }
- [[nodiscard]] int getStepIndex() const { return stepIndex; }
-
-private:
- shared_ptr_list<PluginViewModel> plugins;
- std::shared_ptr<Group> group;
- int ownIndex;
- int stepIndex;
-};
-
-/*
---------------------------------------------------------------------------------
- Steps
---------------------------------------------------------------------------------
-*/
-class StepViewModel {
-public:
- StepViewModel(const std::shared_ptr<InstallStep>& installStep_, const shared_ptr_list<GroupViewModel>& groups,
- const int index)
- : installStep(installStep_), groups(groups), ownIndex(index) {}
-
- [[nodiscard]] CompositeDependency& getVisibilityConditions() const { return installStep->visible; }
- [[nodiscard]] std::string getName() const { return installStep->name; }
- [[nodiscard]] const shared_ptr_list<GroupViewModel>& getGroups() const { return groups; }
- [[nodiscard]] int getOwnIndex() const { return ownIndex; }
- [[nodiscard]] bool getHasVisited() const { return visited; }
- void setVisited(const bool visited) { this->visited = visited; }
-
-private:
- bool visited{ false };
- std::shared_ptr<InstallStep> installStep;
- shared_ptr_list<GroupViewModel> groups;
- int ownIndex;
-};
-
-
-/*
---------------------------------------------------------------------------------
- Outbound Types
---------------------------------------------------------------------------------
-*/
-using StepRef = const std::shared_ptr<StepViewModel>&;
-using GroupRef = const std::shared_ptr<GroupViewModel>&;
-using PluginRef = const std::shared_ptr<PluginViewModel>&;