Compare commits

...
55 Commits
Author SHA1 Message Date
Chris Bessent 1df1ea5e11 Bump version to 2.4.4 2021-12-27 02:03:08 -07:00
Chris Bessent dfcbf45e1d Fix discord invite 2021-12-27 01:58:50 -07:00
Chris Bessent 1e5c019767 Bump version to 2.4.3 2021-12-25 14:54:32 -07:00
Chris Bessent d41fbfd7e1 Bump version to 2.4.3rc3 2021-12-23 13:29:21 -07:00
Chris Bessent 2626649cc6 Bump version to 2.4.3 RC 2 2021-12-19 12:19:29 -07:00
Chris Bessent 8d08d7f6ba Update about dialog 2021-12-17 19:46:42 -07:00
Chris Bessent d30a095b3e Bump version to 2.4.3rc1 2021-12-17 05:57:40 -07:00
Chris Bessent b5efa60db9 Fix optional access on profiles dialog 2021-12-07 06:46:28 -07:00
Chris Bessent d82f196aa5 Bump version to 2.4.3alpha4 2021-11-26 13:39:28 -07:00
Chris Bessent 9f8827e3a5 Fix parsing plugins with dots in name 2021-11-26 13:37:28 -07:00
Chris Bessent d58f773c76 Don't blacklist empty usernames and passwords 2021-11-25 13:00:15 -07:00
Chris Bessent 01ad45c7aa Update translations 2021-11-23 20:58:03 -07:00
Chris Bessent 13bdf538a8 Bump version to 2.4.3alpha3 2021-11-23 20:12:03 -07:00
Chris Bessent fc40619fd9 Merge pull request #1621 from LostDragonist/misc_fixes
Misc fixes
2021-11-23 20:05:29 -07:00
Chris Bessent 565f2fddf4 Blacklist %USERNAME% from the log in case it contains PII 2021-11-23 17:48:40 -07:00
Chris Bessent 1d80c547d7 Blacklist Steam username and password from the log 2021-11-23 17:19:00 -07:00
Chris Bessent 1755f526c9 Fix extension parsing for the plugin list
The old code was just looking at the last three letters of the filename instead of looking for any kind of proper extension. This led to something like "alice.__esp" being considered a plugin which is just wrong.
2021-11-23 11:49:44 -07:00
Chris Bessent a1e953f409 Don't add invalid games to the "visit Nexus" list 2021-11-22 21:05:49 -07:00
Chris Bessent b1db3019f4 Fix selecting a new profile
This fixes clicking the "select" button in the profiles dialog when a newly created profile is selected. This was broken as the profile was being selected before the profile was added to the list of selections.

Fixes #1489
2021-11-22 17:12:51 -07:00
Chris Bessent cf137606fd Add Brave to the default blacklist
Fixes #1592
2021-11-22 17:12:51 -07:00
Chris Bessent 9644fef8bd Update Windows detection
Windows 11 reports itself as Windows 10 using the methods normally employed.  Microsoft seems to want people to use the BuildId to identify the version of Windows but I'm going to refuse to do that because it's stupid.

This change also prefers DisplayVersion over ReleaseId as somehow I have both and DisplayVersion is more correct (21H2 vs 2009).  /shrug.

Fixes #1607
2021-11-22 17:12:51 -07:00
Chris Bessent 1077a5bad0 Allow portable instances to set custom paths
Fixes #1610
2021-11-22 17:12:51 -07:00
Chris Bessent 57acd4949b Fix tab order of create instance dialog 2021-11-22 17:12:50 -07:00
Al 6ab88d5de1 Merge pull request #1616 from rux616/template-update
Update old Discord server link
2021-11-19 18:13:30 +01:00
Dan Cassidy 4a687cbfcc Update old Discord server link 2021-11-19 14:14:09 +13:00
Al 6df197579f Merge pull request #1599 from Qudix/disable_cache
Disable qml cache
2021-10-16 16:18:46 +02:00
Qudix 1d20b993d7 Remove from instance ignore list 2021-10-15 21:23:42 -05:00
Qudix 468a8b9383 Disable qml cache 2021-10-15 20:52:51 -05:00
Al 081fa3ec93 Merge pull request #1588 from Qudix/menubar_alt
Add a toggle for the Alt key showing the menubar
2021-10-13 13:12:21 +02:00
Qudix b261f3a1e5 Revert row changes 2021-09-16 15:49:28 -05:00
Qudix acf1730093 Add a toggle for the Alt key showing the menubar 2021-09-11 20:42:20 -05:00
Chris Bessent b5800435aa Bump to version 2.4.3alpha2 2021-08-27 16:53:32 -07:00
Chris Bessent 0e9da8d4f5 Fix checking for updates when no installedFile 2021-08-26 17:57:08 -07:00
Chris Bessent ee745353d9 Merge pull request #1578 from LostDragonist/forcedlib
Enable forced libraries by default
2021-08-23 10:13:23 -07:00
Chris Bessent ae311dd4dc Escape ampersands in game names in instance window
These are technically buttons so Qt tries to make shortcuts.
2021-08-22 13:10:47 -07:00
Chris Bessent 1b2f85a371 Enable forced libraries by default 2021-08-22 11:48:27 -07:00
Al a279745aa2 Merge pull request #1558 from ModOrganizer2/facegen
Added Facegen content icon
2021-07-16 14:40:28 +02:00
AL ac8b67dd63 Added Facegen content icon (found and edited by Atlntssplayer, original creator: Exuin) 2021-07-16 14:37:30 +02:00
Al 9256739e3a Merge pull request #1555 from ModOrganizer2/nexus-archived-support
Nexus archived support
2021-07-16 12:31:29 +02:00
AL d413f673e8 Added ARCHIVED support for nexus update check and download query.
Use FileStatus enum instead of scattered int literals.
Re-wrote the update check code
2021-07-12 02:33:05 +02:00
AL 4bdb4aff50 Added enum for nexus mod file status (main, update, etc). 2021-07-12 00:07:55 +02:00
Al 58e5058a2b Merge pull request #1553 from ModOrganizer2/download-sorting
Sort filetimes from newer to older while sorting by status in downloadlist
2021-07-10 20:59:47 +02:00
AL 197f619335 Sort filetimes from newer to older while sorting by status in downloadlist 2021-07-10 20:48:24 +02:00
Al 4e3d7d0180 Merge pull request #1552 from ModOrganizer2/fix-no-compile
Fix compiler error due to using same name for new dialog (VS v16.10.3)
2021-07-10 20:40:41 +02:00
AL b5cb1a2311 Fix compiler error due to using same name for new dialog (VS v16.10.3) 2021-07-10 20:36:36 +02:00
Al b5d04eff4f Update readme.md 2021-06-08 17:55:02 +02:00
Mikaël Capelle b2353278aa Merge pull request #1528 from Holt59/remove-boost-uibase
Update after removal of boost in uibase.
2021-05-27 15:43:26 +02:00
Mikaël Capelle 0bdfd4813f Update after removal of boost in uibase. 2021-05-26 20:03:08 +02:00
Mikaël Capelle e7f15ff4fd Merge pull request #1518 from LostDragonist/shortcut
Sanitize shortcut names as they result in files
2021-05-20 20:22:30 +02:00
Mikaël Capelle e8149a4b42 Merge pull request #1523 from Holt59/ignore-meta-filetree
Add option to ignore root meta.ini when creating IFileTree for QDir.
2021-05-20 20:21:17 +02:00
Mikaël Capelle d531245dea Merge pull request #1524 from Holt59/iorganizer-virtualfiletree
Add IOrganizer::virtualFileTree().
2021-05-20 20:20:45 +02:00
Mikaël Capelle ac38371648 Add IOrganizer::virtualFileTree(). 2021-05-14 14:40:15 +02:00
Mikaël Capelle c44b1cb3a3 Add option to ignore root meta.ini when creating IFileTree for QDir. 2021-05-12 20:41:41 +02:00
Chris Bessent 2a5dd6c7cf Bump to version 2.4.2 2021-05-11 06:25:20 -07:00
Chris Bessent ef5b9228da Sanitize shortcut names as they result in files 2021-05-06 03:44:40 -07:00
43 changed files with 1106 additions and 693 deletions
+1 -1
View File
@@ -8,7 +8,7 @@ assignees: ''
---
## The problem:
Briefly describe the issue you are experiencing. Tell us what you were trying to do and what happened instead. Remember, this is _not_ a place to ask questions. For that, go to our [discord channel](https://discord.gg/5tCqt6V)
Briefly describe the issue you are experiencing. Tell us what you were trying to do and what happened instead. Remember, this is _not_ a place to ask questions. For that, go to our [Discord server](https://discord.gg/6GKR9jZ).
## To Reproduce:
+2 -2
View File
@@ -12,7 +12,7 @@ The project took up speed again after a few more coders showed up in late 2017,
## Help Wanted!
Mod Organizer 2 is an open project in the hands of the community, there are problems that need to be solved and things that could be added. MO2 really needs developers and if you have the programming skills and some free time you can really improve the experience of the modding community.
To have more information, please join the open MO2 Development discord server: [Mod Organizer 2](https://discord.gg/vD2ZbfX)
To have more information, please join the open MO2 Development discord server: [Mod Organizer 2](https://discord.gg/6GKR9jZ)
If you want to help translate MO2 to your language you should join the discord server too and head to the #translation channel.
To setup a development environment on your machine, there is the [mob project](https://github.com/modorganizer2/mob) that handles that.
If you want to submit your code changes, please use a good formatting style like the default one in Visual Studio.
@@ -20,7 +20,7 @@ If you want to submit your code changes, please use a good formatting style like
Through the work of a few people of the community MO2 has come quite far, now it needs some more of those people to go further.
## Reporting Issues:
Issues should be reported to the GitHub page or on the open discord server: [Mod Organizer 2](https://discord.gg/vD2ZbfX). Here is also where dev builds are tested, bugs are reported and investigated, suggestions are discussed and a lot more.
Issues should be reported to the GitHub page or on the open discord server: [Mod Organizer 2](https://discord.gg/6GKR9jZ). Here is also where dev builds are tested, bugs are reported and investigated, suggestions are discussed and a lot more.
Credits to Tannin, LePresidente, Silarn, erasmux, AL12, LostDragonist, AnyOldName3, isa, Holt59 and many others for the development.
+1
View File
@@ -37,6 +37,7 @@ add_filter(NAME src/core GROUPS
apiuseraccount
processrunner
qdirfiletree
virtualfiletree
uilocker
)
+79 -9
View File
@@ -127,7 +127,7 @@
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string notr="true">&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Copyright © 2011-2016 Sebastian Herbord&lt;br/&gt;Copyright © 2016-2019 Mod Organizer 2 Contributors&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string notr="true">&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Copyright © 2011-2016 Sebastian Herbord&lt;br/&gt;Copyright © 2016-2021 Mod Organizer 2 Contributors&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
@@ -261,6 +261,11 @@
<string notr="true">przester</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">Qudix</string>
</property>
</item>
</widget>
</item>
</layout>
@@ -292,11 +297,31 @@
<string notr="true">miraclefreak (Czech)</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">Meiton (Czech)</string>
</property>
</item>
<item>
<property name="text">
<string>Cyb3r (Dutch)</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">madmedic (Dutch)</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">Ilja (Finnish)</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">Mikmet (Finnish)</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">Alyndiar (French)</string>
@@ -322,6 +347,11 @@
<string>Yoplala (French)</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">CircleOfDao (German)</string>
</property>
</item>
<item>
<property name="text">
<string>Faron (German)</string>
@@ -337,6 +367,11 @@
<string notr="true">Xahtax (German)</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">Ypselon (German)</string>
</property>
</item>
<item>
<property name="text">
<string>yohru (Japanese)</string>
@@ -400,6 +435,11 @@
<string notr="true">Hakan &quot;Nyks45&quot; Albayrak (Turkish)</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">Kato (Turkish)</string>
</property>
</item>
<item>
<property name="text">
<string>...and all other contributors!</string>
@@ -426,7 +466,7 @@
</property>
<item>
<property name="text">
<string>Tannin (Original Creator)</string>
<string notr="true">Tannin (Original Creator)</string>
</property>
</item>
<item>
@@ -454,11 +494,21 @@
<string notr="true">Brixified</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">ddbb07</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">deathneko11</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">dekart811</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">DoubleYou</string>
@@ -469,6 +519,21 @@
<string notr="true">Drew Warwick</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">erri120</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">ianpatt</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">Falsellyu</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">foresto</string>
@@ -496,7 +561,17 @@
</item>
<item>
<property name="text">
<string>Luca|EzioTheDeadPoet</string>
<string notr="true">JayLCypher</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">jimfcarroll</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">Luca|EzioTheDeadPoet</string>
</property>
</item>
<item>
@@ -511,7 +586,7 @@
</item>
<item>
<property name="text">
<string>Patchier</string>
<string notr="true">Patchier</string>
</property>
</item>
<item>
@@ -519,11 +594,6 @@
<string notr="true">PurpleFez</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">Qudix</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">reedts</string>
+17 -20
View File
@@ -341,28 +341,25 @@ void CreateInstanceDialog::finish()
s.game().setEdition(ci.gameVariant);
}
if (ci.type == Global) {
if (ci.paths.base != ci.dataPath) {
s.paths().setBase(ci.paths.base);
}
if (ci.paths.downloads != cid::makeDefaultPath(AppConfig::downloadPath())) {
s.paths().setDownloads(ci.paths.downloads);
}
if (ci.paths.mods != cid::makeDefaultPath(AppConfig::modsPath())) {
s.paths().setMods(ci.paths.mods);
}
if (ci.paths.profiles != cid::makeDefaultPath(AppConfig::profilesPath())) {
s.paths().setProfiles(ci.paths.profiles);
}
if (ci.paths.overwrite != cid::makeDefaultPath(AppConfig::overwritePath())) {
s.paths().setOverwrite(ci.paths.overwrite);
}
if (ci.paths.base != ci.dataPath) {
s.paths().setBase(ci.paths.base);
}
if (ci.paths.downloads != cid::makeDefaultPath(AppConfig::downloadPath())) {
s.paths().setDownloads(ci.paths.downloads);
}
if (ci.paths.mods != cid::makeDefaultPath(AppConfig::modsPath())) {
s.paths().setMods(ci.paths.mods);
}
if (ci.paths.profiles != cid::makeDefaultPath(AppConfig::profilesPath())) {
s.paths().setProfiles(ci.paths.profiles);
}
if (ci.paths.overwrite != cid::makeDefaultPath(AppConfig::overwritePath())) {
s.paths().setOverwrite(ci.paths.overwrite);
}
logCreation(tr("Writing %1...").arg(ci.iniPath));
+40 -8
View File
@@ -67,7 +67,7 @@
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="page_10">
<widget class="QWidget" name="page_1">
<layout class="QVBoxLayout" name="verticalLayout_21">
<item>
<widget class="QLabel" name="label_15">
@@ -115,7 +115,7 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="page">
<widget class="QWidget" name="page_2">
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QWidget" name="widget_7" native="true">
@@ -226,7 +226,7 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="page_2">
<widget class="QWidget" name="page_3">
<layout class="QVBoxLayout" name="verticalLayout_7">
<item>
<widget class="QWidget" name="widget_5" native="true">
@@ -367,7 +367,7 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="page_9">
<widget class="QWidget" name="page_4">
<layout class="QVBoxLayout" name="verticalLayout_19">
<item>
<widget class="QWidget" name="widget_16" native="true">
@@ -594,7 +594,7 @@
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="page_7">
<widget class="QWidget" name="page_6_simple">
<layout class="QVBoxLayout" name="verticalLayout_12">
<property name="leftMargin">
<number>0</number>
@@ -676,7 +676,7 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="page_8">
<widget class="QWidget" name="page_6_advanced">
<layout class="QVBoxLayout" name="verticalLayout_13">
<property name="leftMargin">
<number>0</number>
@@ -906,7 +906,7 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="page_3">
<widget class="QWidget" name="page_7">
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QWidget" name="widget_4" native="true">
@@ -1048,7 +1048,7 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="page_11">
<widget class="QWidget" name="page_8">
<layout class="QVBoxLayout" name="verticalLayout_22">
<item>
<widget class="QWidget" name="widget_18" native="true">
@@ -1232,6 +1232,38 @@
<header location="global">linklabel.h</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>next</tabstop>
<tabstop>cancel</tabstop>
<tabstop>hideIntro</tabstop>
<tabstop>createGlobal</tabstop>
<tabstop>createPortable</tabstop>
<tabstop>scrollArea_3</tabstop>
<tabstop>showAllGames</tabstop>
<tabstop>gamesFilter</tabstop>
<tabstop>scrollArea_2</tabstop>
<tabstop>instanceName</tabstop>
<tabstop>location</tabstop>
<tabstop>browseLocation</tabstop>
<tabstop>advancedPathOptions</tabstop>
<tabstop>base</tabstop>
<tabstop>browseBase</tabstop>
<tabstop>downloads</tabstop>
<tabstop>browseDownloads</tabstop>
<tabstop>mods</tabstop>
<tabstop>browseMods</tabstop>
<tabstop>profiles</tabstop>
<tabstop>browseProfiles</tabstop>
<tabstop>overwrite</tabstop>
<tabstop>browseOverwrite</tabstop>
<tabstop>nexusConnect</tabstop>
<tabstop>nexusManual</tabstop>
<tabstop>nexusLog</tabstop>
<tabstop>review</tabstop>
<tabstop>creationLog</tabstop>
<tabstop>launch</tabstop>
<tabstop>back</tabstop>
</tabstops>
<resources>
<include location="resources.qrc"/>
</resources>
+1 -1
View File
@@ -474,7 +474,7 @@ void GamePage::updateButton(Game* g)
return;
}
g->button->setText(g->game->gameName());
g->button->setText(g->game->gameName().replace("&", "&&"));
g->button->setIcon(g->game->gameIcon());
if (g->installed) {
+5
View File
@@ -265,6 +265,11 @@ void DirectoryRefresher::stealModFilesIntoStructure(
for (const QString &filename : stealFiles) {
if (filename.isEmpty()) {
log::warn("Trying to find file with no name");
log::warn(" . modName: {}", modName);
log::warn(" . directory: {}", directory);
log::warn(" . priority: {}", priority);
for (int i = 0; i < stealFiles.length(); ++i)
log::warn(" . stealFiles[{}]: {}", i, stealFiles[i]);
continue;
}
QFileInfo fileInfo(filename);
+1 -1
View File
@@ -273,7 +273,7 @@ bool DownloadList::lessThanPredicate(const QModelIndex &left, const QModelIndex
DownloadManager::DownloadState leftState = m_manager.getState(left.row());
DownloadManager::DownloadState rightState = m_manager.getState(right.row());
if (leftState == rightState)
return m_manager.getFileTime(left.row()) < m_manager.getFileTime(right.row());
return m_manager.getFileTime(left.row()) > m_manager.getFileTime(right.row());
else
return leftState < rightState;
} else if (left.column() == DownloadList::COL_SIZE) {
+5 -2
View File
@@ -1223,6 +1223,7 @@ QString DownloadManager::getFileTypeString(int fileType)
case 4: return tr("Old");
case 5: return tr("Miscellaneous");
case 6: return tr("Deleted");
case 7: return tr("Archived");
default: return tr("Unknown");
}
}
@@ -1723,7 +1724,8 @@ void DownloadManager::nxmFilesAvailable(QString, int, QVariant userData, QVarian
{return lhs.toMap()["uploaded_timestamp"].toInt() > rhs.toMap()["uploaded_timestamp"].toInt();});
for (QVariant file : files) {
QVariantMap fileInfo = file.toMap();
if (fileInfo["category_id"].toInt() != 6)
if (fileInfo["category_id"].toInt() != NexusInterface::FileStatus::REMOVED &&
fileInfo["category_id"].toInt() != NexusInterface::FileStatus::ARCHIVED)
selection.addChoice(fileInfo["file_name"].toString(), "", file);
}
if (selection.exec() == QDialog::Accepted) {
@@ -1958,7 +1960,8 @@ void DownloadManager::nxmFileInfoFromMd5Available(QString gameName, QVariant use
auto results = resultlist[i].toMap();
auto fileDetails = results["file_details"].toMap();
if (fileDetails["category_id"].toInt() != 6) { //6 = deleted
if (fileDetails["category_id"].toInt() != NexusInterface::FileStatus::REMOVED &&
fileDetails["category_id"].toInt() != NexusInterface::FileStatus::ARCHIVED) {
if (chosenIdx < 0) {
chosenIdx = i; //intentional to not break in order to check other results
} else {
+3 -2
View File
@@ -2,6 +2,7 @@
#include "env.h"
#include "executableslist.h"
#include "instancemanager.h"
#include "filesystemutilities.h"
#include <utility.h>
#include <log.h>
@@ -147,7 +148,7 @@ Shortcut::Shortcut(const Executable& exe)
{
const auto i = *InstanceManager::singleton().currentInstance();
m_name = exe.title();
m_name = MOBase::sanitizeFileName(exe.title());
m_target = QFileInfo(qApp->applicationFilePath()).absoluteFilePath();
m_arguments = QString("\"moshortcut://%1:%2\"")
@@ -165,7 +166,7 @@ Shortcut::Shortcut(const Executable& exe)
Shortcut& Shortcut::name(const QString& s)
{
m_name = s;
m_name = MOBase::sanitizeFileName(s);
return *this;
}
+4 -9
View File
@@ -94,11 +94,6 @@ QString WindowsInfo::toString() const
sl.push_back(m_release.buildLab);
}
// product name
if (!m_release.productName.isEmpty()) {
sl.push_back(m_release.productName);
}
// elevated
QString elevated = "?";
if (m_elevated.has_value()) {
@@ -187,11 +182,11 @@ WindowsInfo::Release WindowsInfo::getRelease() const
}
}
// localized name of windows, such as "Windows 10 Pro"
r.productName = settings.value("ProductName", "").toString();
// release ID, such as 1803
r.ID = settings.value("ReleaseId", "").toString();
r.ID = settings.value("DisplayVersion", "").toString();
if (r.ID.isEmpty()) {
r.ID = settings.value("ReleaseId", "").toString();
}
// some other build number, shown in winver.exe
r.UBR = settings.value("UBR", 0).toUInt();
+1 -5
View File
@@ -40,11 +40,7 @@ public:
// the BuildLab entry from the registry, may be empty
QString buildLab;
// product name such as "Windows 10 Pro", may not be in English, may be
// empty
QString productName;
// release ID such as 1809, may be mepty
// release ID such as 1809, may be empty
QString ID;
// some sub-build number, undocumented, may be empty
+1 -3
View File
@@ -601,9 +601,7 @@ QString InstanceManager::iniPath(const QString& instanceDir) const
std::vector<QString> InstanceManager::globalInstancePaths() const
{
const std::set<QString> ignore = {
"cache", "qtwebengine",
};
const std::set<QString> ignore = { "qtwebengine" };
const QDir root(globalInstancesRootPath());
const auto dirs = root.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
+2
View File
@@ -359,6 +359,8 @@ void initLogging()
log::getDefault().setCallback(
[](log::Entry e){ LogModel::instance().add(e); });
log::getDefault().addToBlacklist(getenv("USERNAME"), "USERNAME");
qInstallMessageHandler(qtLogCallback);
}
+174 -88
View File
@@ -1447,15 +1447,20 @@ bool MainWindow::registerNexusPage(const QString& gameName)
if (plugin == nullptr)
return false;
// Get the gameURL
QString gameURL = NexusInterface::instance().getGameURL(gameName);
if (gameURL.isEmpty())
return false;
// Create an action
QAction* action = new QAction(
plugin->gameIcon(),
QObject::tr("Visit %1 on Nexus").arg(plugin->gameName()),
QObject::tr("Visit %1 on Nexus").arg(gameName),
this);
// Bind the action
connect(action, &QAction::triggered, this, [this, gameName]() {
shell::Open(QUrl(NexusInterface::instance().getGameURL(gameName)));
connect(action, &QAction::triggered, this, [this, gameURL]() {
shell::Open(QUrl(gameURL));
}, Qt::QueuedConnection);
// Add the action
@@ -1479,7 +1484,7 @@ void MainWindow::updateModPageMenu()
}
);
// Remove disabled plugins:
// Remove disabled plugins
modPagePlugins.erase(
std::remove_if(std::begin(modPagePlugins), std::end(modPagePlugins), [&](auto* tool) {
return !m_PluginContainer.isEnabled(tool);
@@ -1490,23 +1495,35 @@ void MainWindow::updateModPageMenu()
registerModPage(modPagePlugin);
}
// Add the primary game (with a separator)
registerNexusPage(m_OrganizerCore.managedGame()->gameShortName());
ui->actionModPage->menu()->addSeparator();
QStringList registeredSources;
// Add the primary game
QString gameShortName = m_OrganizerCore.managedGame()->gameShortName();
if (registerNexusPage(gameShortName))
registeredSources << gameShortName;
// Add the primary sources
for (auto gameName : m_OrganizerCore.managedGame()->primarySources())
{
if (!registeredSources.contains(gameName) && registerNexusPage(gameName))
registeredSources << gameName;
}
// Add a separator if needed
if (registeredSources.length() > 0)
ui->actionModPage->menu()->addSeparator();
// Add the secondary games (sorted)
bool secondaryGameAdded = false;
QStringList secondaryGames = m_OrganizerCore.managedGame()->validShortNames();
secondaryGames.sort(Qt::CaseInsensitive);
for (auto gameName : secondaryGames)
{
if (registerNexusPage(gameName)) {
secondaryGameAdded = true;
}
if (!registeredSources.contains(gameName) && registerNexusPage(gameName))
registeredSources << gameName;
}
// No mod page plugin and the menu was visible:
bool keepOriginalAction = modPagePlugins.size() == 0 && !secondaryGameAdded;
// No mod page plugin and the menu was visible
bool keepOriginalAction = modPagePlugins.size() == 0 && registeredSources.length() <= 1;
if (keepOriginalAction) {
ui->toolBar->insertAction(ui->actionAdd_Profile, ui->actionNexus);
}
@@ -1646,7 +1663,7 @@ void MainWindow::on_profileBox_currentIndexChanged(int index)
}
}
bool MainWindow::refreshProfiles(bool selectProfile)
bool MainWindow::refreshProfiles(bool selectProfile, QString newProfile)
{
QComboBox* profileBox = findChild<QComboBox*>("profileBox");
@@ -1675,7 +1692,12 @@ bool MainWindow::refreshProfiles(bool selectProfile)
if (selectProfile) {
if (profileBox->count() > 1) {
profileBox->setCurrentText(currentProfileName);
if (newProfile.isEmpty()) {
profileBox->setCurrentText(currentProfileName);
}
else {
profileBox->setCurrentText(newProfile);
}
if (profileBox->currentIndex() == 0) {
profileBox->setCurrentIndex(1);
}
@@ -1760,7 +1782,7 @@ void MainWindow::updateBSAList(const QStringList &defaultArchives, const QString
auto hasAssociatedPlugin = [&](const QString &bsaName) -> bool {
for (const QString &pluginName : plugins) {
QFileInfo pluginInfo(pluginName);
if (bsaName.startsWith(QFileInfo(pluginName).baseName(), Qt::CaseInsensitive)
if (bsaName.startsWith(QFileInfo(pluginName).completeBaseName(), Qt::CaseInsensitive)
&& (m_OrganizerCore.pluginList()->state(pluginInfo.fileName()) == IPluginList::STATE_ACTIVE)) {
return true;
}
@@ -2145,7 +2167,7 @@ void MainWindow::wikiTriggered()
void MainWindow::discordTriggered()
{
shell::Open(QUrl("https://discord.gg/cYwdcxj"));
shell::Open(QUrl("https://discord.gg/6GKR9jZ"));
}
void MainWindow::issueTriggered()
@@ -2188,16 +2210,7 @@ void MainWindow::on_actionAdd_Profile_triggered()
profilesDialog.exec();
m_SavesTab->refreshSaveList(); // since the save list may now be outdated we have to refresh it completely
if (profilesDialog.selectedProfile())
{
// Change profile while blocking signals to prevent extra signals being sent
// Doesn't matter much as refreshProfiles() is being called after this
ui->profileBox->blockSignals(true);
ui->profileBox->setCurrentText(profilesDialog.selectedProfile().value());
ui->profileBox->blockSignals(false);
}
if (refreshProfiles() && !profilesDialog.failed()) {
if (refreshProfiles(true, profilesDialog.selectedProfile().value_or("")) && !profilesDialog.failed()) {
break;
}
}
@@ -2911,112 +2924,176 @@ void MainWindow::nxmUpdatesAvailable(QString gameName, int modID, QVariant userD
QList files = resultInfo["files"].toList();
QList fileUpdates = resultInfo["file_updates"].toList();
QString gameNameReal;
for (IPluginGame *game : m_PluginContainer.plugins<IPluginGame>()) {
if (game->gameNexusName() == gameName) {
gameNameReal = game->gameShortName();
break;
}
}
std::vector<ModInfo::Ptr> modsList = ModInfo::getByModID(gameNameReal, modID);
bool requiresInfo = false;
for (auto mod : modsList) {
bool foundUpdate = false;
bool oldFile = false;
QString validNewVersion;
int newModStatus = -1;
QString installedFile = mod->installationFile();
if (!installedFile.isEmpty()) {
QVariantMap foundFile;
for (auto file : files) {
QVariantMap foundFileData;
// update the file status
for (auto &file : files) {
QVariantMap fileData = file.toMap();
if (fileData["file_name"].toString().compare(installedFile, Qt::CaseInsensitive) == 0) {
foundFile = fileData;
mod->setNexusFileStatus(foundFile["category_id"].toInt());
if (mod->getNexusFileStatus() == 4 || mod->getNexusFileStatus() == 6)
oldFile = true;
}
}
for (auto update : fileUpdates) {
QVariantMap updateData = update.toMap();
// Locate the current install file as an update
if (installedFile == updateData["old_file_name"].toString()) {
int currentUpdate = updateData["new_file_id"].toInt();
bool finalUpdate = false;
// Crawl the updates to make sure we have the latest file version
while (!finalUpdate) {
finalUpdate = true;
for (auto updateScan : fileUpdates) {
QVariantMap updateScanData = updateScan.toMap();
if (currentUpdate == updateScanData["old_file_id"].toInt()) {
currentUpdate = updateScanData["new_file_id"].toInt();
finalUpdate = false;
// Apply the version data from the latest file
for (auto file : files) {
QVariantMap fileData = file.toMap();
if (fileData["file_id"].toInt() == currentUpdate) {
if (fileData["category_id"].toInt() != 6) {
mod->setNewestVersion(fileData["version"].toString());
foundUpdate = true;
}
}
}
break;
}
}
foundFileData = fileData;
newModStatus = foundFileData["category_id"].toInt();
if (newModStatus != NexusInterface::FileStatus::OLD_VERSION &&
newModStatus != NexusInterface::FileStatus::REMOVED &&
newModStatus != NexusInterface::FileStatus::ARCHIVED) {
// since the file is still active if there are no updates for it, use this as current version
validNewVersion = foundFileData["version"].toString();
}
break;
} else if (installedFile == updateData["new_file_name"].toString()) {
// This is a safety mechanism if this is the latest update file so we don't use the mod version
if (!foundUpdate && !oldFile) {
foundUpdate = true;
mod->setNewestVersion(foundFile["version"].toString());
}
}
if (foundFileData.isEmpty()) {
// The file was not listed, the file is likely archived and archived files are being hidden on the mod
newModStatus = NexusInterface::FileStatus::ARCHIVED_HIDDEN;
}
// look for updates of the file
int currentUpdateId = -1;
// find installed file ID from the updates list since old filenames are not guaranteed to be unique
for (auto& updateEntry : fileUpdates) {
const QVariantMap& updateData = updateEntry.toMap();
if (installedFile.compare(updateData["old_file_name"].toString(), Qt::CaseInsensitive) == 0) {
currentUpdateId = updateData["old_file_id"].toInt();
break;
}
}
bool foundActiveUpdate = false;
// there is at least one update
if (currentUpdateId > 0) {
bool lookForMoreUpdates = true;
// follow the update chain until there are no more updates
while (lookForMoreUpdates) {
lookForMoreUpdates = false;
for (auto& updateEntry : fileUpdates) {
const QVariantMap& updateData = updateEntry.toMap();
if (currentUpdateId == updateData["old_file_id"].toInt()) {
currentUpdateId = updateData["new_file_id"].toInt();
// check if the new file is still active
for (auto& file : files) {
const QVariantMap& fileData = file.toMap();
if (currentUpdateId == fileData["file_id"].toInt()) {
int updateStatus = fileData["category_id"].toInt();
if (updateStatus != NexusInterface::FileStatus::OLD_VERSION &&
updateStatus != NexusInterface::FileStatus::REMOVED &&
updateStatus != NexusInterface::FileStatus::ARCHIVED) {
// new version is active, so record it
validNewVersion = fileData["version"].toString();
foundActiveUpdate = true;
}
break;
}
}
lookForMoreUpdates = true;
break;
}
}
}
}
// if there were no active direct file updates for the installedFile
if (!foundActiveUpdate) {
// get the global mod version in case the file isn't an optional
if (newModStatus != NexusInterface::FileStatus::OPTIONAL_FILE &&
newModStatus != NexusInterface::FileStatus::MISCELLANEOUS) {
requiresInfo = true;
}
}
}
if (foundUpdate) {
// Just get the standard data updates for endorsements and descriptions
mod->setLastNexusUpdate(QDateTime::currentDateTimeUtc());
} else {
// Scrape mod data here so we can use the mod version if no file update was located
else {
// No installedFile means we don't know what to look at for a version so
// just get the global mod version
requiresInfo = true;
}
if (newModStatus > 0) {
mod->setNexusFileStatus(newModStatus);
}
if (!validNewVersion.isEmpty()) {
mod->setNewestVersion(validNewVersion);
mod->setLastNexusUpdate(QDateTime::currentDateTimeUtc());
}
}
// invalidate the filter to display mods with an update
ui->modList->invalidateFilter();
if (requiresInfo)
if (requiresInfo) {
NexusInterface::instance().requestModInfo(gameNameReal, modID, this, QVariant(), QString());
}
}
void MainWindow::nxmModInfoAvailable(QString gameName, int modID, QVariant userData, QVariant resultData, int requestID)
{
QVariantMap result = resultData.toMap();
QString gameNameReal;
bool foundUpdate = false;
for (IPluginGame *game : m_PluginContainer.plugins<IPluginGame>()) {
if (game->gameNexusName() == gameName) {
gameNameReal = game->gameShortName();
break;
}
}
std::vector<ModInfo::Ptr> modsList = ModInfo::getByModID(gameNameReal, modID);
for (auto mod : modsList) {
QDateTime now = QDateTime::currentDateTimeUtc();
QDateTime updateTarget = mod->getExpires();
if (now >= updateTarget) {
// if file is still listed as optional or miscellaneous don't update the version as often optional files are left
// with an older version than the main mod version.
if (mod->getNexusFileStatus() != 3 && mod->getNexusFileStatus() != 5) {
mod->setNewestVersion(result["version"].toString());
}
// update the LastNexusUpdate time in any case since we did perform the check.
mod->setLastNexusUpdate(QDateTime::currentDateTimeUtc());
// if file is still listed as optional or miscellaneous don't update the version as often optional files are left
// with an older version than the main mod version.
if (!result["version"].toString().isEmpty() &&
mod->getNexusFileStatus() != NexusInterface::FileStatus::OPTIONAL_FILE &&
mod->getNexusFileStatus() != NexusInterface::FileStatus::MISCELLANEOUS) {
mod->setNewestVersion(result["version"].toString());
foundUpdate = true;
}
// update the LastNexusUpdate time in any case since we did perform the check.
mod->setLastNexusUpdate(QDateTime::currentDateTimeUtc());
mod->setNexusDescription(result["description"].toString());
if ((mod->endorsedState() != EndorsedState::ENDORSED_NEVER) && (result.contains("endorsement"))) {
QVariantMap endorsement = result["endorsement"].toMap();
QString endorsementStatus = endorsement["endorse_status"].toString();
if (endorsementStatus.compare("Endorsed") == 00)
mod->setIsEndorsed(true);
else if (endorsementStatus.compare("Abstained") == 00)
@@ -3024,12 +3101,17 @@ void MainWindow::nxmModInfoAvailable(QString gameName, int modID, QVariant userD
else
mod->setIsEndorsed(false);
}
mod->setLastNexusQuery(QDateTime::currentDateTimeUtc());
mod->setNexusLastModified(QDateTime::fromSecsSinceEpoch(result["updated_timestamp"].toInt(), Qt::UTC));
mod->saveMeta();
m_OrganizerCore.modList()->notifyChange(ModInfo::getIndex(mod->name()));
}
if (foundUpdate) {
// invalidate the filter to display mods with an update
ui->modList->invalidateFilter();
}
}
void MainWindow::nxmEndorsementToggled(QString, int, QVariant, QVariant resultData, int)
@@ -3576,10 +3658,14 @@ void MainWindow::keyReleaseEvent(QKeyEvent *event)
{
// if the ui is locked, ignore the ALT key event
// alt-tabbing out of a game triggers this
if (!UILocker::instance().locked()) {
// if the menubar is hidden, pressing Alt will make it visible
auto& uilocker = UILocker::instance();
auto& settings = Settings::instance();
if (!uilocker.locked()) {
// if the menubar is hidden and showMenuBarOnAlt is true,
// pressing Alt will make it visible
if (event->key() == Qt::Key_Alt) {
if (!ui->menuBar->isVisible()) {
bool showMenubarOnAlt = settings.interface().showMenubarOnAlt();
if (showMenubarOnAlt && !ui->menuBar->isVisible()) {
ui->menuBar->show();
}
}
+1 -1
View File
@@ -198,7 +198,7 @@ private:
QMenu* createPopupMenu() override;
void activateSelectedProfile();
bool refreshProfiles(bool selectProfile = true);
bool refreshProfiles(bool selectProfile = true, QString newProfile = QString());
void refreshExecutablesList();
bool modifyExecutablesDialog(int selection);
+2
View File
@@ -160,6 +160,8 @@ MOApplication::MOApplication(int& argc, char** argv)
{
TimeThis tt("MOApplication()");
qputenv("QML_DISABLE_DISK_CACHE", "true");
connect(&m_styleWatcher, &QFileSystemWatcher::fileChanged, [&](auto&& file){
log::debug("style file '{}' changed, reloading", file);
updateStyle(file);
+4 -2
View File
@@ -441,10 +441,12 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const
"(i.e. due to a bug) or the author uses a non-standard versioning scheme and that newest version is actually newer. "
"Either way you may want to \"upgrade\".");
}
if (modInfo->getNexusFileStatus() == 4) {
if (modInfo->getNexusFileStatus() == NexusInterface::FileStatus::OLD_VERSION) {
text += "<br>" + tr("This file has been marked as \"Old\". There is most likely an updated version of this file available.");
}
else if (modInfo->getNexusFileStatus() == 6) {
else if (modInfo->getNexusFileStatus() == NexusInterface::FileStatus::REMOVED ||
modInfo->getNexusFileStatus() == NexusInterface::FileStatus::ARCHIVED ||
modInfo->getNexusFileStatus() == NexusInterface::FileStatus::ARCHIVED_HIDDEN) {
text += "<br>" + tr("This file has been marked as \"Deleted\"! You may want to check for an update or remove the nexus ID from this mod!");
}
if (modInfo->nexusId() > 0) {
+7 -1
View File
@@ -371,7 +371,13 @@ QString NexusInterface::getGameURL(QString gameName) const
{
IPluginGame *game = getGame(gameName);
if (game != nullptr) {
return "https://www.nexusmods.com/" + game->gameNexusName().toLower();
QString gameNexusName = game->gameNexusName().toLower();
if (gameNexusName.isEmpty()) {
return "";
}
else {
return "https://www.nexusmods.com/" + gameNexusName;
}
} else {
log::error("getGameURL can't find plugin for {}", gameName);
return "";

Some files were not shown because too many files have changed in this diff Show More