From 255f96ce7e27124f1bed071564e67a2e4a25c8aa Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Tue, 26 Nov 2019 05:47:11 -0500 Subject: only log crash dumps message on startup --- src/main.cpp | 14 ++++++++++++++ src/organizercore.cpp | 6 ------ 2 files changed, 14 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/main.cpp b/src/main.cpp index 02347ee3..41ce0eb5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -554,6 +554,20 @@ int runApplication(MOApplication &application, SingleInstance &instance, return 1; } + { + // log if there are any dmp files + const auto hasCrashDumps = + !QDir(QString::fromStdWString(organizer.crashDumpsPath())) + .entryList({"*.dmp"}, QDir::Files) + .empty(); + + if (hasCrashDumps) { + log::debug( + "there are crash dumps in '{}'", + QString::fromStdWString(organizer.crashDumpsPath())); + } + } + log::debug("initializing plugins"); PluginContainer pluginContainer(&organizer); pluginContainer.loadPlugins(); diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 9ceb149e..c585ba09 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -484,12 +484,6 @@ bool OrganizerCore::cycleDiagnostics() removeOldFiles(path, "*.dmp", maxDumps, QDir::Time|QDir::Reversed); } - // log if there are any files left - const auto files = QDir(path).entryList({"*.dmp"}, QDir::Files); - if (!files.isEmpty()) { - log::debug("there are crash dumps in '{}'", path); - } - return true; } -- cgit v1.3.1 From e67381b4b8731d80a4f11fb441d46424b571a659 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Tue, 26 Nov 2019 06:14:13 -0500 Subject: log timezone --- src/env.cpp | 44 ++++++++++++++++++++++++++++++++++++++++++++ src/env.h | 4 ++++ 2 files changed, 48 insertions(+) (limited to 'src') diff --git a/src/env.cpp b/src/env.cpp index 507607d1..a23e65a4 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -104,10 +104,54 @@ const Metrics& Environment::metrics() const return *m_metrics; } +QString Environment::timezone() const +{ + TIME_ZONE_INFORMATION tz = {}; + + const auto r = GetTimeZoneInformation(&tz); + if (r == TIME_ZONE_ID_INVALID) { + const auto e = GetLastError(); + log::error("failed to get timezone, {}", formatSystemMessage(e)); + return "unknown"; + } + + auto offsetString = [](int o) { + return + QString("%1%2:%3") + .arg(o < 0 ? "" : "+") + .arg(QString::number(o / 60), 2, QChar::fromLatin1('0')) + .arg(QString::number(o % 60), 2, QChar::fromLatin1('0')); + }; + + const auto stdName = QString::fromWCharArray(tz.StandardName); + const auto stdOffset = -(tz.Bias + tz.StandardBias); + const auto std = QString("%1, %2") + .arg(stdName) + .arg(offsetString(stdOffset)); + + const auto dstName = QString::fromWCharArray(tz.DaylightName); + const auto dstOffset = -(tz.Bias + tz.DaylightBias); + const auto dst = QString("%1, %2") + .arg(dstName) + .arg(offsetString(dstOffset)); + + QString s; + + if (r == TIME_ZONE_ID_DAYLIGHT) { + s = dst + " (dst is active, std is " + std + ")"; + } else { + s = std + " (std is active, dst is " + dst + ")"; + } + + return s; +} + void Environment::dump(const Settings& s) const { log::debug("windows: {}", windowsInfo().toString()); + log::debug("time zone: {}", timezone()); + if (windowsInfo().compatibilityMode()) { log::warn("MO seems to be running in compatibility mode"); } diff --git a/src/env.h b/src/env.h index f95d1013..f8b1eb70 100644 --- a/src/env.h +++ b/src/env.h @@ -147,6 +147,10 @@ public: // const Metrics& metrics() const; + // timezone + // + QString timezone() const; + // logs the environment // void dump(const Settings& s) const; -- cgit v1.3.1 From 7e1403dd28ec5141e7f4ca3aa6b0bb6e8b0375b0 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Tue, 26 Nov 2019 06:18:13 -0500 Subject: simplified security products: don't log guids, remove duplicate entries --- src/env.cpp | 14 ++++++++++++-- src/envsecurity.cpp | 7 ------- 2 files changed, 12 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/env.cpp b/src/env.cpp index a23e65a4..b3f9525f 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -157,8 +157,18 @@ void Environment::dump(const Settings& s) const } log::debug("security products:"); - for (const auto& sp : securityProducts()) { - log::debug(" . {}", sp.toString()); + + { + // ignore products with identical names, some AVs register themselves with + // the same names and provider, but different guids + std::set productNames; + for (const auto& sp : securityProducts()) { + productNames.insert(sp.toString()); + } + + for (auto&& name : productNames) { + log::debug(" . {}", name); + } } log::debug("modules loaded in process:"); diff --git a/src/envsecurity.cpp b/src/envsecurity.cpp index 6d62728b..87db98ce 100644 --- a/src/envsecurity.cpp +++ b/src/envsecurity.cpp @@ -207,13 +207,6 @@ QString SecurityProduct::toString() const s += ", definitions outdated"; } - // all products have a guid, but the windows firewall is not actually a real - // one from wmi, it's queried independently in getWindowsFirewall() and has a - // null guid, so just don't log it - if (!m_guid.isNull()) { - s += ", " + m_guid.toString(QUuid::QUuid::WithoutBraces); - } - return s; } -- cgit v1.3.1 From 637188a58bd99e6355ced6c296533c362fb8efd6 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Tue, 26 Nov 2019 06:21:16 -0500 Subject: don't log md5 for any system file --- src/envmodule.cpp | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/envmodule.cpp b/src/envmodule.cpp index 13831631..8d348b5e 100644 --- a/src/envmodule.cpp +++ b/src/envmodule.cpp @@ -295,10 +295,19 @@ QDateTime Module::getTimestamp(const VS_FIXEDFILEINFO& fi) const QString Module::getMD5() const { - if (m_path.contains("\\windows\\", Qt::CaseInsensitive)) { - // don't calculate md5 for system files, it's not really relevant and - // it takes a while - return {}; + static const std::set ignore = { + "\\windows\\", + "\\program files\\", + "\\program files (x86)\\", + "\\programdata\\" + }; + + // don't calculate md5 for system files, it's not really relevant and + // it takes a while + for (auto&& i : ignore) { + if (m_path.contains(i, Qt::CaseInsensitive)) { + return {}; + } } // opening the file -- cgit v1.3.1 From 08bf03854d0688cd8dbbfed7d596888dbedfcb4b Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Tue, 26 Nov 2019 06:27:56 -0500 Subject: removed useless logging about servers --- src/downloadmanager.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index c84d4bd4..3143a22e 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -996,8 +996,8 @@ void DownloadManager::queryInfoMd5(int index) QCryptographicHash hash(QCryptographicHash::Md5); const qint64 progressStep = 10 * 1024 * 1024; QProgressDialog progress(tr("Hashing download file '%1'").arg(info->m_FileName), - tr("Cancel"), - 0, + tr("Cancel"), + 0, downloadFile.size() / progressStep); progress.setWindowModality(Qt::WindowModal); progress.setMinimumDuration(1000); @@ -1606,7 +1606,7 @@ void DownloadManager::nxmFilesAvailable(QString, int, QVariant userData, QVarian emit showMessage(tr("No matching file found on Nexus! Maybe this file is no longer available or it was renamed?")); } else { SelectionDialog selection(tr("No file on Nexus matches the selected file by name. Please manually choose the correct one.")); - std::sort(files.begin(), files.end(), [](const QVariant& lhs, const QVariant& rhs) + std::sort(files.begin(), files.end(), [](const QVariant& lhs, const QVariant& rhs) {return lhs.toMap()["uploaded_timestamp"].toInt() > rhs.toMap()["uploaded_timestamp"].toInt();}); for (QVariant file : files) { QVariantMap fileInfo = file.toMap(); @@ -1692,7 +1692,6 @@ static int evaluateFileInfoMap( } if (!found) { - log::error("server '{}' not found while sorting by preference", name); return 0; } -- cgit v1.3.1 From 2c2c47c21502db6471fe7d727f942c2400b150c1 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Tue, 26 Nov 2019 07:06:20 -0500 Subject: log new modules being loaded after startup --- src/env.cpp | 147 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/env.h | 24 ++++++++++ src/main.cpp | 4 ++ 3 files changed, 175 insertions(+) (limited to 'src') diff --git a/src/env.cpp b/src/env.cpp index b3f9525f..51631607 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -56,6 +56,55 @@ Console::~Console() } +ModuleNotification::ModuleNotification(std::function f) + : m_cookie(nullptr), m_f(std::move(f)) +{ +} + +ModuleNotification::~ModuleNotification() +{ + if (!m_cookie) { + return; + } + + typedef NTSTATUS NTAPI LdrUnregisterDllNotificationType( + PVOID Cookie + ); + + LibraryPtr ntdll(LoadLibraryW(L"ntdll.dll")); + + if (!ntdll) { + log::error("failed to load ntdll.dll while unregistering for module notifications"); + return; + } + + auto* LdrUnregisterDllNotification = reinterpret_cast( + GetProcAddress(ntdll.get(), "LdrUnregisterDllNotification")); + + if (!LdrUnregisterDllNotification) { + log::error("LdrUnregisterDllNotification not found in ntdll.dll"); + return; + } + + const auto r = LdrUnregisterDllNotification(m_cookie); + if (r != 0) { + log::error("failed to unregister for module notifications, error {}", r); + } +} + +void ModuleNotification::setCookie(void* c) +{ + m_cookie = c; +} + +void ModuleNotification::fire(const Module& m) +{ + if (m_f) { + m_f(m); + } +} + + Environment::Environment() { } @@ -146,6 +195,104 @@ QString Environment::timezone() const return s; } +std::unique_ptr Environment::onModuleLoaded( + std::function f) +{ + typedef struct _UNICODE_STRING { + USHORT Length; + USHORT MaximumLength; + PWSTR Buffer; + } UNICODE_STRING, *PUNICODE_STRING; + + typedef const PUNICODE_STRING PCUNICODE_STRING; + + typedef struct _LDR_DLL_LOADED_NOTIFICATION_DATA { + ULONG Flags; //Reserved. + PCUNICODE_STRING FullDllName; //The full path name of the DLL module. + PCUNICODE_STRING BaseDllName; //The base file name of the DLL module. + PVOID DllBase; //A pointer to the base address for the DLL in memory. + ULONG SizeOfImage; //The size of the DLL image, in bytes. + } LDR_DLL_LOADED_NOTIFICATION_DATA, *PLDR_DLL_LOADED_NOTIFICATION_DATA; + + typedef struct _LDR_DLL_UNLOADED_NOTIFICATION_DATA { + ULONG Flags; //Reserved. + PCUNICODE_STRING FullDllName; //The full path name of the DLL module. + PCUNICODE_STRING BaseDllName; //The base file name of the DLL module. + PVOID DllBase; //A pointer to the base address for the DLL in memory. + ULONG SizeOfImage; //The size of the DLL image, in bytes. + } LDR_DLL_UNLOADED_NOTIFICATION_DATA, *PLDR_DLL_UNLOADED_NOTIFICATION_DATA; + + typedef union _LDR_DLL_NOTIFICATION_DATA { + LDR_DLL_LOADED_NOTIFICATION_DATA Loaded; + LDR_DLL_UNLOADED_NOTIFICATION_DATA Unloaded; + } LDR_DLL_NOTIFICATION_DATA, *PLDR_DLL_NOTIFICATION_DATA; + + typedef VOID CALLBACK LDR_DLL_NOTIFICATION_FUNCTION( + ULONG NotificationReason, + const PLDR_DLL_NOTIFICATION_DATA NotificationData, + PVOID Context + ); + + typedef LDR_DLL_NOTIFICATION_FUNCTION* PLDR_DLL_NOTIFICATION_FUNCTION; + + typedef NTSTATUS NTAPI LdrRegisterDllNotificationType( + ULONG Flags, + PLDR_DLL_NOTIFICATION_FUNCTION NotificationFunction, + PVOID Context, + PVOID *Cookie + ); + + const ULONG LDR_DLL_NOTIFICATION_REASON_LOADED = 1; + const ULONG LDR_DLL_NOTIFICATION_REASON_UNLOADED = 2; + + + // loading ntdll.dll, the function will be found with GetProcAddress() + LibraryPtr ntdll(LoadLibraryW(L"ntdll.dll")); + + if (!ntdll) { + log::error("failed to load ntdll.dll while registering for module notifications"); + return {}; + } + + auto* LdrRegisterDllNotification = reinterpret_cast( + GetProcAddress(ntdll.get(), "LdrRegisterDllNotification")); + + if (!LdrRegisterDllNotification) { + log::error("LdrRegisterDllNotification not found in ntdll.dll"); + return {}; + } + + + auto context = std::make_unique(f); + void* cookie = nullptr; + + auto OnDllLoaded = [](ULONG reason, const PLDR_DLL_NOTIFICATION_DATA data, void* context) { + if (reason == LDR_DLL_NOTIFICATION_REASON_LOADED) { + const Module m( + QString::fromWCharArray( + data->Loaded.FullDllName->Buffer, + data->Loaded.FullDllName->Length / sizeof(wchar_t)), + data->Loaded.SizeOfImage); + + if (context) { + static_cast(context)->fire(m); + } + } + }; + + const auto r = LdrRegisterDllNotification( + 0, OnDllLoaded, context.get(), &cookie); + + if (r != 0) { + log::error("failed to register for module notifications, error {}", r); + return {}; + } + + context->setCookie(cookie); + + return context; +} + void Environment::dump(const Settings& s) const { log::debug("windows: {}", windowsInfo().toString()); diff --git a/src/env.h b/src/env.h index f8b1eb70..946cb13b 100644 --- a/src/env.h +++ b/src/env.h @@ -119,6 +119,27 @@ private: }; +class ModuleNotification +{ +public: + ModuleNotification(std::function f); + ~ModuleNotification(); + + ModuleNotification(const ModuleNotification&) = delete; + ModuleNotification& operator=(const ModuleNotification&) = delete; + + ModuleNotification(ModuleNotification&&) = default; + ModuleNotification& operator=(ModuleNotification&&) = default; + + void setCookie(void* c); + void fire(const Module& m); + +private: + void* m_cookie; + std::function m_f; +}; + + // represents the process's environment // class Environment @@ -151,6 +172,9 @@ public: // QString timezone() const; + std::unique_ptr onModuleLoaded( + std::function f); + // logs the environment // void dump(const Settings& s) const; diff --git a/src/main.cpp b/src/main.cpp index 41ce0eb5..6e112479 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -547,6 +547,10 @@ int runApplication(MOApplication &application, SingleInstance &instance, settings.dump(); sanityChecks(env); + const auto moduleNotification = env.onModuleLoaded([](auto&& m) { + log::debug("loaded module {}", m.toString()); + }); + log::debug("initializing core"); OrganizerCore organizer(settings); if (!organizer.bootstrap()) { -- cgit v1.3.1 From c3068ce50ab6e25e21452c2ea2f83086ddf555d5 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Tue, 26 Nov 2019 08:39:22 -0500 Subject: added rivatuner to sanity checks now also checks modules loaded after startup fixed crash on w7 when checking some modules --- src/env.cpp | 44 ++++++++++++++++++++++++++++++-------------- src/env.h | 11 ++++++++--- src/main.cpp | 4 +++- src/sanitychecks.cpp | 37 ++++++++++++++++++++++--------------- 4 files changed, 63 insertions(+), 33 deletions(-) (limited to 'src') diff --git a/src/env.cpp b/src/env.cpp index 51631607..f9507dc1 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -56,8 +56,8 @@ Console::~Console() } -ModuleNotification::ModuleNotification(std::function f) - : m_cookie(nullptr), m_f(std::move(f)) +ModuleNotification::ModuleNotification(QObject* o, std::function f) + : m_cookie(nullptr), m_object(o), m_f(std::move(f)) { } @@ -97,10 +97,26 @@ void ModuleNotification::setCookie(void* c) m_cookie = c; } -void ModuleNotification::fire(const Module& m) +void ModuleNotification::fire(QString path, std::size_t fileSize) { + if (m_loaded.contains(path)) { + // don't notify if it's been loaded before + } + + m_loaded.insert(path); + + // constructing a Module will query the version info of the file, which seems + // to generate an access violation for at least plugin_python.dll on Windows 7 + // + // it's not clear what the problem is, but making sure this is deferred until + // _after_ the dll is loaded seems to fix it + // + // so this queues the callback in the main thread + if (m_f) { - m_f(m); + QMetaObject::invokeMethod(m_object, [path, fileSize, f=m_f] { + f(Module(path, fileSize)); + }, Qt::QueuedConnection); } } @@ -196,7 +212,7 @@ QString Environment::timezone() const } std::unique_ptr Environment::onModuleLoaded( - std::function f) + QObject* o, std::function f) { typedef struct _UNICODE_STRING { USHORT Length; @@ -263,19 +279,19 @@ std::unique_ptr Environment::onModuleLoaded( } - auto context = std::make_unique(f); + auto context = std::make_unique(o, f); void* cookie = nullptr; auto OnDllLoaded = [](ULONG reason, const PLDR_DLL_NOTIFICATION_DATA data, void* context) { if (reason == LDR_DLL_NOTIFICATION_REASON_LOADED) { - const Module m( - QString::fromWCharArray( - data->Loaded.FullDllName->Buffer, - data->Loaded.FullDllName->Length / sizeof(wchar_t)), - data->Loaded.SizeOfImage); - - if (context) { - static_cast(context)->fire(m); + if (data && data->Loaded.FullDllName) { + if (context) { + static_cast(context)->fire( + QString::fromWCharArray( + data->Loaded.FullDllName->Buffer, + data->Loaded.FullDllName->Length / sizeof(wchar_t)), + data->Loaded.SizeOfImage); + } } } }; diff --git a/src/env.h b/src/env.h index 946cb13b..dc0fd864 100644 --- a/src/env.h +++ b/src/env.h @@ -122,7 +122,7 @@ private: class ModuleNotification { public: - ModuleNotification(std::function f); + ModuleNotification(QObject* o, std::function f); ~ModuleNotification(); ModuleNotification(const ModuleNotification&) = delete; @@ -132,10 +132,12 @@ public: ModuleNotification& operator=(ModuleNotification&&) = default; void setCookie(void* c); - void fire(const Module& m); + void fire(QString path, std::size_t fileSize); private: void* m_cookie; + QObject* m_object; + std::set m_loaded; std::function m_f; }; @@ -172,8 +174,11 @@ public: // QString timezone() const; + // will call `f` on the same thread `o` is running on every time a module + // is loaded in the process + // std::unique_ptr onModuleLoaded( - std::function f); + QObject* o, std::function f); // logs the environment // diff --git a/src/main.cpp b/src/main.cpp index 6e112479..3cccf365 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -93,6 +93,7 @@ using namespace MOShared; void sanityChecks(const env::Environment& env); +int checkIncompatibleModule(const env::Module& m); bool createAndMakeWritable(const std::wstring &subPath) { QString const dataPath = qApp->property("dataPath").toString(); @@ -547,8 +548,9 @@ int runApplication(MOApplication &application, SingleInstance &instance, settings.dump(); sanityChecks(env); - const auto moduleNotification = env.onModuleLoaded([](auto&& m) { + const auto moduleNotification = env.onModuleLoaded(qApp, [](auto&& m) { log::debug("loaded module {}", m.toString()); + checkIncompatibleModule(m); }); log::debug("initializing core"); diff --git a/src/sanitychecks.cpp b/src/sanitychecks.cpp index 3b4185a7..495795f2 100644 --- a/src/sanitychecks.cpp +++ b/src/sanitychecks.cpp @@ -202,29 +202,36 @@ int checkMissingFiles() return n; } -bool checkNahimic(const env::Environment& e) +int checkIncompatibleModule(const env::Module& m) { - // Nahimic seems to interfere mostly with dialogs, like the mod info dialog: - // it renders dialogs fully white and makes it impossible to interact with - // them + // these dlls seems to interfere mostly with dialogs, like the mod info + // dialog: it renders dialogs fully white and makes it impossible to interact + // with them // - // NahimicOSD.dll is usually loaded on startup, but there has been some - // reports where it got loaded later, so this check is not entirely accurate + // the dlls is usually loaded on startup, but there has been some reports + // where it got loaded later, so this is also called every time a new module + // is loaded into this process - for (auto&& m : e.loadedModules()) { - const QFileInfo file(m.path()); + static const std::map names = { + {"NahimicOSD.dll", "Nahimic"}, + {"RTSSHooks64.dll", "RivaTuner Statistics Server"} + }; + + const QFileInfo file(m.path()); + int n = 0; - if (file.fileName().compare("NahimicOSD.dll", Qt::CaseInsensitive) == 0) { + for (auto&& p : names) { + if (file.fileName().compare(p.first, Qt::CaseInsensitive) == 0) { log::warn( - "NahimicOSD.dll is loaded. Nahimic is known to cause issues with " + "{} is loaded. This program is known to cause issues with " "Mod Organizer, such as freezing or blank windows. Consider " - "uninstalling it."); + "uninstalling it. ({})", p.second, file.absoluteFilePath()); - return true; + ++n; } } - return false; + return n; } int checkIncompatibilities(const env::Environment& e) @@ -233,8 +240,8 @@ int checkIncompatibilities(const env::Environment& e) int n = 0; - if (checkNahimic(e)) { - ++n; + for (auto&& m : e.loadedModules()) { + n += checkIncompatibleModule(m); } return n; -- cgit v1.3.1 From 9194bfa16bb78c10bc4e23abd26ba16c33956794 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Tue, 26 Nov 2019 08:59:04 -0500 Subject: added option to hide confirmation when switching instances --- src/mainwindow.cpp | 16 ++++++++++------ src/settings.cpp | 10 ++++++++++ src/settings.h | 5 +++++ src/settingsdialog.ui | 7 +++++++ src/settingsdialoggeneral.cpp | 2 ++ 5 files changed, 34 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 9ea554a2..844456a6 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -6139,14 +6139,18 @@ void MainWindow::on_actionNotifications_triggered() void MainWindow::on_actionChange_Game_triggered() { - const auto r = QMessageBox::question( - this, tr("Are you sure?"), tr("This will restart MO, continue?"), - QMessageBox::Yes | QMessageBox::Cancel); + if (m_OrganizerCore.settings().interface().showChangeGameConfirmation()) { + const auto r = QMessageBox::question( + this, tr("Are you sure?"), tr("This will restart MO, continue?"), + QMessageBox::Yes | QMessageBox::Cancel); - if (r == QMessageBox::Yes) { - InstanceManager::instance().clearCurrentInstance(); - ExitModOrganizer(Exit::Restart); + if (r != QMessageBox::Yes) { + return; + } } + + InstanceManager::instance().clearCurrentInstance(); + ExitModOrganizer(Exit::Restart); } void MainWindow::setCategoryListVisible(bool visible) diff --git a/src/settings.cpp b/src/settings.cpp index b533b400..e1e5c2da 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -1961,6 +1961,16 @@ void InterfaceSettings::setTutorialCompleted(const QString& windowName, bool b) set(m_Settings, "CompletedWindowTutorials", windowName, b); } +bool InterfaceSettings::showChangeGameConfirmation() const +{ + return get(m_Settings, "Settings", "show_change_game_confirmation", true); +} + +void InterfaceSettings::setShowChangeGameConfirmation(bool b) const +{ + set(m_Settings, "Settings", "show_change_game_confirmation", b); +} + DiagnosticsSettings::DiagnosticsSettings(QSettings& settings) : m_Settings(settings) diff --git a/src/settings.h b/src/settings.h index d71fabf4..870e0fc4 100644 --- a/src/settings.h +++ b/src/settings.h @@ -605,6 +605,11 @@ public: bool isTutorialCompleted(const QString& windowName) const; void setTutorialCompleted(const QString& windowName, bool b=true); + // whether to show the confirmation when switching instances + // + bool showChangeGameConfirmation() const; + void setShowChangeGameConfirmation(bool b) const; + private: QSettings& m_Settings; }; diff --git a/src/settingsdialog.ui b/src/settingsdialog.ui index b88c8b71..e63ca692 100644 --- a/src/settingsdialog.ui +++ b/src/settingsdialog.ui @@ -108,6 +108,13 @@ + + + + Show confirmation when changing instance + + + diff --git a/src/settingsdialoggeneral.cpp b/src/settingsdialoggeneral.cpp index e21fc5d0..b0e64305 100644 --- a/src/settingsdialoggeneral.cpp +++ b/src/settingsdialoggeneral.cpp @@ -17,6 +17,7 @@ GeneralSettingsTab::GeneralSettingsTab(Settings& s, SettingsDialog& d) ui->colorTable->load(s); ui->centerDialogs->setChecked(settings().geometry().centerDialogs()); + ui->changeGameConfirmation->setChecked(settings().interface().showChangeGameConfirmation()); ui->compactBox->setChecked(settings().interface().compactDownloads()); ui->showMetaBox->setChecked(settings().interface().metaDownloads()); ui->checkForUpdates->setChecked(settings().checkForUpdates()); @@ -59,6 +60,7 @@ void GeneralSettingsTab::update() ui->colorTable->commitColors(); settings().geometry().setCenterDialogs(ui->centerDialogs->isChecked()); + settings().interface().setShowChangeGameConfirmation(ui->changeGameConfirmation->isChecked()); settings().interface().setCompactDownloads(ui->compactBox->isChecked()); settings().interface().setMetaDownloads(ui->showMetaBox->isChecked()); settings().setCheckForUpdates(ui->checkForUpdates->isChecked()); -- cgit v1.3.1 From cde6137ca586239b36da59d46d7a1d3e55ed43ce Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Tue, 26 Nov 2019 08:59:15 -0500 Subject: added loot to missing files check --- src/sanitychecks.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/sanitychecks.cpp b/src/sanitychecks.cpp index 495795f2..ef57a503 100644 --- a/src/sanitychecks.cpp +++ b/src/sanitychecks.cpp @@ -177,9 +177,14 @@ int checkMissingFiles() { // files that are likely to be eaten static const QStringList files({ - "helper.exe", "nxmhandler.exe", - "usvfs_proxy_x64.exe", "usvfs_proxy_x86.exe", - "usvfs_x64.dll", "usvfs_x86.dll" + "helper.exe", + "nxmhandler.exe", + "usvfs_proxy_x64.exe", + "usvfs_proxy_x86.exe", + "usvfs_x64.dll", + "usvfs_x86.dll", + "loot/loot.dll", + "loot/lootcli.exe" }); log::debug(" . missing files"); -- cgit v1.3.1 From c65fbcdb374f688cc92080643669349766181f80 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Tue, 26 Nov 2019 09:07:43 -0500 Subject: ignore some instance folders like "cache" and "qtwebengine" --- src/instancemanager.cpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/instancemanager.cpp b/src/instancemanager.cpp index ec3c1add..a9da30d9 100644 --- a/src/instancemanager.cpp +++ b/src/instancemanager.cpp @@ -259,7 +259,22 @@ QString InstanceManager::instancePath() const QStringList InstanceManager::instances() const { - return QDir(instancePath()).entryList(QDir::Dirs | QDir::NoDotAndDotDot); + const std::set ignore = { + "cache", "qtwebengine", + }; + + const auto dirs = QDir(instancePath()) + .entryList(QDir::Dirs | QDir::NoDotAndDotDot); + + QStringList list; + + for (auto&& d : dirs) { + if (!ignore.contains(QFileInfo(d).fileName().toLower())) { + list.push_back(d); + } + } + + return list; } -- cgit v1.3.1 From 4db719a3bb0c80db8720456885489a72f4edb05b Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Tue, 26 Nov 2019 09:19:36 -0500 Subject: don't log EPT_S_NOT_REGISTERED errors, treat it as firewall disabled --- src/envsecurity.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/envsecurity.cpp b/src/envsecurity.cpp index 87db98ce..6f4826e7 100644 --- a/src/envsecurity.cpp +++ b/src/envsecurity.cpp @@ -383,7 +383,18 @@ std::optional getWindowsFirewall() hr = policy->get_FirewallEnabled(NET_FW_PROFILE2_PUBLIC, &enabledVariant); if (FAILED(hr)) { - log::error("get_FirewallEnabled failed, {}", formatSystemMessage(hr)); + // EPT_S_NOT_REGISTERED is "There are no more endpoints available from the + // endpoint mapper", which seems to happen sometimes on Windows 7 when the + // firewall has been disabled, so treat it as such and don't log it + // + // however the user reported the error was actually 0x800706d9, not just + // 0x6d9 (1753, what EPT_S_NOT_REGISTERED is defined to), so this is + // testing for both because it's not clear which it is and nobody can + // reproduce it + if (hr != EPT_S_NOT_REGISTERED && hr != 0x800706d9) { + log::error("get_FirewallEnabled failed, {}", formatSystemMessage(hr)); + } + return {}; } } -- cgit v1.3.1 From f85478a5b40ba609a0b36a2095e2b7fd3588857f Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Tue, 26 Nov 2019 10:00:37 -0500 Subject: fix sort in overwrite, remember settings remove useless header in archives list --- src/mainwindow.cpp | 1 + src/overwriteinfodialog.cpp | 13 +++++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 844456a6..79604c9a 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -277,6 +277,7 @@ MainWindow::MainWindow(Settings &settings ui->espList->installEventFilter(m_OrganizerCore.pluginList()); ui->bsaList->setLocalMoveOnly(true); + ui->bsaList->setHeaderHidden(true); initDownloadView(); diff --git a/src/overwriteinfodialog.cpp b/src/overwriteinfodialog.cpp index 078bcfc9..b2f2f1f9 100644 --- a/src/overwriteinfodialog.cpp +++ b/src/overwriteinfodialog.cpp @@ -85,6 +85,7 @@ OverwriteInfoDialog::OverwriteInfoDialog(ModInfo::Ptr modInfo, QWidget *parent) ui->filesView->setModel(m_FileSystemModel); ui->filesView->setRootIndex(m_FileSystemModel->index(modInfo->absolutePath())); ui->filesView->setColumnWidth(0, 250); + ui->filesView->sortByColumn(0, Qt::AscendingOrder); m_DeleteAction = new QAction(tr("&Delete"), ui->filesView); m_RenameAction = new QAction(tr("&Rename"), ui->filesView); @@ -106,13 +107,21 @@ OverwriteInfoDialog::~OverwriteInfoDialog() void OverwriteInfoDialog::showEvent(QShowEvent* e) { - Settings::instance().geometry().restoreGeometry(this); + const auto& s = Settings::instance(); + + s.geometry().restoreGeometry(this); + s.geometry().restoreState(ui->filesView->header()); + QDialog::showEvent(e); } void OverwriteInfoDialog::done(int r) { - Settings::instance().geometry().saveGeometry(this); + auto& s = Settings::instance(); + + s.geometry().saveGeometry(this); + s.geometry().saveState(ui->filesView->header()); + QDialog::done(r); } -- cgit v1.3.1 From b8e1a45840a72e1e959f1fad2f00015a72500161 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Tue, 26 Nov 2019 10:05:48 -0500 Subject: sortable filetree, remember settings --- src/modinfodialog.ui | 3 +++ src/modinfodialogfiletree.cpp | 11 +++++++++++ src/modinfodialogfiletree.h | 2 ++ 3 files changed, 16 insertions(+) (limited to 'src') diff --git a/src/modinfodialog.ui b/src/modinfodialog.ui index 13a245f1..59263e1c 100644 --- a/src/modinfodialog.ui +++ b/src/modinfodialog.ui @@ -1258,6 +1258,9 @@ p, li { white-space: pre-wrap; } true + + true + diff --git a/src/modinfodialogfiletree.cpp b/src/modinfodialogfiletree.cpp index 71ea9210..ca007c1c 100644 --- a/src/modinfodialogfiletree.cpp +++ b/src/modinfodialogfiletree.cpp @@ -21,6 +21,7 @@ FileTreeTab::FileTreeTab(ModInfoDialogTabContext cx) m_fs->setReadOnly(false); ui->filetree->setModel(m_fs); ui->filetree->setColumnWidth(0, 300); + ui->filetree->sortByColumn(0, Qt::AscendingOrder); m_actions.newFolder = new QAction(tr("&New Folder"), ui->filetree); m_actions.open = new QAction(tr("&Open/Execute"), ui->filetree); @@ -55,6 +56,16 @@ void FileTreeTab::clear() setHasData(true); } +void FileTreeTab::saveState(Settings& s) +{ + s.geometry().saveState(ui->filetree->header()); +} + +void FileTreeTab::restoreState(const Settings& s) +{ + s.geometry().restoreState(ui->filetree->header()); +} + void FileTreeTab::update() { const auto rootPath = mod().absolutePath(); diff --git a/src/modinfodialogfiletree.h b/src/modinfodialogfiletree.h index 42773899..494a7e14 100644 --- a/src/modinfodialogfiletree.h +++ b/src/modinfodialogfiletree.h @@ -12,6 +12,8 @@ public: FileTreeTab(ModInfoDialogTabContext cx); void clear() override; + void saveState(Settings& s); + void restoreState(const Settings& s); void update() override; bool deleteRequested() override; -- cgit v1.3.1 From 9519bd62193ac1233e8087c4c143689557288c34 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Tue, 26 Nov 2019 10:31:26 -0500 Subject: added open mod info to data tab context menu --- src/mainwindow.cpp | 23 +++++++++++++++++++++++ src/mainwindow.h | 1 + 2 files changed, 24 insertions(+) (limited to 'src') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 79604c9a..06e22e4e 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -5428,6 +5428,27 @@ void MainWindow::openDataOriginExplorer_clicked() shell::Explore(fullPath); } +void MainWindow::openDataModInfo_clicked() +{ + if (m_ContextItem == nullptr) { + return; + } + + const auto originID = m_ContextItem->data(1, Qt::UserRole + 1).toInt(); + const auto& origin = m_OrganizerCore.directoryStructure()->getOriginByID(originID); + const auto& name = QString::fromStdWString(origin.getName()); + + unsigned int index = ModInfo::getIndex(name); + if (index == UINT_MAX) { + return; + } + + ModInfo::Ptr modInfo = ModInfo::getByIndex(index); + if (modInfo) { + displayModInformation(modInfo, index, ModInfoTabIDs::None); + } +} + void MainWindow::updateAvailable() { ui->actionUpdate->setEnabled(true); @@ -5473,6 +5494,8 @@ void MainWindow::on_dataTree_customContextMenuRequested(const QPoint &pos) menu.addAction("Open Origin in Explorer", this, SLOT(openDataOriginExplorer_clicked())); } + menu.addAction("Open Mod Info", this, SLOT(openDataModInfo_clicked())); + // offer to hide/unhide file, but not for files from archives if (!isArchive) { if (m_ContextItem->text(0).endsWith(ModInfo::s_HiddenExt)) { diff --git a/src/mainwindow.h b/src/mainwindow.h index 7c2ee1eb..0c96c15d 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -448,6 +448,7 @@ private slots: void hideFile(); void unhideFile(); void openDataOriginExplorer_clicked(); + void openDataModInfo_clicked(); // pluginlist context menu void enableSelectedPlugins_clicked(); -- cgit v1.3.1 From a5bd29f1117ec56632138640d508afad0232e5ed Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Wed, 27 Nov 2019 11:25:53 -0500 Subject: only default sort file trees if no setting was saved --- src/modinfodialogfiletree.cpp | 5 +++-- src/overwriteinfodialog.cpp | 6 ++++-- 2 files changed, 7 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/modinfodialogfiletree.cpp b/src/modinfodialogfiletree.cpp index ca007c1c..79ed4cba 100644 --- a/src/modinfodialogfiletree.cpp +++ b/src/modinfodialogfiletree.cpp @@ -21,7 +21,6 @@ FileTreeTab::FileTreeTab(ModInfoDialogTabContext cx) m_fs->setReadOnly(false); ui->filetree->setModel(m_fs); ui->filetree->setColumnWidth(0, 300); - ui->filetree->sortByColumn(0, Qt::AscendingOrder); m_actions.newFolder = new QAction(tr("&New Folder"), ui->filetree); m_actions.open = new QAction(tr("&Open/Execute"), ui->filetree); @@ -63,7 +62,9 @@ void FileTreeTab::saveState(Settings& s) void FileTreeTab::restoreState(const Settings& s) { - s.geometry().restoreState(ui->filetree->header()); + if (!s.geometry().restoreState(ui->filetree->header())) { + ui->filetree->sortByColumn(0, Qt::AscendingOrder); + } } void FileTreeTab::update() diff --git a/src/overwriteinfodialog.cpp b/src/overwriteinfodialog.cpp index b2f2f1f9..ab726fb3 100644 --- a/src/overwriteinfodialog.cpp +++ b/src/overwriteinfodialog.cpp @@ -85,7 +85,6 @@ OverwriteInfoDialog::OverwriteInfoDialog(ModInfo::Ptr modInfo, QWidget *parent) ui->filesView->setModel(m_FileSystemModel); ui->filesView->setRootIndex(m_FileSystemModel->index(modInfo->absolutePath())); ui->filesView->setColumnWidth(0, 250); - ui->filesView->sortByColumn(0, Qt::AscendingOrder); m_DeleteAction = new QAction(tr("&Delete"), ui->filesView); m_RenameAction = new QAction(tr("&Rename"), ui->filesView); @@ -110,7 +109,10 @@ void OverwriteInfoDialog::showEvent(QShowEvent* e) const auto& s = Settings::instance(); s.geometry().restoreGeometry(this); - s.geometry().restoreState(ui->filesView->header()); + + if (!s.geometry().restoreState(ui->filesView->header())) { + ui->filesView->sortByColumn(0, Qt::AscendingOrder); + } QDialog::showEvent(e); } -- cgit v1.3.1 From 21cddd0ad3cef165c3860c2031b4cd00802c499d Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Wed, 27 Nov 2019 12:05:11 -0500 Subject: changed mod name of unmanaged files from "data" to "" in the data tab and for the root item in the archives tab log error when trying to open mod info for a managed file but the mod isn't found --- src/mainwindow.cpp | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 06e22e4e..e134d64a 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -190,6 +190,11 @@ const QSize SmallToolbarSize(24, 24); const QSize MediumToolbarSize(32, 32); const QSize LargeToolbarSize(42, 36); +QString UnmanagedModName() +{ + return QObject::tr(""); +} + bool runLoot(QWidget* parent, OrganizerCore& core, bool didUpdateMasterList); @@ -1661,9 +1666,13 @@ void MainWindow::updateTo(QTreeWidgetItem *subTree, const std::wstring &director QString fileName = ToQString(current->getName()); QStringList columns(fileName); FilesOrigin origin = m_OrganizerCore.directoryStructure()->getOriginByID(originID); - QString source("data"); - unsigned int modIndex = ModInfo::getIndex(ToQString(origin.getName())); - if (modIndex != UINT_MAX) { + + QString source; + const unsigned int modIndex = ModInfo::getIndex(ToQString(origin.getName())); + + if (modIndex == UINT_MAX) { + source = UnmanagedModName(); + } else { ModInfo::Ptr modInfo = ModInfo::getByIndex(modIndex); source = modInfo->name(); } @@ -2046,12 +2055,17 @@ void MainWindow::updateBSAList(const QStringList &defaultArchives, const QString int originID = iter->second->data(1, Qt::UserRole).toInt(); FilesOrigin origin = m_OrganizerCore.directoryStructure()->getOriginByID(originID); - QString modName("data"); - unsigned int modIndex = ModInfo::getIndex(ToQString(origin.getName())); - if (modIndex != UINT_MAX) { + + QString modName; + const unsigned int modIndex = ModInfo::getIndex(ToQString(origin.getName())); + + if (modIndex == UINT_MAX) { + modName = UnmanagedModName(); + } else { ModInfo::Ptr modInfo = ModInfo::getByIndex(modIndex); modName = modInfo->name(); } + QList items = ui->bsaList->findItems(modName, Qt::MatchFixedString); QTreeWidgetItem * subItem = nullptr; if (items.length() > 0) { @@ -5435,11 +5449,17 @@ void MainWindow::openDataModInfo_clicked() } const auto originID = m_ContextItem->data(1, Qt::UserRole + 1).toInt(); + if (originID == 0) { + // unmanaged + return; + } + const auto& origin = m_OrganizerCore.directoryStructure()->getOriginByID(originID); const auto& name = QString::fromStdWString(origin.getName()); unsigned int index = ModInfo::getIndex(name); if (index == UINT_MAX) { + log::error("can't open mod info, mod '{}' not found", name); return; } -- cgit v1.3.1