Merge branch 'launcher-datadirs' into 'master'

Make launcher handle data dirs #2858 and BSA

See merge request OpenMW/openmw!192
This commit is contained in:
psi29a 2022-04-27 17:31:52 +00:00
commit 66a96bfa5e
14 changed files with 662 additions and 54 deletions

View file

@ -7,7 +7,9 @@
#include <components/files/configurationmanager.hpp>
const char Config::GameSettings::sArchiveKey[] = "fallback-archive";
const char Config::GameSettings::sContentKey[] = "content";
const char Config::GameSettings::sDirectoryKey[] = "data";
Config::GameSettings::GameSettings(Files::ConfigurationManager &cfg)
: mCfgMgr(cfg)
@ -63,6 +65,14 @@ void Config::GameSettings::validatePaths()
}
}
std::string Config::GameSettings::getGlobalDataDir() const
{
// global data dir may not exists if OpenMW is not installed (ie if run from build directory)
if (boost::filesystem::exists(mCfgMgr.getGlobalDataPath()))
return boost::filesystem::canonical(mCfgMgr.getGlobalDataPath()).string();
return {};
}
QStringList Config::GameSettings::values(const QString &key, const QStringList &defaultValues) const
{
if (!mSettings.values(key).isEmpty())
@ -475,13 +485,29 @@ bool Config::GameSettings::hasMaster()
return result;
}
void Config::GameSettings::setContentList(const QStringList& fileNames)
void Config::GameSettings::setContentList(const QStringList& dirNames, const QStringList& archiveNames, const QStringList& fileNames)
{
remove(sContentKey);
for (const QString& fileName : fileNames)
auto const reset = [this](const char* key, const QStringList& list)
{
setMultiValue(sContentKey, fileName);
}
remove(key);
for (auto const& item : list)
setMultiValue(key, item);
};
reset(sDirectoryKey, dirNames);
reset(sArchiveKey, archiveNames);
reset(sContentKey, fileNames);
}
QStringList Config::GameSettings::getDataDirs() const
{
return Config::LauncherSettings::reverse(mDataDirs);
}
QStringList Config::GameSettings::getArchiveList() const
{
// QMap returns multiple rows in LIFO order, so need to reverse
return Config::LauncherSettings::reverse(values(sArchiveKey));
}
QStringList Config::GameSettings::getContentList() const

View file

@ -53,7 +53,8 @@ namespace Config
mUserSettings.remove(key);
}
inline QStringList getDataDirs() const { return mDataDirs; }
QStringList getDataDirs() const;
std::string getGlobalDataDir() const;
inline void removeDataDir(const QString &dir) { if(!dir.isEmpty()) mDataDirs.removeAll(dir); }
inline void addDataDir(const QString &dir) { if(!dir.isEmpty()) mDataDirs.append(dir); }
@ -70,7 +71,8 @@ namespace Config
bool writeFile(QTextStream &stream);
bool writeFileWithComments(QFile &file);
void setContentList(const QStringList& fileNames);
QStringList getArchiveList() const;
void setContentList(const QStringList& dirNames, const QStringList& archiveNames, const QStringList& fileNames);
QStringList getContentList() const;
void clear();
@ -85,7 +87,9 @@ namespace Config
QStringList mDataDirs;
QString mDataLocal;
static const char sArchiveKey[];
static const char sContentKey[];
static const char sDirectoryKey[];
static bool isOrderedLine(const QString& line) ;
};

View file

@ -7,9 +7,15 @@
#include <QDebug>
#include <boost/filesystem/operations.hpp>
#include <components/files/configurationmanager.hpp>
const char Config::LauncherSettings::sCurrentContentListKey[] = "Profiles/currentprofile";
const char Config::LauncherSettings::sLauncherConfigFileName[] = "launcher.cfg";
const char Config::LauncherSettings::sContentListsSectionPrefix[] = "Profiles/";
const char Config::LauncherSettings::sDirectoryListSuffix[] = "/data";
const char Config::LauncherSettings::sArchiveListSuffix[] = "/fallback-archive";
const char Config::LauncherSettings::sContentListSuffix[] = "/content";
QStringList Config::LauncherSettings::subKeys(const QString &key)
@ -86,6 +92,16 @@ QStringList Config::LauncherSettings::getContentLists()
return subKeys(QString(sContentListsSectionPrefix));
}
QString Config::LauncherSettings::makeDirectoryListKey(const QString& contentListName)
{
return QString(sContentListsSectionPrefix) + contentListName + QString(sDirectoryListSuffix);
}
QString Config::LauncherSettings::makeArchiveListKey(const QString& contentListName)
{
return QString(sContentListsSectionPrefix) + contentListName + QString(sArchiveListSuffix);
}
QString Config::LauncherSettings::makeContentListKey(const QString& contentListName)
{
return QString(sContentListsSectionPrefix) + contentListName + QString(sContentListSuffix);
@ -94,18 +110,28 @@ QString Config::LauncherSettings::makeContentListKey(const QString& contentListN
void Config::LauncherSettings::setContentList(const GameSettings& gameSettings)
{
// obtain content list from game settings (if present)
QStringList dirs(gameSettings.getDataDirs());
const QStringList archives(gameSettings.getArchiveList());
const QStringList files(gameSettings.getContentList());
// if openmw.cfg has no content, exit so we don't create an empty content list.
if (files.isEmpty())
if (dirs.isEmpty() || files.isEmpty())
{
return;
}
// global and local data directories are not part of any profile
const auto globalDataDir = QString(gameSettings.getGlobalDataDir().c_str());
const auto dataLocal = gameSettings.getDataLocal();
dirs.removeAll(globalDataDir);
dirs.removeAll(dataLocal);
// if any existing profile in launcher matches the content list, make that profile the default
for (const QString &listName : getContentLists())
{
if (isEqual(files, getContentListFiles(listName)))
if (isEqual(files, getContentListFiles(listName)) &&
isEqual(archives, getArchiveList(listName)) &&
isEqual(dirs, getDataDirectoryList(listName)))
{
setCurrentContentListName(listName);
return;
@ -115,11 +141,13 @@ void Config::LauncherSettings::setContentList(const GameSettings& gameSettings)
// otherwise, add content list
QString newContentListName(makeNewContentListName());
setCurrentContentListName(newContentListName);
setContentList(newContentListName, files);
setContentList(newContentListName, dirs, archives, files);
}
void Config::LauncherSettings::removeContentList(const QString &contentListName)
{
remove(makeDirectoryListKey(contentListName));
remove(makeArchiveListKey(contentListName));
remove(makeContentListKey(contentListName));
}
@ -129,14 +157,18 @@ void Config::LauncherSettings::setCurrentContentListName(const QString &contentL
setValue(QString(sCurrentContentListKey), contentListName);
}
void Config::LauncherSettings::setContentList(const QString& contentListName, const QStringList& fileNames)
void Config::LauncherSettings::setContentList(const QString& contentListName, const QStringList& dirNames, const QStringList& archiveNames, const QStringList& fileNames)
{
removeContentList(contentListName);
QString key = makeContentListKey(contentListName);
for (const QString& fileName : fileNames)
auto const assign = [this](const QString key, const QStringList& list)
{
setMultiValue(key, fileName);
}
for (auto const& item : list)
setMultiValue(key, item);
};
removeContentList(contentListName);
assign(makeDirectoryListKey(contentListName), dirNames);
assign(makeArchiveListKey(contentListName), archiveNames);
assign(makeContentListKey(contentListName), fileNames);
}
QString Config::LauncherSettings::getCurrentContentListName() const
@ -144,6 +176,17 @@ QString Config::LauncherSettings::getCurrentContentListName() const
return value(QString(sCurrentContentListKey));
}
QStringList Config::LauncherSettings::getDataDirectoryList(const QString& contentListName) const
{
// QMap returns multiple rows in LIFO order, so need to reverse
return reverse(getSettings().values(makeDirectoryListKey(contentListName)));
}
QStringList Config::LauncherSettings::getArchiveList(const QString& contentListName) const
{
// QMap returns multiple rows in LIFO order, so need to reverse
return reverse(getSettings().values(makeArchiveListKey(contentListName)));
}
QStringList Config::LauncherSettings::getContentListFiles(const QString& contentListName) const
{
// QMap returns multiple rows in LIFO order, so need to reverse

View file

@ -18,7 +18,7 @@ namespace Config
void setContentList(const GameSettings& gameSettings);
/// Create a Content List (or replace if it already exists)
void setContentList(const QString& contentListName, const QStringList& fileNames);
void setContentList(const QString& contentListName, const QStringList& dirNames, const QStringList& archiveNames, const QStringList& fileNames);
void removeContentList(const QString &contentListName);
@ -26,15 +26,23 @@ namespace Config
QString getCurrentContentListName() const;
QStringList getDataDirectoryList(const QString& contentListName) const;
QStringList getArchiveList(const QString& contentListName) const;
QStringList getContentListFiles(const QString& contentListName) const;
/// \return new list that is reversed order of input
static QStringList reverse(const QStringList& toReverse);
static const char sLauncherConfigFileName[];
private:
/// \return key to use to get/set the files in the specified data Directory List
static QString makeDirectoryListKey(const QString& contentListName);
/// \return key to use to get/set the files in the specified Archive List
static QString makeArchiveListKey(const QString& contentListName);
/// \return key to use to get/set the files in the specified Content List
static QString makeContentListKey(const QString& contentListName);
@ -51,6 +59,8 @@ namespace Config
/// section of launcher.cfg holding the Content Lists
static const char sContentListsSectionPrefix[];
static const char sDirectoryListSuffix[];
static const char sArchiveListSuffix[];
static const char sContentListSuffix[];
};
}

View file

@ -160,6 +160,15 @@ QVariant ContentSelectorModel::ContentModel::data(const QModelIndex &index, int
return isLoadOrderError(file) ? mWarningIcon : QVariant();
}
case Qt::BackgroundRole:
{
if (isNew(file->fileName()))
{
return QVariant(QColor(Qt::green));
}
return QVariant();
}
case Qt::EditRole:
case Qt::DisplayRole:
{
@ -413,7 +422,7 @@ void ContentSelectorModel::ContentModel::addFile(EsmFile *file)
emit dataChanged (idx, idx);
}
void ContentSelectorModel::ContentModel::addFiles(const QString &path)
void ContentSelectorModel::ContentModel::addFiles(const QString &path, bool newfiles)
{
QDir dir(path);
QStringList filters;
@ -471,6 +480,7 @@ void ContentSelectorModel::ContentModel::addFiles(const QString &path)
// Put the file in the table
addFile(file);
setNew(file->fileName(), newfiles);
} catch(std::runtime_error &e) {
// An error occurred while reading the .esp
@ -481,6 +491,16 @@ void ContentSelectorModel::ContentModel::addFiles(const QString &path)
}
}
bool ContentSelectorModel::ContentModel::containsDataFiles(const QString &path)
{
QDir dir(path);
QStringList filters;
filters << "*.esp" << "*.esm" << "*.omwgame" << "*.omwaddon";
dir.setNameFilters(filters);
return dir.entryList().count() != 0;
}
void ContentSelectorModel::ContentModel::clearFiles()
{
const int filesCount = mFiles.count();
@ -553,6 +573,28 @@ bool ContentSelectorModel::ContentModel::isEnabled (const QModelIndex& index) co
return (flags(index) & Qt::ItemIsEnabled);
}
bool ContentSelectorModel::ContentModel::isNew(const QString& filepath) const
{
if (mNewFiles.contains(filepath))
return mNewFiles[filepath];
return false;
}
void ContentSelectorModel::ContentModel::setNew(const QString &filepath, bool isNew)
{
if (filepath.isEmpty())
return;
const EsmFile *file = item(filepath);
if (!file)
return;
mNewFiles[filepath] = isNew;
}
bool ContentSelectorModel::ContentModel::isLoadOrderError(const EsmFile *file) const
{
return mPluginsWithLoadOrderError.contains(file->filePath());

View file

@ -43,8 +43,9 @@ namespace ContentSelectorModel
QMimeData *mimeData(const QModelIndexList &indexes) const override;
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override;
void addFiles(const QString &path);
void addFiles(const QString &path, bool newfiles);
void sortFiles();
bool containsDataFiles(const QString &path);
void clearFiles();
QModelIndex indexFromItem(const EsmFile *item) const;
@ -56,6 +57,8 @@ namespace ContentSelectorModel
bool isEnabled (const QModelIndex& index) const;
bool isChecked(const QString &filepath) const;
bool setCheckState(const QString &filepath, bool isChecked);
bool isNew(const QString &filepath) const;
void setNew(const QString &filepath, bool isChecked);
void setContentList(const QStringList &fileList);
ContentFileList checkedItems() const;
void uncheckAll();
@ -79,7 +82,9 @@ namespace ContentSelectorModel
QString toolTip(const EsmFile *file) const;
ContentFileList mFiles;
QStringList mArchives;
QHash<QString, Qt::CheckState> mCheckStates;
QHash<QString, bool> mNewFiles;
QSet<QString> mPluginsWithLoadOrderError;
QString mEncoding;
QIcon mWarningIcon;

View file

@ -153,9 +153,9 @@ ContentSelectorModel::ContentFileList
return mContentModel->checkedItems();
}
void ContentSelectorView::ContentSelector::addFiles(const QString &path)
void ContentSelectorView::ContentSelector::addFiles(const QString &path, bool newfiles)
{
mContentModel->addFiles(path);
mContentModel->addFiles(path, newfiles);
// add any game files to the combo box
for (const QString& gameFileName : mContentModel->gameFiles())
@ -178,6 +178,11 @@ void ContentSelectorView::ContentSelector::sortFiles()
mContentModel->sortFiles();
}
bool ContentSelectorView::ContentSelector::containsDataFiles(const QString &path)
{
return mContentModel->containsDataFiles(path);
}
void ContentSelectorView::ContentSelector::clearFiles()
{
mContentModel->clearFiles();

View file

@ -27,8 +27,9 @@ namespace ContentSelectorView
QString currentFile() const;
void addFiles(const QString &path);
void addFiles(const QString &path, bool newfiles = false);
void sortFiles();
bool containsDataFiles(const QString &path);
void clearFiles();
void setProfileContent (const QStringList &fileList);