/* Copyright (C) 2012 Sebastian Herbord. All rights reserved. This file is part of Mod Organizer. Mod Organizer is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Mod Organizer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Mod Organizer. If not, see . */ #ifndef INSTALLATIONMANAGER_H #define INSTALLATIONMANAGER_H #include #include #include #include #define WIN32_LEAN_AND_MEAN #include #include #include #include #include /** * @brief manages the installation of mod archives * This currently supports two special kind of archives: * - "simple" archives: properly packaged archives without options, so they can be extracted to the (virtual) data directory directly * - "complex" bain archives: archives with options for the bain system. * All other archives are managed through the manual "InstallDialog" * @todo this may be a good place to support plugins **/ class InstallationManager : public QObject, public MOBase::IInstallationManager { Q_OBJECT public: /** * @brief constructor * * @param parent parent object. **/ explicit InstallationManager(); virtual ~InstallationManager(); void setParentWidget(QWidget *widget); void setURL(const QString &url); /** * @brief update the directory where mods are to be installed * @param modsDirectory the mod directory * @note this is called a lot, probably redundantly */ void setModsDirectory(const QString &modsDirectory) { m_ModsDirectory = modsDirectory; } /** * @brief update the directory where downloads are stored * @param downloadDirectory the download directory */ void setDownloadDirectory(const QString &downloadDirectory) { m_DownloadsDirectory = downloadDirectory; } /** * @brief install a mod from an archive * * @param fileName absolute file name of the archive to install * @param modName suggested name of the mod. If this is empty (the default), a name will be guessed based on the filename. The user will always have a chance to rename the mod * @return true if the archive was installed, false if installation failed or was refused * @exception std::exception an exception may be thrown if the archive can't be opened (maybe the format is invalid or the file is damaged) **/ bool install(const QString &fileName, MOBase::GuessedValue &modName, bool &hasIniTweaks, int modID = 0); /** * @return true if the installation was canceled **/ bool wasCancelled(); /** * @return true if an installation is currently in progress **/ bool isRunning(); /** * @brief retrieve a string describing the specified error code * * @param errorCode an error code as returned by the archiving function * @return the error string * @todo This function doesn't belong here, it is only public because the SelfUpdater class also uses "Archive" to get to the package.txt file **/ static QString getErrorString(Archive::Error errorCode); /** * @brief register an installer-plugin * @param the installer to register */ 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" */ virtual QString extractFile(const QString &fileName); /** * @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" */ virtual QStringList extractFiles(const QStringList &files, bool flatten); /** * @brief installs an archive * @param modName suggested name of the mod * @param archiveFile path to the archive to install * @return the installation result */ virtual MOBase::IPluginInstaller::EInstallResult installArchive(MOBase::GuessedValue &modName, const QString &archiveName, const int &modId = 0); /** * @brief test if the specified mod name is free. If not, query the user how to proceed * @param modName current possible names for the mod * @param merge if this value is not null, the value will be set to whether the use chose to merge or replace * @return true if we can proceed with the installation, false if the user canceled or in case of an unrecoverable error */ virtual bool testOverwrite(MOBase::GuessedValue &modName, bool *merge = nullptr) const; QString generateBackupName(const QString &directoryName) const; private: void queryPassword(QString *password); void updateProgress(float percentage); 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); bool unpackSingleFile(const QString &fileName); bool isSimpleArchiveTopLayer(const MOBase::DirectoryTree::Node *node, bool bainStyle); MOBase::DirectoryTree::Node *getSimpleArchiveBase(MOBase::DirectoryTree *dataTree); bool doInstall(MOBase::GuessedValue &modName, QString gameName, int modID, const QString &version, const QString &newestVersion, int categoryID, int fileCategoryID, const QString &repository); //QString generateBackupName(const QString &directoryName) const; bool ensureValidModName(MOBase::GuessedValue &name) const; void postInstallCleanup(); signals: void progressUpdate(float percentage); void progressUpdate(QString const fileName); private: struct ByPriority { bool operator()(MOBase::IPluginInstaller *LHS, MOBase::IPluginInstaller *RHS) const { return LHS->priority() > RHS->priority(); } }; struct CaseInsensitive { bool operator() (const QString &LHS, const QString &RHS) const { return QString::compare(LHS, RHS, Qt::CaseInsensitive) < 0; } }; private: bool m_IsRunning; QWidget *m_ParentWidget; QString m_ModsDirectory; QString m_DownloadsDirectory; std::vector m_Installers; std::set m_SupportedExtensions; Archive *m_ArchiveHandler; QString m_CurrentFile; QString m_ErrorMessage; QProgressDialog *m_InstallationProgress { nullptr }; int m_Progress; QString m_ProgressFile; std::set m_TempFilesToDelete; QString m_URL; }; #endif // INSTALLATIONMANAGER_H