/* 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 . */ #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include "installerfomod.h" class QAbstractButton; class QXmlStreamReader; namespace Ui { class FomodInstallerDialog; } class ValueCondition; class ConditionFlag; class SubCondition; class FileCondition; class VersionCondition; class XmlReader; class IConditionTester { public: virtual std::pair testCondition(int maxIndex, const ValueCondition* condition) const = 0; virtual std::pair testCondition(int maxIndex, const ConditionFlag* condition) const = 0; virtual std::pair testCondition(int maxIndex, const SubCondition* condition) const = 0; virtual std::pair testCondition(int maxIndex, const FileCondition* condition) const = 0; virtual std::pair testCondition(int maxIndex, const VersionCondition* condition) const = 0; }; enum ConditionOperator { OP_AND, OP_OR }; class Condition { public: Condition() {} virtual std::pair test(int maxIndex, const IConditionTester* tester) const = 0; private: Condition& operator=(const Condition&) = delete; }; class ConditionFlag : public Condition { public: ConditionFlag() : Condition(), m_Name(), m_Value() {} ConditionFlag(const QString& name, const QString& value) : Condition(), m_Name(name), m_Value(value) {} virtual std::pair test(int maxIndex, const IConditionTester* tester) const { return tester->testCondition(maxIndex, this); } QString m_Name; QString m_Value; }; Q_DECLARE_METATYPE(ConditionFlag) class ValueCondition : public Condition { public: ValueCondition() : Condition(), m_Name(), m_Value() {} ValueCondition(const QString& name, const QString& value) : Condition(), m_Name(name), m_Value(value) {} virtual std::pair test(int maxIndex, const IConditionTester* tester) const { return tester->testCondition(maxIndex, this); } QString m_Name; QString m_Value; }; Q_DECLARE_METATYPE(ValueCondition) class FileCondition : public Condition { public: FileCondition() : Condition(), m_File(), m_State() {} FileCondition(const QString& file, const QString& state) : Condition(), m_File(file), m_State(state) {} virtual std::pair test(int maxIndex, const IConditionTester* tester) const { return tester->testCondition(maxIndex, this); } QString m_File; QString m_State; }; Q_DECLARE_METATYPE(FileCondition) class SubCondition : public Condition { public: virtual std::pair test(int maxIndex, const IConditionTester* tester) const { return tester->testCondition(maxIndex, this); } ConditionOperator m_Operator; std::vector m_Conditions; }; Q_DECLARE_METATYPE(SubCondition) class VersionCondition : public Condition { public: enum Type { v_Game, v_FOMM, v_FOSE }; VersionCondition() : Condition(), m_Type(), m_RequiredVersion() {} VersionCondition(Type type, const QString& requiredVersion) : Condition(), m_Type(type), m_RequiredVersion(requiredVersion) {} virtual std::pair test(int maxIndex, const IConditionTester* tester) const { return tester->testCondition(maxIndex, this); } Type m_Type; QString m_RequiredVersion; }; Q_DECLARE_METATYPE(VersionCondition) class FileDescriptor : public QObject { Q_OBJECT public: FileDescriptor(QObject* parent) : QObject(parent), m_Source(), m_Destination(), m_Priority(0), m_IsFolder(false), m_AlwaysInstall(false), m_InstallIfUsable(false), m_FileSystemItemSequence(0) {} FileDescriptor(const FileDescriptor& reference) : QObject(reference.parent()), m_Source(reference.m_Source), m_Destination(reference.m_Destination), m_Priority(reference.m_Priority), m_IsFolder(reference.m_IsFolder), m_AlwaysInstall(reference.m_AlwaysInstall), m_InstallIfUsable(reference.m_InstallIfUsable), m_FileSystemItemSequence(reference.m_FileSystemItemSequence) {} QString m_Source; QString m_Destination; int m_Priority; bool m_IsFolder; bool m_AlwaysInstall; bool m_InstallIfUsable; int m_FileSystemItemSequence; private: FileDescriptor& operator=(const FileDescriptor&); }; Q_DECLARE_METATYPE(FileDescriptor*) class FomodInstallerDialog : public QDialog, public IConditionTester { Q_OBJECT public: explicit FomodInstallerDialog( InstallerFomod* installer, const MOBase::GuessedValue& modName, const QString& fomodPath, const QString& fomodDirName, const std::function& fileCheck, QWidget* parent = 0); ~FomodInstallerDialog(); void initData(MOBase::IOrganizer* moInfo); /** * @return bool true if the user requested the manual dialog **/ bool manualRequested() const { return m_Manual; } /** * @return the (user-modified) name to be used for the mod **/ QString getName() const; /** * @return the version of the mod as specified in the fomod info.xml */ QString getVersion() const; /** * @return the mod id as specified in the info.xml */ int getModID() const; /** * @return the mod url as specified in the fomod file */ QString getURL() const; /** * @brief Updated the archive tree from the dialog. * * @param tree The input archive tree. **/ MOBase::IPluginInstaller::EInstallResult updateTree(std::shared_ptr& tree); bool hasOptions(); void transformToSmallInstall(); protected: virtual bool eventFilter(QObject* object, QEvent* event); private slots: void on_cancelBtn_clicked(); void on_manualBtn_clicked(); void on_websiteLabel_linkActivated(const QString& link); void on_nextBtn_clicked(); void on_prevBtn_clicked(); // detect signals for people playing with checkboxes/buttons void widgetButtonClicked(); void on_screenshotExpand_clicked(); private: enum ItemOrder { ORDER_ASCENDING, ORDER_DESCENDING, ORDER_EXPLICIT }; // So I can make GroupType and PluginTypeInfo into QVariants public: enum GroupType { TYPE_SELECTATLEASTONE, TYPE_SELECTATMOSTONE, TYPE_SELECTEXACTLYONE, TYPE_SELECTANY, TYPE_SELECTALL }; enum PluginType { TYPE_REQUIRED, TYPE_RECOMMENDED, TYPE_OPTIONAL, TYPE_NOTUSABLE, TYPE_COULDBEUSABLE }; struct DependencyPattern { PluginType type; SubCondition condition; }; typedef std::vector DependencyPatternList; struct PluginTypeInfo { PluginType m_DefaultType; DependencyPatternList m_DependencyPatterns; }; private: typedef std::vector FileDescriptorList; typedef std::vector ConditionFlagList; struct Plugin { QString m_Name; QString m_Description; QString m_ImagePath; PluginTypeInfo m_PluginTypeInfo; ConditionFlagList m_ConditionFlags; FileDescriptorList m_Files; }; struct ConditionalInstall { SubCondition m_Condition; FileDescriptorList m_Files; }; struct LeafInfo { int priority; QString path; }; using Leaves = std::map; private: QString readContent(QXmlStreamReader& reader); /** * @brief Read XML from the given file, trying various encoding, and using * the given callback on each try. * * @param file The file to read, must already be opened. * @param callback The callback used for every encoding try. */ void readXml(QFile& file, void (FomodInstallerDialog::*callback)(XmlReader&)); void readInfoXml(); void readModuleConfigXml(); void parseInfo(XmlReader& data); void parseModuleConfig(XmlReader& data); void updateNameEdit(); static int bomOffset(const QByteArray& buffer); static ItemOrder getItemOrder(const QString& orderString); static GroupType getGroupType(const QString& typeString); static PluginType getPluginType(const QString& typeString); static bool byPriority(const FileDescriptor* LHS, const FileDescriptor* RHS); PluginType getPluginDependencyType(int page, PluginTypeInfo const& info) const; typedef void (FomodInstallerDialog::*TagProcessor)(XmlReader& reader); void processXmlTag(XmlReader& reader, char const* tag, TagProcessor func); void readFileList(XmlReader& reader, FileDescriptorList& fileList); void readDependencyPattern(XmlReader& reader, DependencyPattern& pattern); void readDependencyPatternList(XmlReader& reader, DependencyPatternList& patterns); void readDependencyPluginType(XmlReader& reader, PluginTypeInfo& info); void readPluginType(XmlReader& reader, Plugin& plugin); void readConditionFlagList(XmlReader& reader, ConditionFlagList& condflags); FomodInstallerDialog::Plugin readPlugin(XmlReader& reader); void readPluginList(XmlReader& reader, QString const& groupName, GroupType& groupType, QLayout* layout); void readGroup(XmlReader& reader, QLayout* layout); void readGroupList(XmlReader& reader, QLayout* layout); QGroupBox* readInstallStep(XmlReader& reader); void readCompositeDependency(XmlReader& reader, SubCondition& conditional); ConditionalInstall readConditionalInstallPattern(XmlReader& reader); void readConditionalFilePatternList(XmlReader& reader); void readConditionalFileInstallList(XmlReader& reader); void readStepList(XmlReader& reader); void readModuleConfiguration(XmlReader& reader); void highlightControl(QAbstractButton* button); std::pair testCondition(int maxIndex, const QString& flag, const QString& value) const; virtual std::pair testCondition(int maxIndex, const ValueCondition* condition) const; virtual std::pair testCondition(int maxIndex, const ConditionFlag* condition) const; virtual std::pair testCondition(int maxIndex, const SubCondition* condition) const; virtual std::pair testCondition(int maxIndex, const FileCondition* condition) const; virtual std::pair testCondition(int maxIndex, const VersionCondition* condition) const; bool testVisible(int pageIndex) const; bool nextPage(); void activateCurrentPage(); void moveTree(std::shared_ptr target, std::shared_ptr source, MOBase::IFileTree::OverwritesType& overwrites); void copyLeaf(std::shared_ptr sourceEntry, std::shared_ptr destinationTree, QString destinationPath, MOBase::IFileTree::OverwritesType& overwrites, Leaves& leaves, int pri); bool copyFileIterator(std::shared_ptr sourceTree, std::shared_ptr destinationTree, const FileDescriptor* descriptor, Leaves& leaves, MOBase::IFileTree::OverwritesType& overwrites); static void applyPriority(Leaves& leaves, MOBase::IFileTree const* tree, int priority); /** * @brief Display a dialog indicating to the user that some files were not found. * * @param missingFiles List of missing files. * * @return true if the user chose to continue with the installation, false otherwize. */ bool displayMissingFilesDialog(std::vector missingFiles); static QString toString(MOBase::IPluginList::PluginStates state); // Set the 'next' button to display 'next' or 'install' void updateNextbtnText(); // Display the current page calculating all the button enables/disables void displayCurrentPage(); private: Ui::FomodInstallerDialog* ui; InstallerFomod* m_Installer; MOBase::GuessedValue m_ModName; int m_ModID; QString m_FomodPath; QString m_FomodDirName; bool m_Manual; FileDescriptorList m_RequiredFiles; std::vector m_ConditionalInstalls; std::vector m_PageVisible; std::function m_FileCheck; // Because NMM maintains the sequence from the xml when dealing with things with // the same priority, we have to as well. This is moderately hacky. int m_FileSystemItemSequence; // So I can find out game info (I hope) MOBase::IOrganizer* m_MoInfo; // The web page in the fomod (if supplied) QString m_URL; }; Q_DECLARE_METATYPE(FomodInstallerDialog::GroupType) Q_DECLARE_METATYPE(FomodInstallerDialog::PluginTypeInfo)