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 | ||
|
|
2a5dd6c7cf | ||
|
|
ef5b9228da | ||
|
|
ce2450885c | ||
|
|
d78cb58593 | ||
|
|
2c54d128b4 | ||
|
|
f71288db16 | ||
|
|
8bac17c2c5 | ||
|
|
4509911a8e | ||
|
|
f098491306 | ||
|
|
bcaceb9637 | ||
|
|
b7e8e69f47 | ||
|
|
a8bcbf6555 | ||
|
|
aafd523736 | ||
|
|
4a9da205cd | ||
|
|
68e7820154 | ||
|
|
3c6190d713 | ||
|
|
1197b562f6 | ||
|
|
7df5329ea0 | ||
|
|
dc3dcd9500 | ||
|
|
cc8fb9bd23 | ||
|
|
9638a797d1 | ||
|
|
8308dfcbff | ||
|
|
dfbaed0ade | ||
|
|
e475893ff4 | ||
|
|
40a4bf1e3a | ||
|
|
3ee2598533 | ||
|
|
234ee923fe | ||
|
|
88ee41ae85 | ||
|
|
8a29bc5110 | ||
|
|
77dbd7ff31 | ||
|
|
7fab050adb | ||
|
|
b547045203 |
@@ -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
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
|
||||
+84
-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,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
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
+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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user