#ifndef ORGANIZERCORE_H #define ORGANIZERCORE_H #include "selfupdater.h" #include "settings.h" #include "modlist.h" #include "modinfo.h" #include "pluginlist.h" #include "installationmanager.h" #include "downloadmanager.h" #include "executableslist.h" #include "usvfsconnector.h" #include "moshortcut.h" #include "processrunner.h" #include "uilocker.h" #include #include #include #include #include #include "executableinfo.h" #include "moddatacontent.h" #include #include #include #include #include #include #include #include #include #include class ModListSortProxy; class PluginListSortProxy; class Profile; class IUserInterface; class PluginContainer; class DirectoryRefresher; namespace MOBase { template class GuessedValue; class IModInterface; class IPluginGame; } namespace MOShared { class DirectoryEntry; } class OrganizerCore : public QObject, public MOBase::IPluginDiagnose { Q_OBJECT Q_INTERFACES(MOBase::IPluginDiagnose) private: struct SignalCombinerAnd { typedef bool result_type; template bool operator()(InputIterator first, InputIterator last) const { while (first != last) { if (!(*first)) { return false; } ++first; } return true; } }; private: typedef boost::signals2::signal SignalAboutToRunApplication; typedef boost::signals2::signal SignalFinishedRunApplication; typedef boost::signals2::signal SignalModInstalled; public: /** * Small holder for the game content returned by the ModDataContent feature (the * list of all possible contents, not the per-mod content). */ struct ModDataContentHolder { using Content = ModDataContent::Content; /** * @return true if the hold list of contents is empty, false otherwise. */ bool empty() const { return m_Contents.empty(); } /** * @param id ID of the content to retrieve. * * @return the content with the given ID, or a null pointer if it is not found. */ const Content* findById(int id) const { auto it = std::find_if(std::begin(m_Contents), std::end(m_Contents), [&id](auto const& content) { return content.id() == id; }); return it == std::end(m_Contents) ? nullptr : &(*it); } /** * Apply the given function to each content whose ID is in the given set. * * @param ids The set of content IDs. * @param fn The function to apply. * @param includeFilter true to also apply the function to filter-only contents, false otherwise. */ template void forEachContentIn(std::set const& ids, Fn const& fn, bool includeFilter = false) const { for (const auto& content : m_Contents) { if ((includeFilter || !content.isOnlyForFilter()) && ids.find(content.id()) != ids.end()) { fn(content); } } } /** * @brief Apply fnIn to each content whose ID is in the given set, and fnOut to each content not in the * given set, excluding filter-only content (from both cases) unless includeFilter is true. * * @param ids The set of content IDs. * @param fnIn Function to apply to content whose IDs are in ids. * @param fnOut Function to apply to content whose IDs are not in ids. * @param includeFilter true to also apply the function to filter-only contents, false otherwise. */ template void forEachContentInOrOut(std::set const& ids, FnIn const& fnIn, FnOut const& fnOut, bool includeFilter = false) const { for (const auto& content : m_Contents) { if ((includeFilter || !content.isOnlyForFilter())) { if (ids.find(content.id()) != ids.end()) { fnIn(content); } else { fnOut(content); } } } } /** * Apply the given function to each content. * * @param fn The function to apply. * @param includeFilter true to also apply the function to filter-only contents, false otherwise. */ template void forEachContent(Fn const& fn, bool includeFilter = false) const { for (const auto& content : m_Contents) { if (includeFilter || !content.isOnlyForFilter()) { fn(content); } } } ModDataContentHolder& operator=(ModDataContentHolder const&) = delete; ModDataContentHolder& operator=(ModDataContentHolder&&) = default; private: std::vector m_Contents; /** * @brief Construct a ModDataContentHolder without any contents (e.g., if the feature is * missing). */ ModDataContentHolder() { } /** * @brief Construct a ModDataContentHold holding the given list of contents. */ ModDataContentHolder(std::vector contents) : m_Contents(std::move(contents)) { } friend class OrganizerCore; }; public: static bool isNxmLink(const QString &link) { return link.startsWith("nxm://", Qt::CaseInsensitive); } OrganizerCore(Settings &settings); ~OrganizerCore(); void setUserInterface(IUserInterface* ui); void connectPlugins(PluginContainer *container); void disconnectPlugins(); void setManagedGame(MOBase::IPluginGame *game); void updateExecutablesList(); void updateModInfoFromDisc(); void checkForUpdates(); void startMOUpdate(); Settings &settings(); SelfUpdater *updater() { return &m_Updater; } InstallationManager *installationManager(); MOShared::DirectoryEntry *directoryStructure() { return m_DirectoryStructure; } DirectoryRefresher *directoryRefresher() { return m_DirectoryRefresher.get(); } ExecutablesList *executablesList() { return &m_ExecutablesList; } void setExecutablesList(const ExecutablesList &executablesList) { m_ExecutablesList = executablesList; } Profile *currentProfile() const { return m_CurrentProfile; } void setCurrentProfile(const QString &profileName); std::vector enabledArchives(); MOBase::VersionInfo getVersion() const { return m_Updater.getVersion(); } ModListSortProxy *createModListProxyModel(); PluginListSortProxy *createPluginListProxyModel(); MOBase::IPluginGame const *managedGame() const; /** * @return the list of contents for the currently managed game, or an empty vector * if the game plugin does not implement the ModDataContent feature. */ const ModDataContentHolder& modDataContents() const { return m_Contents; } bool isArchivesInit() const { return m_ArchivesInit; } bool saveCurrentLists(); ProcessRunner processRunner(); bool beforeRun( const QFileInfo& binary, const QString& profileName, const QString& customOverwrite, const QList& forcedLibraries); void afterRun(const QFileInfo& binary, DWORD exitCode); ProcessRunner::Results waitForAllUSVFSProcesses( UILocker::Reasons reason=UILocker::PreventExit); void refreshESPList(bool force = false); void refreshBSAList(); void refreshDirectoryStructure(); void updateModInDirectoryStructure(unsigned int index, ModInfo::Ptr modInfo); void updateModsInDirectoryStructure(QMap modInfos); void doAfterLogin(const std::function &function) { m_PostLoginTasks.append(function); } void loggedInAction(QWidget* parent, std::function f); bool previewFileWithAlternatives(QWidget* parent, QString filename, int selectedOrigin=-1); bool previewFile(QWidget* parent, const QString& originName, const QString& path); void loginSuccessfulUpdate(bool necessary); void loginFailedUpdate(const QString &message); static bool createAndMakeWritable(const QString &path); bool checkPathSymlinks(); bool bootstrap(); void createDefaultProfile(); MOBase::DelayedFileWriter &pluginsWriter() { return m_PluginListsWriter; } void prepareVFS(); void updateVFSParams( MOBase::log::Levels logLevel, CrashDumpsType crashDumpsType, const QString& crashDumpsPath, std::chrono::seconds spawnDelay, QString executableBlacklist); void setLogLevel(MOBase::log::Levels level); bool cycleDiagnostics(); static CrashDumpsType getGlobalCrashDumpsType() { return m_globalCrashDumpsType; } static void setGlobalCrashDumpsType(CrashDumpsType crashDumpsType); static std::wstring crashDumpsPath(); public: MOBase::IModRepositoryBridge *createNexusBridge() const; QString profileName() const; QString profilePath() const; QString downloadsPath() const; QString overwritePath() const; QString basePath() const; QString modsPath() const; MOBase::VersionInfo appVersion() const; MOBase::IModInterface *getMod(const QString &name) const; MOBase::IPluginGame *getGame(const QString &gameName) const; MOBase::IModInterface *createMod(MOBase::GuessedValue &name); bool removeMod(MOBase::IModInterface *mod); void modDataChanged(MOBase::IModInterface *mod); QVariant pluginSetting(const QString &pluginName, const QString &key) const; void setPluginSetting(const QString &pluginName, const QString &key, const QVariant &value); QVariant persistent(const QString &pluginName, const QString &key, const QVariant &def) const; void setPersistent(const QString &pluginName, const QString &key, const QVariant &value, bool sync); QString pluginDataPath() const; virtual MOBase::IModInterface *installMod(const QString &fileName, const QString &initModName); QString resolvePath(const QString &fileName) const; QStringList listDirectories(const QString &directoryName) const; QStringList findFiles(const QString &path, const std::function &filter) const; QStringList getFileOrigins(const QString &fileName) const; QList findFileInfos(const QString &path, const std::function &filter) const; DownloadManager *downloadManager(); PluginList *pluginList(); ModList *modList(); bool onModInstalled(const std::function &func); bool onAboutToRun(const std::function &func); bool onFinishedRun(const std::function &func); void refreshModList(bool saveChanges = true); QStringList modsSortedByProfilePriority() const; bool getArchiveParsing() const { return m_ArchiveParsing; } void setArchiveParsing(bool archiveParsing) { m_ArchiveParsing = archiveParsing; } public: // IPluginDiagnose interface virtual std::vector activeProblems() const; virtual QString shortDescription(unsigned int key) const; virtual QString fullDescription(unsigned int key) const; virtual bool hasGuidedFix(unsigned int key) const; virtual void startGuidedFix(unsigned int key) const; public slots: void profileRefresh(); void externalMessage(const QString &message); void syncOverwrite(); void savePluginList(); void refreshLists(); void installDownload(int downloadIndex); void modStatusChanged(unsigned int index); void modStatusChanged(QList index); void requestDownload(const QUrl &url, QNetworkReply *reply); void downloadRequestedNXM(const QString &url); bool nexusApi(bool retry = false); signals: /** * @brief emitted after a mod has been installed * @node this is currently only used for tutorials */ void modInstalled(const QString &modName); void managedGameChanged(MOBase::IPluginGame const *gamePlugin); void close(); private: void saveCurrentProfile(); void storeSettings(); bool queryApi(QString &apiKey); void updateModActiveState(int index, bool active); void updateModsActiveState(const QList &modIndices, bool active); bool testForSteam(bool *found, bool *access); bool createDirectory(const QString &path); QString oldMO1HookDll() const; /** * @brief return a descriptor of the mappings real file->virtual file */ std::vector fileMapping(const QString &profile, const QString &customOverwrite); std::vector fileMapping(const QString &dataPath, const QString &relPath, const MOShared::DirectoryEntry *base, const MOShared::DirectoryEntry *directoryEntry, int createDestination); private slots: void directory_refreshed(); void downloadRequested(QNetworkReply *reply, QString gameName, int modID, const QString &fileName); void removeOrigin(const QString &name); void downloadSpeed(const QString &serverName, int bytesPerSecond); void loginSuccessful(bool necessary); void loginFailed(const QString &message); private: static const unsigned int PROBLEM_MO1SCRIPTEXTENDERWORKAROUND = 1; private: IUserInterface* m_UserInterface; PluginContainer *m_PluginContainer; QString m_GameName; MOBase::IPluginGame *m_GamePlugin; ModDataContentHolder m_Contents; Profile *m_CurrentProfile; Settings& m_Settings; SelfUpdater m_Updater; SignalAboutToRunApplication m_AboutToRun; SignalFinishedRunApplication m_FinishedRun; SignalModInstalled m_ModInstalled; ModList m_ModList; PluginList m_PluginList; QList> m_PostLoginTasks; QList> m_PostRefreshTasks; ExecutablesList m_ExecutablesList; QStringList m_PendingDownloads; QStringList m_DefaultArchives; QStringList m_ActiveArchives; std::unique_ptr m_DirectoryRefresher; MOShared::DirectoryEntry *m_DirectoryStructure; DownloadManager m_DownloadManager; InstallationManager m_InstallationManager; QThread m_RefresherThread; std::thread m_StructureDeleter; bool m_DirectoryUpdate; bool m_ArchivesInit; bool m_ArchiveParsing{ m_Settings.archiveParsing() }; MOBase::DelayedFileWriter m_PluginListsWriter; UsvfsConnector m_USVFS; UILocker m_UILocker; static CrashDumpsType m_globalCrashDumpsType; }; #endif // ORGANIZERCORE_H