/*
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
#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)
**/
MOBase::IPluginInstaller::EInstallResult 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 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(std::shared_ptr entry) override;
/**
* @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(std::vector> const& entries) override;
/**
* @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, int modId = 0) override;
/**
* @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);
// Recursive worker function for mapToArchive (takes raw reference for "speed").
bool unpackSingleFile(const QString &fileName);
MOBase::IPluginInstaller::EInstallResult 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;
}
};
/**
* @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;
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