mirror of
https://github.com/ModOrganizer2/modorganizer.git
synced 2026-07-27 13:58:24 -07:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ec01532ecb | ||
|
|
3b5aeebb72 | ||
|
|
1929e5e57b | ||
|
|
cfe03a8561 | ||
|
|
164c6af962 | ||
|
|
c6874c1771 | ||
|
|
8fb36b0b28 | ||
|
|
8159a476cf | ||
|
|
0b9c96e845 | ||
|
|
6d73f9a18a | ||
|
|
5b855f2e19 | ||
|
|
21487ad8ee | ||
|
|
e78f46cc9e | ||
|
|
25cdbe3918 | ||
|
|
da4a013590 | ||
|
|
0b14cc4753 | ||
|
|
cee8d5ed72 | ||
|
|
0783eb373b | ||
|
|
feb411fb2a | ||
|
|
3b1ee9d43b | ||
|
|
3f9020ea4b | ||
|
|
b4bbb09cb5 | ||
|
|
9fdf3b759d | ||
|
|
1df1ea5e11 | ||
|
|
dfcbf45e1d | ||
|
|
1e5c019767 | ||
|
|
d41fbfd7e1 | ||
|
|
2626649cc6 | ||
|
|
8d08d7f6ba | ||
|
|
d30a095b3e | ||
|
|
b5efa60db9 | ||
|
|
d82f196aa5 | ||
|
|
9f8827e3a5 | ||
|
|
d58f773c76 | ||
|
|
01ad45c7aa | ||
|
|
13bdf538a8 | ||
|
|
fc40619fd9 | ||
|
|
565f2fddf4 | ||
|
|
1d80c547d7 | ||
|
|
1755f526c9 | ||
|
|
a1e953f409 | ||
|
|
b1db3019f4 | ||
|
|
cf137606fd | ||
|
|
9644fef8bd | ||
|
|
1077a5bad0 | ||
|
|
57acd4949b | ||
|
|
6ab88d5de1 | ||
|
|
4a687cbfcc | ||
|
|
6df197579f | ||
|
|
1d20b993d7 | ||
|
|
468a8b9383 | ||
|
|
081fa3ec93 | ||
|
|
b261f3a1e5 | ||
|
|
acf1730093 | ||
|
|
b5800435aa | ||
|
|
0e9da8d4f5 | ||
|
|
ee745353d9 | ||
|
|
ae311dd4dc | ||
|
|
1b2f85a371 | ||
|
|
a279745aa2 | ||
|
|
ac8b67dd63 | ||
|
|
9256739e3a | ||
|
|
d413f673e8 | ||
|
|
4bdb4aff50 | ||
|
|
58e5058a2b | ||
|
|
197f619335 | ||
|
|
4e3d7d0180 | ||
|
|
b5cb1a2311 | ||
|
|
b5d04eff4f | ||
|
|
b2353278aa | ||
|
|
0bdfd4813f | ||
|
|
e7f15ff4fd | ||
|
|
e8149a4b42 | ||
|
|
d531245dea | ||
|
|
ac38371648 | ||
|
|
c44b1cb3a3 | ||
|
|
ef5b9228da |
@@ -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:
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ add_filter(NAME src/core GROUPS
|
||||
apiuseraccount
|
||||
processrunner
|
||||
qdirfiletree
|
||||
virtualfiletree
|
||||
uilocker
|
||||
)
|
||||
|
||||
|
||||
+79
-9
@@ -127,7 +127,7 @@
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string notr="true"><html><head/><body><p>Copyright © 2011-2016 Sebastian Herbord<br/>Copyright © 2016-2019 Mod Organizer 2 Contributors</p></body></html></string>
|
||||
<string notr="true"><html><head/><body><p>Copyright © 2011-2016 Sebastian Herbord<br/>Copyright © 2016-2021 Mod Organizer 2 Contributors</p></body></html></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 "Nyks45" 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>
|
||||
|
||||
+1
-1
@@ -232,7 +232,7 @@ void CategoryFactory::loadDefaultCategories()
|
||||
addCategory(44, "Equipment", { 1, 44 }, 43);
|
||||
addCategory(45, "Home/Settlement", { 1, 45 }, 43);
|
||||
addCategory(10, "Body, Face, & Hair", { 2, 17, 26 }, 0);
|
||||
addCategory(39, "Tattoos", { 1, 57 }, 10);
|
||||
addCategory(56, "Tattoos", { 1, 57 }, 10);
|
||||
addCategory(40, "Character Presets", { 1, 58 }, 0);
|
||||
addCategory(11, "Items", { 2, 27, 85 }, 0);
|
||||
addCategory(32, "Mercantile", { 2, 23, 69 }, 0);
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -359,6 +359,9 @@ void initLogging()
|
||||
log::getDefault().setCallback(
|
||||
[](log::Entry e){ LogModel::instance().add(e); });
|
||||
|
||||
log::getDefault().addToBlacklist(std::string("\\") + getenv("USERNAME"), "\\USERNAME");
|
||||
log::getDefault().addToBlacklist(std::string("/") + getenv("USERNAME"), "/USERNAME");
|
||||
|
||||
qInstallMessageHandler(qtLogCallback);
|
||||
}
|
||||
|
||||
|
||||
+175
-89
@@ -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 installedFile = mod->installationFile();
|
||||
QString validNewVersion;
|
||||
int newModStatus = -1;
|
||||
QString installedFile = QFileInfo(mod->installationFile()).fileName();
|
||||
|
||||
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
@@ -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);
|
||||
|
||||
@@ -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
@@ -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) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user