mirror of
https://github.com/ModOrganizer2/modorganizer.git
synced 2026-07-27 13:58:24 -07:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8a29bc5110 | ||
|
|
77dbd7ff31 | ||
|
|
7fab050adb | ||
|
|
b547045203 | ||
|
|
fcd2e1473f | ||
|
|
287dd6d805 | ||
|
|
717c2ca9cf | ||
|
|
abd1a11768 | ||
|
|
34636301ea | ||
|
|
f298ea0ae3 | ||
|
|
eee1b4b3f1 | ||
|
|
00120605fb | ||
|
|
32088b47f1 | ||
|
|
726b381ea7 | ||
|
|
071550f7fb | ||
|
|
f8a50edd0f | ||
|
|
08c7113ecb | ||
|
|
d436fe8cb3 | ||
|
|
d5183ebe6b | ||
|
|
c72326ee4b | ||
|
|
75e90f2f3f | ||
|
|
be012715f3 | ||
|
|
2b86a14c86 | ||
|
|
c96c01f40e | ||
|
|
d6edc5d6b7 | ||
|
|
4040dc12b7 | ||
|
|
d4fc7c3de1 | ||
|
|
27c4861907 | ||
|
|
8decb9df68 | ||
|
|
8365f26d83 | ||
|
|
0ca21d9a5e | ||
|
|
f60d0e5ec6 | ||
|
|
d22d77d921 | ||
|
|
7084021067 | ||
|
|
1dd2acf07f | ||
|
|
3fad475013 | ||
|
|
010a5d80d6 | ||
|
|
2bd755743c | ||
|
|
9eb1dd0ec0 | ||
|
|
e473caf14a | ||
|
|
1c87f1d8d4 | ||
|
|
bb6cde175a | ||
|
|
bd93864ed2 | ||
|
|
406d6f28c0 | ||
|
|
ee4c364ccc | ||
|
|
607fafa364 | ||
|
|
7dc28b10d9 | ||
|
|
9f0c8c31a6 | ||
|
|
2fe627c261 | ||
|
|
879c37984e | ||
|
|
1cf8a2bfee | ||
|
|
7531a860cf | ||
|
|
31c8415e88 | ||
|
|
48274f3357 | ||
|
|
f486e6fa4e | ||
|
|
9e5a64b545 | ||
|
|
3dd05ff869 | ||
|
|
eba98e8469 | ||
|
|
73418ec616 | ||
|
|
7283e6c3ba | ||
|
|
d179ad01c8 | ||
|
|
04d80bb567 | ||
|
|
595f6eccf6 | ||
|
|
f058125e66 | ||
|
|
7d36bc20d0 |
+3
-3
@@ -52,10 +52,10 @@ build_script:
|
||||
if($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode ) }
|
||||
test: off
|
||||
artifacts:
|
||||
- path: '\modorganizer-build\install\bin'
|
||||
- path: '\modorganizer-build\install\bin\ModOrganizer.exe'
|
||||
name: Mod.Organizer-$(APPVEYOR_BUILD_VERSION)
|
||||
- path: '\modorganizer-build\install\pdb'
|
||||
name: PDBs-Mod.Organizer-$(APPVEYOR_BUILD_VERSION)
|
||||
- path: '\modorganizer-build\install\pdb\ModOrganizer.pdb'
|
||||
name: PDB-Mod.Organizer-$(APPVEYOR_BUILD_VERSION)
|
||||
deploy: off
|
||||
on_success:
|
||||
- ps: >-
|
||||
|
||||
@@ -18,6 +18,7 @@ add_filter(NAME src/application GROUPS
|
||||
multiprocess
|
||||
sanitychecks
|
||||
selfupdater
|
||||
updatedialog
|
||||
)
|
||||
|
||||
add_filter(NAME src/browser GROUPS
|
||||
|
||||
+5
-6
@@ -251,7 +251,7 @@ std::optional<int> CommandLine::runPostApplication(MOApplication& a)
|
||||
env::Console c;
|
||||
|
||||
if (auto i=InstanceManager::singleton().currentInstance()) {
|
||||
std::cout << i->name().toStdString() << "\n";
|
||||
std::cout << i->displayName().toStdString() << "\n";
|
||||
} else {
|
||||
std::cout << "no instance configured\n";
|
||||
}
|
||||
@@ -289,9 +289,8 @@ std::optional<int> CommandLine::runPostOrganizer(OrganizerCore& core)
|
||||
|
||||
return 0;
|
||||
}
|
||||
catch (const std::exception &e) {
|
||||
reportError(
|
||||
QObject::tr("failed to start shortcut: %1").arg(e.what()));
|
||||
catch (std::exception&) {
|
||||
// user was already warned
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -451,7 +450,7 @@ std::optional<QString> CommandLine::instance() const
|
||||
// note that moshortcut:// overrides -i
|
||||
|
||||
if (m_shortcut.isValid() && m_shortcut.hasInstance()) {
|
||||
return m_shortcut.instance();
|
||||
return m_shortcut.instanceName();
|
||||
} else if (m_vm.count("instance")) {
|
||||
return QString::fromStdString(m_vm["instance"].as<std::string>());
|
||||
}
|
||||
@@ -817,7 +816,7 @@ std::optional<int> RunCommand::runPostOrganizer(OrganizerCore& core)
|
||||
reportError(
|
||||
QObject::tr("Executable '%1' not found in instance '%2'.")
|
||||
.arg(program)
|
||||
.arg(InstanceManager::singleton().currentInstance()->name()));
|
||||
.arg(InstanceManager::singleton().currentInstance()->displayName()));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -226,7 +226,7 @@ bool DownloadList::lessThanPredicate(const QModelIndex &left, const QModelIndex
|
||||
if ((leftIndex < m_manager.numTotalDownloads())
|
||||
&& (rightIndex < m_manager.numTotalDownloads())) {
|
||||
if (left.column() == DownloadList::COL_NAME) {
|
||||
return m_manager.getFileName(left.row()).compare(m_manager.getFileName(right.row()), Qt::CaseInsensitive) < 0;
|
||||
return left.data(Qt::DisplayRole).toString().compare(right.data(Qt::DisplayRole).toString(), Qt::CaseInsensitive) < 0;
|
||||
} else if (left.column() == DownloadList::COL_MODNAME) {
|
||||
QString leftName, rightName;
|
||||
|
||||
@@ -275,7 +275,7 @@ bool DownloadList::lessThanPredicate(const QModelIndex &left, const QModelIndex
|
||||
if (leftState == rightState)
|
||||
return m_manager.getFileTime(left.row()) < m_manager.getFileTime(right.row());
|
||||
else
|
||||
return leftState > rightState;
|
||||
return leftState < rightState;
|
||||
} else if (left.column() == DownloadList::COL_SIZE) {
|
||||
return m_manager.getFileSize(left.row()) < m_manager.getFileSize(right.row());
|
||||
} else if (left.column() == DownloadList::COL_FILETIME) {
|
||||
|
||||
+3
-5
@@ -418,9 +418,9 @@ QString prependToPath(const QString& s)
|
||||
return old;
|
||||
}
|
||||
|
||||
QString setPath(const QString& s)
|
||||
void setPath(const QString& s)
|
||||
{
|
||||
return set("PATH", s);
|
||||
set("PATH", s);
|
||||
}
|
||||
|
||||
QString get(const QString& name)
|
||||
@@ -457,11 +457,9 @@ QString get(const QString& name)
|
||||
return QString::fromWCharArray(buffer.get(), realSize);
|
||||
}
|
||||
|
||||
QString set(const QString& n, const QString& v)
|
||||
void set(const QString& n, const QString& v)
|
||||
{
|
||||
auto old = get(n);
|
||||
::SetEnvironmentVariableW(n.toStdWString().c_str(), v.toStdWString().c_str());
|
||||
return old;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -233,12 +233,12 @@ private:
|
||||
// environment variables
|
||||
//
|
||||
QString get(const QString& name);
|
||||
QString set(const QString& name, const QString& value);
|
||||
void set(const QString& name, const QString& value);
|
||||
|
||||
QString path();
|
||||
QString appendToPath(const QString& s);
|
||||
QString prependToPath(const QString& s);
|
||||
QString setPath(const QString& s);
|
||||
void setPath(const QString& s);
|
||||
|
||||
|
||||
class Service
|
||||
|
||||
+23
-5
@@ -232,8 +232,8 @@ void forEachEntryImpl(
|
||||
|
||||
if (status < 0) {
|
||||
log::error(
|
||||
"NtOpenFile() failed for '{}', {}",
|
||||
toString(poa), formatSystemMessage(status));
|
||||
"failed to open directory '{}': {}",
|
||||
toString(poa), formatNtMessage(status));
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -264,8 +264,9 @@ void forEachEntryImpl(
|
||||
break;
|
||||
} else if (status < 0) {
|
||||
log::error(
|
||||
"NtQueryDirectoryFile() failed for '{}', {}",
|
||||
toString(poa), formatSystemMessage(status));
|
||||
"failed to read directory '{}': {}",
|
||||
toString(poa), formatNtMessage(status));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -321,6 +322,23 @@ void forEachEntryImpl(
|
||||
}
|
||||
}
|
||||
|
||||
std::wstring makeNtPath(const std::wstring& path)
|
||||
{
|
||||
constexpr const wchar_t* nt_prefix = L"\\??\\";
|
||||
constexpr const wchar_t* nt_unc_prefix = L"\\??\\UNC\\";
|
||||
constexpr const wchar_t* share_prefix = L"\\\\";
|
||||
|
||||
if (path.starts_with(nt_prefix)) {
|
||||
// already an nt path
|
||||
return path;
|
||||
} else if (path.starts_with(share_prefix)) {
|
||||
// network shared need \??\UNC\ as a prefix
|
||||
return nt_unc_prefix + path.substr(2);
|
||||
} else {
|
||||
// prepend the \??\ prefix
|
||||
return nt_prefix + path;
|
||||
}
|
||||
}
|
||||
|
||||
void DirectoryWalker::forEachEntry(
|
||||
const std::wstring& path, void* cx,
|
||||
@@ -335,7 +353,7 @@ void DirectoryWalker::forEachEntry(
|
||||
NtClose = (NtClose_type)::GetProcAddress(m.get(), "NtClose");
|
||||
}
|
||||
|
||||
const std::wstring ntpath = std::wstring(L"\\??\\") + path;
|
||||
const std::wstring ntpath = makeNtPath(path);
|
||||
|
||||
UNICODE_STRING ObjectName = {};
|
||||
ObjectName.Buffer = const_cast<wchar_t*>(ntpath.c_str());
|
||||
|
||||
+21
-5
@@ -603,9 +603,16 @@ Process getProcessTreeFromProcess(HANDLE h)
|
||||
|
||||
std::vector<DWORD> processesInJob(HANDLE h)
|
||||
{
|
||||
for (int tries=0; tries<5; ++tries) {
|
||||
DWORD maxIds = 100;
|
||||
const int MaxTries = 5;
|
||||
|
||||
// doubled MaxTries times on failure
|
||||
DWORD maxIds = 100;
|
||||
|
||||
// for logging
|
||||
DWORD lastCount=0, lastAssigned=0;
|
||||
|
||||
|
||||
for (int tries=0; tries<MaxTries; ++tries) {
|
||||
const DWORD idsSize = sizeof(ULONG_PTR) * maxIds;
|
||||
const DWORD bufferSize = sizeof(JOBOBJECT_BASIC_PROCESS_ID_LIST) + idsSize;
|
||||
|
||||
@@ -617,8 +624,10 @@ std::vector<DWORD> processesInJob(HANDLE h)
|
||||
|
||||
if (!r) {
|
||||
const auto e = GetLastError();
|
||||
log::error("failed to get process ids in job, {}", formatSystemMessage(e));
|
||||
return {};
|
||||
if (e != ERROR_MORE_DATA) {
|
||||
log::error("failed to get process ids in job, {}", formatSystemMessage(e));
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
if (ids->NumberOfProcessIdsInList >= ids->NumberOfAssignedProcesses) {
|
||||
@@ -632,9 +641,16 @@ std::vector<DWORD> processesInJob(HANDLE h)
|
||||
|
||||
// try again with a larger buffer
|
||||
maxIds *= 2;
|
||||
|
||||
// for logging
|
||||
lastCount = ids->NumberOfProcessIdsInList;
|
||||
lastAssigned = ids->NumberOfAssignedProcesses;
|
||||
}
|
||||
|
||||
log::error("failed to get processes in job, can't get a buffer large enough");
|
||||
log::error(
|
||||
"failed to get processes in job, can't get a buffer large enough, "
|
||||
"{}/{} ids", lastCount, lastAssigned);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
+3
-1
@@ -145,11 +145,13 @@ Shortcut::Shortcut()
|
||||
Shortcut::Shortcut(const Executable& exe)
|
||||
: Shortcut()
|
||||
{
|
||||
const auto i = *InstanceManager::singleton().currentInstance();
|
||||
|
||||
m_name = exe.title();
|
||||
m_target = QFileInfo(qApp->applicationFilePath()).absoluteFilePath();
|
||||
|
||||
m_arguments = QString("\"moshortcut://%1:%2\"")
|
||||
.arg(InstanceManager::singleton().currentInstance()->name())
|
||||
.arg(i.isPortable() ? "" : i.displayName())
|
||||
.arg(exe.title());
|
||||
|
||||
m_description = QString("Run %1 with ModOrganizer").arg(exe.title());
|
||||
|
||||
@@ -48,7 +48,7 @@ Instance::Instance(QString dir, bool portable, QString profileName) :
|
||||
{
|
||||
}
|
||||
|
||||
QString Instance::name() const
|
||||
QString Instance::displayName() const
|
||||
{
|
||||
if (isPortable())
|
||||
return QObject::tr("Portable");
|
||||
@@ -105,7 +105,7 @@ bool Instance::isActive() const
|
||||
if (m_portable) {
|
||||
return i->isPortable();
|
||||
} else {
|
||||
return (i->name() == name());
|
||||
return (i->displayName() == displayName());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -822,7 +822,7 @@ SetupInstanceResults selectGame(Instance& instance, PluginContainer& pc)
|
||||
CreateInstanceDialog dlg(pc, nullptr);
|
||||
|
||||
// only show the game page
|
||||
dlg.setSinglePage<cid::GamePage>(instance.name());
|
||||
dlg.setSinglePage<cid::GamePage>(instance.displayName());
|
||||
|
||||
dlg.show();
|
||||
dlg.activateWindow();
|
||||
@@ -860,7 +860,7 @@ SetupInstanceResults selectVariant(Instance& instance, PluginContainer& pc)
|
||||
instance.gamePlugin(), instance.gameDirectory());
|
||||
|
||||
// only show the variant page
|
||||
dlg.setSinglePage<cid::VariantsPage>(instance.name());
|
||||
dlg.setSinglePage<cid::VariantsPage>(instance.displayName());
|
||||
|
||||
dlg.show();
|
||||
dlg.activateWindow();
|
||||
@@ -897,7 +897,7 @@ SetupInstanceResults setupInstance(Instance& instance, PluginContainer& pc)
|
||||
|
||||
reportError(
|
||||
QObject::tr("Cannot open instance '%1', failed to read INI file %2.")
|
||||
.arg(instance.name()).arg(instance.iniPath()));
|
||||
.arg(instance.displayName()).arg(instance.iniPath()));
|
||||
|
||||
return SetupInstanceResults::SelectAnother;
|
||||
}
|
||||
@@ -914,7 +914,7 @@ SetupInstanceResults setupInstance(Instance& instance, PluginContainer& pc)
|
||||
QObject::tr(
|
||||
"Cannot open instance '%1', the managed game was not found in the INI "
|
||||
"file %2. Select the game managed by this instance.")
|
||||
.arg(instance.name()).arg(instance.iniPath()));
|
||||
.arg(instance.displayName()).arg(instance.iniPath()));
|
||||
|
||||
return selectGame(instance, pc);
|
||||
}
|
||||
@@ -928,7 +928,7 @@ SetupInstanceResults setupInstance(Instance& instance, PluginContainer& pc)
|
||||
QObject::tr(
|
||||
"Cannot open instance '%1', the game plugin '%2' doesn't exist. It "
|
||||
"may have been deleted by an antivirus. Select another instance.")
|
||||
.arg(instance.name()).arg(instance.gameName()));
|
||||
.arg(instance.displayName()).arg(instance.gameName()));
|
||||
|
||||
return SetupInstanceResults::SelectAnother;
|
||||
}
|
||||
@@ -943,7 +943,7 @@ SetupInstanceResults setupInstance(Instance& instance, PluginContainer& pc)
|
||||
"Cannot open instance '%1', the game directory '%2' doesn't exist or "
|
||||
"the game plugin '%3' doesn't recognize it. Select the game managed "
|
||||
"by this instance.")
|
||||
.arg(instance.name())
|
||||
.arg(instance.displayName())
|
||||
.arg(instance.gameDirectory())
|
||||
.arg(instance.gameName()));
|
||||
|
||||
|
||||
@@ -132,9 +132,14 @@ public:
|
||||
// returns the instance name; this is the directory name or "Portable" for
|
||||
// portable instances
|
||||
//
|
||||
// be careful when using this function to check whether two instances are the
|
||||
// same, some parts of MO use an empty string to represent portable instances,
|
||||
// but this function will return "Portable" for them; it's safer to check
|
||||
// for isPortable() first
|
||||
//
|
||||
// can be called without setup()
|
||||
//
|
||||
QString name() const;
|
||||
QString displayName() const;
|
||||
|
||||
// returns either:
|
||||
// 1) the game name from the INI, if readFromIni() was called;
|
||||
|
||||
@@ -221,7 +221,7 @@ void InstanceManagerDialog::updateInstances()
|
||||
|
||||
// sort first, prepend portable after so it's always on top
|
||||
std::sort(m_instances.begin(), m_instances.end(), [](auto&& a, auto&& b) {
|
||||
return (MOBase::naturalCompare(a->name(), b->name()) < 0);
|
||||
return (MOBase::naturalCompare(a->displayName(), b->displayName()) < 0);
|
||||
});
|
||||
|
||||
if (m.portableInstanceExists()) {
|
||||
@@ -249,7 +249,7 @@ void InstanceManagerDialog::updateList()
|
||||
for (std::size_t i=0; i<m_instances.size(); ++i) {
|
||||
const auto& ii = *m_instances[i];
|
||||
|
||||
auto* item = new QStandardItem(ii.name());
|
||||
auto* item = new QStandardItem(ii.displayName());
|
||||
item->setIcon(instanceIcon(m_pc, ii));
|
||||
|
||||
m_model->appendRow(item);
|
||||
@@ -295,7 +295,7 @@ void InstanceManagerDialog::select(std::size_t i)
|
||||
void InstanceManagerDialog::select(const QString& name)
|
||||
{
|
||||
for (std::size_t i=0; i<m_instances.size(); ++i) {
|
||||
if (m_instances[i]->name() == name) {
|
||||
if (m_instances[i]->displayName() == name) {
|
||||
select(i);
|
||||
return;
|
||||
}
|
||||
@@ -310,7 +310,7 @@ void InstanceManagerDialog::selectActiveInstance()
|
||||
|
||||
if (active) {
|
||||
for (std::size_t i=0; i<m_instances.size(); ++i) {
|
||||
if (m_instances[i]->name() == active->name()) {
|
||||
if (m_instances[i]->displayName() == active->displayName()) {
|
||||
select(i);
|
||||
|
||||
ui->list->scrollTo(
|
||||
@@ -340,7 +340,7 @@ void InstanceManagerDialog::openSelectedInstance()
|
||||
if (to.isPortable()) {
|
||||
InstanceManager::singleton().setCurrentInstance("");
|
||||
} else {
|
||||
InstanceManager::singleton().setCurrentInstance(to.name());
|
||||
InstanceManager::singleton().setCurrentInstance(to.displayName());
|
||||
}
|
||||
|
||||
if (m_restartOnSelect) {
|
||||
@@ -372,7 +372,7 @@ bool InstanceManagerDialog::confirmSwitch(const Instance& to)
|
||||
const auto r = dlg
|
||||
.title(tr("Switching instances"))
|
||||
.main(tr("Mod Organizer must restart to manage the instance '%1'.")
|
||||
.arg(to.name()))
|
||||
.arg(to.displayName()))
|
||||
.content(tr("This confirmation can be disabled in the settings."))
|
||||
.icon(QMessageBox::Question)
|
||||
.button({tr("Restart Mod Organizer"), QMessageBox::Ok})
|
||||
@@ -401,7 +401,7 @@ void InstanceManagerDialog::rename()
|
||||
|
||||
// getting new name
|
||||
const auto newName = getInstanceName(
|
||||
this, tr("Rename instance"), "", tr("Instance name"), i->name());
|
||||
this, tr("Rename instance"), "", tr("Instance name"), i->displayName());
|
||||
|
||||
if (newName.isEmpty()) {
|
||||
return;
|
||||
@@ -661,7 +661,7 @@ const Instance* InstanceManagerDialog::singleSelection() const
|
||||
|
||||
void InstanceManagerDialog::fillData(const Instance& ii)
|
||||
{
|
||||
ui->name->setText(ii.name());
|
||||
ui->name->setText(ii.displayName());
|
||||
ui->location->setText(ii.directory());
|
||||
ui->baseDirectory->setText(ii.baseDirectory());
|
||||
ui->gameName->setText(ii.gameName());
|
||||
|
||||
+7
-2
@@ -80,7 +80,8 @@ int run(int argc, char *argv[])
|
||||
// stuff that's done only once, even if MO restarts in the loop below
|
||||
app.firstTimeSetup(multiProcess);
|
||||
|
||||
// force the "Select instance" dialog on startup (only for first loop)
|
||||
// force the "Select instance" dialog on startup, only for first loop or when
|
||||
// the current instance cannot be used
|
||||
bool pick = cl.pick();
|
||||
|
||||
// MO runs in a loop because it can be restarted in several ways, such as
|
||||
@@ -105,13 +106,17 @@ int run(int argc, char *argv[])
|
||||
const auto r = app.setup(multiProcess, pick);
|
||||
pick = false;
|
||||
|
||||
if (r == RestartExitCode) {
|
||||
if (r == RestartExitCode || r == ReselectExitCode) {
|
||||
// resets things when MO is "restarted"
|
||||
app.resetForRestart();
|
||||
|
||||
// don't reprocess command line
|
||||
cl.clear();
|
||||
|
||||
if (r == ReselectExitCode) {
|
||||
pick = true;
|
||||
}
|
||||
|
||||
continue;
|
||||
} else if (r != 0) {
|
||||
// something failed, quit
|
||||
|
||||
+23
-25
@@ -351,6 +351,7 @@ MainWindow::MainWindow(Settings &settings
|
||||
}
|
||||
|
||||
settings.geometry().restoreState(ui->downloadView->header());
|
||||
settings.geometry().restoreState(ui->savegameList->header());
|
||||
|
||||
ui->splitter->setStretchFactor(0, 3);
|
||||
ui->splitter->setStretchFactor(1, 2);
|
||||
@@ -512,6 +513,8 @@ void MainWindow::setupModList()
|
||||
|
||||
connect(&ui->modList->actions(), &ModListViewActions::overwriteCleared, [=]() { scheduleCheckForProblems(); });
|
||||
connect(&ui->modList->actions(), &ModListViewActions::originModified, this, &MainWindow::originModified);
|
||||
connect(&ui->modList->actions(), &ModListViewActions::modInfoDisplayed, this, &MainWindow::modInfoDisplayed);
|
||||
|
||||
connect(m_OrganizerCore.modList(), &ModList::modPrioritiesChanged, [&]() { m_ArchiveListWriter.write(); });
|
||||
}
|
||||
|
||||
@@ -2003,6 +2006,7 @@ void MainWindow::storeSettings()
|
||||
|
||||
s.geometry().saveState(ui->espList->header());
|
||||
s.geometry().saveState(ui->downloadView->header());
|
||||
s.geometry().saveState(ui->savegameList->header());
|
||||
|
||||
s.widgets().saveIndex(ui->executablesListBox);
|
||||
s.widgets().saveIndex(ui->tabWidget);
|
||||
@@ -2819,41 +2823,37 @@ void MainWindow::nxmUpdateInfoAvailable(QString gameName, QVariant userData, QVa
|
||||
}
|
||||
QVariantList resultList = resultData.toList();
|
||||
|
||||
QFutureWatcher<std::pair<QString, std::set<QSharedPointer<ModInfo>>>> *watcher = new QFutureWatcher<std::pair<QString, std::set<QSharedPointer<ModInfo>>>>();
|
||||
QObject::connect(watcher, &QFutureWatcher<std::set<QSharedPointer<ModInfo>>>::finished, this, &MainWindow::finishUpdateInfo);
|
||||
QFuture<std::pair<QString, std::set<QSharedPointer<ModInfo>>>> future = QtConcurrent::run([=]() -> std::pair<QString, std::set<QSharedPointer<ModInfo>>> {
|
||||
return std::make_pair(gameNameReal, ModInfo::filteredMods(gameNameReal, resultList, userData.toBool(), true));
|
||||
auto* watcher = new QFutureWatcher<NxmUpdateInfoData>();
|
||||
QObject::connect(watcher, &QFutureWatcher<NxmUpdateInfoData>::finished, [this, watcher]() {
|
||||
finishUpdateInfo(watcher->result());
|
||||
watcher->deleteLater();
|
||||
});
|
||||
auto future = QtConcurrent::run([=]() {
|
||||
return NxmUpdateInfoData{ gameNameReal, ModInfo::filteredMods(gameNameReal, resultList, userData.toBool(), true) };
|
||||
});
|
||||
watcher->setFuture(future);
|
||||
ui->modList->invalidateFilter();
|
||||
}
|
||||
|
||||
void MainWindow::finishUpdateInfo()
|
||||
void MainWindow::finishUpdateInfo(const NxmUpdateInfoData& data)
|
||||
{
|
||||
QFutureWatcher<std::pair<QString, std::set<QSharedPointer<ModInfo>>>> *watcher = static_cast<QFutureWatcher<std::pair<QString, std::set<QSharedPointer<ModInfo>>>> *>(sender());
|
||||
|
||||
QString game = watcher->result().first;
|
||||
auto finalMods = watcher->result().second;
|
||||
|
||||
if (finalMods.empty()) {
|
||||
log::info("{}", tr("None of your %1 mods appear to have had recent file updates.").arg(game));
|
||||
if (data.finalMods.empty()) {
|
||||
log::info("{}", tr("None of your %1 mods appear to have had recent file updates.").arg(data.game));
|
||||
}
|
||||
|
||||
std::set<std::pair<QString, int>> organizedGames;
|
||||
for (auto mod : finalMods) {
|
||||
for (auto& mod : data.finalMods) {
|
||||
if (mod->canBeUpdated()) {
|
||||
organizedGames.insert(std::make_pair<QString, int>(mod->gameName().toLower(), mod->nexusId()));
|
||||
}
|
||||
m_OrganizerCore.modList()->notifyChange(ModInfo::getIndex(mod->name()));
|
||||
}
|
||||
|
||||
if (!finalMods.empty() && organizedGames.empty())
|
||||
if (!data.finalMods.empty() && organizedGames.empty())
|
||||
log::warn("{}", tr("All of your mods have been checked recently. We restrict update checks to help preserve your available API requests."));
|
||||
|
||||
for (auto game : organizedGames)
|
||||
for (const auto& game : organizedGames) {
|
||||
NexusInterface::instance().requestUpdates(game.second, this, QVariant(), game.first, QString());
|
||||
|
||||
disconnect(sender());
|
||||
delete sender();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::nxmUpdatesAvailable(QString gameName, int modID, QVariant userData, QVariant resultData, int requestID)
|
||||
@@ -2928,13 +2928,15 @@ void MainWindow::nxmUpdatesAvailable(QString gameName, int modID, QVariant userD
|
||||
if (foundUpdate) {
|
||||
// Just get the standard data updates for endorsements and descriptions
|
||||
mod->setLastNexusUpdate(QDateTime::currentDateTimeUtc());
|
||||
m_OrganizerCore.modList()->notifyChange(ModInfo::getIndex(mod->name()));
|
||||
} else {
|
||||
// Scrape mod data here so we can use the mod version if no file update was located
|
||||
requiresInfo = true;
|
||||
}
|
||||
}
|
||||
|
||||
// invalidate the filter to display mods with an update
|
||||
ui->modList->invalidateFilter();
|
||||
|
||||
if (requiresInfo)
|
||||
NexusInterface::instance().requestModInfo(gameNameReal, modID, this, QVariant(), QString());
|
||||
}
|
||||
@@ -2951,7 +2953,6 @@ void MainWindow::nxmModInfoAvailable(QString gameName, int modID, QVariant userD
|
||||
}
|
||||
std::vector<ModInfo::Ptr> modsList = ModInfo::getByModID(gameNameReal, modID);
|
||||
for (auto mod : modsList) {
|
||||
bool foundUpdate = false;
|
||||
QDateTime now = QDateTime::currentDateTimeUtc();
|
||||
QDateTime updateTarget = mod->getExpires();
|
||||
if (now >= updateTarget) {
|
||||
@@ -2959,7 +2960,6 @@ void MainWindow::nxmModInfoAvailable(QString gameName, int modID, QVariant userD
|
||||
// with an older version than the main mod version.
|
||||
if (mod->getNexusFileStatus() != 3 && mod->getNexusFileStatus() != 5) {
|
||||
mod->setNewestVersion(result["version"].toString());
|
||||
foundUpdate = true;
|
||||
}
|
||||
// update the LastNexusUpdate time in any case since we did perform the check.
|
||||
mod->setLastNexusUpdate(QDateTime::currentDateTimeUtc());
|
||||
@@ -2979,9 +2979,7 @@ void MainWindow::nxmModInfoAvailable(QString gameName, int modID, QVariant userD
|
||||
mod->setNexusLastModified(QDateTime::fromSecsSinceEpoch(result["updated_timestamp"].toInt(), Qt::UTC));
|
||||
mod->saveMeta();
|
||||
|
||||
if (foundUpdate) {
|
||||
m_OrganizerCore.modList()->notifyChange(ModInfo::getIndex(mod->name()));
|
||||
}
|
||||
m_OrganizerCore.modList()->notifyChange(ModInfo::getIndex(mod->name()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+12
-2
@@ -143,6 +143,9 @@ public slots:
|
||||
void refresherProgress(const DirectoryRefreshProgress* p);
|
||||
|
||||
signals:
|
||||
// emitted after the information dialog has been closed, used by tutorials
|
||||
//
|
||||
void modInfoDisplayed();
|
||||
|
||||
/**
|
||||
* @brief emitted when the selected style changes
|
||||
@@ -224,6 +227,13 @@ private:
|
||||
void toggleMO2EndorseState();
|
||||
void toggleUpdateAction();
|
||||
|
||||
// update info
|
||||
struct NxmUpdateInfoData {
|
||||
QString game;
|
||||
std::set<ModInfo::Ptr> finalMods;
|
||||
};
|
||||
void finishUpdateInfo(const NxmUpdateInfoData& data);
|
||||
|
||||
private:
|
||||
|
||||
static const char *PATTERN_BACKUP_GLOB;
|
||||
@@ -331,10 +341,10 @@ private slots:
|
||||
|
||||
void modInstalled(const QString &modName);
|
||||
|
||||
void finishUpdateInfo();
|
||||
// update info
|
||||
void nxmUpdateInfoAvailable(QString gameName, QVariant userData, QVariant resultData, int requestID);
|
||||
|
||||
void nxmEndorsementsAvailable(QVariant userData, QVariant resultData, int);
|
||||
void nxmUpdateInfoAvailable(QString gameName, QVariant userData, QVariant resultData, int requestID);
|
||||
void nxmUpdatesAvailable(QString gameName, int modID, QVariant userData, QVariant resultData, int requestID);
|
||||
void nxmModInfoAvailable(QString gameName, int modID, QVariant userData, QVariant resultData, int requestID);
|
||||
void nxmEndorsementToggled(QString, int, QVariant, QVariant resultData, int);
|
||||
|
||||
+39
-2
@@ -1199,7 +1199,7 @@ p, li { white-space: pre-wrap; }
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QListWidget" name="savegameList">
|
||||
<widget class="QTreeWidget" name="savegameList">
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::CustomContextMenu</enum>
|
||||
</property>
|
||||
@@ -1215,6 +1215,9 @@ p, li { white-space: pre-wrap; }
|
||||
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;"></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">If you click &quot;Fix Mods...&quot; in the context menu, MO will try to activate all mods and esps to fix those missing esps. It will not disable anything!</span></p></body></html></string>
|
||||
</property>
|
||||
<property name="sizeAdjustPolicy">
|
||||
<enum>QAbstractScrollArea::AdjustIgnored</enum>
|
||||
</property>
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
@@ -1224,9 +1227,43 @@ p, li { white-space: pre-wrap; }
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
</property>
|
||||
<property name="uniformItemSizes">
|
||||
<property name="indentation">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rootIsDecorated">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="uniformRowHeights">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="sortingEnabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="columnCount">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<attribute name="headerCascadingSectionResizes">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="headerStretchLastSection">
|
||||
<bool>true</bool>
|
||||
</attribute>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Name</string>
|
||||
</property>
|
||||
<property name="textAlignment">
|
||||
<set>AlignLeading|AlignVCenter</set>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>File</string>
|
||||
</property>
|
||||
<property name="textAlignment">
|
||||
<set>AlignLeading|AlignVCenter</set>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
||||
+11
-7
@@ -392,10 +392,14 @@ void MOApplication::externalMessage(const QString& message)
|
||||
|
||||
if (moshortcut.isValid()) {
|
||||
if(moshortcut.hasExecutable()) {
|
||||
m_core->processRunner()
|
||||
.setFromShortcut(moshortcut)
|
||||
.setWaitForCompletion(ProcessRunner::TriggerRefresh)
|
||||
.run();
|
||||
try {
|
||||
m_core->processRunner()
|
||||
.setFromShortcut(moshortcut)
|
||||
.setWaitForCompletion(ProcessRunner::TriggerRefresh)
|
||||
.run();
|
||||
} catch(std::exception&) {
|
||||
// user was already warned
|
||||
}
|
||||
}
|
||||
} else if (isNxmLink(message)) {
|
||||
MessageDialog::showMessage(tr("Download started"), qApp->activeWindow(), false);
|
||||
@@ -414,11 +418,11 @@ void MOApplication::externalMessage(const QString& message)
|
||||
if (auto i=cl.instance()) {
|
||||
const auto ci = InstanceManager::singleton().currentInstance();
|
||||
|
||||
if (*i != ci->name()) {
|
||||
if (*i != ci->displayName()) {
|
||||
reportError(tr(
|
||||
"This shortcut or command line is for instance '%1', but the current "
|
||||
"instance is '%2'.")
|
||||
.arg(*i).arg(ci->name()));
|
||||
.arg(*i).arg(ci->displayName()));
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -488,7 +492,7 @@ std::optional<int> MOApplication::setupInstanceLoop(
|
||||
continue;
|
||||
} else if (setupResult == SetupInstanceResults::SelectAnother) {
|
||||
InstanceManager::singleton().clearCurrentInstance();
|
||||
return RestartExitCode;
|
||||
return ReselectExitCode;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -897,11 +897,6 @@ public: // Conflicts
|
||||
//
|
||||
virtual const std::set<unsigned int>& getModArchiveLooseOverwritten() const { return s_EmptySet; }
|
||||
|
||||
/**
|
||||
* @brief Update conflict information.
|
||||
*/
|
||||
virtual void doConflictCheck() const {}
|
||||
|
||||
public slots:
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,11 +19,11 @@ ModInfoWithConflictInfo::ModInfoWithConflictInfo(OrganizerCore& core) :
|
||||
m_FileTree([this]() { return QDirFileTree::makeTree(absolutePath()); }),
|
||||
m_Valid([this]() { return doIsValid(); }),
|
||||
m_Contents([this]() { return doGetContents(); }),
|
||||
m_HasLooseOverwrite(false), m_HasHiddenFiles(false) {}
|
||||
m_Conflicts([this]() { return doConflictCheck(); }) { }
|
||||
|
||||
void ModInfoWithConflictInfo::clearCaches()
|
||||
{
|
||||
m_LastConflictCheck = QTime();
|
||||
m_Conflicts.invalidate();
|
||||
}
|
||||
|
||||
std::vector<ModInfo::EFlag> ModInfoWithConflictInfo::getFlags() const
|
||||
@@ -82,14 +82,9 @@ std::vector<ModInfo::EConflictFlag> ModInfoWithConflictInfo::getConflictFlags()
|
||||
}
|
||||
|
||||
|
||||
void ModInfoWithConflictInfo::doConflictCheck() const
|
||||
ModInfoWithConflictInfo::Conflicts ModInfoWithConflictInfo::doConflictCheck() const
|
||||
{
|
||||
m_OverwriteList.clear();
|
||||
m_OverwrittenList.clear();
|
||||
m_ArchiveOverwriteList.clear();
|
||||
m_ArchiveOverwrittenList.clear();
|
||||
m_ArchiveLooseOverwriteList.clear();
|
||||
m_ArchiveLooseOverwrittenList.clear();
|
||||
Conflicts conflicts;
|
||||
|
||||
bool providesAnything = false;
|
||||
bool hasHiddenFiles = false;
|
||||
@@ -102,10 +97,6 @@ void ModInfoWithConflictInfo::doConflictCheck() const
|
||||
std::wstring name = ToWString(this->name());
|
||||
const std::wstring hideExt = ToWString(ModInfo::s_HiddenExt);
|
||||
|
||||
m_CurrentConflictState = CONFLICT_NONE;
|
||||
m_ArchiveConflictState = CONFLICT_NONE;
|
||||
m_ArchiveConflictLooseState = CONFLICT_NONE;
|
||||
|
||||
if (m_Core.directoryStructure()->originExists(name)) {
|
||||
FilesOrigin &origin = m_Core.directoryStructure()->getOriginByName(name);
|
||||
std::vector<FileEntryPtr> files = origin.getFiles();
|
||||
@@ -168,12 +159,12 @@ void ModInfoWithConflictInfo::doConflictCheck() const
|
||||
unsigned int altIndex = ModInfo::getIndex(ToQString(altOrigin.getName()));
|
||||
if (!file->isFromArchive()) {
|
||||
if (!archiveData.isValid())
|
||||
m_OverwrittenList.insert(altIndex);
|
||||
conflicts.m_OverwrittenList.insert(altIndex);
|
||||
else
|
||||
m_ArchiveLooseOverwrittenList.insert(altIndex);
|
||||
conflicts.m_ArchiveLooseOverwrittenList.insert(altIndex);
|
||||
}
|
||||
else {
|
||||
m_ArchiveOverwrittenList.insert(altIndex);
|
||||
conflicts.m_ArchiveOverwrittenList.insert(altIndex);
|
||||
}
|
||||
} else {
|
||||
providesAnything = true;
|
||||
@@ -188,21 +179,21 @@ void ModInfoWithConflictInfo::doConflictCheck() const
|
||||
if (!altInfo.isFromArchive()) {
|
||||
if (!archiveData.isValid()) {
|
||||
if (origin.getPriority() > altOrigin.getPriority()) {
|
||||
m_OverwriteList.insert(altIndex);
|
||||
conflicts.m_OverwriteList.insert(altIndex);
|
||||
} else {
|
||||
m_OverwrittenList.insert(altIndex);
|
||||
conflicts.m_OverwrittenList.insert(altIndex);
|
||||
}
|
||||
} else {
|
||||
m_ArchiveLooseOverwrittenList.insert(altIndex);
|
||||
conflicts.m_ArchiveLooseOverwrittenList.insert(altIndex);
|
||||
}
|
||||
} else {
|
||||
if (!archiveData.isValid()) {
|
||||
m_ArchiveLooseOverwriteList.insert(altIndex);
|
||||
conflicts.m_ArchiveLooseOverwriteList.insert(altIndex);
|
||||
} else {
|
||||
if (archiveData.order() > altInfo.archive().order()) {
|
||||
m_ArchiveOverwriteList.insert(altIndex);
|
||||
conflicts.m_ArchiveOverwriteList.insert(altIndex);
|
||||
} else if (archiveData.order() < altInfo.archive().order()) {
|
||||
m_ArchiveOverwrittenList.insert(altIndex);
|
||||
conflicts.m_ArchiveOverwrittenList.insert(altIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -210,66 +201,51 @@ void ModInfoWithConflictInfo::doConflictCheck() const
|
||||
}
|
||||
}
|
||||
}
|
||||
m_LastConflictCheck = QTime::currentTime();
|
||||
|
||||
if (files.size() != 0) {
|
||||
if (!providesAnything)
|
||||
m_CurrentConflictState = CONFLICT_REDUNDANT;
|
||||
else if (!m_OverwriteList.empty() && !m_OverwrittenList.empty())
|
||||
m_CurrentConflictState = CONFLICT_MIXED;
|
||||
else if (!m_OverwriteList.empty())
|
||||
m_CurrentConflictState = CONFLICT_OVERWRITE;
|
||||
else if (!m_OverwrittenList.empty())
|
||||
m_CurrentConflictState = CONFLICT_OVERWRITTEN;
|
||||
conflicts.m_CurrentConflictState = CONFLICT_REDUNDANT;
|
||||
else if (!conflicts.m_OverwriteList.empty() && !conflicts.m_OverwrittenList.empty())
|
||||
conflicts.m_CurrentConflictState = CONFLICT_MIXED;
|
||||
else if (!conflicts.m_OverwriteList.empty())
|
||||
conflicts.m_CurrentConflictState = CONFLICT_OVERWRITE;
|
||||
else if (!conflicts.m_OverwrittenList.empty())
|
||||
conflicts.m_CurrentConflictState = CONFLICT_OVERWRITTEN;
|
||||
|
||||
if (!m_ArchiveOverwriteList.empty() && !m_ArchiveOverwrittenList.empty())
|
||||
m_ArchiveConflictState = CONFLICT_MIXED;
|
||||
else if (!m_ArchiveOverwriteList.empty())
|
||||
m_ArchiveConflictState = CONFLICT_OVERWRITE;
|
||||
else if (!m_ArchiveOverwrittenList.empty())
|
||||
m_ArchiveConflictState = CONFLICT_OVERWRITTEN;
|
||||
if (!conflicts.m_ArchiveOverwriteList.empty() && !conflicts.m_ArchiveOverwrittenList.empty())
|
||||
conflicts.m_ArchiveConflictState = CONFLICT_MIXED;
|
||||
else if (!conflicts.m_ArchiveOverwriteList.empty())
|
||||
conflicts.m_ArchiveConflictState = CONFLICT_OVERWRITE;
|
||||
else if (!conflicts.m_ArchiveOverwrittenList.empty())
|
||||
conflicts.m_ArchiveConflictState = CONFLICT_OVERWRITTEN;
|
||||
|
||||
if (!m_ArchiveLooseOverwrittenList.empty() && !m_ArchiveLooseOverwriteList.empty())
|
||||
m_ArchiveConflictLooseState = CONFLICT_MIXED;
|
||||
else if (!m_ArchiveLooseOverwrittenList.empty())
|
||||
m_ArchiveConflictLooseState = CONFLICT_OVERWRITTEN;
|
||||
else if (!m_ArchiveLooseOverwriteList.empty())
|
||||
m_ArchiveConflictLooseState = CONFLICT_OVERWRITE;
|
||||
if (!conflicts.m_ArchiveLooseOverwrittenList.empty() && !conflicts.m_ArchiveLooseOverwriteList.empty())
|
||||
conflicts.m_ArchiveConflictLooseState = CONFLICT_MIXED;
|
||||
else if (!conflicts.m_ArchiveLooseOverwrittenList.empty())
|
||||
conflicts.m_ArchiveConflictLooseState = CONFLICT_OVERWRITTEN;
|
||||
else if (!conflicts.m_ArchiveLooseOverwriteList.empty())
|
||||
conflicts.m_ArchiveConflictLooseState = CONFLICT_OVERWRITE;
|
||||
|
||||
m_HasHiddenFiles = hasHiddenFiles;
|
||||
conflicts.m_HasHiddenFiles = hasHiddenFiles;
|
||||
}
|
||||
}
|
||||
|
||||
return conflicts;
|
||||
}
|
||||
|
||||
ModInfoWithConflictInfo::EConflictType ModInfoWithConflictInfo::isConflicted() const
|
||||
{
|
||||
// this is costy so cache the result
|
||||
QTime now = QTime::currentTime();
|
||||
if (m_LastConflictCheck.isNull() || (m_LastConflictCheck.secsTo(now) > 10)) {
|
||||
doConflictCheck();
|
||||
}
|
||||
|
||||
return m_CurrentConflictState;
|
||||
return m_Conflicts.value().m_CurrentConflictState;
|
||||
}
|
||||
|
||||
ModInfoWithConflictInfo::EConflictType ModInfoWithConflictInfo::isArchiveConflicted() const
|
||||
{
|
||||
QTime now = QTime::currentTime();
|
||||
if (m_LastConflictCheck.isNull() || (m_LastConflictCheck.secsTo(now) > 10)) {
|
||||
doConflictCheck();
|
||||
}
|
||||
|
||||
return m_ArchiveConflictState;
|
||||
return m_Conflicts.value().m_ArchiveConflictState;
|
||||
}
|
||||
|
||||
ModInfoWithConflictInfo::EConflictType ModInfoWithConflictInfo::isLooseArchiveConflicted() const
|
||||
{
|
||||
QTime now = QTime::currentTime();
|
||||
if (m_LastConflictCheck.isNull() || (m_LastConflictCheck.secsTo(now) > 10)) {
|
||||
doConflictCheck();
|
||||
}
|
||||
|
||||
return m_ArchiveConflictLooseState;
|
||||
return m_Conflicts.value().m_ArchiveConflictLooseState;
|
||||
}
|
||||
|
||||
|
||||
@@ -294,12 +270,7 @@ bool ModInfoWithConflictInfo::isRedundant() const
|
||||
|
||||
bool ModInfoWithConflictInfo::hasHiddenFiles() const
|
||||
{
|
||||
QTime now = QTime::currentTime();
|
||||
if (m_LastConflictCheck.isNull() || (m_LastConflictCheck.secsTo(now) > 10)) {
|
||||
doConflictCheck();
|
||||
}
|
||||
|
||||
return m_HasHiddenFiles;
|
||||
return m_Conflicts.value().m_HasHiddenFiles;
|
||||
}
|
||||
|
||||
void ModInfoWithConflictInfo::diskContentModified() {
|
||||
|
||||
@@ -54,14 +54,12 @@ public:
|
||||
*/
|
||||
void clearCaches() override;
|
||||
|
||||
const std::set<unsigned int>& getModOverwrite() const override { return m_OverwriteList; }
|
||||
const std::set<unsigned int>& getModOverwritten() const override { return m_OverwrittenList; }
|
||||
const std::set<unsigned int>& getModArchiveOverwrite() const override { return m_ArchiveOverwriteList; }
|
||||
const std::set<unsigned int>& getModArchiveOverwritten() const override { return m_ArchiveOverwrittenList; }
|
||||
const std::set<unsigned int>& getModArchiveLooseOverwrite() const override { return m_ArchiveLooseOverwriteList; }
|
||||
const std::set<unsigned int>& getModArchiveLooseOverwritten() const override { return m_ArchiveLooseOverwrittenList; }
|
||||
|
||||
void doConflictCheck() const override;
|
||||
const std::set<unsigned int>& getModOverwrite() const override { return m_Conflicts.value().m_OverwriteList; }
|
||||
const std::set<unsigned int>& getModOverwritten() const override { return m_Conflicts.value().m_OverwrittenList; }
|
||||
const std::set<unsigned int>& getModArchiveOverwrite() const override { return m_Conflicts.value().m_ArchiveOverwriteList; }
|
||||
const std::set<unsigned int>& getModArchiveOverwritten() const override { return m_Conflicts.value().m_ArchiveOverwrittenList; }
|
||||
const std::set<unsigned int>& getModArchiveLooseOverwrite() const override { return m_Conflicts.value().m_ArchiveLooseOverwriteList; }
|
||||
const std::set<unsigned int>& getModArchiveLooseOverwritten() const override { return m_Conflicts.value().m_ArchiveLooseOverwrittenList; }
|
||||
|
||||
public slots:
|
||||
|
||||
@@ -72,11 +70,8 @@ public slots:
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* @brief Check if the content of this mod is valid.
|
||||
*
|
||||
* @return true if the content is valid, false otherwise.
|
||||
**/
|
||||
// check if the content of this mod is valid
|
||||
//
|
||||
virtual bool doIsValid() const;
|
||||
|
||||
/**
|
||||
@@ -136,23 +131,27 @@ protected:
|
||||
|
||||
private:
|
||||
|
||||
struct Conflicts {
|
||||
EConflictType m_CurrentConflictState = CONFLICT_NONE;
|
||||
EConflictType m_ArchiveConflictState = CONFLICT_NONE;
|
||||
EConflictType m_ArchiveConflictLooseState = CONFLICT_NONE;
|
||||
bool m_HasLooseOverwrite = false;
|
||||
bool m_HasHiddenFiles = false;
|
||||
|
||||
std::set<unsigned int> m_OverwriteList; // indices of mods overritten by this mod
|
||||
std::set<unsigned int> m_OverwrittenList; // indices of mods overwriting this mod
|
||||
std::set<unsigned int> m_ArchiveOverwriteList; // indices of mods with archive files overritten by this mod
|
||||
std::set<unsigned int> m_ArchiveOverwrittenList; // indices of mods with archive files overwriting this mod
|
||||
std::set<unsigned int> m_ArchiveLooseOverwriteList; // indices of mods with archives being overwritten by this mod's loose files
|
||||
std::set<unsigned int> m_ArchiveLooseOverwrittenList; // indices of mods with loose files overwriting this mod's archive files
|
||||
};
|
||||
|
||||
Conflicts doConflictCheck() const;
|
||||
|
||||
MOBase::MemoizedLocked<std::shared_ptr<const MOBase::IFileTree>> m_FileTree;
|
||||
MOBase::MemoizedLocked<bool> m_Valid;
|
||||
MOBase::MemoizedLocked<std::set<int>> m_Contents;
|
||||
|
||||
mutable EConflictType m_CurrentConflictState;
|
||||
mutable EConflictType m_ArchiveConflictState;
|
||||
mutable EConflictType m_ArchiveConflictLooseState;
|
||||
mutable bool m_HasLooseOverwrite;
|
||||
mutable bool m_HasHiddenFiles;
|
||||
mutable QTime m_LastConflictCheck;
|
||||
|
||||
mutable std::set<unsigned int> m_OverwriteList; // indices of mods overritten by this mod
|
||||
mutable std::set<unsigned int> m_OverwrittenList; // indices of mods overwriting this mod
|
||||
mutable std::set<unsigned int> m_ArchiveOverwriteList; // indices of mods with archive files overritten by this mod
|
||||
mutable std::set<unsigned int> m_ArchiveOverwrittenList; // indices of mods with archive files overwriting this mod
|
||||
mutable std::set<unsigned int> m_ArchiveLooseOverwriteList; // indices of mods with archives being overwritten by this mod's loose files
|
||||
mutable std::set<unsigned int> m_ArchiveLooseOverwrittenList; // indices of mods with loose files overwriting this mod's archive files
|
||||
MOBase::MemoizedLocked<Conflicts> m_Conflicts;
|
||||
|
||||
};
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user