From 817e8f5cd26739c69d930d21cd9dc4c0b6e4984e Mon Sep 17 00:00:00 2001 From: SulfurNitride Date: Sat, 14 Feb 2026 02:45:12 -0600 Subject: Add FUSE external mapping support, BG3/Oblivion Remastered fixes, fomod-plus and NaK integration FUSE VFS now deploys non-data-dir mod mappings (Paks, OBSE, UE4SS, etc.) via real symlinks and injects file-level data-dir mappings (plugins.txt, loadorder.txt) into the VFS tree. Fixes game launches for Oblivion Remastered (Root Builder path resolution, script extender support) and BG3 (Wine prefix documents directory, file mapper symlinks on Linux). Vendors mo2-fomod-plus plugin and NaK crate for FOMOD installer and game finder/runtime support. Co-Authored-By: Claude Opus 4.6 --- .../installer/ui/FomodViewModel.h | 154 +++++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 libs/installer_fomod_plus/installer/ui/FomodViewModel.h (limited to 'libs/installer_fomod_plus/installer/ui/FomodViewModel.h') diff --git a/libs/installer_fomod_plus/installer/ui/FomodViewModel.h b/libs/installer_fomod_plus/installer/ui/FomodViewModel.h new file mode 100644 index 0000000..f0ce070 --- /dev/null +++ b/libs/installer_fomod_plus/installer/ui/FomodViewModel.h @@ -0,0 +1,154 @@ +#pragma once + +#include +#include + +#include "lib/ConditionTester.h" +#include "lib/FlagMap.h" +#include "lib/FileInstaller.h" +#include "lib/ViewModels.h" +#include "xml/FomodInfoFile.h" + +/* +-------------------------------------------------------------------------------- + Info +-------------------------------------------------------------------------------- +*/ +class InfoViewModel { +public: + explicit InfoViewModel(const std::unique_ptr& infoFile) + { + if (infoFile) { + // Copy the necessary members from FomodInfoFile to InfoViewModel + mName = infoFile->getName(); + mVersion = infoFile->getVersion(); + mAuthor = infoFile->getAuthor(); + mWebsite = infoFile->getWebsite(); + } + } + + // Accessor methods + [[nodiscard]] std::string getName() const { return mName; } + [[nodiscard]] std::string getVersion() const { return mVersion; } + [[nodiscard]] std::string getAuthor() const { return mAuthor; } + [[nodiscard]] std::string getWebsite() const { return mWebsite; } + +private: + std::string mName; + std::string mVersion; + std::string mAuthor; + std::string mWebsite; +}; + +/* +-------------------------------------------------------------------------------- + View Model +-------------------------------------------------------------------------------- +*/ +class FomodViewModel { +public: + FomodViewModel( + MOBase::IOrganizer* organizer, + std::unique_ptr fomodFile, + std::unique_ptr infoFile); + + static std::shared_ptr create( + MOBase::IOrganizer* organizer, + std::unique_ptr fomodFile, + std::unique_ptr infoFile); + + void forEachGroup(const std::function& callback) const; + + void forEachPlugin(const std::function& callback) const; + + void forEachFuturePlugin(int fromStepIndex, const std::function& callback) const; + + void selectFromJson(nlohmann::json json) const; + + void resetToDefaults(); + + [[nodiscard]] std::shared_ptr getFirstPluginForActiveStep() const; + + // Steps + [[nodiscard]] shared_ptr_list getSteps() const { return mSteps; } + [[nodiscard]] StepRef getActiveStep() const { return mActiveStep; } + [[nodiscard]] int getCurrentStepIndex() const { return mCurrentStepIndex; } + [[deprecated]] void setCurrentStepIndex(const int index) { mCurrentStepIndex = index; } + + void updateVisibleSteps() const; + + void rebuildConditionFlagsForStep(int stepIndex) const; + + void preinstall(const std::shared_ptr& tree, const QString& fomodPath); + + std::shared_ptr getFileInstaller() { return mFileInstaller; } + + std::string getDisplayImage() const; + + // Plugins + [[nodiscard]] PluginRef getActivePlugin() const { return mActivePlugin; } + + // Info + [[nodiscard]] std::shared_ptr getInfoViewModel() const { return mInfoViewModel; } + + // Interactions + void stepBack(); + + void stepForward(); + + bool isLastVisibleStep() const; + + bool isFirstVisibleStep() const; + + bool togglePlugin(const GroupRef, const PluginRef, bool selected) const; + + bool ctrlTogglePlugin(const GroupRef, const PluginRef, bool selected) const; + + void setActivePlugin(const PluginRef plugin) const { mActivePlugin = plugin; } + + static void markManuallySet(PluginRef plugin); + +private: + Logger& log = Logger::getInstance(); + MOBase::IOrganizer* mOrganizer = nullptr; + std::unique_ptr mFomodFile; + std::unique_ptr mInfoFile; + std::shared_ptr mFlags{ nullptr }; + ConditionTester mConditionTester; + std::shared_ptr mInfoViewModel; + std::vector > mSteps; + mutable std::shared_ptr mActivePlugin{ nullptr }; + mutable std::shared_ptr mActiveStep{ nullptr }; + mutable std::vector mVisibleStepIndices; + std::shared_ptr mFileInstaller{ nullptr }; + bool mInitialized{ false }; + + void createStepViewModels(); + + void setFlagForPluginState(const std::shared_ptr& plugin) const; + + static void createNonePluginForGroup(const std::shared_ptr& group); + + void processPlugin(const std::shared_ptr& group, + const std::shared_ptr& plugin) const; + + void enforceRadioGroupConstraints(const std::shared_ptr& group) const; + + void enforceSelectAllConstraint(const std::shared_ptr& groupViewModel) const; + + void enforceSelectAtLeastOneConstraint(const std::shared_ptr& group) const; + + void enforceGroupConstraints() const; + + void processPluginConditions(int fromStepIndex) const; + + // Indices + int mCurrentStepIndex{ 0 }; + + void logMessage(const LogLevel level, const std::string& message) const + { + log.logMessage(level, "[VIEWMODEL] " + message); + } + + std::string toString() const; +}; -- cgit v1.3.1