Compare commits

...
79 Commits
Author SHA1 Message Date
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
Chris Bessent ce2450885c Bump version to 2.4.2rc1.1 2021-05-01 19:38:14 -07:00
Chris Bessent d78cb58593 Update translation file 2021-05-01 17:53:37 -07:00
Chris Bessent 2c54d128b4 Add some more Microsoft Store errors 2021-05-01 17:10:27 -07:00
Chris Bessent f71288db16 Bump to 2.4.2 RC 1 2021-05-01 15:23:16 -07:00
Chris Bessent 8bac17c2c5 Merge pull request #1508 from LostDragonist/gamepass
Add log for Microsoft Store / Gamepass installs
2021-05-01 15:21:45 -07:00
Chris Bessent 4509911a8e Add log for Microsoft Store / Gamepass installs 2021-05-01 15:18:15 -07:00
Chris Bessent f098491306 Merge pull request #1503 from LostDragonist/filename
Sanitize download file names
2021-05-01 14:38:17 -07:00
Chris Bessent bcaceb9637 Merge pull request #1504 from LostDragonist/skipcwd
Ignore "." and ".." in archives
2021-05-01 14:38:10 -07:00
Chris Bessent b7e8e69f47 Merge pull request #1507 from LostDragonist/tutorials
Fix up tutorials
2021-05-01 14:37:59 -07:00
Chris Bessent a8bcbf6555 Fix up tutorials 2021-04-30 08:41:29 -07:00
Chris Bessent aafd523736 Merge pull request #1505 from LostDragonist/foresto
Add foresto as a contributor
2021-04-29 02:02:19 -07:00
Chris Bessent 4a9da205cd Merge pull request #1485 from foresto/patch-1
Reduce log level of harmless failures
2021-04-29 02:02:05 -07:00
Chris Bessent 68e7820154 Add foresto as a contributor 2021-04-29 02:01:38 -07:00
Chris Bessent 3c6190d713 Merge pull request #1470 from isanae/filtered-nav-crash
Crash for next/previous in mod info dialog
2021-04-29 01:59:05 -07:00
Chris Bessent 1197b562f6 Merge pull request #1496 from LostDragonist/nexus_links
Change the "Visit Nexus" button to support alternate sources
2021-04-29 01:57:13 -07:00
Chris Bessent 7df5329ea0 Merge pull request #1497 from LostDragonist/profile_fix
Fix selecting the profile in the dialog
2021-04-29 01:56:52 -07:00
Chris Bessent dc3dcd9500 Fix fixDirectoryName includes 2021-04-29 01:50:23 -07:00
Chris Bessent cc8fb9bd23 Ignore "." and ".." in archives 2021-04-29 01:29:38 -07:00
Chris Bessent 9638a797d1 Sanitize download file names
Also switch instance name sanitizer to MOBase version
2021-04-29 00:50:11 -07:00
Chris Bessent 8308dfcbff Fix selecting the profile in the dialog
Previously, when clicking the "select" button in the profiles
dialog (when the dialog was launched in the menu or toolbar),
everything would be refreshed without checking the selected
profile in the dialog.

Now, the selected profile is set with some signal blocking to
help prevent redundant refreshes from occurring.
2021-04-25 05:09:58 -07:00
Chris Bessent dfbaed0ade Add a separator after the primary game and sort the secondary games 2021-04-25 04:45:35 -07:00
Chris Bessent e475893ff4 Change the "Visit Nexus" button to support alternate sources
If a game plugin supports more than one Nexus site for downloads,
the "Visit Nexus" button will be turned into a drop-down that
lets you select each site.
2021-04-25 04:13:12 -07:00
Al 40a4bf1e3a Update issue templates 2021-04-08 16:51:11 +02:00
Forest 3ee2598533 Reduce log level of harmless failures
These two messages (along with their glaring red X icons) are cluttering up the log on linux/wine systems, misleading people who are trying to diagnose real problems. Let's reduce their log priority to make it clear that they're harmless.

As mentioned here:
https://github.com/ModOrganizer2/modorganizer/issues/372#issuecomment-575231673
2021-04-05 23:10:54 -07:00
isanae 234ee923fe model() can return null for invalid indexes
happens when looking at the mod info for a mod filtered out of the mod list
2021-03-20 14:51:44 -04:00
Mikaël Capelle 88ee41ae85 Bump to 2.4.1. 2021-03-20 17:03:36 +01:00
59 changed files with 1404 additions and 856 deletions
@@ -2,7 +2,7 @@
name: Game support Request
about: Request support for a new game
title: Add support for game [GAME NAME]
labels: 'additional games support, Feature Request, area: mo2 game plugins'
labels: 'Feature Request, additional games support, area: mo2 game plugins'
assignees: ''
---
@@ -18,7 +18,7 @@ assignees: ''
- **Nexus ID [optional]:** ID of the game on Nexus (you can usually find this in Nexus URL).
- **Executable:** Name of the main executable for the game (relative to the game folder).
- **Launcher [optional]:** Name of the game launcher (relative to the game folder).
- **Data path:** Path to the data folder of the game (relative to the game folder).
- **Data path:** Path to the data folder of the game (relative to the game folder). Please note that the Virtual Files System often does not work for top level dlls or exe files.
- **Documents path:** Path to folder containing INI files, etc., for the game (usually under "My Games", or the game folder itself).
- **Saves directory [optional]:** Path to the folder containing save games (this default to the path above).
- **Save extension [optional]:** Extension of the saves
+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
)
+84 -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,26 @@
<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>
</property>
</item>
<item>
<property name="text">
<string notr="true">GamerPoet</string>
@@ -491,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>
@@ -506,7 +586,7 @@
</item>
<item>
<property name="text">
<string>Patchier</string>
<string notr="true">Patchier</string>
</property>
</item>
<item>
@@ -514,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>
+13 -6
View File
@@ -158,14 +158,14 @@ protected:
currentName = std::get<0>(p)[0];
}
// If the name is different, we need to create a directory from what we have
// If the name is different, we need to create a directory from what we have
// accumulated:
if (currentName != std::get<0>(p)[0]) {
// We may or may not have an index here, it depends on the type of archive (some archives list
// intermediate non-empty folders, some don't):
entries.push_back(std::make_shared<ArchiveFileTreeImpl>(parent, currentName, currentIndex, std::move(currentFiles)));
currentFiles.clear(); // Back to a valid state.
// Reset the index:
@@ -200,7 +200,7 @@ protected:
if (currentName != "") {
entries.push_back(std::make_shared<ArchiveFileTreeImpl>(parent, currentName, currentIndex, std::move(currentFiles)));
}
// Let the parent class sort the entries:
return false;
}
@@ -214,7 +214,7 @@ private:
mutable std::vector<File> m_Files;
};
std::shared_ptr<ArchiveFileTree> ArchiveFileTree::makeTree(Archive const& archive)
std::shared_ptr<ArchiveFileTree> ArchiveFileTree::makeTree(Archive const& archive)
{
auto const& data = archive.getFileList();
@@ -222,9 +222,16 @@ std::shared_ptr<ArchiveFileTree> ArchiveFileTree::makeTree(Archive const& archiv
files.reserve(data.size());
for (size_t i = 0; i < data.size(); ++i) {
// Ignore "." and ".." as they're useless and muck things up
if (data[i]->getArchiveFilePath().compare(L".") == 0 ||
data[i]->getArchiveFilePath().compare(L"..") == 0)
{
continue;
}
files.push_back(std::make_tuple(
QString::fromStdWString(data[i]->getArchiveFilePath()).replace("\\", "/").split("/", Qt::SkipEmptyParts),
data[i]->isDirectory(),
QString::fromStdWString(data[i]->getArchiveFilePath()).replace("\\", "/").split("/", Qt::SkipEmptyParts),
data[i]->isDirectory(),
(int) i));
}
+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>
+50 -5
View File
@@ -8,6 +8,7 @@
#include <iplugingame.h>
#include <report.h>
#include <utility.h>
#include "filesystemutilities.h"
namespace cid
{
@@ -292,7 +293,7 @@ void GamePage::select(IPluginGame* game, const QString& dir)
Game* checked = findGame(game);
if (checked) {
if (!checked->installed) {
if (!checked->installed || (detectMicrosoftStore(checked->dir) && !confirmMicrosoftStore(checked->dir, checked->game))) {
if (dir.isEmpty()) {
// the selected game has no installation directory and none was given,
// ask the user
@@ -304,6 +305,9 @@ void GamePage::select(IPluginGame* game, const QString& dir)
if (path.isEmpty()) {
// cancelled
checked = nullptr;
} else if (detectMicrosoftStore(path) && !confirmMicrosoftStore(path, game)) {
// cancelled
checked = nullptr;
} else {
// check whether a plugin supports the given directory; this can
// return the same plugin, a different one, or null
@@ -353,6 +357,13 @@ void GamePage::selectCustom()
return;
}
// Microsoft store games are not supported
if (detectMicrosoftStore(path) && !confirmMicrosoftStore(path, nullptr)) {
// reselect the previous button
selectButton(m_selection);
return;
}
// try to find a plugin that likes this directory
for (auto& g : m_games) {
if (g->game->looksValid(path)) {
@@ -463,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) {
@@ -593,6 +604,11 @@ GamePage::Game* GamePage::checkInstallation(const QString& path, Game* g)
return g;
}
if (detectMicrosoftStore(path) && confirmMicrosoftStore(path, g->game)) {
// okay
return g;
}
// the selected game can't use that folder, find another one
IPluginGame* otherGame = nullptr;
@@ -639,6 +655,35 @@ GamePage::Game* GamePage::checkInstallation(const QString& path, Game* g)
return g;
}
bool GamePage::detectMicrosoftStore(const QString& path)
{
return path.contains("/ModifiableWindowsApps/") ||
path.contains("/WindowsApps/");
}
bool GamePage::confirmMicrosoftStore(const QString& path, IPluginGame* game)
{
const auto r = TaskDialog(&m_dlg)
.title(QObject::tr("Microsoft Store game"))
.main(QObject::tr("Microsoft Store game"))
.content(QObject::tr(
"The folder %1 seems to be a Microsoft Store game install. Games"
" installed through the Microsoft Store are not supported by Mod Organizer"
" and will not work properly.")
.arg(path))
.button({
game ? QObject::tr("Use this folder for %1").arg(game->gameName())
: QObject::tr("Use this folder"),
QObject::tr("I know what I'm doing"),
QMessageBox::Ignore})
.button({
QObject::tr("Cancel"),
QMessageBox::Cancel})
.exec();
return (r == QMessageBox::Ignore);
}
bool GamePage::confirmUnknown(const QString& path, IPluginGame* game)
{
const auto r = TaskDialog(&m_dlg)
@@ -856,7 +901,7 @@ QString NamePage::selectedInstanceName() const
}
const auto text = ui->instanceName->text().trimmed();
return InstanceManager::singleton().sanitizeInstanceName(text);
return MOBase::sanitizeFileName(text);
}
void NamePage::onChanged()
@@ -883,7 +928,7 @@ bool NamePage::checkName(QString parentDir, QString name)
if (name.isEmpty()) {
empty = true;
} else {
if (InstanceManager::singleton().validInstanceName(name)) {
if (MOBase::validFileName(name)) {
exists = QDir(parentDir).exists(name);
} else {
invalid = true;
@@ -1108,7 +1153,7 @@ bool PathsPage::checkPath(
} else {
const QDir d(path);
if (m.validInstanceName(d.dirName())) {
if (MOBase::validFileName(d.dirName())) {
if (m_dlg.rawCreationInfo().type == CreateInstanceDialog::Portable) {
// the default data path for a portable instance is the application
// directory, so it's not an error if it exists
+9
View File
@@ -354,6 +354,15 @@ private:
MOBase::IPluginGame* confirmOtherGame(
const QString& path,
MOBase::IPluginGame* selectedGame, MOBase::IPluginGame* guessedGame);
// detects if the given path likely contains a Microsoft Store game
//
bool detectMicrosoftStore(const QString& path);
// tells the user that the path probably contains a Microsoft Store game that
// is not supported, returns true if the user decides to accept anyway.
//
bool confirmMicrosoftStore(const QString& path, MOBase::IPluginGame* game);
};
+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) {
+8 -4
View File
@@ -33,6 +33,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include "shared/util.h"
#include <utility.h>
#include <report.h>
#include "filesystemutilities.h"
#include <QTimer>
#include <QFileInfo>
@@ -1222,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");
}
}
@@ -1456,7 +1458,7 @@ void DownloadManager::markUninstalled(QString fileName)
QString DownloadManager::getDownloadFileName(const QString &baseName, bool rename) const
{
QString fullPath = m_OutputDirectory + "/" + baseName;
QString fullPath = m_OutputDirectory + "/" + MOBase::sanitizeFileName(baseName);
if (QFile::exists(fullPath) && rename) {
int i = 1;
while (QFile::exists(QString("%1/%2_%3").arg(m_OutputDirectory).arg(i).arg(baseName))) {
@@ -1476,7 +1478,7 @@ QString DownloadManager::getFileNameFromNetworkReply(QNetworkReply *reply)
std::cmatch result;
if (std::regex_search(reply->rawHeader("Content-Disposition").constData(), result, exp)) {
return QString::fromUtf8(result.str(1).c_str());
return MOBase::sanitizeFileName(QString::fromUtf8(result.str(1).c_str()));
}
}
@@ -1722,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) {
@@ -1957,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 {
+2 -2
View File
@@ -130,7 +130,7 @@ Module::FileInfo Module::getFileInfo() const
return {};
}
log::error(
log::debug(
"GetFileVersionInfoSizeW() failed on '{}', {}",
m_path, formatSystemMessage(e));
@@ -268,7 +268,7 @@ QDateTime Module::getTimestamp(const VS_FIXEDFILEINFO& fi) const
if (h.get() == INVALID_HANDLE_VALUE) {
const auto e = GetLastError();
log::error(
log::debug(
"can't open file '{}' for timestamp, {}",
m_path, formatSystemMessage(e));
+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 -1
View File
@@ -21,7 +21,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include "installationmanager.h"
#include "utility.h"
#include "filesystemutilities.h"
#include "report.h"
#include "categories.h"
#include "questionboxmemory.h"
+3 -33
View File
@@ -32,6 +32,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <iplugingame.h>
#include <utility.h>
#include <log.h>
#include "filesystemutilities.h"
#include <QCoreApplication>
#include <QDir>
@@ -600,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);
@@ -721,7 +720,7 @@ const MOBase::IPluginGame* InstanceManager::gamePluginForDirectory(
QString InstanceManager::makeUniqueName(const QString& instanceName) const
{
const QString sanitized = sanitizeInstanceName(instanceName);
const QString sanitized = MOBase::sanitizeFileName(instanceName);
// trying "name (N)"
QString name = sanitized;
@@ -742,35 +741,6 @@ bool InstanceManager::instanceExists(const QString& instanceName) const
return root.exists(instanceName);
}
QString InstanceManager::sanitizeInstanceName(const QString &name) const
{
QString new_name = name;
// Restrict the allowed characters
new_name = new_name.remove(QRegExp("[^A-Za-z0-9 _=+;!@#$%^'\\-\\.\\[\\]\\{\\}\\(\\)]"));
// Don't end in spaces and periods
new_name = new_name.remove(QRegExp("\\.*$"));
new_name = new_name.remove(QRegExp(" *$"));
// Recurse until stuff stops changing
if (new_name != name) {
return sanitizeInstanceName(new_name);
}
return new_name;
}
bool InstanceManager::validInstanceName(const QString& instanceName) const
{
if (instanceName.isEmpty()) {
return false;
}
return (instanceName == sanitizeInstanceName(instanceName));
}
std::unique_ptr<Instance> selectInstance()
{
auto& m = InstanceManager::singleton();
-10
View File
@@ -305,10 +305,6 @@ public:
//
std::vector<QString> globalInstancePaths() const;
// returns `name` modified so that it is a valid instance name
//
QString sanitizeInstanceName(const QString &name) const;
// sanitizes the given instance name and either
// 1) returns it if there is no instance with this name
// 2) tries to add " (N)" at the end until it works
@@ -321,12 +317,6 @@ public:
//
bool instanceExists(const QString& instanceName) const;
// returns whether the given instance name would be a valid name; this does
// not check whether the instance already exists, it's basiscally just a check
// against what sanitizeInstanceName() returns
//
bool validInstanceName(const QString& instanceName) const;
// returns the absolute path of a global instance with the given name; this
// does not check if the name is valid or if exists
//

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