From cd2b7683b78503d4fef1946de088108b812b387c Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Fri, 1 May 2020 17:31:37 +0200 Subject: Avoid using GamePlugins when not provided by the game plugin. --- src/directoryrefresher.cpp | 13 +++++++++---- src/pluginlist.cpp | 17 ++++++++++++----- 2 files changed, 21 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/directoryrefresher.cpp b/src/directoryrefresher.cpp index 0369b1b0..383e6b33 100644 --- a/src/directoryrefresher.cpp +++ b/src/directoryrefresher.cpp @@ -216,9 +216,12 @@ void DirectoryRefresher::addModBSAToStructure( { const IPluginGame *game = qApp->property("managed_game").value(); - GamePlugins *gamePlugins = game->feature(); QStringList loadOrder = QStringList(); - gamePlugins->getLoadOrder(loadOrder); + + GamePlugins* gamePlugins = game->feature(); + if (gamePlugins) { + gamePlugins->getLoadOrder(loadOrder); + } std::vector lo; for (auto&& s : loadOrder) { @@ -364,9 +367,11 @@ struct ModThread if (Settings::instance().archiveParsing()) { const IPluginGame *game = qApp->property("managed_game").value(); - GamePlugins *gamePlugins = game->feature(); QStringList loadOrder = QStringList(); - gamePlugins->getLoadOrder(loadOrder); + GamePlugins* gamePlugins = game->feature(); + if (gamePlugins) { + gamePlugins->getLoadOrder(loadOrder); + } std::vector lo; for (auto&& s : loadOrder) { diff --git a/src/pluginlist.cpp b/src/pluginlist.cpp index 1f320982..c2667557 100644 --- a/src/pluginlist.cpp +++ b/src/pluginlist.cpp @@ -185,7 +185,8 @@ void PluginList::refresh(const QString &profileName ChangeBracket layoutChange(this); QStringList primaryPlugins = m_GamePlugin->primaryPlugins(); - bool lightPluginsAreSupported = m_GamePlugin->feature()->lightPluginsAreSupported(); + GamePlugins *gamePlugins = m_GamePlugin->feature(); + const bool lightPluginsAreSupported = gamePlugins ? gamePlugins->lightPluginsAreSupported() : false; m_CurrentProfile = profileName; @@ -265,8 +266,9 @@ void PluginList::refresh(const QString &profileName // indices need to work. priority will be off however updateIndices(); - GamePlugins *gamePlugins = m_GamePlugin->feature(); - gamePlugins->readPluginLists(this); + if (gamePlugins) { + gamePlugins->readPluginLists(this); + } testMasters(); @@ -521,7 +523,9 @@ void PluginList::saveTo(const QString &lockedOrderFileName , bool hideUnchecked) const { GamePlugins *gamePlugins = m_GamePlugin->feature(); - gamePlugins->writePluginLists(this); + if (gamePlugins) { + gamePlugins->writePluginLists(this); + } writeLockedOrder(lockedOrderFileName); @@ -870,7 +874,10 @@ void PluginList::generatePluginIndexes() { int numESLs = 0; int numSkipped = 0; - bool lightPluginsSupported = m_GamePlugin->feature()->lightPluginsAreSupported(); + + GamePlugins* gamePlugins = m_GamePlugin->feature(); + const bool lightPluginsSupported = gamePlugins ? gamePlugins->lightPluginsAreSupported() : false; + for (int l = 0; l < m_ESPs.size(); ++l) { int i = m_ESPsByPriority.at(l); if (!m_ESPs[i].enabled) { -- cgit v1.3.1 From 40c433580f69006a42dc27a6444ebd9a6cccc440 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Fri, 1 May 2020 18:00:31 +0200 Subject: Update ModInfo classes to handle plugin game and clean some declarations. --- src/modinfo.cpp | 27 +------- src/modinfo.h | 49 ++++++++++----- src/modinfobackup.h | 55 ++++++++-------- src/modinfodialognexus.cpp | 2 +- src/modinfoforeign.h | 111 ++++++++++++++++---------------- src/modinfooverwrite.h | 107 ++++++++++++++++--------------- src/modinforegular.cpp | 36 ++++++++++- src/modinforegular.h | 143 +++++++++++++++++++++++------------------- src/modinfoseparator.h | 88 +++++++++++--------------- src/modinfowithconflictinfo.h | 20 +++--- 10 files changed, 332 insertions(+), 306 deletions(-) (limited to 'src') diff --git a/src/modinfo.cpp b/src/modinfo.cpp index 085eaf80..7f888bdc 100644 --- a/src/modinfo.cpp +++ b/src/modinfo.cpp @@ -25,11 +25,9 @@ along with Mod Organizer. If not, see . #include "modinfooverwrite.h" #include "modinfoseparator.h" -#include "installationtester.h" #include "categories.h" #include "modinfodialog.h" #include "overwriteinfodialog.h" -#include "filenamestring.h" #include "versioninfo.h" #include @@ -475,7 +473,7 @@ bool ModInfo::removeCategory(const QString &categoryName) return true; } -QStringList ModInfo::categories() +QStringList ModInfo::categories() const { QStringList result; @@ -529,28 +527,7 @@ bool ModInfo::categorySet(int categoryID) const void ModInfo::testValid() { - m_Valid = false; - QDirIterator dirIter(absolutePath()); - while (dirIter.hasNext()) { - dirIter.next(); - if (dirIter.fileInfo().isDir()) { - if (InstallationTester::isTopLevelDirectory(dirIter.fileName())) { - m_Valid = true; - break; - } - } else { - if (InstallationTester::isTopLevelSuffix(dirIter.fileName())) { - m_Valid = true; - break; - } - } - } - - // NOTE: in Qt 4.7 it seems that QDirIterator leaves a file handle open if it is not iterated to the - // end - while (dirIter.hasNext()) { - dirIter.next(); - } + m_Valid = doTestValid(); } QUrl ModInfo::parseCustomURL() const diff --git a/src/modinfo.h b/src/modinfo.h index 34b1ecdf..d611fa87 100644 --- a/src/modinfo.h +++ b/src/modinfo.h @@ -319,11 +319,23 @@ public: virtual void setNotes(const QString ¬es) = 0; /** - * @brief set/change the source game of this mod + * @brief set/change the game plgin for this mod * - * @param gameName the source game shortName + * @param gamePlugin the game plugin */ - virtual void setGameName(const QString &gameName) = 0; + virtual void setGamePlugin(const MOBase::IPluginGame* gamePlugin) = 0; + + /** + * @brief set the name of this mod + * + * set the name of this mod. This will also update the name of the + * directory that contains this mod + * + * @param name new name of the mod + * @return true on success, false if the new name can't be used (i.e. because the new + * directory name wouldn't be valid) + **/ + virtual bool setName(const QString& name) = 0; /** * @brief set/change the nexus mod id of this mod @@ -336,7 +348,7 @@ public: * @brief set/change the version of this mod * @param version new version of the mod */ - virtual void setVersion(const MOBase::VersionInfo &version); + virtual void setVersion(const MOBase::VersionInfo &version) override; /** * @brief Controls if mod should be highlighted based on plugin selection @@ -382,7 +394,7 @@ public: virtual void addCategory(const QString &categoryName) override; virtual bool removeCategory(const QString &categoryName) override; - virtual QStringList categories() override; + virtual QStringList categories() const override; /** * update the endorsement state for the mod. This only changes the @@ -645,12 +657,12 @@ public: /* *@return the color choosen by the user for the mod/separator */ - virtual QColor getColor() { return QColor(); } + virtual QColor getColor() const { return QColor(); } /* *@return true if the color has been set successfully. */ - virtual void setColor(QColor color) { } + virtual void setColor(QColor) { } /** * @brief adds the information that a file has been installed into this mod @@ -709,12 +721,12 @@ public: /** * @brief updates the mod to flag it as converted in order to ignore the alternate game warning */ - virtual void markConverted(bool converted) {} + virtual void markConverted(bool) {} /** * @brief updates the mod to flag it as valid in order to ignore the invalid game data flag */ - virtual void markValidated(bool validated) {} + virtual void markValidated(bool) {} /** * @brief reads meta information from disk @@ -729,32 +741,32 @@ public: /** * @return retrieve list of mods (as mod index) that are overwritten by this one. Updates may be delayed */ - virtual std::set getModOverwrite() { return std::set(); } + virtual std::set getModOverwrite() const { return std::set(); } /** * @return list of mods (as mod index) that overwrite this one. Updates may be delayed */ - virtual std::set getModOverwritten() { return std::set(); } + virtual std::set getModOverwritten() const { return std::set(); } /** * @return retrieve list of mods (as mod index) with archives that are overwritten by this one. Updates may be delayed */ - virtual std::set getModArchiveOverwrite() { return std::set(); } + virtual std::set getModArchiveOverwrite() const { return std::set(); } /** * @return list of mods (as mod index) with archives that overwrite this one. Updates may be delayed */ - virtual std::set getModArchiveOverwritten() { return std::set(); } + virtual std::set getModArchiveOverwritten() const { return std::set(); } /** * @return retrieve list of mods (as mod index) with archives that are overwritten by thos mod's loose files. Updates may be delayed */ - virtual std::set getModArchiveLooseOverwrite() { return std::set(); } + virtual std::set getModArchiveLooseOverwrite() const { return std::set(); } /** * @return list of mods (as mod index) with loose files that overwrite this one's archive files. Updates may be delayed */ - virtual std::set getModArchiveLooseOverwritten() { return std::set(); } + virtual std::set getModArchiveLooseOverwritten() const { return std::set(); } /** * @brief update conflict information @@ -804,6 +816,13 @@ protected: static void updateIndices(); static bool ByName(const ModInfo::Ptr &LHS, const ModInfo::Ptr &RHS); + /** + * @brief check if the content of this mod is valid. + * + * @return true if the content is valid, false otherwize. + **/ + virtual bool doTestValid() const = 0; + private: static void createFromOverwrite(PluginContainer *pluginContainer, diff --git a/src/modinfobackup.h b/src/modinfobackup.h index 393c2e38..bc340b3e 100644 --- a/src/modinfobackup.h +++ b/src/modinfobackup.h @@ -12,35 +12,32 @@ class ModInfoBackup : public ModInfoRegular public: - virtual bool updateAvailable() const { return false; } - virtual bool updateIgnored() const { return false; } - virtual bool downgradeAvailable() const { return false; } - virtual bool updateNXMInfo() { return false; } - virtual void setGameName(const QString&) {} - virtual void setNexusID(int) {} - virtual void endorse(bool) {} - virtual void parseNexusInfo() {} - virtual int getFixedPriority() const { return -1; } - virtual void ignoreUpdate(bool) {} - virtual bool canBeUpdated() const { return false; } - virtual QDateTime getExpires() const { return QDateTime(); } - virtual bool canBeEnabled() const { return false; } - virtual std::vector getIniTweaks() const { return std::vector(); } - virtual std::vector getFlags() const; - virtual QString getDescription() const; - virtual int getNexusFileStatus() const { return 0; } - virtual void setNexusFileStatus(int) {} - virtual QDateTime getLastNexusQuery() const { return QDateTime(); } - virtual void setLastNexusQuery(QDateTime) {} - virtual QDateTime getLastNexusUpdate() const { return QDateTime(); } - virtual void setLastNexusUpdate(QDateTime) {} - virtual QDateTime getNexusLastModified() const { return QDateTime(); } - virtual void setNexusLastModified(QDateTime) {} - virtual void getNexusFiles(QList::const_iterator&, - QList::const_iterator&) {} - virtual QString getNexusDescription() const { return QString(); } - - virtual void addInstalledFile(int, int) {} + virtual bool updateAvailable() const override { return false; } + virtual bool updateIgnored() const override { return false; } + virtual bool downgradeAvailable() const override { return false; } + virtual bool updateNXMInfo() override { return false; } + virtual void setGamePlugin(const MOBase::IPluginGame*) override {} + virtual void setNexusID(int) override {} + virtual void endorse(bool) override {} + virtual int getFixedPriority() const override { return -1; } + virtual void ignoreUpdate(bool) override {} + virtual bool canBeUpdated() const override { return false; } + virtual QDateTime getExpires() const override { return QDateTime(); } + virtual bool canBeEnabled() const override { return false; } + virtual std::vector getIniTweaks() const override { return std::vector(); } + virtual std::vector getFlags() const override; + virtual QString getDescription() const override; + virtual int getNexusFileStatus() const override { return 0; } + virtual void setNexusFileStatus(int) override {} + virtual QDateTime getLastNexusQuery() const override { return QDateTime(); } + virtual void setLastNexusQuery(QDateTime) override {} + virtual QDateTime getLastNexusUpdate() const override { return QDateTime(); } + virtual void setLastNexusUpdate(QDateTime) override {} + virtual QDateTime getNexusLastModified() const override { return QDateTime(); } + virtual void setNexusLastModified(QDateTime) override {} + virtual QString getNexusDescription() const override { return QString(); } + + virtual void addInstalledFile(int, int) override {} private: diff --git a/src/modinfodialognexus.cpp b/src/modinfodialognexus.cpp index 59bfe930..447beea6 100644 --- a/src/modinfodialognexus.cpp +++ b/src/modinfodialognexus.cpp @@ -324,7 +324,7 @@ void NexusTab::onSourceGameChanged() for (auto game : plugin().plugins()) { if (game->gameName() == ui->sourceGame->currentText()) { - mod().setGameName(game->gameShortName()); + mod().setGamePlugin(game); mod().setLastNexusQuery(QDateTime::fromSecsSinceEpoch(0)); refreshData(mod().getNexusID()); return; diff --git a/src/modinfoforeign.h b/src/modinfoforeign.h index c5763d6b..7725c793 100644 --- a/src/modinfoforeign.h +++ b/src/modinfoforeign.h @@ -1,9 +1,11 @@ #ifndef MODINFOFOREIGN_H #define MODINFOFOREIGN_H +#include + #include "modinfowithconflictinfo.h" -class ModInfoForeign : public ModInfoWithConflictInfo +class ModInfoForeign: public ModInfoWithConflictInfo { Q_OBJECT @@ -12,65 +14,66 @@ class ModInfoForeign : public ModInfoWithConflictInfo public: - virtual bool updateAvailable() const { return false; } - virtual bool updateIgnored() const { return false; } - virtual bool downgradeAvailable() const { return false; } - virtual bool updateNXMInfo() { return false; } - virtual void setCategory(int, bool) {} - virtual bool setName(const QString&) { return false; } - virtual void setComments(const QString&) {} - virtual void setNotes(const QString&) {} - virtual void setGameName(const QString&) {} - virtual void setNexusID(int) {} - virtual void setNewestVersion(const MOBase::VersionInfo&) {} - virtual void ignoreUpdate(bool) {} - virtual void setNexusDescription(const QString&) {} - virtual void setInstallationFile(const QString&) {} - virtual void addNexusCategory(int) {} - virtual void setIsEndorsed(bool) {} - virtual void setNeverEndorse() {} - virtual void setIsTracked(bool) {} - virtual bool remove() { return false; } - virtual void endorse(bool) {} - virtual void track(bool) {} - virtual void parseNexusInfo() {} - virtual bool isEmpty() const { return false; } - virtual QString name() const { return m_Name; } - virtual QString internalName() const { return m_InternalName; } - virtual QString comments() const { return ""; } - virtual QString notes() const { return ""; } - virtual QDateTime creationTime() const; - virtual QString absolutePath() const; - virtual MOBase::VersionInfo getNewestVersion() const { return QString(); } - virtual QString getInstallationFile() const { return ""; } - virtual QString getGameName() const { return ""; } - virtual int getNexusID() const { return -1; } - virtual QDateTime getExpires() const { return QDateTime(); } - virtual std::vector getIniTweaks() const { return std::vector(); } - virtual std::vector getFlags() const; - virtual int getHighlight() const; - virtual QString getDescription() const; - virtual int getNexusFileStatus() const { return 0; } - virtual void setNexusFileStatus(int) {} - virtual QDateTime getLastNexusUpdate() const { return QDateTime(); } - virtual void setLastNexusUpdate(QDateTime) {} - virtual QDateTime getLastNexusQuery() const { return QDateTime(); } - virtual void setLastNexusQuery(QDateTime) {} - virtual QDateTime getNexusLastModified() const { return QDateTime(); } - virtual void setNexusLastModified(QDateTime) {} - virtual QString getNexusDescription() const { return QString(); } - virtual int getFixedPriority() const { return INT_MIN; } - virtual QStringList archives(bool checkOnDisk = false) { return m_Archives; } - virtual QStringList stealFiles() const { return m_Archives + QStringList(m_ReferenceFile); } - virtual bool alwaysEnabled() const { return true; } - virtual void addInstalledFile(int, int) {} + virtual bool updateAvailable() const override { return false; } + virtual bool updateIgnored() const override { return false; } + virtual bool downgradeAvailable() const override { return false; } + virtual bool updateNXMInfo() override { return false; } + virtual void setCategory(int, bool) override {} + virtual bool setName(const QString&) override { return false; } + virtual void setComments(const QString&) override {} + virtual void setNotes(const QString&) override {} + virtual void setGamePlugin(const MOBase::IPluginGame*) override {} + virtual void setNexusID(int) override {} + virtual void setNewestVersion(const MOBase::VersionInfo&) override {} + virtual void ignoreUpdate(bool) override {} + virtual void setNexusDescription(const QString&) override {} + virtual void setInstallationFile(const QString&) override {} + virtual void addNexusCategory(int) override {} + virtual void setIsEndorsed(bool) override {} + virtual void setNeverEndorse() override {} + virtual void setIsTracked(bool) override {} + virtual bool remove() override { return false; } + virtual void endorse(bool) override {} + virtual void track(bool) override {} + virtual bool isEmpty() const override { return false; } + virtual QString name() const override { return m_Name; } + virtual QString internalName() const override { return m_InternalName; } + virtual QString comments() const override { return ""; } + virtual QString notes() const override { return ""; } + virtual QDateTime creationTime() const override; + virtual QString absolutePath() const override; + virtual MOBase::VersionInfo getNewestVersion() const override { return QString(); } + virtual QString getInstallationFile() const override { return ""; } + virtual QString getGameName() const override { return ""; } + virtual int getNexusID() const override { return -1; } + virtual QDateTime getExpires() const override { return QDateTime(); } + virtual std::vector getIniTweaks() const override { return std::vector(); } + virtual std::vector getFlags() const override; + virtual int getHighlight() const override; + virtual QString getDescription() const override; + virtual int getNexusFileStatus() const override { return 0; } + virtual void setNexusFileStatus(int) override {} + virtual QDateTime getLastNexusUpdate() const override { return QDateTime(); } + virtual void setLastNexusUpdate(QDateTime) override {} + virtual QDateTime getLastNexusQuery() const override { return QDateTime(); } + virtual void setLastNexusQuery(QDateTime) override {} + virtual QDateTime getNexusLastModified() const override { return QDateTime(); } + virtual void setNexusLastModified(QDateTime) override {} + virtual QString getNexusDescription() const override { return QString(); } + virtual int getFixedPriority() const override { return std::numeric_limits::min(); } + virtual QStringList archives(bool = false) override { return m_Archives; } + virtual QStringList stealFiles() const override { return m_Archives + QStringList(m_ReferenceFile); } + virtual bool alwaysEnabled() const override { return true; } + virtual void addInstalledFile(int, int) override {} ModInfo::EModType modType() const { return m_ModType; } protected: ModInfoForeign(const QString &modName, const QString &referenceFile, const QStringList &archives, ModInfo::EModType modType, - MOShared::DirectoryEntry **directoryStructure, PluginContainer *pluginContainer); + MOShared::DirectoryEntry **directoryStructure, PluginContainer *pluginContainer); + + virtual bool doTestValid() const { return true; } private: QString m_Name; diff --git a/src/modinfooverwrite.h b/src/modinfooverwrite.h index ecbdbe3d..070ba6ef 100644 --- a/src/modinfooverwrite.h +++ b/src/modinfooverwrite.h @@ -1,6 +1,8 @@ #ifndef MODINFOOVERWRITE_H #define MODINFOOVERWRITE_H +#include + #include "modinfowithconflictinfo.h" #include @@ -14,60 +16,61 @@ class ModInfoOverwrite : public ModInfoWithConflictInfo public: - virtual bool updateAvailable() const { return false; } - virtual bool updateIgnored() const { return false; } - virtual bool downgradeAvailable() const { return false; } - virtual bool updateNXMInfo() { return false; } - virtual void setCategory(int, bool) {} - virtual bool setName(const QString&) { return false; } - virtual void setComments(const QString&) {} - virtual void setNotes(const QString&) {} - virtual void setGameName(const QString&) {} - virtual void setNexusID(int) {} - virtual void setNewestVersion(const MOBase::VersionInfo&) {} - virtual void ignoreUpdate(bool) {} - virtual void setNexusDescription(const QString&) {} - virtual void setInstallationFile(const QString&) {} - virtual void addNexusCategory(int) {} - virtual void setIsEndorsed(bool) {} - virtual void setNeverEndorse() {} - virtual void setIsTracked(bool) {} - virtual bool remove() { return false; } - virtual void endorse(bool) {} - virtual void track(bool) {} - virtual void parseNexusInfo() {} - virtual bool alwaysEnabled() const { return true; } - virtual bool isEmpty() const; - virtual QString name() const { return "Overwrite"; } - virtual QString comments() const { return ""; } - virtual QString notes() const { return ""; } - virtual QDateTime creationTime() const { return QDateTime(); } - virtual QString absolutePath() const; - virtual MOBase::VersionInfo getNewestVersion() const { return QString(); } - virtual QString getInstallationFile() const { return ""; } - virtual int getFixedPriority() const { return INT_MAX; } - virtual QString getGameName() const { return ""; } - virtual int getNexusID() const { return -1; } - virtual QDateTime getExpires() const { return QDateTime(); } - virtual std::vector getIniTweaks() const { return std::vector(); } - virtual std::vector getFlags() const; - virtual std::vector getConflictFlags() const; - virtual int getHighlight() const; - virtual QString getDescription() const; - virtual int getNexusFileStatus() const { return 0; } - virtual void setNexusFileStatus(int) {} - virtual QDateTime getLastNexusUpdate() const { return QDateTime(); } - virtual void setLastNexusUpdate(QDateTime) {} - virtual QDateTime getLastNexusQuery() const { return QDateTime(); } - virtual void setLastNexusQuery(QDateTime) {} - virtual QDateTime getNexusLastModified() const { return QDateTime(); } - virtual void setNexusLastModified(QDateTime) {} - virtual QString getNexusDescription() const { return QString(); } - virtual QStringList archives(bool checkOnDisk = false); - virtual void addInstalledFile(int, int) {} + virtual bool updateAvailable() const override { return false; } + virtual bool updateIgnored() const override { return false; } + virtual bool downgradeAvailable() const override { return false; } + virtual bool updateNXMInfo() override { return false; } + virtual void setCategory(int, bool) override {} + virtual bool setName(const QString&) override { return false; } + virtual void setComments(const QString&) override {} + virtual void setNotes(const QString&) override {} + virtual void setGamePlugin(const MOBase::IPluginGame*) override {} + virtual void setNexusID(int) override {} + virtual void setNewestVersion(const MOBase::VersionInfo&) override {} + virtual void ignoreUpdate(bool) override {} + virtual void setNexusDescription(const QString&) override {} + virtual void setInstallationFile(const QString&) override {} + virtual void addNexusCategory(int) override {} + virtual void setIsEndorsed(bool) override {} + virtual void setNeverEndorse() override {} + virtual void setIsTracked(bool) override {} + virtual bool remove() override { return false; } + virtual void endorse(bool) override {} + virtual void track(bool) override {} + virtual bool alwaysEnabled() const override { return true; } + virtual bool isEmpty() const override; + virtual QString name() const override { return "Overwrite"; } + virtual QString comments() const override { return ""; } + virtual QString notes() const override { return ""; } + virtual QDateTime creationTime() const override { return QDateTime(); } + virtual QString absolutePath() const override; + virtual MOBase::VersionInfo getNewestVersion() const override { return QString(); } + virtual QString getInstallationFile() const override { return ""; } + virtual int getFixedPriority() const override { return std::numeric_limits::min(); } + virtual QString getGameName() const override { return ""; } + virtual int getNexusID() const override { return -1; } + virtual QDateTime getExpires() const override { return QDateTime(); } + virtual std::vector getIniTweaks() const override { return std::vector(); } + virtual std::vector getFlags() const override; + virtual std::vector getConflictFlags() const override; + virtual int getHighlight() const override; + virtual QString getDescription() const override; + virtual int getNexusFileStatus() const override { return 0; } + virtual void setNexusFileStatus(int) override {} + virtual QDateTime getLastNexusUpdate() const override { return QDateTime(); } + virtual void setLastNexusUpdate(QDateTime) override {} + virtual QDateTime getLastNexusQuery() const override { return QDateTime(); } + virtual void setLastNexusQuery(QDateTime) override {} + virtual QDateTime getNexusLastModified() const override { return QDateTime(); } + virtual void setNexusLastModified(QDateTime) override {} + virtual QString getNexusDescription() const override { return QString(); } + virtual QStringList archives(bool checkOnDisk = false) override; + virtual void addInstalledFile(int, int) override {} -private: +protected: + virtual bool doTestValid() const { return true; } +private: ModInfoOverwrite(PluginContainer *pluginContainer, MOShared::DirectoryEntry **directoryStructure ); }; diff --git a/src/modinforegular.cpp b/src/modinforegular.cpp index 3cff914a..6a0a1b7d 100644 --- a/src/modinforegular.cpp +++ b/src/modinforegular.cpp @@ -1,4 +1,5 @@ #include "modinforegular.h" +#include "installationtester.h" #include "categories.h" #include "messagedialog.h" @@ -29,6 +30,7 @@ ModInfoRegular::ModInfoRegular(PluginContainer *pluginContainer, const IPluginGa , m_Name(path.dirName()) , m_Path(path.absolutePath()) , m_Repository() + , m_GamePlugin(game) , m_GameName(game->gameShortName()) , m_IsAlternate(false) , m_Converted(false) @@ -269,6 +271,34 @@ void ModInfoRegular::saveMeta() } } +bool ModInfoRegular::doTestValid() const { + + bool valid = false; + QDirIterator dirIter(absolutePath()); + while (dirIter.hasNext()) { + dirIter.next(); + if (dirIter.fileInfo().isDir()) { + if (InstallationTester::isTopLevelDirectory(dirIter.fileName())) { + valid = true; + break; + } + } + else { + if (InstallationTester::isTopLevelSuffix(dirIter.fileName())) { + valid = true; + break; + } + } + } + + // NOTE: in Qt 4.7 it seems that QDirIterator leaves a file handle open if it is not iterated to the + // end + while (dirIter.hasNext()) { + dirIter.next(); + } + + return valid; +} bool ModInfoRegular::updateAvailable() const { @@ -467,10 +497,12 @@ void ModInfoRegular::setNotes(const QString ¬es) m_MetaInfoChanged = true; } -void ModInfoRegular::setGameName(const QString &gameName) +void ModInfoRegular::setGamePlugin(const MOBase::IPluginGame* gamePlugin) { - m_GameName = gameName; + m_GamePlugin = gamePlugin; + m_GameName = gamePlugin->gameShortName(); m_MetaInfoChanged = true; + testValid(); } void ModInfoRegular::setNexusID(int modID) diff --git a/src/modinforegular.h b/src/modinforegular.h index 705e66a8..1cef24fb 100644 --- a/src/modinforegular.h +++ b/src/modinforegular.h @@ -1,6 +1,8 @@ #ifndef MODINFOREGULAR_H #define MODINFOREGULAR_H +#include + #include "modinfowithconflictinfo.h" #include "nexusinterface.h" @@ -22,9 +24,9 @@ public: ~ModInfoRegular(); - virtual bool isRegular() const { return true; } + virtual bool isRegular() const override { return true; } - virtual bool isEmpty() const; + virtual bool isEmpty() const override; bool isAlternate() { return m_IsAlternate; } bool isConverted() { return m_Converted; } @@ -39,12 +41,12 @@ public: * * @return true if there is a newer version **/ - bool updateAvailable() const; + bool updateAvailable() const override; /** * @return true if the current update is being ignored */ - virtual bool updateIgnored() const { return m_IgnoredVersion.isValid() && m_IgnoredVersion == m_NewestVersion; } + virtual bool updateIgnored() const override { return m_IgnoredVersion.isValid() && m_IgnoredVersion == m_NewestVersion; } /** * @brief test if there is a newer version of the mod @@ -55,7 +57,7 @@ public: * * @return true if there is a newer version **/ - bool downgradeAvailable() const; + bool downgradeAvailable() const override; /** * @brief request an update of nexus description for this mod. @@ -65,7 +67,7 @@ public: * * @return returns true if information for this mod will be updated, false if there is no nexus mod id to use **/ - bool updateNXMInfo(); + bool updateNXMInfo() override; /** * @brief assign or unassign the specified category @@ -76,7 +78,7 @@ public: * @param active determines wheter the category is assigned or unassigned * @note this function does not test whether categoryID actually identifies a valid category **/ - void setCategory(int categoryID, bool active); + void setCategory(int categoryID, bool active) override; /** * @brief set the name of this mod @@ -88,33 +90,33 @@ public: * @return true on success, false if the new name can't be used (i.e. because the new * directory name wouldn't be valid) **/ - bool setName(const QString &name); + bool setName(const QString &name) override; /** * @brief changes the comments (manually set information displayed in the mod list) for this mod * @param comments new comments */ - void setComments(const QString &comments); + void setComments(const QString &comments) override; /** * @brief change the notes (manually set information) for this mod * @param notes new notes */ - void setNotes(const QString ¬es); + void setNotes(const QString ¬es) override; /** - * @brief set/change the source game of this mod - * - * @param gameName the source game shortName - */ - void setGameName(const QString &gameName); + * @brief set/change the game plgin for this mod + * + * @param gamePlugin the game plugin + */ + virtual void setGamePlugin(const MOBase::IPluginGame* gamePlugin) override; /** * @brief set/change the nexus mod id of this mod * * @param modID the nexus mod id **/ - void setNexusID(int modID); + void setNexusID(int modID) override; /** * @brief set the version of this mod @@ -124,7 +126,7 @@ public: * * @param version the new version to use **/ - void setVersion(const MOBase::VersionInfo &version); + void setVersion(const MOBase::VersionInfo &version) override; /** * @brief set the newest version of this mod on the nexus @@ -136,73 +138,73 @@ public: * @todo this function should be made obsolete. All queries for mod information should go through * this class so no public function for this change is required **/ - void setNewestVersion(const MOBase::VersionInfo &version); + void setNewestVersion(const MOBase::VersionInfo &version) override; /** * @brief changes/updates the nexus description text * @param description the current description text */ - virtual void setNexusDescription(const QString &description); + virtual void setNexusDescription(const QString &description) override; - virtual void setInstallationFile(const QString &fileName); + virtual void setInstallationFile(const QString &fileName) override; /** * @brief sets the category id from a nexus category id. Conversion to MO id happens internally * @param categoryID the nexus category id * @note if a mapping is not possible, the category is set to the default value */ - virtual void addNexusCategory(int categoryID); + virtual void addNexusCategory(int categoryID) override; /** * @brief sets the new primary category of the mod * @param categoryID the category to set */ - virtual void setPrimaryCategory(int categoryID) { m_PrimaryCategory = categoryID; m_MetaInfoChanged = true; } + virtual void setPrimaryCategory(int categoryID) override { m_PrimaryCategory = categoryID; m_MetaInfoChanged = true; } /** * @brief sets the download repository * @param repository */ - virtual void setRepository(const QString &repository) { m_Repository = repository; } + virtual void setRepository(const QString &repository) override { m_Repository = repository; } /** * update the endorsement state for the mod. This only changes the * buffered state, it does not sync with Nexus * @param endorsed the new endorsement state */ - virtual void setIsEndorsed(bool endorsed); + virtual void setIsEndorsed(bool endorsed) override; /** * set the mod to "i don't intend to endorse". The mod will not show as unendorsed but can still be endorsed */ - virtual void setNeverEndorse(); + virtual void setNeverEndorse() override; /** * update the tracked state for the mod. This only changes the * buffered state. It does not sync with Nexus * @param tracked the new tracked state */ - virtual void setIsTracked(bool tracked); + virtual void setIsTracked(bool tracked) override; /** * @brief delete the mod from the disc. This does not update the global ModInfo structure or indices * @return true if the mod was successfully removed **/ - bool remove(); + bool remove() override; /** * @brief endorse or un-endorse the mod * @param doEndorse if true, the mod is endorsed, if false, it's un-endorsed. * @note if doEndorse doesn't differ from the current value, nothing happens. */ - virtual void endorse(bool doEndorse); + virtual void endorse(bool doEndorse) override; /** * @brief track or untrack the mod. This will sync with nexus! * @param doTrack if true, the mod is tracked, if false, it's untracked. * @note if doTrack doesn't differ from the current value, nothing happens. */ - virtual void track(bool doTrack); + virtual void track(bool doTrack) override; /** * @brief updates the mod to flag it as converted in order to ignore the alternate game warning @@ -219,183 +221,183 @@ public: * * @return the mod name **/ - QString name() const { return m_Name; } + QString name() const override { return m_Name; } /** * @brief getter for the mod path * * @return the (absolute) path to the mod **/ - QString absolutePath() const; + QString absolutePath() const override; /** * @brief getter for the newest version number of this mod * * @return newest version of the mod **/ - MOBase::VersionInfo getNewestVersion() const { return m_NewestVersion; } + MOBase::VersionInfo getNewestVersion() const override { return m_NewestVersion; } /** * @brief ignore the newest version for updates */ - void ignoreUpdate(bool ignore); + void ignoreUpdate(bool ignore) override; /** * @brief getter for the installation file * * @return file used to install this mod from */ - virtual QString getInstallationFile() const { return m_InstallationFile; } + virtual QString getInstallationFile() const override { return m_InstallationFile; } /** * @brief getter for the source game repository * * @return the source game repository. should default to the active game. **/ - QString getGameName() const { return m_GameName; } + QString getGameName() const override { return m_GameName; } /** * @brief getter for the nexus mod id * * @return the nexus mod id. may be 0 if the mod id isn't known or doesn't exist **/ - int getNexusID() const { return m_NexusID; } + int getNexusID() const override { return m_NexusID; } /** * @return the fixed priority of mods of this type or INT_MIN if the priority of mods * needs to be user-modifiable */ - virtual int getFixedPriority() const { return INT_MIN; } + virtual int getFixedPriority() const override { return std::numeric_limits::min(); } /** * @return true if the mod can be updated */ - virtual bool canBeUpdated() const; + virtual bool canBeUpdated() const override; /** * @return the update expiration date based on the last updated date from Nexus */ - virtual QDateTime getExpires() const; + virtual QDateTime getExpires() const override; /** * @return true if the mod can be enabled/disabled */ - virtual bool canBeEnabled() const { return true; } + virtual bool canBeEnabled() const override { return true; } /** * @return a list of flags for this mod */ - virtual std::vector getFlags() const; + virtual std::vector getFlags() const override; - virtual std::vector getContents() const; + virtual std::vector getContents() const override; /** * @return an indicator if and how this mod should be highlighted by the UI */ - virtual int getHighlight() const; + virtual int getHighlight() const override; /** * @return list of names of ini tweaks **/ - std::vector getIniTweaks() const; + std::vector getIniTweaks() const override; /** * @return a description about the mod, to be displayed in the ui */ - virtual QString getDescription() const; + virtual QString getDescription() const override; /** * @return the nexus file status (aka category ID) */ - virtual int getNexusFileStatus() const; + virtual int getNexusFileStatus() const override; /** * @brief sets the file status (category ID) from Nexus * @param status the status id of the installed file */ - virtual void setNexusFileStatus(int status); + virtual void setNexusFileStatus(int status) override; /** * @return comments for this mod */ - virtual QString comments() const; + virtual QString comments() const override; /** * @return manually set notes for this mod */ - virtual QString notes() const; + virtual QString notes() const override; /** * @return time this mod was created (file time of the directory) */ - virtual QDateTime creationTime() const; + virtual QDateTime creationTime() const override; /** * @return nexus description of the mod (html) */ - QString getNexusDescription() const; + QString getNexusDescription() const override; /** * @return repository from which the file was downloaded */ - virtual QString repository() const; + virtual QString repository() const override; /** * @return true if the file has been endorsed on nexus */ - virtual EEndorsedState endorsedState() const; + virtual EEndorsedState endorsedState() const override; /** * @return true if the file is being tracked on nexus */ - virtual ETrackedState trackedState() const; + virtual ETrackedState trackedState() const override; /** * @brief get the last time nexus was checked for file updates on this mod */ - virtual QDateTime getLastNexusUpdate() const; + virtual QDateTime getLastNexusUpdate() const override; /** * @brief set the last time nexus was checked for file updates on this mod */ - virtual void setLastNexusUpdate(QDateTime time); + virtual void setLastNexusUpdate(QDateTime time) override; /** * @return last time nexus was queried for infos on this mod */ - virtual QDateTime getLastNexusQuery() const; + virtual QDateTime getLastNexusQuery() const override; /** * @brief set the last time nexus was queried for info on this mod */ - virtual void setLastNexusQuery(QDateTime time); + virtual void setLastNexusQuery(QDateTime time) override; /** * @return last time the mod was updated on Nexus */ - virtual QDateTime getNexusLastModified() const; + virtual QDateTime getNexusLastModified() const override; /** * @brief set the last time the mod was updated on Nexus */ - virtual void setNexusLastModified(QDateTime time); + virtual void setNexusLastModified(QDateTime time) override; - virtual QStringList archives(bool checkOnDisk = false); + virtual QStringList archives(bool checkOnDisk = false) override; - virtual void setColor(QColor color); + virtual void setColor(QColor color) override; virtual QColor getColor(); - virtual void addInstalledFile(int modId, int fileId); + virtual void addInstalledFile(int modId, int fileId) override; /** * @brief stores meta information back to disk */ - virtual void saveMeta(); + virtual void saveMeta() override; - void readMeta(); + void readMeta() override; virtual void setHasCustomURL(bool b) override; virtual bool hasCustomURL() const override; @@ -418,6 +420,13 @@ protected: ModInfoRegular(PluginContainer *pluginContainer, const MOBase::IPluginGame *game, const QDir &path, MOShared::DirectoryEntry **directoryStructure); + /** + * @brief check if the content of this mod is valid. + * + * @return true if the content is valid, false otherwize. + **/ + virtual bool doTestValid() const; + private: QString m_Name; @@ -429,6 +438,10 @@ private: QString m_Repository; QString m_CustomURL; bool m_HasCustomURL; + + // Storing both the game name and game plugin since we can have a game + // name that is not one of the primary names of the game plugin: + const MOBase::IPluginGame* m_GamePlugin; QString m_GameName; mutable QStringList m_Archives; diff --git a/src/modinfoseparator.h b/src/modinfoseparator.h index 4b1e5217..d715d548 100644 --- a/src/modinfoseparator.h +++ b/src/modinfoseparator.h @@ -12,62 +12,44 @@ class ModInfoSeparator: public: - virtual bool updateAvailable() const { return false; } - virtual bool updateIgnored() const { return false; } - virtual bool downgradeAvailable() const { return false; } - virtual bool updateNXMInfo() { return false; } - virtual bool isValid() const { return true; } + virtual bool updateAvailable() const override { return false; } + virtual bool updateIgnored() const override { return false; } + virtual bool downgradeAvailable() const override { return false; } + virtual bool updateNXMInfo() override { return false; } + virtual bool isValid() const override { return true; } //TODO: Fix renaming method to avoid priority reset virtual bool setName(const QString& name); - virtual int getNexusID() const { return -1; } - - virtual void setGameName(const QString& /*gameName*/) {} - - virtual void setNexusID(int /*modID*/) {} - - virtual void endorse(bool /*doEndorse*/) {} - - virtual void parseNexusInfo() {} - - virtual void ignoreUpdate(bool /*ignore*/) {} - - virtual bool canBeUpdated() const { return false; } - virtual QDateTime getExpires() const { return QDateTime(); } - virtual bool canBeEnabled() const { return false; } - virtual std::vector getIniTweaks() const { return std::vector(); } - - virtual std::vector getFlags() const; - virtual int getHighlight() const; - - virtual QString getDescription() const; - virtual QString name() const; - virtual QString getGameName() const { return ""; } - virtual QString getInstallationFile() const { return ""; } - virtual QString getURL() const { return ""; } - virtual QString repository() const { return ""; } - virtual int getNexusFileStatus() const { return 0; } - virtual void setNexusFileStatus(int) {} - virtual QDateTime getLastNexusUpdate() const { return QDateTime(); } - virtual void setLastNexusUpdate(QDateTime) {} - virtual QDateTime getLastNexusQuery() const { return QDateTime(); } - virtual void setLastNexusQuery(QDateTime) {} - virtual QDateTime getNexusLastModified() const { return QDateTime(); } - virtual void setNexusLastModified(QDateTime) {} - virtual QDateTime creationTime() const { return QDateTime(); } - - virtual void getNexusFiles - ( - QList::const_iterator& /*unused*/, - QList::const_iterator& /*unused*/) - { - } - - virtual QString getNexusDescription() const { return QString(); } - - virtual void addInstalledFile(int /*modId*/, int /*fileId*/) - { - } + virtual int getNexusID() const override { return -1; } + virtual void setGamePlugin(const MOBase::IPluginGame* /*gamePlugin*/) override {} + virtual void setNexusID(int /*modID*/) override {} + virtual void endorse(bool /*doEndorse*/) override {} + virtual void ignoreUpdate(bool /*ignore*/) override {} + virtual bool canBeUpdated() const override { return false; } + virtual QDateTime getExpires() const override { return QDateTime(); } + virtual bool canBeEnabled() const override { return false; } + virtual std::vector getIniTweaks() const override { return std::vector(); } + virtual std::vector getFlags() const override; + virtual int getHighlight() const override; + virtual QString getDescription() const override; + virtual QString name() const override; + virtual QString getGameName() const override { return ""; } + virtual QString getInstallationFile() const override { return ""; } + virtual QString repository() const override { return ""; } + virtual int getNexusFileStatus() const override { return 0; } + virtual void setNexusFileStatus(int) override {} + virtual QDateTime getLastNexusUpdate() const override { return QDateTime(); } + virtual void setLastNexusUpdate(QDateTime) override {} + virtual QDateTime getLastNexusQuery() const override { return QDateTime(); } + virtual void setLastNexusQuery(QDateTime) override {} + virtual QDateTime getNexusLastModified() const override { return QDateTime(); } + virtual void setNexusLastModified(QDateTime) override {} + virtual QDateTime creationTime() const override { return QDateTime(); } + virtual QString getNexusDescription() const override { return QString(); } + virtual void addInstalledFile(int /*modId*/, int /*fileId*/) override { } + +protected: + virtual bool doTestValid() const override { return true; } private: diff --git a/src/modinfowithconflictinfo.h b/src/modinfowithconflictinfo.h index 460a7f49..c2a49afb 100644 --- a/src/modinfowithconflictinfo.h +++ b/src/modinfowithconflictinfo.h @@ -12,27 +12,27 @@ public: ModInfoWithConflictInfo(PluginContainer *pluginContainer, MOShared::DirectoryEntry **directoryStructure); - std::vector getConflictFlags() const; - virtual std::vector getFlags() const; + std::vector getConflictFlags() const override; + virtual std::vector getFlags() const override; /** * @brief clear all caches held for this mod */ - virtual void clearCaches(); + virtual void clearCaches() override; - virtual std::set getModOverwrite() { return m_OverwriteList; } + virtual std::set getModOverwrite() const override { return m_OverwriteList; } - virtual std::set getModOverwritten() { return m_OverwrittenList; } + virtual std::set getModOverwritten() const override { return m_OverwrittenList; } - virtual std::set getModArchiveOverwrite() { return m_ArchiveOverwriteList; } + virtual std::set getModArchiveOverwrite() const override { return m_ArchiveOverwriteList; } - virtual std::set getModArchiveOverwritten() { return m_ArchiveOverwrittenList; } + virtual std::set getModArchiveOverwritten() const override { return m_ArchiveOverwrittenList; } - virtual std::set getModArchiveLooseOverwrite() { return m_ArchiveLooseOverwriteList; } + virtual std::set getModArchiveLooseOverwrite() const override { return m_ArchiveLooseOverwriteList; } - virtual std::set getModArchiveLooseOverwritten() { return m_ArchiveLooseOverwrittenList; } + virtual std::set getModArchiveLooseOverwritten() const override { return m_ArchiveLooseOverwrittenList; } - virtual void doConflictCheck() const; + virtual void doConflictCheck() const override; private: -- cgit v1.3.1 From 068e12bbb2cd774af89715105241ba7d3e3d0dcd Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Fri, 1 May 2020 18:03:27 +0200 Subject: Move to the new filetree for the installation manager. --- src/CMakeLists.txt | 1 + src/archivefiletree.cpp | 252 ++++++++++++++++++++++++++++++++++ src/archivefiletree.h | 72 ++++++++++ src/installationmanager.cpp | 328 ++++++++------------------------------------ src/installationmanager.h | 93 ++++++++----- 5 files changed, 442 insertions(+), 304 deletions(-) create mode 100644 src/archivefiletree.cpp create mode 100644 src/archivefiletree.h (limited to 'src') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6e454fc1..99d31541 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -26,6 +26,7 @@ add_filter(NAME src/browser GROUPS add_filter(NAME src/core GROUPS categories + archivefiletree installationmanager instancemanager loadmechanism diff --git a/src/archivefiletree.cpp b/src/archivefiletree.cpp new file mode 100644 index 00000000..b629cc8e --- /dev/null +++ b/src/archivefiletree.cpp @@ -0,0 +1,252 @@ +/* +Copyright (C) MO2 Team. 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 . +*/ + +// For QObject::tr: +#include + +#include "archivefiletree.h" + +#include "log.h" + +using namespace MOBase; + +/** + * We use custom file entries to store the index. + */ +class ArchiveFileEntry : public virtual FileTreeEntry { +public: + + /** + * @brief Create a new entry corresponding to a file. + * + * @param parent The tree containing this file. + * @param name The name of this file. + * @param index The index of the file in the archive. + * @param time The modification time of this file. + */ + ArchiveFileEntry(std::shared_ptr parent, QString name, int index, QDateTime time) : + FileTreeEntry(parent, name, time), m_Index(index) { + } + + /** + * @brief Create a new entry corresponding to a directory. + * + * @param parent The tree containing this directory. + * @param name The name of this directory. + * @param index The index of the directory in the archive, or -1. + */ + ArchiveFileEntry(std::shared_ptr parent, QString name, int index) : + FileTreeEntry(parent, name), m_Index(index) { + } + + // No private since we are in an implementation file: + const int m_Index; +}; + + +/** + * + */ +class ArchiveFileTreeImpl: public virtual ArchiveFileTree, public virtual ArchiveFileEntry { +public: + + using File = std::tuple; + +public: // Public for make_shared (but not accessible by other since not exposed in .h): + + ArchiveFileTreeImpl(std::shared_ptr parent, QString name, int index, std::vector&& files) + : FileTreeEntry(parent, name), ArchiveFileEntry(parent, name, index), IFileTree(), m_Files(std::move(files)) { } + +public: // Overrides: + + /** + * @override + */ + std::shared_ptr addFile(QString path, QDateTime time = QDateTime()) override { + // Cannot add file to an archive. + throw UnsupportedOperationException(QObject::tr("Cannot create file within an archive.")); + } + + /** + * + */ + static void mapToArchive(IFileTree const& tree, QString path, FileData* const* data) + { + if (path.length() > 0) { + // when using a long windows path (starting with \\?\) we apparently can have redundant + // . components in the path. This wasn't a problem with "regular" path names. + if (path == ".") { + path.clear(); + } + else { + path.append("\\"); + } + } + + for (auto const& entry : tree) { + if (entry->isDir()) { + const ArchiveFileTreeImpl& archiveEntry = dynamic_cast(*entry); + QString tmp = path + archiveEntry.name(); + if (archiveEntry.m_Index != -1) { + data[archiveEntry.m_Index]->addOutputFileName(tmp); + } + mapToArchive(*archiveEntry.astree(), tmp, data); + } + else { + const ArchiveFileEntry& archiveFileEntry = dynamic_cast(*entry); + data[archiveFileEntry.m_Index]->addOutputFileName(path + archiveFileEntry.name()); + } + } + } + + /** + * + */ + void mapToArchive(Archive* archive) const override { + FileData* const* data; + size_t size; + archive->getFileList(data, size); + mapToArchive(*this, "", data); + } + +protected: + + /* + * Overriding this to create custom FileTreeEntry with index set to -1. No need to + * override makeFile() since we addFile is overriden. Note that this will not be + * used to create existing tree since we do this manually in doPopulate. + * + * @override + */ + virtual std::shared_ptr makeDirectory( + std::shared_ptr parent, QString name) const override { + return std::make_shared(parent, name, -1, std::vector{}); + } + + virtual void doPopulate(std::shared_ptr parent, std::vector>& entries) const override { + + // Sort by name: + std::sort(std::begin(m_Files), std::end(m_Files), + [](const auto& a, const auto& b) { + return std::get<0>(a)[0].compare(std::get<0>(b)[0], Qt::CaseInsensitive) < 0; }); + + // We know that the files are sorted: + QString currentName = ""; + int currentIndex; + std::vector currentFiles; + for (auto& p : m_Files) { + + // At the start or if we have reset, just retrieve the current name and index - The + // index might not be valid in this case (e.g., if the path is a/b, the index is the + // one for a/b while we would want the one for a, but we correct that later): + if (currentName == "") { + currentName = std::get<0>(p)[0]; + currentIndex = std::get<2>(p); + } + + // If the name is different, we need to create a directory from what we have + // accumulated: + if (currentName != std::get<0>(p)[0]) { + // No index here since this is not an empty tree: + entries.push_back(std::make_shared(parent, currentName, currentIndex, std::move(currentFiles))); + currentFiles.clear(); // Back to a valid state. + + // Retrieve the next index: + currentIndex = std::get<2>(p); + } + + // We can always override the current name: + currentName = std::get<0>(p)[0]; + + // If the current path contains only one components: + if (std::get<0>(p).size() == 1) { + // If it is not a directory, then it is a file in directly under this tree: + if (!std::get<1>(p)) { + entries.push_back( + std::make_shared(parent, currentName, std::get<2>(p), QDateTime())); + currentName = ""; + } + // Otherwize, it is the actual "file" corresponding to the directory, so we can retrieve + // the index here: + currentIndex = std::get<2>(p); + } + else { + currentFiles.push_back({ + QStringList(std::get<0>(p).begin() + 1, std::get<0>(p).end()), std::get<1>(p), std::get<2>(p) + }); + } + } + + if (currentName != "") { + entries.push_back(std::make_shared(parent, currentName, currentIndex, std::move(currentFiles))); + } + } + +private: + + mutable std::vector m_Files; +}; + +std::shared_ptr ArchiveFileTree::makeTree(Archive* archive) { + + FileData* const* data; + size_t size; + archive->getFileList(data, size); + + std::vector files; + files.reserve(size); + + for (size_t i = 0; i < size; ++i) { + files.push_back(std::make_tuple(data[i]->getFileName().replace("\\", "/").split("/", Qt::SkipEmptyParts), data[i]->isDirectory(), (int) i)); + } + + auto tree = std::make_shared(nullptr, "", -1, std::move(files)); + return tree; +} + +/** + * @brief Recursive function for the ArchiveFileTree::mapToArchive method. Need a template + * here because iterators from a vector of entries are not exactly the same as the iterators + * returned by a IFileTree. + * + */ +template +void mapToArchive(FileData* const* data, It begin, It end) { + for (auto it = begin; it != end; ++it) { + auto entry = *it; + auto* aentry = dynamic_cast(entry.get()); + + if (aentry->m_Index != -1) { + data[aentry->m_Index]->addOutputFileName(aentry->path()); + } + + if (entry->isDir()) { + auto tree = entry->astree(); + mapToArchive(data, tree->begin(), tree->end()); + } + } +} + +void ArchiveFileTree::mapToArchive(Archive* archive, std::vector> const& entries) { + FileData* const* data; + size_t size; + archive->getFileList(data, size); + + ::mapToArchive(data, entries.cbegin(), entries.cend()); +} diff --git a/src/archivefiletree.h b/src/archivefiletree.h new file mode 100644 index 00000000..0041acd6 --- /dev/null +++ b/src/archivefiletree.h @@ -0,0 +1,72 @@ +/* +Copyright (C) MO2 Team. 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 . +*/ + +#ifndef ARCHIVEFILENETRY_H +#define ARCHIVEFILENTRY_H + +#include "archive.h" +#include "ifiletree.h" + + +/** + * + */ +class ArchiveFileTree: public virtual MOBase::IFileTree { +public: + + /** + * @brief Create a new file tree representing the given archive. + * + * @param archive Archive to represent by the file tree. + * + * @return a file tree representing the given archive. + */ + static std::shared_ptr makeTree(Archive* archive); + + /** + * @brief Update the given archive to reflect change in this tree. + * + * This method disables files that have been removed from the file + * tree and move the ones that have been moved. + * + * @param archive The archive to update. Must be the one used to + * create the tree. + */ + virtual void mapToArchive(Archive *archive) const = 0; + + /** + * @brief Update the given archive to prepare for the extraction + * of the given entries. + * + * This method "enables" files that correspond to the given entry. + * + * @param archive The archive to update. Must be the one used to + * create the tree. + * @param entries List of entries to mark for extraction. All the entries must + * come from a tree created with the given archive. + */ + static void mapToArchive(Archive* archive, std::vector> const& entries); + +protected: + + using IFileTree::IFileTree; + +}; + +#endif diff --git a/src/installationmanager.cpp b/src/installationmanager.cpp index d62067c2..d4e6a444 100644 --- a/src/installationmanager.cpp +++ b/src/installationmanager.cpp @@ -17,6 +17,8 @@ You should have received a copy of the GNU General Public License along with Mod Organizer. If not, see . */ +#include + #include "installationmanager.h" #include "utility.h" @@ -56,6 +58,8 @@ along with Mod Organizer. If not, see . #include #include +#include "archivefiletree.h" + using namespace MOBase; using namespace MOShared; @@ -121,172 +125,18 @@ void InstallationManager::queryPassword(QString *password) tr("Password"), QLineEdit::Password); } -void InstallationManager::mapToArchive(const DirectoryTree::Node *node, QString path, FileData * const *data) -{ - if (path.length() > 0) { - // when using a long windows path (starting with \\?\) we apparently can have redundant - // . components in the path. This wasn't a problem with "regular" path names. - if (path == ".") { - path.clear(); - } else { - path.append("\\"); - } - } - - for (DirectoryTree::const_leaf_iterator iter = node->leafsBegin(); iter != node->leafsEnd(); ++iter) { - data[iter->getIndex()]->addOutputFileName(path + iter->getName().toQString()); - } - - for (DirectoryTree::const_node_iterator iter = node->nodesBegin(); iter != node->nodesEnd(); ++iter) { - QString temp = path + (*iter)->getData().name.toQString(); - if ((*iter)->getData().index != -1) { - data[(*iter)->getData().index]->addOutputFileName(temp); - } - mapToArchive(*iter, temp, data); - } -} - - -void InstallationManager::mapToArchive(const DirectoryTree::Node *baseNode) -{ - FileData* const *data; - size_t size; - m_ArchiveHandler->getFileList(data, size); - - mapToArchive(baseNode, "", data); -} - -bool InstallationManager::unpackSingleFile(const QString &fileName) +bool InstallationManager::extractFiles(QDir extractPath) { - FileData* const *data; - size_t size; - m_ArchiveHandler->getFileList(data, size); - - QString baseName = QFileInfo(fileName).fileName(); - - bool available = false; - for (size_t i = 0; i < size; ++i) { - if (data[i]->getFileName().compare(fileName, Qt::CaseInsensitive) == 0) { - available = true; - data[i]->addOutputFileName(baseName); - m_TempFilesToDelete.insert(baseName); - } - } - - if (!available) { - return false; - } - m_InstallationProgress = new QProgressDialog(m_ParentWidget); - ON_BLOCK_EXIT([this] () { + ON_BLOCK_EXIT([this]() { m_InstallationProgress->hide(); m_InstallationProgress->deleteLater(); m_InstallationProgress = nullptr; m_Progress = 0; - }); + }); m_InstallationProgress->setWindowFlags( - m_InstallationProgress->windowFlags() & ~Qt::WindowContextHelpButtonHint); - - m_InstallationProgress->setWindowTitle(tr("Extracting files")); - m_InstallationProgress->setWindowModality(Qt::WindowModal); - m_InstallationProgress->setFixedSize(600, 100); - m_InstallationProgress->show(); - - QFuture future = QtConcurrent::run([&]() -> bool { - return m_ArchiveHandler->extract( - QDir::tempPath(), - new MethodCallback(this, &InstallationManager::updateProgress), - nullptr, - new MethodCallback(this, &InstallationManager::report7ZipError) - ); - }); - do { - if (m_Progress != m_InstallationProgress->value()) - m_InstallationProgress->setValue(m_Progress); - QCoreApplication::processEvents(); - } while (!future.isFinished() || m_InstallationProgress->isVisible()); - bool res = future.result(); - - return res; -} - - -QString InstallationManager::extractFile(const QString &fileName) -{ - if (unpackSingleFile(fileName)) { - return QDir::tempPath() + "/" + QFileInfo(fileName).fileName(); - } else { - return QString(); - } -} - - -static QString canonicalize(const QString &name) -{ - QString result(name); - if ((result.startsWith('/')) || - (result.startsWith('\\'))) { - result.remove(0, 1); - } - result.replace('/', '\\'); - - return result; -} - - -QStringList InstallationManager::extractFiles(const QStringList &filesOrig, bool flatten) -{ - QStringList files; - - for (const QString &file : filesOrig) { - files.append(canonicalize(file)); - } - - QStringList result; - - FileData* const *data; - size_t size; - m_ArchiveHandler->getFileList(data, size); - - for (size_t i = 0; i < size; ++i) { - //FIXME Use qstring all the way through - if (files.contains(data[i]->getFileName(), Qt::CaseInsensitive)) { - std::wstring temp = data[i]->getFileName().toStdWString(); - wchar_t const * const origFile = temp.c_str(); - const wchar_t *targetFile = origFile; - //Note: I don't think 'flatten' is ever set to true. so this code - //might never be executed - if (flatten) { - targetFile = wcsrchr(origFile/*data[i]->getFileName()*/, '\\'); - if (targetFile == nullptr) { - targetFile = wcsrchr(origFile/*data[i]->getFileName()*/, '/'); - } - if (targetFile == nullptr) { - log::error("Failed to find backslash in {}", data[i]->getFileName()); - continue; - } else { - // skip the slash - ++targetFile; - } - } - data[i]->addOutputFileName(ToQString(targetFile)); - - result.append(QDir::tempPath().append("/").append(ToQString(targetFile))); - - m_TempFilesToDelete.insert(ToQString(targetFile)); - } - } - - m_InstallationProgress = new QProgressDialog(m_ParentWidget); - ON_BLOCK_EXIT([this] () { - m_InstallationProgress->hide(); - m_InstallationProgress->deleteLater(); - m_InstallationProgress = nullptr; - m_Progress = 0; - }); - m_InstallationProgress->setWindowFlags( - m_InstallationProgress->windowFlags() & (~Qt::WindowContextHelpButtonHint)); + m_InstallationProgress->windowFlags() & (~Qt::WindowContextHelpButtonHint)); m_InstallationProgress->setWindowTitle(tr("Extracting files")); m_InstallationProgress->setWindowModality(Qt::WindowModal); m_InstallationProgress->setFixedSize(600, 100); @@ -298,9 +148,9 @@ QStringList InstallationManager::extractFiles(const QStringList &filesOrig, bool QDir::tempPath(), new MethodCallback(this, &InstallationManager::updateProgress), nullptr, - new MethodCallback(this, &InstallationManager::report7ZipError) + new MethodCallback(this, &InstallationManager::report7ZipError) ); - }); + }); do { if (m_Progress != m_InstallationProgress->value()) m_InstallationProgress->setValue(m_Progress); @@ -310,8 +160,9 @@ QStringList InstallationManager::extractFiles(const QStringList &filesOrig, bool if (m_ArchiveHandler->getLastError() == Archive::ERROR_EXTRACT_CANCELLED) { if (!m_ErrorMessage.isEmpty()) { throw MyException(tr("Extraction failed: %1").arg(m_ErrorMessage)); - } else { - return QStringList(); + } + else { + return false; } } else { @@ -319,122 +170,53 @@ QStringList InstallationManager::extractFiles(const QStringList &filesOrig, bool } } - return result; + return true; } -IPluginInstaller::EInstallResult InstallationManager::installArchive(GuessedValue &modName, const QString &archiveName, const int &modId) -{ - // in earlier versions the modName was copied here and the copy passed to install. I don't know why I did this and it causes - // a problem if this is called by the bundle installer and the bundled installer adds additional names that then end up being used, - // because the caller will then not have the right name. - bool iniTweaks; - return install(archiveName, modName, iniTweaks, modId); -} -DirectoryTree *InstallationManager::createFilesTree() +QString InstallationManager::extractFile(std::shared_ptr entry) { - FileData* const *data; - size_t size; - m_ArchiveHandler->getFileList(data, size); - - QScopedPointer result(new DirectoryTree); - - for (size_t i = 0; i < size; ++i) { - // the files are in a flat list where each file has a a full path relative to the archive root - // to create a tree, we have to iterate over each path component of each. This could be sped up by - // grouping the filenames first, but so far there doesn't seem to be an actual performance problem - DirectoryTree::Node *currentNode = result.data(); - - QString fileName = data[i]->getFileName(); - QStringList components = fileName.split("\\"); - - // iterate over all path-components of this filename (including the filename itself) - for (QStringList::iterator componentIter = components.begin(); componentIter != components.end(); ++componentIter) { - if (componentIter->size() == 0) { - // empty string indicates fileName is actually only a directory name and we have - // completely processed it already. - break; - } + QStringList result = this->extractFiles({ entry }); + return result.isEmpty() ? QString() : result[0]; +} - bool exists = false; - // test if this path is already in the tree - for (DirectoryTree::node_iterator nodeIter = currentNode->nodesBegin(); nodeIter != currentNode->nodesEnd(); ++nodeIter) { - if ((*nodeIter)->getData().name == *componentIter) { - currentNode = *nodeIter; - exists = true; - break; - } - } - if (!exists) { - if (componentIter + 1 == components.end()) { - // last path component. directory or file? - if (data[i]->isDirectory()) { - // this is a bit problematic. archives will often only list directories if they are empty, - // otherwise the dir only appears in the path of a file. In the UI however we allow the user - // to uncheck all files in a directory while keeping the dir checked. Those directories are - // currently not installed. - DirectoryTree::Node *newNode = new DirectoryTree::Node; - newNode->setData( - DirectoryTreeInformation(*componentIter, static_cast(i))); - currentNode->addNode(newNode, false); - currentNode = newNode; - } else { - currentNode->addLeaf(FileTreeInformation(*componentIter, i)); - } - } else { - DirectoryTree::Node *newNode = new DirectoryTree::Node; - newNode->setData(DirectoryTreeInformation(*componentIter, -1)); - currentNode->addNode(newNode, false); - currentNode = newNode; - } - } - } - } +QStringList InstallationManager::extractFiles(std::vector> const& entries) +{ + // Remove the directory since mapToArchive would add them: + std::vector> files; + std::copy_if(entries.begin(), entries.end(), std::back_inserter(files), + [](auto const& entry) { return entry->isFile(); }); - return result.take(); -} + // Update the archive: + ArchiveFileTree::mapToArchive(m_ArchiveHandler, files); + // Retrieve the file path: + QStringList result; -bool InstallationManager::isSimpleArchiveTopLayer(const DirectoryTree::Node *node, bool bainStyle) -{ - // see if there is at least one directory that makes sense on the top level - for (DirectoryTree::const_node_iterator iter = node->nodesBegin(); iter != node->nodesEnd(); ++iter) { - if ((bainStyle && InstallationTester::isTopLevelDirectoryBain((*iter)->getData().name)) || - (!bainStyle && InstallationTester::isTopLevelDirectory((*iter)->getData().name))) { - log::debug("{} on the top level", (*iter)->getData().name.toQString()); - return true; - } + for (auto &entry : files) { + auto path = entry->path(); + result.append(QDir::tempPath().append("/").append(path)); + m_TempFilesToDelete.insert(path); } - // see if there is a file that makes sense on the top level - for (DirectoryTree::const_leaf_iterator iter = node->leafsBegin(); iter != node->leafsEnd(); ++iter) { - if (InstallationTester::isTopLevelSuffix(iter->getName())) { - return true; - } + if (!extractFiles(QDir::tempPath())) { + return QStringList(); } - return false; + + return result; } -DirectoryTree::Node *InstallationManager::getSimpleArchiveBase(DirectoryTree *dataTree) +IPluginInstaller::EInstallResult InstallationManager::installArchive(GuessedValue &modName, const QString &archiveName, int modId) { - DirectoryTree::Node *currentNode = dataTree; - - while (true) { - if (isSimpleArchiveTopLayer(currentNode, false)) { - return currentNode; - } else if ((currentNode->numLeafs() == 0) && - (currentNode->numNodes() == 1)) { - currentNode = *currentNode->nodesBegin(); - } else { - log::debug("not a simple archive"); - return nullptr; - } - } + // in earlier versions the modName was copied here and the copy passed to install. I don't know why I did this and it causes + // a problem if this is called by the bundle installer and the bundled installer adds additional names that then end up being used, + // because the caller will then not have the right name. + bool iniTweaks; + return install(archiveName, modName, iniTweaks, modId); } - void InstallationManager::updateProgress(float percentage) { if (m_InstallationProgress != nullptr) { @@ -804,7 +586,8 @@ IPluginInstaller::EInstallResult InstallationManager::install(const QString &fil } ON_BLOCK_EXIT(std::bind(&InstallationManager::postInstallCleanup, this)); - QScopedPointer filesTree(archiveOpen ? createFilesTree() : nullptr); + std::shared_ptr filesTree = + archiveOpen ? ArchiveFileTree::makeTree(m_ArchiveHandler) : nullptr; IPluginInstaller::EInstallResult installResult = IPluginInstaller::RESULT_NOTATTEMPTED; std::sort(m_Installers.begin(), m_Installers.end(), [] (IPluginInstaller *LHS, IPluginInstaller *RHS) { @@ -831,11 +614,20 @@ IPluginInstaller::EInstallResult InstallationManager::install(const QString &fil IPluginInstallerSimple *installerSimple = dynamic_cast(installer); if ((installerSimple != nullptr) && (filesTree != nullptr) - && (installer->isArchiveSupported(*filesTree))) { + && (installer->isArchiveSupported(filesTree))) { installResult - = installerSimple->install(modName, *filesTree, version, modID); + = installerSimple->install(modName, filesTree, version, modID); if (installResult == IPluginInstaller::RESULT_SUCCESS) { - mapToArchive(filesTree.data()); + + // Note: Have to maintain a pointer to IFileTree because install() takes a + // reference. This could be an issue if ever a plugin creates a new tree that + // is not a ArchiveFileTree. + ArchiveFileTree* p = dynamic_cast(filesTree.get()); + if (p == nullptr) { + throw IncompatibilityException(tr("Invalid file tree returned by plugin.")); + } + p->mapToArchive(m_ArchiveHandler); + // the simple installer only prepares the installation, the rest // works the same for all installers installResult = doInstall(modName, gameName, modID, version, newestVersion, categoryID, fileCategoryID, repository); @@ -848,7 +640,7 @@ IPluginInstaller::EInstallResult InstallationManager::install(const QString &fil = dynamic_cast(installer); if ((installerCustom != nullptr) && (((filesTree != nullptr) - && installer->isArchiveSupported(*filesTree)) + && installer->isArchiveSupported(filesTree)) || ((filesTree == nullptr) && installerCustom->isArchiveSupported(fileName)))) { std::set installerExt @@ -880,9 +672,9 @@ IPluginInstaller::EInstallResult InstallationManager::install(const QString &fil case IPluginInstaller::RESULT_SUCCESS: case IPluginInstaller::RESULT_SUCCESSCANCEL: { if (filesTree != nullptr) { - DirectoryTree::node_iterator iniTweakNode = filesTree->nodeFind(DirectoryTreeInformation("INI Tweaks")); - hasIniTweaks = (iniTweakNode != filesTree->nodesEnd()) && - ((*iniTweakNode)->numLeafs() != 0); + auto iniTweakEntry = filesTree->find("INI Tweaks", FileTreeEntry::DIRECTORY); + hasIniTweaks = iniTweakEntry != nullptr + && !iniTweakEntry->astree()->empty(); } return IPluginInstaller::RESULT_SUCCESS; } break; diff --git a/src/installationmanager.h b/src/installationmanager.h index 199a0f82..008a0916 100644 --- a/src/installationmanager.h +++ b/src/installationmanager.h @@ -20,7 +20,7 @@ along with Mod Organizer. If not, see . #ifndef INSTALLATIONMANAGER_H #define INSTALLATIONMANAGER_H - +#include #include #include #include @@ -107,42 +107,61 @@ public: * @brief register an installer-plugin * @param the installer to register */ - void registerInstaller(MOBase::IPluginInstaller *installer); - + void registerInstaller(MOBase::IPluginInstaller *installer); + /** * @return list of file extensions we can install */ QStringList getSupportedExtensions() const; /** - * @brief extract the specified file from the currently open archive to a temporary location - * @param (relative) name of the file within the archive - * @return the absolute name of the temporary file - * @note the call will fail with an exception if no archive is open (plugins deriving - * from IPluginInstallerSimple can rely on that, custom installers shouldn't) - * @note the temporary file is automatically cleaned up after the installation - * @note This call can be very slow if the archive is large and "solid" + * @brief Extract the specified file from the currently opened archive to a temporary location. + * + * This method cannot be used to extract directory. + * + * @param entry Entry corresponding to the file to extract. + * + * @return the absolute path to the temporary file. + * + * @note The call will fail with an exception if no archive is open (plugins deriving + * from IPluginInstallerSimple can rely on that, custom installers should not). + * @note The temporary file is automatically cleaned up after the installation. + * @note This call can be very slow if the archive is large and "solid". */ - virtual QString extractFile(const QString &fileName); + virtual QString extractFile(std::shared_ptr entry) override; /** - * @brief extract the specified files from the currently open archive to a temporary location - * @param (relative) names of files within the archive - * @return the absolute names of the temporary files - * @note the call will fail with an exception if no archive is open (plugins deriving - * from IPluginInstallerSimple can rely on that, custom installers shouldn't) - * @note the temporary file is automatically cleaned up after the installation - * @note This call can be very slow if the archive is large and "solid" + * @brief Extract the specified files from the currently opened archive to a temporary location. + * + * This method cannot be used to extract directory. + * + * @param entres Entries corresponding to the files to extract. + * + * @return the list of absolute paths to the temporary files. + * + * @note The call will fail with an exception if no archive is open (plugins deriving + * from IPluginInstallerSimple can rely on that, custom installers should not). + * @note The temporary file is automatically cleaned up after the installation. + * @note This call can be very slow if the archive is large and "solid". + * + * The flatten argument is not present here while it is present in the deprecated QStringList + * version for multiple reasons: 1) it was never used, 2) it is kind of fishy because there + * is no way to know if a file is going to be overriden, 3) it is quite easy to flatten a + * IFileTree and thus to given a list of entries flattened (this was not possible with the + * QStringList version since these were based on the name of the file inside the archive). */ - virtual QStringList extractFiles(const QStringList &files, bool flatten); + virtual QStringList extractFiles(std::vector> const& entries) override; /** - * @brief installs an archive - * @param modName suggested name of the mod - * @param archiveFile path to the archive to install - * @return the installation result + * @brief Installs the given archive. + * + * @param modName Suggested name of the mod. + * @param archiveFile Path to the archive to install. + * @param modId ID of the mod, if available. + * + * @return the installation result. */ - virtual MOBase::IPluginInstaller::EInstallResult installArchive(MOBase::GuessedValue &modName, const QString &archiveName, const int &modId = 0); + virtual MOBase::IPluginInstaller::EInstallResult installArchive(MOBase::GuessedValue &modName, const QString &archiveName, int modId = 0) override; /** * @brief test if the specified mod name is free. If not, query the user how to proceed @@ -161,20 +180,9 @@ private: void updateProgressFile(const QString &fileName); void report7ZipError(const QString &errorMessage); - MOBase::DirectoryTree *createFilesTree(); - - // remap all files in the archive to the directory structure represented by baseNode - // files not present in baseNode are disabled - void mapToArchive(const MOBase::DirectoryTree::Node *baseNode); - - // recursive worker function for mapToArchive - void mapToArchive(const MOBase::DirectoryTree::Node *node, QString path, FileData * const *data); + // Recursive worker function for mapToArchive (takes raw reference for "speed"). bool unpackSingleFile(const QString &fileName); - - bool isSimpleArchiveTopLayer(const MOBase::DirectoryTree::Node *node, bool bainStyle); - MOBase::DirectoryTree::Node *getSimpleArchiveBase(MOBase::DirectoryTree *dataTree); - MOBase::IPluginInstaller::EInstallResult doInstall(MOBase::GuessedValue &modName, QString gameName, int modID, const QString &version, const QString &newestVersion, int categoryID, int fileCategoryID, const QString &repository); @@ -205,6 +213,19 @@ private: } }; + /** + * @brief Extract the files from the archived that are not disabled (that have + * output filenames associated with them) to the given path. + * + * @param extractPath Path (on the disk) were the extracted files should be put. + * + * This method is mainly a convenience method for the extractFiles() methods. + * + * @return true if the extraction was successful, false if the extraciton was + * cancelled. If an error occured, an exception is thrown. + */ + bool extractFiles(QDir extractPath); + private: bool m_IsRunning; -- cgit v1.3.1 From 5a90eecc21412fa705837eb724250ef1b758c993 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Thu, 7 May 2020 02:07:15 +0200 Subject: Fix issue with mods not being checked properly and overwrite not having the right priority. --- src/modinfoforeign.h | 1 - src/modinfooverwrite.h | 5 +---- src/modinforegular.cpp | 29 ----------------------------- src/modinforegular.h | 7 ------- src/modinfowithconflictinfo.cpp | 31 +++++++++++++++++++++++++++++++ src/modinfowithconflictinfo.h | 9 +++++++++ 6 files changed, 41 insertions(+), 41 deletions(-) (limited to 'src') diff --git a/src/modinfoforeign.h b/src/modinfoforeign.h index 7725c793..0c15c7a4 100644 --- a/src/modinfoforeign.h +++ b/src/modinfoforeign.h @@ -73,7 +73,6 @@ protected: const QStringList &archives, ModInfo::EModType modType, MOShared::DirectoryEntry **directoryStructure, PluginContainer *pluginContainer); - virtual bool doTestValid() const { return true; } private: QString m_Name; diff --git a/src/modinfooverwrite.h b/src/modinfooverwrite.h index 070ba6ef..10d405bd 100644 --- a/src/modinfooverwrite.h +++ b/src/modinfooverwrite.h @@ -46,7 +46,7 @@ public: virtual QString absolutePath() const override; virtual MOBase::VersionInfo getNewestVersion() const override { return QString(); } virtual QString getInstallationFile() const override { return ""; } - virtual int getFixedPriority() const override { return std::numeric_limits::min(); } + virtual int getFixedPriority() const override { return std::numeric_limits::max(); } virtual QString getGameName() const override { return ""; } virtual int getNexusID() const override { return -1; } virtual QDateTime getExpires() const override { return QDateTime(); } @@ -67,9 +67,6 @@ public: virtual QStringList archives(bool checkOnDisk = false) override; virtual void addInstalledFile(int, int) override {} -protected: - virtual bool doTestValid() const { return true; } - private: ModInfoOverwrite(PluginContainer *pluginContainer, MOShared::DirectoryEntry **directoryStructure ); diff --git a/src/modinforegular.cpp b/src/modinforegular.cpp index 6a0a1b7d..e74c5070 100644 --- a/src/modinforegular.cpp +++ b/src/modinforegular.cpp @@ -1,5 +1,4 @@ #include "modinforegular.h" -#include "installationtester.h" #include "categories.h" #include "messagedialog.h" @@ -271,34 +270,6 @@ void ModInfoRegular::saveMeta() } } -bool ModInfoRegular::doTestValid() const { - - bool valid = false; - QDirIterator dirIter(absolutePath()); - while (dirIter.hasNext()) { - dirIter.next(); - if (dirIter.fileInfo().isDir()) { - if (InstallationTester::isTopLevelDirectory(dirIter.fileName())) { - valid = true; - break; - } - } - else { - if (InstallationTester::isTopLevelSuffix(dirIter.fileName())) { - valid = true; - break; - } - } - } - - // NOTE: in Qt 4.7 it seems that QDirIterator leaves a file handle open if it is not iterated to the - // end - while (dirIter.hasNext()) { - dirIter.next(); - } - - return valid; -} bool ModInfoRegular::updateAvailable() const { diff --git a/src/modinforegular.h b/src/modinforegular.h index 1cef24fb..11a78765 100644 --- a/src/modinforegular.h +++ b/src/modinforegular.h @@ -420,13 +420,6 @@ protected: ModInfoRegular(PluginContainer *pluginContainer, const MOBase::IPluginGame *game, const QDir &path, MOShared::DirectoryEntry **directoryStructure); - /** - * @brief check if the content of this mod is valid. - * - * @return true if the content is valid, false otherwize. - **/ - virtual bool doTestValid() const; - private: QString m_Name; diff --git a/src/modinfowithconflictinfo.cpp b/src/modinfowithconflictinfo.cpp index 2b4fa11c..d0516d06 100644 --- a/src/modinfowithconflictinfo.cpp +++ b/src/modinfowithconflictinfo.cpp @@ -1,4 +1,5 @@ #include "modinfowithconflictinfo.h" +#include "installationtester.h" #include "utility.h" #include "shared/directoryentry.h" #include "shared/filesorigin.h" @@ -291,3 +292,33 @@ bool ModInfoWithConflictInfo::hasHiddenFiles() const return m_HasHiddenFiles; } + + +bool ModInfoWithConflictInfo::doTestValid() const { + + bool valid = false; + QDirIterator dirIter(absolutePath()); + while (dirIter.hasNext()) { + dirIter.next(); + if (dirIter.fileInfo().isDir()) { + if (InstallationTester::isTopLevelDirectory(dirIter.fileName())) { + valid = true; + break; + } + } + else { + if (InstallationTester::isTopLevelSuffix(dirIter.fileName())) { + valid = true; + break; + } + } + } + + // NOTE: in Qt 4.7 it seems that QDirIterator leaves a file handle open if it is not iterated to the + // end + while (dirIter.hasNext()) { + dirIter.next(); + } + + return valid; +} diff --git a/src/modinfowithconflictinfo.h b/src/modinfowithconflictinfo.h index c2a49afb..0bb7c422 100644 --- a/src/modinfowithconflictinfo.h +++ b/src/modinfowithconflictinfo.h @@ -34,6 +34,15 @@ public: virtual void doConflictCheck() const override; +protected: + + /** + * @brief check if the content of this mod is valid. + * + * @return true if the content is valid, false otherwize. + **/ + virtual bool doTestValid() const; + private: enum EConflictType { -- cgit v1.3.1 From ff990c77434a33dd1b500e7704511fed73ea1ea4 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Thu, 7 May 2020 12:17:27 +0200 Subject: Fix issue with getColor() not being overriden properly. --- src/modinforegular.cpp | 2 +- src/modinforegular.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/modinforegular.cpp b/src/modinforegular.cpp index e74c5070..d248f530 100644 --- a/src/modinforegular.cpp +++ b/src/modinforegular.cpp @@ -559,7 +559,7 @@ void ModInfoRegular::setColor(QColor color) m_MetaInfoChanged = true; } -QColor ModInfoRegular::getColor() +QColor ModInfoRegular::getColor() const { return m_Color; } diff --git a/src/modinforegular.h b/src/modinforegular.h index 11a78765..b4a7bfd2 100644 --- a/src/modinforegular.h +++ b/src/modinforegular.h @@ -388,7 +388,7 @@ public: virtual void setColor(QColor color) override; - virtual QColor getColor(); + virtual QColor getColor() const override; virtual void addInstalledFile(int modId, int fileId) override; -- cgit v1.3.1 From f405a51bf63373403cb0682dff243a90219af2bb Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Thu, 7 May 2020 16:22:07 +0200 Subject: Override astree() in ArchiveFileTreeImpl to avoid VS warnings. --- src/archivefiletree.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'src') diff --git a/src/archivefiletree.cpp b/src/archivefiletree.cpp index b629cc8e..ffee7612 100644 --- a/src/archivefiletree.cpp +++ b/src/archivefiletree.cpp @@ -73,6 +73,16 @@ public: // Public for make_shared (but not accessible by other since not exposed ArchiveFileTreeImpl(std::shared_ptr parent, QString name, int index, std::vector&& files) : FileTreeEntry(parent, name), ArchiveFileEntry(parent, name, index), IFileTree(), m_Files(std::move(files)) { } +public: // Override to avoid VS warnings: + + virtual std::shared_ptr astree() override { + return IFileTree::astree(); + } + + virtual std::shared_ptr astree() const override { + return IFileTree::astree(); + } + public: // Overrides: /** -- cgit v1.3.1 From 411b73e0aa68899d04b309b443aebf8de4b1a057 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Thu, 7 May 2020 19:18:58 +0200 Subject: Fix issue with some archives not being extracted correctly. --- src/archivefiletree.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/archivefiletree.cpp b/src/archivefiletree.cpp index ffee7612..77b17112 100644 --- a/src/archivefiletree.cpp +++ b/src/archivefiletree.cpp @@ -158,7 +158,7 @@ protected: // We know that the files are sorted: QString currentName = ""; - int currentIndex; + int currentIndex = -1; std::vector currentFiles; for (auto& p : m_Files) { @@ -167,7 +167,6 @@ protected: // one for a/b while we would want the one for a, but we correct that later): if (currentName == "") { currentName = std::get<0>(p)[0]; - currentIndex = std::get<2>(p); } // If the name is different, we need to create a directory from what we have @@ -178,7 +177,7 @@ protected: currentFiles.clear(); // Back to a valid state. // Retrieve the next index: - currentIndex = std::get<2>(p); + currentIndex = -1; } // We can always override the current name: -- cgit v1.3.1 From b2bd13e3db1d1469c59527fb90e763a8e42dd1f8 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Thu, 7 May 2020 22:09:40 +0200 Subject: Fix another issue with some archives not being loading properly. --- src/archivefiletree.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/archivefiletree.cpp b/src/archivefiletree.cpp index 77b17112..c95cc3cd 100644 --- a/src/archivefiletree.cpp +++ b/src/archivefiletree.cpp @@ -191,9 +191,11 @@ protected: std::make_shared(parent, currentName, std::get<2>(p), QDateTime())); currentName = ""; } - // Otherwize, it is the actual "file" corresponding to the directory, so we can retrieve - // the index here: - currentIndex = std::get<2>(p); + else { + // Otherwize, it is the actual "file" corresponding to the directory, so we can retrieve + // the index here: + currentIndex = std::get<2>(p); + } } else { currentFiles.push_back({ -- cgit v1.3.1 From 572fa7050f3dddf7a1190e07a2692a5ae9d07766 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Fri, 8 May 2020 21:43:27 +0200 Subject: Minor comment updates. --- src/archivefiletree.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/archivefiletree.cpp b/src/archivefiletree.cpp index c95cc3cd..96b7e42b 100644 --- a/src/archivefiletree.cpp +++ b/src/archivefiletree.cpp @@ -162,9 +162,7 @@ protected: std::vector currentFiles; for (auto& p : m_Files) { - // At the start or if we have reset, just retrieve the current name and index - The - // index might not be valid in this case (e.g., if the path is a/b, the index is the - // one for a/b while we would want the one for a, but we correct that later): + // At the start or if we have reset, just retrieve the current name: if (currentName == "") { currentName = std::get<0>(p)[0]; } @@ -172,11 +170,14 @@ protected: // If the name is different, we need to create a directory from what we have // accumulated: if (currentName != std::get<0>(p)[0]) { - // No index here since this is not an empty tree: + + // We may or may not have an index here, it depends on the type of archive (some archives list + // intermediate non-empty folders, some don't): entries.push_back(std::make_shared(parent, currentName, currentIndex, std::move(currentFiles))); + currentFiles.clear(); // Back to a valid state. - // Retrieve the next index: + // Reset the index: currentIndex = -1; } @@ -185,6 +186,7 @@ protected: // If the current path contains only one components: if (std::get<0>(p).size() == 1) { + // If it is not a directory, then it is a file in directly under this tree: if (!std::get<1>(p)) { entries.push_back( @@ -192,7 +194,7 @@ protected: currentName = ""; } else { - // Otherwize, it is the actual "file" corresponding to the directory, so we can retrieve + // Otherwize, it is the actual "file" corresponding to the directory we are listing, so we can retrieve // the index here: currentIndex = std::get<2>(p); } -- cgit v1.3.1 From 0a3dade905e4112b8ba2eb29b3781ee03b243ea6 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Tue, 12 May 2020 19:32:31 +0200 Subject: Update after const_cast removal in uibase. --- src/archivefiletree.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/archivefiletree.cpp b/src/archivefiletree.cpp index 96b7e42b..6bd2b820 100644 --- a/src/archivefiletree.cpp +++ b/src/archivefiletree.cpp @@ -40,7 +40,7 @@ public: * @param index The index of the file in the archive. * @param time The modification time of this file. */ - ArchiveFileEntry(std::shared_ptr parent, QString name, int index, QDateTime time) : + ArchiveFileEntry(std::shared_ptr parent, QString name, int index, QDateTime time) : FileTreeEntry(parent, name, time), m_Index(index) { } @@ -51,7 +51,7 @@ public: * @param name The name of this directory. * @param index The index of the directory in the archive, or -1. */ - ArchiveFileEntry(std::shared_ptr parent, QString name, int index) : + ArchiveFileEntry(std::shared_ptr parent, QString name, int index) : FileTreeEntry(parent, name), m_Index(index) { } @@ -70,7 +70,7 @@ public: public: // Public for make_shared (but not accessible by other since not exposed in .h): - ArchiveFileTreeImpl(std::shared_ptr parent, QString name, int index, std::vector&& files) + ArchiveFileTreeImpl(std::shared_ptr parent, QString name, int index, std::vector&& files) : FileTreeEntry(parent, name), ArchiveFileEntry(parent, name, index), IFileTree(), m_Files(std::move(files)) { } public: // Override to avoid VS warnings: @@ -145,11 +145,11 @@ protected: * @override */ virtual std::shared_ptr makeDirectory( - std::shared_ptr parent, QString name) const override { + std::shared_ptr parent, QString name) const override { return std::make_shared(parent, name, -1, std::vector{}); } - virtual void doPopulate(std::shared_ptr parent, std::vector>& entries) const override { + virtual void doPopulate(std::shared_ptr parent, std::vector>& entries) const override { // Sort by name: std::sort(std::begin(m_Files), std::end(m_Files), -- cgit v1.3.1 From 04698417a93a51acc80112feab170f3aff7572b7 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Sat, 16 May 2020 14:00:24 +0200 Subject: Add implementation of clone() and doClone() for the archive filetree. --- src/archivefiletree.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/archivefiletree.cpp b/src/archivefiletree.cpp index 6bd2b820..0b715f4f 100644 --- a/src/archivefiletree.cpp +++ b/src/archivefiletree.cpp @@ -55,6 +55,10 @@ public: FileTreeEntry(parent, name), m_Index(index) { } + virtual std::shared_ptr clone() const override { + return std::make_shared(nullptr, name(), m_Index); + } + // No private since we are in an implementation file: const int m_Index; }; @@ -70,7 +74,7 @@ public: public: // Public for make_shared (but not accessible by other since not exposed in .h): - ArchiveFileTreeImpl(std::shared_ptr parent, QString name, int index, std::vector&& files) + ArchiveFileTreeImpl(std::shared_ptr parent, QString name, int index, std::vector files) : FileTreeEntry(parent, name), ArchiveFileEntry(parent, name, index), IFileTree(), m_Files(std::move(files)) { } public: // Override to avoid VS warnings: @@ -83,6 +87,12 @@ public: // Override to avoid VS warnings: return IFileTree::astree(); } +protected: + + virtual std::shared_ptr clone() const override { + return IFileTree::clone(); + } + public: // Overrides: /** @@ -211,6 +221,10 @@ protected: } } + virtual std::shared_ptr doClone() const override { + return std::make_shared(nullptr, name(), m_Index, m_Files); + } + private: mutable std::vector m_Files; -- cgit v1.3.1 From c391c2b584a309f64f87dd5e00b0083279e6211c Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Tue, 19 May 2020 18:00:12 +0200 Subject: Rollback to setGameName instead of setGamePlugin for mod interface. --- src/modinfo.h | 10 +++++----- src/modinfobackup.h | 2 +- src/modinfodialognexus.cpp | 2 +- src/modinfoforeign.h | 2 +- src/modinfooverwrite.h | 2 +- src/modinforegular.cpp | 6 ++---- src/modinforegular.h | 18 ++++++++++-------- src/modinfoseparator.h | 2 +- 8 files changed, 22 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/modinfo.h b/src/modinfo.h index d611fa87..2c108378 100644 --- a/src/modinfo.h +++ b/src/modinfo.h @@ -319,11 +319,11 @@ public: virtual void setNotes(const QString ¬es) = 0; /** - * @brief set/change the game plgin for this mod - * - * @param gamePlugin the game plugin - */ - virtual void setGamePlugin(const MOBase::IPluginGame* gamePlugin) = 0; + * @brief set/change the source game of this mod + * + * @param gameName the source game shortName + */ + virtual void setGameName(const QString& gameName) = 0; /** * @brief set the name of this mod diff --git a/src/modinfobackup.h b/src/modinfobackup.h index bc340b3e..9eba545c 100644 --- a/src/modinfobackup.h +++ b/src/modinfobackup.h @@ -16,7 +16,7 @@ public: virtual bool updateIgnored() const override { return false; } virtual bool downgradeAvailable() const override { return false; } virtual bool updateNXMInfo() override { return false; } - virtual void setGamePlugin(const MOBase::IPluginGame*) override {} + virtual void setGameName(const QString& gameName) override {} virtual void setNexusID(int) override {} virtual void endorse(bool) override {} virtual int getFixedPriority() const override { return -1; } diff --git a/src/modinfodialognexus.cpp b/src/modinfodialognexus.cpp index 447beea6..8d99d230 100644 --- a/src/modinfodialognexus.cpp +++ b/src/modinfodialognexus.cpp @@ -324,7 +324,7 @@ void NexusTab::onSourceGameChanged() for (auto game : plugin().plugins()) { if (game->gameName() == ui->sourceGame->currentText()) { - mod().setGamePlugin(game); + mod().setGameName(game->gameShortName()); mod().setLastNexusQuery(QDateTime::fromSecsSinceEpoch(0)); refreshData(mod().getNexusID()); return; diff --git a/src/modinfoforeign.h b/src/modinfoforeign.h index 0c15c7a4..da2c865b 100644 --- a/src/modinfoforeign.h +++ b/src/modinfoforeign.h @@ -22,7 +22,7 @@ public: virtual bool setName(const QString&) override { return false; } virtual void setComments(const QString&) override {} virtual void setNotes(const QString&) override {} - virtual void setGamePlugin(const MOBase::IPluginGame*) override {} + virtual void setGameName(const QString& gameName) override {} virtual void setNexusID(int) override {} virtual void setNewestVersion(const MOBase::VersionInfo&) override {} virtual void ignoreUpdate(bool) override {} diff --git a/src/modinfooverwrite.h b/src/modinfooverwrite.h index 10d405bd..0bcf6f27 100644 --- a/src/modinfooverwrite.h +++ b/src/modinfooverwrite.h @@ -24,7 +24,7 @@ public: virtual bool setName(const QString&) override { return false; } virtual void setComments(const QString&) override {} virtual void setNotes(const QString&) override {} - virtual void setGamePlugin(const MOBase::IPluginGame*) override {} + virtual void setGameName(const QString& gameName) override {} virtual void setNexusID(int) override {} virtual void setNewestVersion(const MOBase::VersionInfo&) override {} virtual void ignoreUpdate(bool) override {} diff --git a/src/modinforegular.cpp b/src/modinforegular.cpp index d248f530..a1ef5701 100644 --- a/src/modinforegular.cpp +++ b/src/modinforegular.cpp @@ -468,12 +468,10 @@ void ModInfoRegular::setNotes(const QString ¬es) m_MetaInfoChanged = true; } -void ModInfoRegular::setGamePlugin(const MOBase::IPluginGame* gamePlugin) +void ModInfoRegular::setGameName(const QString& gameName) { - m_GamePlugin = gamePlugin; - m_GameName = gamePlugin->gameShortName(); + m_GameName = gameName; m_MetaInfoChanged = true; - testValid(); } void ModInfoRegular::setNexusID(int modID) diff --git a/src/modinforegular.h b/src/modinforegular.h index b4a7bfd2..75b24a6b 100644 --- a/src/modinforegular.h +++ b/src/modinforegular.h @@ -105,11 +105,11 @@ public: void setNotes(const QString ¬es) override; /** - * @brief set/change the game plgin for this mod - * - * @param gamePlugin the game plugin - */ - virtual void setGamePlugin(const MOBase::IPluginGame* gamePlugin) override; + * @brief set/change the source game of this mod + * + * @param gameName the source game shortName + */ + virtual void setGameName(const QString& gameName) override; /** * @brief set/change the nexus mod id of this mod @@ -432,9 +432,11 @@ private: QString m_CustomURL; bool m_HasCustomURL; - // Storing both the game name and game plugin since we can have a game - // name that is not one of the primary names of the game plugin: - const MOBase::IPluginGame* m_GamePlugin; + // Current game plugin running in MO2: + MOBase::IPluginGame const* m_GamePlugin; + + // Game name for the mod, can be different from the actual game running in MO2 + // e.g., for Skyrim / Skyrim SE. QString m_GameName; mutable QStringList m_Archives; diff --git a/src/modinfoseparator.h b/src/modinfoseparator.h index d715d548..80734bcf 100644 --- a/src/modinfoseparator.h +++ b/src/modinfoseparator.h @@ -21,7 +21,7 @@ public: virtual bool setName(const QString& name); virtual int getNexusID() const override { return -1; } - virtual void setGamePlugin(const MOBase::IPluginGame* /*gamePlugin*/) override {} + virtual void setGameName(const QString& gameName) override {} virtual void setNexusID(int /*modID*/) override {} virtual void endorse(bool /*doEndorse*/) override {} virtual void ignoreUpdate(bool /*ignore*/) override {} -- cgit v1.3.1