summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeremy Rimpo <jeremy.rimpo@servermonkey.com>2025-05-23 14:08:01 -0500
committerGitHub <noreply@github.com>2025-05-23 14:08:01 -0500
commit3a8ea3cccee06397e90305e1e39a305f8b7c3cb3 (patch)
tree6c16bf9825bf3f1613a8536d4702f24667fc4c80
parent5d766e94a7a3b3fa861d57d3ad6ae619d98dd5d8 (diff)
Oblivion Remastered Meta PR (#2241)
* Allow for mod directory maps - Set main data files based on game - Mapped mod directories to VFS - Update overwrite setup * Skip if mod contains no 'data' dir * More mod directory compatibility fixes * Workaround for Obl:Rem save location - SLocalSavePath does nothing yet MO2 wants to use it to override the default save location - This only applies to BGS games anyway, we should move this logic * First pass for overwrite mod directory support * More overwrite move / delete restrictions * Fix issue with moving directories that are not required * Formatting pass * More modDataDirectory updates * Formatting --------- Co-authored-by: Mikaƫl Capelle <capelle.mikael@gmail.com>
-rw-r--r--src/directoryrefresher.cpp11
-rw-r--r--src/mainwindow.cpp5
-rw-r--r--src/moapplication.cpp1
-rw-r--r--src/modinfodialog.cpp29
-rw-r--r--src/modinfooverwrite.cpp23
-rw-r--r--src/modlist.cpp49
-rw-r--r--src/modlistviewactions.cpp63
-rw-r--r--src/organizercore.cpp53
-rw-r--r--src/organizercore.h1
-rw-r--r--src/overwriteinfodialog.cpp48
-rw-r--r--src/overwriteinfodialog.h29
-rw-r--r--src/pluginlist.cpp16
-rw-r--r--src/savestab.cpp5
-rw-r--r--src/shared/directoryentry.cpp25
14 files changed, 280 insertions, 78 deletions
diff --git a/src/directoryrefresher.cpp b/src/directoryrefresher.cpp
index 9afbfca8..09defe90 100644
--- a/src/directoryrefresher.cpp
+++ b/src/directoryrefresher.cpp
@@ -179,10 +179,13 @@ void DirectoryRefresher::setMods(
m_Mods.clear();
for (auto mod = mods.begin(); mod != mods.end(); ++mod) {
- QString name = std::get<0>(*mod);
- ModInfo::Ptr info = ModInfo::getByIndex(ModInfo::getIndex(name));
- m_Mods.push_back(EntryInfo(name, std::get<1>(*mod), info->stealFiles(),
- info->archives(), std::get<2>(*mod)));
+ QString name = std::get<0>(*mod);
+ ModInfo::Ptr info = ModInfo::getByIndex(ModInfo::getIndex(name));
+ QString path = std::get<1>(*mod);
+ QString modDataDir = m_Core.managedGame()->modDataDirectory();
+ path = modDataDir.isEmpty() ? path : path + "/" + modDataDir;
+ m_Mods.push_back(
+ EntryInfo(name, path, info->stealFiles(), info->archives(), std::get<2>(*mod)));
}
m_EnabledArchives = managedArchives;
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index aa2c7641..4137c456 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -2916,8 +2916,11 @@ void MainWindow::originModified(int originID)
origin.enable(false);
DirectoryStats dummy;
+ QString path = QString::fromStdWString(origin.getPath());
+ QString modDataDir = m_OrganizerCore.managedGame()->modDataDirectory();
+ path = modDataDir.isEmpty() ? path : path + "/" + modDataDir;
m_OrganizerCore.directoryStructure()->addFromOrigin(
- origin.getName(), origin.getPath(), origin.getPriority(), dummy);
+ origin.getName(), path.toStdWString(), origin.getPriority(), dummy);
DirectoryRefresher::cleanStructure(m_OrganizerCore.directoryStructure());
}
diff --git a/src/moapplication.cpp b/src/moapplication.cpp
index cac60f48..4640540f 100644
--- a/src/moapplication.cpp
+++ b/src/moapplication.cpp
@@ -287,6 +287,7 @@ int MOApplication::setup(MOMultiProcess& multiProcess, bool forceSelect)
// setting up organizer core
m_core->setManagedGame(m_instance->gamePlugin());
m_core->createDefaultProfile();
+ m_core->createOverwriteDirectories();
log::info("using game plugin '{}' ('{}', variant {}, steam id '{}') at {}",
m_instance->gamePlugin()->gameName(),
diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp
index 1798e987..04ef003a 100644
--- a/src/modinfodialog.cpp
+++ b/src/modinfodialog.cpp
@@ -571,25 +571,30 @@ void ModInfoDialog::updateTabs(bool becauseOriginChanged)
void ModInfoDialog::feedFiles(std::vector<TabInfo*>& interestedTabs)
{
- const auto rootPath = m_mod->absolutePath();
+ const auto rootPath =
+ m_mod->absolutePath() + (m_core.managedGame()->modDataDirectory().isEmpty()
+ ? ""
+ : "/" + m_core.managedGame()->modDataDirectory());
if (rootPath.isEmpty()) {
return;
}
- const fs::path fsPath(rootPath.toStdWString());
+ if (fs::exists(rootPath.toStdWString())) {
+ const fs::path fsPath(rootPath.toStdWString());
- for (const auto& entry : fs::recursive_directory_iterator(fsPath)) {
- if (!entry.is_regular_file()) {
- // skip directories
- continue;
- }
+ for (const auto& entry : fs::recursive_directory_iterator(fsPath)) {
+ if (!entry.is_regular_file()) {
+ // skip directories
+ continue;
+ }
- const auto filePath = QString::fromStdWString(entry.path().native());
+ const auto filePath = QString::fromStdWString(entry.path().native());
- // for each tab
- for (auto* tabInfo : interestedTabs) {
- if (tabInfo->tab->feedFile(rootPath, filePath)) {
- break;
+ // for each tab
+ for (auto* tabInfo : interestedTabs) {
+ if (tabInfo->tab->feedFile(rootPath, filePath)) {
+ break;
+ }
}
}
}
diff --git a/src/modinfooverwrite.cpp b/src/modinfooverwrite.cpp
index 9d0ed5f9..c65cbace 100644
--- a/src/modinfooverwrite.cpp
+++ b/src/modinfooverwrite.cpp
@@ -3,6 +3,7 @@
#include "settings.h"
#include "shared/appconfig.h"
+#include "organizercore.h"
#include <QApplication>
#include <QDirIterator>
@@ -14,10 +15,24 @@ bool ModInfoOverwrite::isEmpty() const
QDirIterator iter(absolutePath(), QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs);
if (!iter.hasNext())
return true;
- iter.next();
- if ((iter.fileName() == "meta.ini") && !iter.hasNext())
- return true;
- return false;
+ while (iter.hasNext()) {
+ iter.next();
+ if (iter.fileInfo().isDir() &&
+ !m_Core.managedGame()->getModMappings().keys().contains(iter.fileName(),
+ Qt::CaseInsensitive))
+ return false;
+ if (iter.fileInfo().isDir() &&
+ m_Core.managedGame()->getModMappings().keys().contains(iter.fileName(),
+ Qt::CaseInsensitive)) {
+ if (QDir(iter.filePath()).count() > 2) {
+ return false;
+ }
+ }
+ if (iter.fileInfo().isFile() && iter.fileName() != "meta.ini")
+ return false;
+ }
+
+ return true;
}
QString ModInfoOverwrite::absolutePath() const
diff --git a/src/modlist.cpp b/src/modlist.cpp
index 8945dea1..4ef3afbc 100644
--- a/src/modlist.cpp
+++ b/src/modlist.cpp
@@ -1028,15 +1028,50 @@ bool ModList::dropLocalFiles(const ModListDropInfo& dropInfo, int row,
QList<QPair<QString, QString>> relativePathList;
for (auto localUrl : dropInfo.localUrls()) {
-
QFileInfo sourceInfo(localUrl.url.toLocalFile());
- QString sourceFile = sourceInfo.canonicalFilePath();
+ if (localUrl.originName.compare("overwrite", Qt::CaseInsensitive) == 0) {
+ bool needsMove = true;
+ if (sourceInfo.isDir()) {
+ for (auto dir : m_Organizer->managedGame()->getModMappings().keys()) {
+ QDir overDir(m_Organizer->overwritePath());
+ if (sourceInfo.canonicalFilePath().compare(overDir.absoluteFilePath(dir),
+ Qt::CaseInsensitive) == 0) {
+ needsMove = false;
+
+ QDirIterator dirIter(overDir.absoluteFilePath(dir),
+ QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot);
+ while (dirIter.hasNext()) {
+ auto entry = dirIter.nextFileInfo();
+ QString sourceFile = entry.canonicalFilePath();
- QFileInfo targetInfo(modDir.absoluteFilePath(localUrl.relativePath));
- sourceList << sourceFile;
- targetList << targetInfo.absoluteFilePath();
- relativePathList << QPair<QString, QString>(localUrl.relativePath,
- localUrl.originName);
+ QFileInfo targetInfo(modDir.absoluteFilePath(
+ overDir.relativeFilePath(entry.absoluteFilePath())));
+ sourceList << sourceFile;
+ targetList << targetInfo.absoluteFilePath();
+ relativePathList << QPair<QString, QString>(localUrl.relativePath,
+ localUrl.originName);
+ }
+ }
+ }
+ }
+ if (needsMove) {
+ QString sourceFile = sourceInfo.canonicalFilePath();
+
+ QFileInfo targetInfo(modDir.absoluteFilePath(localUrl.relativePath));
+ sourceList << sourceFile;
+ targetList << targetInfo.absoluteFilePath();
+ relativePathList << QPair<QString, QString>(localUrl.relativePath,
+ localUrl.originName);
+ }
+ } else {
+ QString sourceFile = sourceInfo.canonicalFilePath();
+
+ QFileInfo targetInfo(modDir.absoluteFilePath(localUrl.relativePath));
+ sourceList << sourceFile;
+ targetList << targetInfo.absoluteFilePath();
+ relativePathList << QPair<QString, QString>(localUrl.relativePath,
+ localUrl.originName);
+ }
}
if (sourceList.count()) {
diff --git a/src/modlistviewactions.cpp b/src/modlistviewactions.cpp
index a38e876b..efe48c31 100644
--- a/src/modlistviewactions.cpp
+++ b/src/modlistviewactions.cpp
@@ -548,7 +548,7 @@ void ModListViewActions::displayModInformation(ModInfo::Ptr modInfo,
QDialog* dialog = m_parent->findChild<QDialog*>("__overwriteDialog");
try {
if (dialog == nullptr) {
- dialog = new OverwriteInfoDialog(modInfo, m_parent);
+ dialog = new OverwriteInfoDialog(modInfo, m_core, m_parent);
dialog->setObjectName("__overwriteDialog");
} else {
qobject_cast<OverwriteInfoDialog*>(dialog)->setModInfo(modInfo);
@@ -599,10 +599,12 @@ void ModListViewActions::displayModInformation(ModInfo::Ptr modInfo,
FilesOrigin& origin =
m_core.directoryStructure()->getOriginByName(ToWString(modInfo->name()));
origin.enable(false);
-
+ QString path = modInfo->absolutePath();
+ QString modDataDir = m_core.managedGame()->modDataDirectory();
+ path = modDataDir.isEmpty() ? path : path + "/" + modDataDir;
m_core.directoryRefresher()->addModToStructure(
m_core.directoryStructure(), modInfo->name(),
- m_core.currentProfile()->getModPriority(modIndex), modInfo->absolutePath(),
+ m_core.currentProfile()->getModPriority(modIndex), path,
modInfo->stealFiles(), modInfo->archives());
DirectoryRefresher::cleanStructure(m_core.directoryStructure());
m_core.directoryStructure()->getFileRegister()->sortOrigins();
@@ -1299,9 +1301,42 @@ void ModListViewActions::restoreBackup(const QModelIndex& index) const
void ModListViewActions::moveOverwriteContentsTo(const QString& absolutePath) const
{
ModInfo::Ptr overwriteInfo = ModInfo::getOverwrite();
- bool successful =
- shellMove((QDir::toNativeSeparators(overwriteInfo->absolutePath()) + "\\*"),
- (QDir::toNativeSeparators(absolutePath)), false, m_parent);
+ bool successful = false;
+ if (m_core.managedGame()->getModMappings().count() > 1 ||
+ m_core.managedGame()->getModMappings().keys().first() != "") {
+ QDirIterator iter(overwriteInfo->absolutePath(),
+ QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot);
+ while (iter.hasNext()) {
+ auto entry = iter.nextFileInfo();
+ if (entry.isDir() && m_core.managedGame()->getModMappings().keys().contains(
+ entry.fileName(), Qt::CaseInsensitive)) {
+ successful =
+ shellCopy((QDir::toNativeSeparators(entry.absolutePath())),
+ (QDir::toNativeSeparators(absolutePath)), false, m_parent);
+ QDirIterator subDirIter(entry.absoluteFilePath(),
+ QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot);
+ while (subDirIter.hasNext()) {
+ auto subDirEntry = subDirIter.nextFileInfo();
+ if (subDirEntry.isDir()) {
+ QDir(subDirEntry.absoluteFilePath()).removeRecursively();
+ } else {
+ QFile(subDirEntry.absoluteFilePath()).remove();
+ }
+ }
+ } else {
+ successful =
+ shellMove((QDir::toNativeSeparators(iter.filePath())),
+ (QDir::toNativeSeparators(absolutePath)), false, m_parent);
+ }
+ if (!successful)
+ break;
+ }
+
+ } else {
+ successful =
+ shellMove((QDir::toNativeSeparators(overwriteInfo->absolutePath()) + "\\*"),
+ (QDir::toNativeSeparators(absolutePath)), false, m_parent);
+ }
if (successful) {
MessageDialog::showMessage(tr("Move successful."), m_parent);
@@ -1396,9 +1431,19 @@ void ModListViewActions::clearOverwrite() const
tr("About to recursively delete:\n") + overwriteDir.absolutePath(),
QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok) {
QStringList delList;
- for (auto f :
- overwriteDir.entryList(QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot))
- delList.push_back(overwriteDir.absoluteFilePath(f));
+ for (auto f : overwriteDir.entryInfoList(QDir::AllDirs | QDir::Files |
+ QDir::NoDotAndDotDot)) {
+ if (f.isDir() && m_core.managedGame()->getModMappings().keys().contains(
+ f.fileName(), Qt::CaseInsensitive)) {
+ for (auto sf :
+ QDir(f.absoluteFilePath())
+ .entryInfoList(QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot)) {
+ delList.push_back(sf.absoluteFilePath());
+ }
+ } else {
+ delList.push_back(f.absoluteFilePath());
+ }
+ }
if (shellDelete(delList, true)) {
emit overwriteCleared();
m_core.refresh();
diff --git a/src/organizercore.cpp b/src/organizercore.cpp
index 19439d76..4eac3d66 100644
--- a/src/organizercore.cpp
+++ b/src/organizercore.cpp
@@ -462,6 +462,16 @@ void OrganizerCore::createDefaultProfile()
}
}
+void OrganizerCore::createOverwriteDirectories()
+{
+ QString overwritePath = settings().paths().overwrite();
+ for (auto modDirectory : managedGame()->getModMappings().keys()) {
+ if (!modDirectory.isEmpty()) {
+ QDir(overwritePath).mkdir(modDirectory);
+ }
+ }
+}
+
void OrganizerCore::prepareVFS()
{
m_USVFS.updateMapping(fileMapping(m_CurrentProfile->name(), QString()));
@@ -1396,8 +1406,11 @@ void OrganizerCore::updateModsInDirectoryStructure(
std::vector<DirectoryRefresher::EntryInfo> entries;
for (auto idx : modInfo.keys()) {
+ QString path = modInfo[idx]->absolutePath();
+ QString modDataDir = managedGame()->modDataDirectory();
+ path = modDataDir.isEmpty() ? path : path + "/" + modDataDir;
entries.push_back({modInfo[idx]->name(),
- modInfo[idx]->absolutePath(),
+ path,
modInfo[idx]->stealFiles(),
{},
m_CurrentProfile->getModPriority(idx)});
@@ -1426,10 +1439,12 @@ void OrganizerCore::updateModsInDirectoryStructure(
// finally also add files from bsas to the directory structure
for (auto idx : modInfo.keys()) {
+ QString path = modInfo[idx]->absolutePath();
+ QString modDataDir = managedGame()->modDataDirectory();
+ path = modDataDir.isEmpty() ? path : path + "/" + modDataDir;
m_DirectoryRefresher->addModBSAToStructure(
m_DirectoryStructure, modInfo[idx]->name(),
- m_CurrentProfile->getModPriority(idx), modInfo[idx]->absolutePath(),
- modInfo[idx]->archives());
+ m_CurrentProfile->getModPriority(idx), path, modInfo[idx]->archives());
}
}
@@ -2034,12 +2049,7 @@ std::vector<Mapping> OrganizerCore::fileMapping(const QString& profileName,
MappingType result;
- QStringList dataPaths;
- dataPaths.append(QDir::toNativeSeparators(game->dataDirectory().absolutePath()));
-
- for (auto directory : game->secondaryDataDirectories()) {
- dataPaths.append(directory.absolutePath());
- }
+ auto dataMaps = game->getModMappings();
bool overwriteActive = false;
@@ -2052,13 +2062,19 @@ std::vector<Mapping> OrganizerCore::fileMapping(const QString& profileName,
ModInfo::Ptr modPtr = ModInfo::getByIndex(modIndex);
bool createTarget = customOverwrite == std::get<0>(mod);
+ QDir modDir = QDir(std::get<1>(mod));
overwriteActive |= createTarget;
if (modPtr->isRegular()) {
- for (auto dataPath : dataPaths) {
- result.insert(result.end(), {QDir::toNativeSeparators(std::get<1>(mod)),
- dataPath, true, createTarget});
+ for (auto dataMap : dataMaps.asKeyValueRange()) {
+ auto mapDir = QDir(modDir.absoluteFilePath(dataMap.first));
+ if (mapDir.exists()) {
+ for (auto dir : dataMap.second) {
+ result.insert(result.end(),
+ {mapDir.absolutePath(), dir, true, createTarget});
+ }
+ }
}
}
}
@@ -2080,10 +2096,15 @@ std::vector<Mapping> OrganizerCore::fileMapping(const QString& profileName,
}
}
- for (auto dataPath : dataPaths) {
- result.insert(result.end(),
- {QDir::toNativeSeparators(m_Settings.paths().overwrite()), dataPath,
- true, customOverwrite.isEmpty()});
+ QDir overwriteDir(m_Settings.paths().overwrite());
+ for (auto dataMap : dataMaps.asKeyValueRange()) {
+ auto overwriteSubpath = overwriteDir.absoluteFilePath(dataMap.first);
+ if (QDir(overwriteSubpath).exists()) {
+ for (auto dir : dataMap.second) {
+ result.insert(result.end(),
+ {overwriteSubpath, dir, true, customOverwrite.isEmpty()});
+ }
+ }
}
for (MOBase::IPluginFileMapper* mapper :
diff --git a/src/organizercore.h b/src/organizercore.h
index 923adc12..3e0cdbba 100644
--- a/src/organizercore.h
+++ b/src/organizercore.h
@@ -332,6 +332,7 @@ public:
bool checkPathSymlinks();
bool bootstrap();
void createDefaultProfile();
+ void createOverwriteDirectories();
MOBase::DelayedFileWriter& pluginsWriter() { return m_PluginListsWriter; }
diff --git a/src/overwriteinfodialog.cpp b/src/overwriteinfodialog.cpp
index 366671e7..11e6fc32 100644
--- a/src/overwriteinfodialog.cpp
+++ b/src/overwriteinfodialog.cpp
@@ -18,7 +18,6 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
*/
#include "overwriteinfodialog.h"
-#include "organizercore.h"
#include "report.h"
#include "ui_overwriteinfodialog.h"
#include "utility.h"
@@ -29,15 +28,17 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
using namespace MOBase;
-OverwriteInfoDialog::OverwriteInfoDialog(ModInfo::Ptr modInfo, QWidget* parent)
- : QDialog(parent), ui(new Ui::OverwriteInfoDialog), m_FileSystemModel(nullptr),
- m_DeleteAction(nullptr), m_RenameAction(nullptr), m_OpenAction(nullptr)
+OverwriteInfoDialog::OverwriteInfoDialog(ModInfo::Ptr modInfo, OrganizerCore& organizer,
+ QWidget* parent)
+ : QDialog(parent), m_Organizer(organizer), ui(new Ui::OverwriteInfoDialog),
+ m_FileSystemModel(nullptr), m_DeleteAction(nullptr), m_RenameAction(nullptr),
+ m_OpenAction(nullptr)
{
ui->setupUi(this);
this->setWindowModality(Qt::NonModal);
- m_FileSystemModel = new OverwriteFileSystemModel(this);
+ m_FileSystemModel = new OverwriteFileSystemModel(this, organizer);
m_FileSystemModel->setReadOnly(false);
setModInfo(modInfo);
ui->filesView->setModel(m_FileSystemModel);
@@ -137,10 +138,19 @@ void OverwriteInfoDialog::delete_activated()
QItemSelectionModel* selection = ui->filesView->selectionModel();
if (selection->hasSelection() && selection->selectedRows().count() >= 1) {
+ auto root = m_FileSystemModel->rootDirectory();
if (selection->selectedRows().count() == 0) {
return;
} else if (selection->selectedRows().count() == 1) {
+ for (auto modDir : m_Organizer.managedGame()->getModMappings().keys()) {
+ if (root.absoluteFilePath(modDir).compare(
+ m_FileSystemModel->filePath(selection->selectedRows().at(0)),
+ Qt::CaseInsensitive) == 0) {
+ return;
+ }
+ }
+
QString fileName = m_FileSystemModel->fileName(selection->selectedRows().at(0));
if (QMessageBox::question(
this, tr("Confirm"),
@@ -158,6 +168,12 @@ void OverwriteInfoDialog::delete_activated()
}
foreach (QModelIndex index, selection->selectedRows()) {
+ for (auto modDir : m_Organizer.managedGame()->getModMappings().keys()) {
+ if (root.absoluteFilePath(modDir).compare(m_FileSystemModel->filePath(index),
+ Qt::CaseInsensitive) == 0) {
+ return;
+ }
+ }
deleteFile(index);
}
}
@@ -166,9 +182,17 @@ void OverwriteInfoDialog::delete_activated()
void OverwriteInfoDialog::deleteTriggered()
{
+ auto root = m_FileSystemModel->rootDirectory();
if (m_FileSelection.count() == 0) {
return;
} else if (m_FileSelection.count() == 1) {
+ for (auto modDir : m_Organizer.managedGame()->getModMappings().keys()) {
+ if (root.absoluteFilePath(modDir).compare(
+ m_FileSystemModel->filePath(m_FileSelection.at(0)),
+ Qt::CaseInsensitive) == 0) {
+ return;
+ }
+ }
QString fileName = m_FileSystemModel->fileName(m_FileSelection.at(0));
if (QMessageBox::question(
this, tr("Confirm"),
@@ -185,18 +209,30 @@ void OverwriteInfoDialog::deleteTriggered()
}
foreach (QModelIndex index, m_FileSelection) {
+ for (auto modDir : m_Organizer.managedGame()->getModMappings().keys()) {
+ if (root.absoluteFilePath(modDir).compare(m_FileSystemModel->filePath(index),
+ Qt::CaseInsensitive) == 0) {
+ return;
+ }
+ }
deleteFile(index);
}
}
void OverwriteInfoDialog::renameTriggered()
{
+ auto root = m_FileSystemModel->rootDirectory();
QModelIndex selection = m_FileSelection.at(0);
QModelIndex index = selection.sibling(selection.row(), 0);
if (!index.isValid() || m_FileSystemModel->isReadOnly()) {
return;
}
-
+ for (auto modDir : m_Organizer.managedGame()->getModMappings().keys()) {
+ if (root.absoluteFilePath(modDir).compare(m_FileSystemModel->filePath(selection),
+ Qt::CaseInsensitive) == 0) {
+ return;
+ }
+ }
ui->filesView->edit(index);
}
diff --git a/src/overwriteinfodialog.h b/src/overwriteinfodialog.h
index e9fa3439..59f6f752 100644
--- a/src/overwriteinfodialog.h
+++ b/src/overwriteinfodialog.h
@@ -21,6 +21,8 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#define OVERWRITEINFODIALOG_H
#include "modinfo.h"
+#include "modlistdropinfo.h"
+#include "organizercore.h"
#include <QDialog>
#include <QFileSystemModel>
@@ -34,8 +36,8 @@ class OverwriteFileSystemModel : public QFileSystemModel
Q_OBJECT;
public:
- OverwriteFileSystemModel(QObject* parent)
- : QFileSystemModel(parent), m_RegularColumnCount(0)
+ OverwriteFileSystemModel(QObject* parent, OrganizerCore& organizer)
+ : QFileSystemModel(parent), m_Organizer(organizer), m_RegularColumnCount(0)
{}
virtual int columnCount(const QModelIndex& parent) const
@@ -70,8 +72,27 @@ public:
}
}
+ virtual bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row,
+ int column, const QModelIndex& parent)
+ {
+ ModListDropInfo dropInfo(data, m_Organizer);
+ if (dropInfo.isLocalFileDrop()) {
+ for (auto entry : dropInfo.localUrls()) {
+ QFileInfo sourceInfo(entry.url.toLocalFile());
+ if (sourceInfo.isDir() &&
+ m_Organizer.managedGame()->getModMappings().keys().contains(
+ entry.relativePath, Qt::CaseInsensitive)) {
+ return false;
+ }
+ }
+ }
+ return QFileSystemModel::dropMimeData(data, action, row, column, parent);
+ }
+
private:
mutable int m_RegularColumnCount;
+
+ OrganizerCore& m_Organizer;
};
class OverwriteInfoDialog : public QDialog
@@ -79,7 +100,8 @@ class OverwriteInfoDialog : public QDialog
Q_OBJECT
public:
- explicit OverwriteInfoDialog(ModInfo::Ptr modInfo, QWidget* parent = 0);
+ explicit OverwriteInfoDialog(ModInfo::Ptr modInfo, OrganizerCore& organizer,
+ QWidget* parent = 0);
~OverwriteInfoDialog();
ModInfo::Ptr modInfo() const { return m_ModInfo; }
@@ -122,6 +144,7 @@ private:
QAction* m_NewFolderAction;
ModInfo::Ptr m_ModInfo;
+ OrganizerCore& m_Organizer;
};
#endif // OVERWRITEINFODIALOG_H
diff --git a/src/pluginlist.cpp b/src/pluginlist.cpp
index fc468cef..62284543 100644
--- a/src/pluginlist.cpp
+++ b/src/pluginlist.cpp
@@ -147,10 +147,18 @@ void PluginList::highlightPlugins(const std::vector<unsigned int>& modIndices,
for (auto& modIndex : modIndices) {
ModInfo::Ptr selectedMod = ModInfo::getByIndex(modIndex);
if (!selectedMod.isNull() && profile->modEnabled(modIndex)) {
- QDir dir(selectedMod->absolutePath());
- QStringList plugins = dir.entryList(QStringList() << "*.esp"
- << "*.esm"
- << "*.esl");
+ QString modDataPath = selectedMod->absolutePath();
+ modDataPath =
+ m_Organizer.managedGame()->modDataDirectory().isEmpty()
+ ? modDataPath
+ : modDataPath + "/" + m_Organizer.managedGame()->modDataDirectory();
+ QDir dir(modDataPath);
+ QStringList plugins;
+ if (dir.exists()) {
+ plugins = dir.entryList(QStringList() << "*.esp"
+ << "*.esm"
+ << "*.esl");
+ }
const MOShared::FilesOrigin& origin =
directoryEntry.getOriginByName(selectedMod->internalName().toStdWString());
if (plugins.size() > 0) {
diff --git a/src/savestab.cpp b/src/savestab.cpp
index 2cbd17de..f59245cb 100644
--- a/src/savestab.cpp
+++ b/src/savestab.cpp
@@ -4,6 +4,7 @@
#include "ui_mainwindow.h"
#include <iplugingame.h>
#include <isavegameinfowidget.h>
+#include <localsavegames.h>
using namespace MOBase;
@@ -128,13 +129,15 @@ void SavesTab::refreshSavesIfOpen()
QDir SavesTab::currentSavesDir() const
{
+ // TODO: This code should probably be handled by the game plugins
QDir savesDir;
if (m_core.currentProfile()->localSavesEnabled()) {
savesDir.setPath(m_core.currentProfile()->savePath());
} else {
auto iniFiles = m_core.managedGame()->iniFiles();
- if (iniFiles.isEmpty()) {
+ if (iniFiles.isEmpty() ||
+ m_core.gameFeatures().gameFeature<LocalSavegames>() == nullptr) {
return m_core.managedGame()->savesDirectory();
}
diff --git a/src/shared/directoryentry.cpp b/src/shared/directoryentry.cpp
index 01c325ef..6351083b 100644
--- a/src/shared/directoryentry.cpp
+++ b/src/shared/directoryentry.cpp
@@ -24,6 +24,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include "originconnection.h"
#include "util.h"
#include "windows_error.h"
+#include <filesystem>
#include <log.h>
#include <utility.h>
@@ -630,19 +631,21 @@ void DirectoryEntry::addFiles(env::DirectoryWalker& walker, FilesOrigin& origin,
Context cx = {origin, stats};
cx.current.push(this);
- walker.forEachEntry(
- path, &cx,
- [](void* pcx, std::wstring_view path) {
- onDirectoryStart((Context*)pcx, path);
- },
+ if (std::filesystem::exists(path)) {
+ walker.forEachEntry(
+ path, &cx,
+ [](void* pcx, std::wstring_view path) {
+ onDirectoryStart((Context*)pcx, path);
+ },
- [](void* pcx, std::wstring_view path) {
- onDirectoryEnd((Context*)pcx, path);
- },
+ [](void* pcx, std::wstring_view path) {
+ onDirectoryEnd((Context*)pcx, path);
+ },
- [](void* pcx, std::wstring_view path, FILETIME ft, uint64_t) {
- onFile((Context*)pcx, path, ft);
- });
+ [](void* pcx, std::wstring_view path, FILETIME ft, uint64_t) {
+ onFile((Context*)pcx, path, ft);
+ });
+ }
}
void DirectoryEntry::onDirectoryStart(Context* cx, std::wstring_view path)