mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-05-01 14:27:59 +03:00
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:
commit
66a96bfa5e
14 changed files with 662 additions and 54 deletions
|
@ -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
|
||||
|
|
|
@ -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) ;
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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[];
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue