Compare commits

..
Author SHA1 Message Date
Jeremy Rimpo ad18983546 Set 'fusion' as base style
- Fusion already implements light/dark mode switching, the original 'windowsvista' style does not
- Applying the stylesheet on top of Fusion colors the titlebar based on the OS setting
2023-11-29 10:00:02 -06:00
Jeremy Rimpo aa381255a9 Use Windows settings to determine titlebar color 2023-11-29 00:09:00 -06:00
77 changed files with 1395 additions and 2425 deletions
+3 -3
View File
@@ -15,6 +15,6 @@ jobs:
with:
qt-modules: qtpositioning qtwebchannel qtwebengine qtwebsockets
mo2-third-parties:
7z zlib gtest libbsarch libloot openssl bzip2 python lz4 spdlog
boost boost-di sip pyqt pybind11 ss licenses explorerpp DirectXTex
mo2-dependencies: usvfs cmake_common uibase githubpp bsatk esptk archive lootcli game_gamebryo
7z zlib fmt gtest libbsarch libloot openssl libffi bzip2 python lz4 spdlog
boost boost-di sip pyqt pybind11 ss licenses explorerpp usvfs
mo2-dependencies: cmake_common uibase githubpp bsatk esptk archive lootcli game_gamebryo
+3 -3
View File
@@ -10,8 +10,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Check format
uses: ModOrganizer2/check-formatting-action@master
- name: Run clang-format
uses: jidicula/clang-format-action@v4.11.0
with:
clang-format-version: "15"
check-path: "."
exclude-regex: "third-party"
+2 -10
View File
@@ -7,18 +7,12 @@ mo2_configure_executable(organizer
EXTRA_TRANSLATIONS ${MO2_SUPER_PATH}/game_gamebryo/src ${MO2_UIBASE_PATH}/src
PRIVATE_DEPENDS
uibase githubpp bsatk esptk archive usvfs lootcli boost::program_options
DirectXTex libbsarch Qt::WebEngineWidgets Qt::WebSockets)
Qt::WebEngineWidgets Qt::WebSockets)
target_link_libraries(organizer PUBLIC Shlwapi)
target_include_directories(organizer PUBLIC ${DDS_ROOT})
mo2_install_target(organizer)
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/dlls.manifest.qt6"
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/dlls.manifest.qt5"
DESTINATION bin/dlls
CONFIGURATIONS Release RelWithDebInfo
RENAME dlls.manifest)
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/dlls.manifest.debug.qt6"
DESTINATION bin/dlls
CONFIGURATIONS Debug
RENAME dlls.manifest)
install(DIRECTORY
@@ -61,7 +55,6 @@ mo2_add_filter(NAME src/core GROUPS
nexusinterface
nxmaccessmanager
organizercore
game_features
plugincontainer
apiuseraccount
processrunner
@@ -203,7 +196,6 @@ mo2_add_filter(NAME src/profiles GROUPS
mo2_add_filter(NAME src/proxies GROUPS
downloadmanagerproxy
gamefeaturesproxy
modlistproxy
organizerproxy
pluginlistproxy
-2
View File
@@ -28,8 +28,6 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <QtGlobal>
using namespace MOBase;
ActivateModsDialog::ActivateModsDialog(SaveGameInfo::MissingAssets const& missingAssets,
QWidget* parent)
: TutorableDialog("ActivateMods", parent), ui(new Ui::ActivateModsDialog)
+4 -3
View File
@@ -24,8 +24,9 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include "tutorabledialog.h"
#include <QObject>
#include <QString>
#include <QWidget>
class QString;
class QWidget;
#include <set>
@@ -48,7 +49,7 @@ public:
* @param missingPlugins a map containing missing plugins that need to be activated
* @param parent ... Defaults to 0.
**/
explicit ActivateModsDialog(MOBase::SaveGameInfo::MissingAssets const& missingAssets,
explicit ActivateModsDialog(SaveGameInfo::MissingAssets const& missingAssets,
QWidget* parent = 0);
~ActivateModsDialog();
+15 -11
View File
@@ -74,7 +74,7 @@ void CategoryFactory::loadCategories()
bool ok = false;
int temp = iter->toInt(&ok);
if (!ok) {
log::error(tr("invalid category id {0}"), iter->constData());
log::error(tr("invalid category id {}").toStdString(), iter->constData());
}
nexusCats.push_back(NexusCategory("Unknown", temp));
}
@@ -84,7 +84,8 @@ void CategoryFactory::loadCategories()
int id = cells[0].toInt(&cell0Ok);
int parentID = cells[3].trimmed().toInt(&cell3Ok);
if (!cell0Ok || !cell3Ok) {
log::error(tr("invalid category line {0}: {1}"), lineNum, line.constData());
log::error(tr("invalid category line {}: {}").toStdString(), lineNum,
line.constData());
}
addCategory(id, QString::fromUtf8(cells[1].constData()), nexusCats, parentID);
} else if (cells.count() == 3) {
@@ -93,13 +94,14 @@ void CategoryFactory::loadCategories()
int id = cells[0].toInt(&cell0Ok);
int parentID = cells[2].trimmed().toInt(&cell3Ok);
if (!cell0Ok || !cell3Ok) {
log::error(tr("invalid category line {0}: {1}"), lineNum, line.constData());
log::error(tr("invalid category line {}: {}").toStdString(), lineNum,
line.constData());
}
addCategory(id, QString::fromUtf8(cells[1].constData()),
std::vector<NexusCategory>(), parentID);
} else {
log::error(tr("invalid category line {0}: {1} ({2} cells)"), lineNum,
log::error(tr("invalid category line {}: {} ({} cells)").toStdString(), lineNum,
line.constData(), cells.count());
}
}
@@ -120,17 +122,19 @@ void CategoryFactory::loadCategories()
bool ok = false;
int nexID = nexCells[2].toInt(&ok);
if (!ok) {
log::error(tr("invalid nexus ID {}"), nexCells[2].constData());
log::error(tr("invalid nexus ID {}").toStdString(),
nexCells[2].constData());
}
int catID = nexCells[0].toInt(&ok);
if (!ok) {
log::error(tr("invalid category id {}"), nexCells[0].constData());
log::error(tr("invalid category id {}").toStdString(),
nexCells[0].constData());
}
m_NexusMap.insert_or_assign(nexID, NexusCategory(nexName, nexID));
m_NexusMap.at(nexID).setCategoryID(catID);
} else {
log::error(tr("invalid nexus category line {0}: {1} ({2} cells)"), lineNum,
nexLine.constData(), nexCells.count());
log::error(tr("invalid nexus category line {}: {} ({} cells)").toStdString(),
lineNum, nexLine.constData(), nexCells.count());
}
}
}
@@ -390,7 +394,7 @@ bool CategoryFactory::isDescendantOfImpl(int id, int parentID,
return isDescendantOfImpl(m_Categories[index].parentID(), parentID, seen);
}
} else {
log::warn(tr("{} is no valid category id"), id);
log::warn(tr("{} is no valid category id").toStdString(), id);
return false;
}
}
@@ -509,11 +513,11 @@ unsigned int CategoryFactory::resolveNexusID(int nexusID) const
auto result = m_NexusMap.find(nexusID);
if (result != m_NexusMap.end()) {
if (m_IDMap.count(result->second.categoryID())) {
log::debug(tr("nexus category id {0} maps to internal {1}"), nexusID,
log::debug(tr("nexus category id {} maps to internal {}").toStdString(), nexusID,
m_IDMap.at(result->second.categoryID()));
return m_IDMap.at(result->second.categoryID());
}
}
log::debug(tr("nexus category id {} not mapped"), nexusID);
log::debug(tr("nexus category id {} not mapped").toStdString(), nexusID);
return 0U;
}
+2 -3
View File
@@ -148,9 +148,8 @@ void CategoriesDialog::commitChanges()
for (int i = 0; i < ui->categoriesTable->rowCount(); ++i) {
int index = ui->categoriesTable->verticalHeader()->logicalIndex(i);
QVariantList nexusData;
if (ui->categoriesTable->item(index, 3) != nullptr)
nexusData = ui->categoriesTable->item(index, 3)->data(Qt::UserRole).toList();
QVariantList nexusData =
ui->categoriesTable->item(index, 3)->data(Qt::UserRole).toList();
std::vector<CategoryFactory::NexusCategory> nexusCats;
for (auto nexusCat : nexusData) {
nexusCats.push_back(CategoryFactory::NexusCategory(
+14 -18
View File
@@ -296,8 +296,7 @@ void GamePage::select(IPluginGame* game, const QString& dir)
// ask the user
const auto path = QFileDialog::getExistingDirectory(
&m_dlg,
QObject::tr("Find game installation for %1").arg(game->displayGameName()));
&m_dlg, QObject::tr("Find game installation for %1").arg(game->gameName()));
if (path.isEmpty()) {
// cancelled
@@ -389,7 +388,7 @@ void GamePage::warnUnrecognized(const QString& path)
// put the list of supported games in the details textbox
QString supportedGames;
for (auto* game : sortedGamePlugins()) {
supportedGames += game->displayGameName() + "\n";
supportedGames += game->gameName() + "\n";
}
QMessageBox dlg(&m_dlg);
@@ -418,7 +417,7 @@ std::vector<IPluginGame*> GamePage::sortedGamePlugins() const
// natsort
std::sort(v.begin(), v.end(), [](auto* a, auto* b) {
return (naturalCompare(a->displayGameName(), b->displayGameName()) < 0);
return (naturalCompare(a->gameName(), b->gameName()) < 0);
});
return v;
@@ -470,7 +469,7 @@ void GamePage::updateButton(Game* g)
return;
}
g->button->setText(g->game->displayGameName().replace("&", "&&"));
g->button->setText(g->game->gameName().replace("&", "&&"));
g->button->setIcon(g->game->gameIcon());
if (g->installed) {
@@ -571,8 +570,7 @@ void GamePage::fillList()
continue;
}
if (!m_filter.matches(g->game->gameName()) &&
!m_filter.matches(g->game->displayGameName())) {
if (!m_filter.matches(g->game->gameName())) {
// filtered out
continue;
}
@@ -669,10 +667,9 @@ bool GamePage::confirmMicrosoftStore(const QString& path, IPluginGame* game)
"Organizer"
" and will not work properly.")
.arg(path))
.button(
{game ? QObject::tr("Use this folder for %1").arg(game->displayGameName())
: QObject::tr("Use this folder"),
QObject::tr("I know what I'm doing"), QMessageBox::Ignore})
.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();
@@ -691,8 +688,8 @@ bool GamePage::confirmUnknown(const QString& path, IPluginGame* game)
"bold;\">%2</span> or "
"for any other game Mod Organizer can manage.")
.arg(path)
.arg(game->displayGameName()))
.button({QObject::tr("Use this folder for %1").arg(game->displayGameName()),
.arg(game->gameName()))
.button({QObject::tr("Use this folder for %1").arg(game->gameName()),
QObject::tr("I know what I'm doing"), QMessageBox::Ignore})
.button({QObject::tr("Cancel"), QMessageBox::Cancel})
.exec();
@@ -714,12 +711,11 @@ IPluginGame* GamePage::confirmOtherGame(const QString& path, IPluginGame* select
"not "
"<span style=\"white-space: nowrap; font-weight: bold;\">%3</span>.")
.arg(path)
.arg(guessedGame->displayGameName())
.arg(selectedGame->displayGameName()))
.button({QObject::tr("Manage %1 instead").arg(guessedGame->displayGameName()),
.arg(guessedGame->gameName())
.arg(selectedGame->gameName()))
.button({QObject::tr("Manage %1 instead").arg(guessedGame->gameName()),
QMessageBox::Ok})
.button({QObject::tr("Use this folder for %1")
.arg(selectedGame->displayGameName()),
.button({QObject::tr("Use this folder for %1").arg(selectedGame->gameName()),
QObject::tr("I know what I'm doing"), QMessageBox::Ignore})
.button({QObject::tr("Cancel"), QMessageBox::Cancel})
.exec();
+7 -7
View File
@@ -16,13 +16,13 @@ QString UnmanagedModName();
DataTab::DataTab(OrganizerCore& core, PluginContainer& pc, QWidget* parent,
Ui::MainWindow* mwui)
: m_core(core), m_pluginContainer(pc), m_parent(parent),
ui{mwui->tabWidget,
mwui->dataTab,
mwui->dataTabRefresh,
mwui->dataTree,
mwui->dataTabShowOnlyConflicts,
mwui->dataTabShowFromArchives},
: m_core(core), m_pluginContainer(pc),
m_parent(parent), ui{mwui->tabWidget,
mwui->dataTab,
mwui->dataTabRefresh,
mwui->dataTree,
mwui->dataTabShowOnlyConflicts,
mwui->dataTabShowFromArchives},
m_needUpdate(true)
{
m_filetree.reset(new FileTree(core, m_pluginContainer, ui.tree));
+16 -16
View File
@@ -22,11 +22,9 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include "shared/filesorigin.h"
#include "envfs.h"
#include "game_features.h"
#include "iplugingame.h"
#include "modinfo.h"
#include "modinfodialogfwd.h"
#include "organizercore.h"
#include "report.h"
#include "settings.h"
#include "shared/util.h"
@@ -140,7 +138,7 @@ void dumpStats(std::vector<DirectoryStats>& stats)
if (run == 0) {
std::ofstream out(file, std::ios::out | std::ios::trunc);
out << std::format("what,run,{}", DirectoryStats::csvHeader()) << "\n";
out << fmt::format("what,run,{}", DirectoryStats::csvHeader()) << "\n";
}
std::sort(stats.begin(), stats.end(), [](auto&& a, auto&& b) {
@@ -152,17 +150,17 @@ void dumpStats(std::vector<DirectoryStats>& stats)
DirectoryStats total;
for (const auto& s : stats) {
out << std::format("{},{},{}", s.mod, run, s.toCsv()) << "\n";
out << fmt::format("{},{},{}", s.mod, run, s.toCsv()) << "\n";
total += s;
}
out << std::format("total,{},{}", run, total.toCsv()) << "\n";
out << fmt::format("total,{},{}", run, total.toCsv()) << "\n";
++run;
}
DirectoryRefresher::DirectoryRefresher(OrganizerCore* core, std::size_t threadCount)
: m_Core(*core), m_threadCount(threadCount), m_lastFileCount(0)
DirectoryRefresher::DirectoryRefresher(std::size_t threadCount)
: m_threadCount(threadCount), m_lastFileCount(0)
{}
DirectoryEntry* DirectoryRefresher::stealDirectoryStructure()
@@ -206,9 +204,11 @@ void DirectoryRefresher::addModBSAToStructure(DirectoryEntry* root,
const QString& directory,
const QStringList& archives)
{
const IPluginGame* game = qApp->property("managed_game").value<IPluginGame*>();
QStringList loadOrder;
auto gamePlugins = m_Core.gameFeatures().gameFeature<GamePlugins>();
GamePlugins* gamePlugins = game->feature<GamePlugins>();
if (gamePlugins) {
loadOrder = gamePlugins->getLoadOrder();
}
@@ -320,7 +320,6 @@ void DirectoryRefresher::addModToStructure(DirectoryEntry* directoryStructure,
struct ModThread
{
GameFeatures* gameFeatures;
DirectoryRefreshProgress* progress = nullptr;
DirectoryEntry* ds = nullptr;
std::wstring modName;
@@ -356,8 +355,10 @@ struct ModThread
ds->addFromOrigin(walker, modName, path, prio, *stats);
if (Settings::instance().archiveParsing()) {
const IPluginGame* game = qApp->property("managed_game").value<IPluginGame*>();
QStringList loadOrder;
auto gamePlugins = gameFeatures->gameFeature<GamePlugins>();
GamePlugins* gamePlugins = game->feature<GamePlugins>();
if (gamePlugins) {
loadOrder = gamePlugins->getLoadOrder();
}
@@ -419,12 +420,11 @@ void DirectoryRefresher::addMultipleModsFilesToStructure(
} else {
auto& mt = g_threads.request();
mt.gameFeatures = &m_Core.gameFeatures();
mt.progress = progress;
mt.ds = directoryStructure;
mt.modName = e.modName.toStdWString();
mt.path = QDir::toNativeSeparators(e.absolutePath).toStdWString();
mt.prio = prio;
mt.progress = progress;
mt.ds = directoryStructure;
mt.modName = e.modName.toStdWString();
mt.path = QDir::toNativeSeparators(e.absolutePath).toStdWString();
mt.prio = prio;
mt.archives.clear();
for (auto&& a : e.archives) {
+1 -5
View File
@@ -30,8 +30,6 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <tuple>
#include <vector>
class OrganizerCore;
/**
* @brief used to asynchronously generate the virtual view of the combined data
*directory
@@ -56,7 +54,7 @@ public:
int priority;
};
DirectoryRefresher(OrganizerCore* core, std::size_t threadCount);
DirectoryRefresher(std::size_t threadCount);
/**
* @brief retrieve the updated directory structure
@@ -148,8 +146,6 @@ signals:
void refreshed();
private:
OrganizerCore& m_Core;
std::vector<EntryInfo> m_Mods;
std::set<QString> m_EnabledArchives;
std::unique_ptr<MOShared::DirectoryEntry> m_Root;
+46
View File
@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity type="win32" name="dlls" version="1.0.0.0" processorArchitecture="x86"/>
<file name="7z.dll"/>
<file name="archive.dll"/>
<file name="d3dcompiler_47.dll"/>
<file name="libbsarch.dll"/>
<file name="libcrypto-1_1-x64.dll"/>
<file name="libEGL.dll"/>
<file name="libGLESV2.dll"/>
<file name="liblz4.dll"/>
<file name="libssl-1_1-x64.dll"/>
<file name="opengl32sw.dll"/>
<file name="Qt6Concurrent.dll"/>
<file name="Qt6Core.dll"/>
<file name="Qt6Core5Compat.dll"/>
<file name="Qt6Gui.dll"/>
<file name="Qt6Network.dll"/>
<file name="Qt6OpenGL.dll"/>
<file name="Qt6OpenGLWidgets.dll"/>
<file name="Qt6Positioning.dll"/>
<file name="Qt6PrintSupport.dll"/>
<file name="Qt6Qml.dll"/>
<file name="Qt6QmlLocalStorage.dll"/>
<file name="Qt6QmlModels.dll"/>
<file name="Qt6QmlWorkerScript.dll"/>
<file name="Qt6QmlXmlListModel.dll"/>
<file name="Qt6Quick.dll"/>
<file name="Qt6QuickControls2.dll"/>
<file name="Qt6QuickControls2Impl.dll"/>
<file name="Qt6QuickDialogs2.dll"/>
<file name="Qt6QuickDialogs2QuickImpl.dll"/>
<file name="Qt6QuickDialogs2Utils.dll"/>
<file name="Qt6QuickLayouts.dll"/>
<file name="Qt6QuickParticles.dll"/>
<file name="Qt6QuickShapes.dll"/>
<file name="Qt6QuickTemplates2.dll"/>
<file name="Qt6QuickWidgets.dll"/>
<file name="Qt6Sql.dll"/>
<file name="Qt6Svg.dll"/>
<file name="Qt6WebChannel.dll"/>
<file name="Qt6WebEngineCore.dll"/>
<file name="Qt6WebEngineWidgets.dll"/>
<file name="Qt6WebSockets.dll"/>
<file name="Qt6Widgets.dll"/>
</assembly>
-46
View File
@@ -1,46 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity type="win32" name="dlls" version="1.0.0.0" processorArchitecture="x86" />
<file name="7z.dll" />
<file name="archive.dll" />
<file name="d3dcompiler_47.dll" />
<file name="libbsarch.dll" />
<file name="libcrypto-3-x64.dll" />
<file name="libEGL.dll" />
<file name="libGLESV2.dll" />
<file name="liblz4.dll" />
<file name="libssl-3-x64.dll" />
<file name="opengl32sw.dll" />
<file name="Qt6Concurrentd.dll" />
<file name="Qt6Cored.dll" />
<file name="Qt6Core5Compatd.dll" />
<file name="Qt6Guid.dll" />
<file name="Qt6Networkd.dll" />
<file name="Qt6OpenGLd.dll" />
<file name="Qt6OpenGLWidgetsd.dll" />
<file name="Qt6Positioningd.dll" />
<file name="Qt6PrintSupportd.dll" />
<file name="Qt6Qmld.dll" />
<file name="Qt6QmlLocalStoraged.dll" />
<file name="Qt6QmlModelsd.dll" />
<file name="Qt6QmlWorkerScriptd.dll" />
<file name="Qt6QmlXmlListModeld.dll" />
<file name="Qt6Quickd.dll" />
<file name="Qt6QuickControls2d.dll" />
<file name="Qt6QuickControls2Impld.dll" />
<file name="Qt6QuickDialogs2d.dll" />
<file name="Qt6QuickDialogs2QuickImpld.dll" />
<file name="Qt6QuickDialogs2Utilsd.dll" />
<file name="Qt6QuickLayoutsd.dll" />
<file name="Qt6QuickParticlesd.dll" />
<file name="Qt6QuickShapesd.dll" />
<file name="Qt6QuickTemplates2d.dll" />
<file name="Qt6QuickWidgetsd.dll" />
<file name="Qt6Sqld.dll" />
<file name="Qt6Svgd.dll" />
<file name="Qt6WebChanneld.dll" />
<file name="Qt6WebEngineCored.dll" />
<file name="Qt6WebEngineWidgetsd.dll" />
<file name="Qt6WebSocketsd.dll" />
<file name="Qt6Widgetsd.dll" />
</assembly>
+46
View File
@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity type="win32" name="dlls" version="1.0.0.0" processorArchitecture="x86"/>
<file name="7z.dll"/>
<file name="archive.dll"/>
<file name="d3dcompiler_47.dll"/>
<file name="libbsarch.dll"/>
<file name="libcrypto-1_1-x64.dll"/>
<file name="libEGL.dll"/>
<file name="libGLESV2.dll"/>
<file name="liblz4.dll"/>
<file name="libssl-1_1-x64.dll"/>
<file name="opengl32sw.dll"/>
<file name="Qt6Concurrent.dll"/>
<file name="Qt6Core.dll"/>
<file name="Qt6Core5Compat.dll"/>
<file name="Qt6Gui.dll"/>
<file name="Qt6Network.dll"/>
<file name="Qt6OpenGL.dll"/>
<file name="Qt6OpenGLWidgets.dll"/>
<file name="Qt6Positioning.dll"/>
<file name="Qt6PrintSupport.dll"/>
<file name="Qt6Qml.dll"/>
<file name="Qt6QmlLocalStorage.dll"/>
<file name="Qt6QmlModels.dll"/>
<file name="Qt6QmlWorkerScript.dll"/>
<file name="Qt6QmlXmlListModel.dll"/>
<file name="Qt6Quick.dll"/>
<file name="Qt6QuickControls2.dll"/>
<file name="Qt6QuickControls2Impl.dll"/>
<file name="Qt6QuickDialogs2.dll"/>
<file name="Qt6QuickDialogs2QuickImpl.dll"/>
<file name="Qt6QuickDialogs2Utils.dll"/>
<file name="Qt6QuickLayouts.dll"/>
<file name="Qt6QuickParticles.dll"/>
<file name="Qt6QuickShapes.dll"/>
<file name="Qt6QuickTemplates2.dll"/>
<file name="Qt6QuickWidgets.dll"/>
<file name="Qt6Sql.dll"/>
<file name="Qt6Svg.dll"/>
<file name="Qt6WebChannel.dll"/>
<file name="Qt6WebEngineCore.dll"/>
<file name="Qt6WebEngineWidgets.dll"/>
<file name="Qt6WebSockets.dll"/>
<file name="Qt6Widgets.dll"/>
</assembly>
-46
View File
@@ -1,46 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity type="win32" name="dlls" version="1.0.0.0" processorArchitecture="x86" />
<file name="7z.dll" />
<file name="archive.dll" />
<file name="d3dcompiler_47.dll" />
<file name="libbsarch.dll" />
<file name="libcrypto-3-x64.dll" />
<file name="libEGL.dll" />
<file name="libGLESV2.dll" />
<file name="liblz4.dll" />
<file name="libssl-3-x64.dll" />
<file name="opengl32sw.dll" />
<file name="Qt6Concurrent.dll" />
<file name="Qt6Core.dll" />
<file name="Qt6Core5Compat.dll" />
<file name="Qt6Gui.dll" />
<file name="Qt6Network.dll" />
<file name="Qt6OpenGL.dll" />
<file name="Qt6OpenGLWidgets.dll" />
<file name="Qt6Positioning.dll" />
<file name="Qt6PrintSupport.dll" />
<file name="Qt6Qml.dll" />
<file name="Qt6QmlLocalStorage.dll" />
<file name="Qt6QmlModels.dll" />
<file name="Qt6QmlWorkerScript.dll" />
<file name="Qt6QmlXmlListModel.dll" />
<file name="Qt6Quick.dll" />
<file name="Qt6QuickControls2.dll" />
<file name="Qt6QuickControls2Impl.dll" />
<file name="Qt6QuickDialogs2.dll" />
<file name="Qt6QuickDialogs2QuickImpl.dll" />
<file name="Qt6QuickDialogs2Utils.dll" />
<file name="Qt6QuickLayouts.dll" />
<file name="Qt6QuickParticles.dll" />
<file name="Qt6QuickShapes.dll" />
<file name="Qt6QuickTemplates2.dll" />
<file name="Qt6QuickWidgets.dll" />
<file name="Qt6Sql.dll" />
<file name="Qt6Svg.dll" />
<file name="Qt6WebChannel.dll" />
<file name="Qt6WebEngineCore.dll" />
<file name="Qt6WebEngineWidgets.dll" />
<file name="Qt6WebSockets.dll" />
<file name="Qt6Widgets.dll" />
</assembly>
+1 -1
View File
@@ -221,7 +221,7 @@ QVariant DownloadList::data(const QModelIndex& index, int role) const
.arg(info->modName)
.arg(m_manager.getModID(index.row()))
.arg(info->version.canonicalString())
.arg(info->description.mid(0, 4096));
.arg(info->description.chopped(4096));
}
return text;
}
+4 -8
View File
@@ -1,12 +1,8 @@
#include <format>
#include "envshell.h"
#include <log.h>
#include <utility.h>
#include <windowsx.h>
#include "envshell.h"
namespace env
{
@@ -20,7 +16,7 @@ class MenuFailed : public std::runtime_error
public:
MenuFailed(HRESULT r, const std::string& what)
: runtime_error(
std::format("{}, {}", what,
fmt::format("{}, {}", what,
QString::fromStdWString(formatSystemMessage(r)).toStdString()))
{}
};
@@ -482,7 +478,7 @@ void ShellMenu::invoke(const QPoint& p, int cmd)
const auto r = m_cm->InvokeCommand((CMINVOKECOMMANDINFO*)&info);
if (FAILED(r)) {
throw MenuFailed(r, std::format("InvokeCommand failed, verb={}", cmd));
throw MenuFailed(r, fmt::format("InvokeCommand failed, verb={}", cmd));
}
}
@@ -572,7 +568,7 @@ void ShellMenuCollection::exec(const QPoint& pos)
}
if (!m_active) {
log::debug("SMC: command {} selected without active submenu", cmd);
log::debug("SMC: command {} selected without active submenu");
return;
}
+6 -2
View File
@@ -246,12 +246,16 @@ void FileTree::activate(FileTreeItem* item)
return;
}
if (item->isFromArchive()) {
log::warn("cannot activate file from archive '{}'", item->filename());
return;
}
const auto tryPreview = m_core.settings().interface().doubleClicksOpenPreviews();
if (tryPreview) {
const QFileInfo fi(item->realPath());
if (m_plugins.previewGenerator().previewSupported(fi.suffix().toLower(),
item->isFromArchive())) {
if (m_plugins.previewGenerator().previewSupported(fi.suffix().toLower())) {
preview(item);
return;
}
-348
View File
@@ -1,348 +0,0 @@
#include "game_features.h"
#include <algorithm>
#include <QTimer>
#include "iplugingame.h"
#include "log.h"
#include "bsainvalidation.h"
#include "dataarchives.h"
#include "gameplugins.h"
#include "localsavegames.h"
#include "organizercore.h"
#include "plugincontainer.h"
#include "savegameinfo.h"
#include "scriptextender.h"
#include "unmanagedmods.h"
using namespace MOBase;
// avoid updating features more than once every 100ms
constexpr std::chrono::milliseconds UPDATE_QUEUE_TIMER{100};
// specific type index for checker and content that behaves differently
const std::type_index ModDataCheckerIndex{typeid(ModDataChecker)};
const std::type_index ModDataContentIndex{typeid(ModDataContent)};
class GameFeatures::CombinedModDataChecker : public ModDataChecker
{
std::vector<std::shared_ptr<ModDataChecker>> m_modDataCheckers;
mutable std::shared_ptr<ModDataChecker> m_fixer = nullptr;
public:
void setCheckers(std::vector<std::shared_ptr<ModDataChecker>> checkers)
{
m_modDataCheckers = std::move(checkers);
m_fixer = nullptr;
}
bool isValid() const { return !m_modDataCheckers.empty(); }
CheckReturn
dataLooksValid(std::shared_ptr<const MOBase::IFileTree> fileTree) const override
{
m_fixer = nullptr;
// go through the available mod-data checker, if any returns valid, we also
// return valid, otherwise, return the first one that is fixable
for (const auto& modDataChecker : m_modDataCheckers) {
auto check = modDataChecker->dataLooksValid(fileTree);
switch (check) {
case CheckReturn::FIXABLE:
// only update fixer if there is not one with higher priority
if (!m_fixer) {
m_fixer = modDataChecker;
}
break;
case CheckReturn::VALID:
// clear fixer if one were found before and return VALID, not mandatory
// but cleaner
m_fixer = nullptr;
return CheckReturn::VALID;
case CheckReturn::INVALID:
break;
}
}
return m_fixer ? CheckReturn::FIXABLE : CheckReturn::INVALID;
}
std::shared_ptr<MOBase::IFileTree>
fix(std::shared_ptr<MOBase::IFileTree> fileTree) const override
{
if (m_fixer) {
return m_fixer->fix(fileTree);
}
return nullptr;
}
};
class GameFeatures::CombinedModDataContent : public ModDataContent
{
// store the ModDataContent and the offset to add to the content
std::vector<std::pair<std::shared_ptr<ModDataContent>, int>> m_modDataContents;
std::vector<Content> m_allContents;
public:
bool isValid() const { return !m_modDataContents.empty(); }
void setContents(std::vector<std::shared_ptr<ModDataContent>> modDataContents)
{
m_modDataContents.clear();
m_modDataContents.reserve(modDataContents.size());
m_allContents.clear();
// update all contents and offsets
std::size_t offset = 0;
for (auto& modDataContent : modDataContents) {
m_modDataContents.emplace_back(modDataContent, static_cast<int>(offset));
// add to the list of contents
auto contents = modDataContent->getAllContents();
m_allContents.insert(m_allContents.end(),
std::make_move_iterator(contents.begin()),
std::make_move_iterator(contents.end()));
// increase offset for next mod data content
offset += contents.size();
}
}
std::vector<Content> getAllContents() const override { return m_allContents; }
std::vector<int>
getContentsFor(std::shared_ptr<const MOBase::IFileTree> fileTree) const
{
std::vector<int> contentsFor;
for (auto& modDataContent : m_modDataContents) {
auto contentsForFrom = modDataContent.first->getContentsFor(fileTree);
std::transform(contentsForFrom.begin(), contentsForFrom.end(),
std::back_inserter(contentsFor),
[offset = modDataContent.second](auto content) {
return content + offset;
});
}
return contentsFor;
}
};
GameFeatures::GameFeatures(OrganizerCore* core, PluginContainer* plugins)
: m_pluginContainer(*plugins),
m_modDataChecker(std::make_unique<CombinedModDataChecker>()),
m_modDataContent(std::make_unique<CombinedModDataContent>())
{
// can be nullptr since the plugin container can be initialized with a Core (e.g.,
// on first MO2 start)
if (core) {
connect(core, &OrganizerCore::managedGameChanged, [this] {
updateCurrentFeatures();
});
}
auto updateFeatures = [this] {
QTimer::singleShot(UPDATE_QUEUE_TIMER, [this] {
updateCurrentFeatures();
});
};
connect(plugins, &PluginContainer::pluginEnabled, updateFeatures);
connect(plugins, &PluginContainer::pluginDisabled, updateFeatures);
connect(plugins, &PluginContainer::pluginRegistered, updateFeatures);
connect(plugins, &PluginContainer::pluginUnregistered,
[this, updateFeatures](MOBase::IPlugin* plugin) {
// remove features from the current plugin
for (auto& [_, features] : m_allFeatures) {
features.erase(std::remove_if(features.begin(), features.end(),
[plugin](const auto& feature) {
return feature.plugin() == plugin;
}),
features.end());
}
// update current features
updateFeatures();
});
}
GameFeatures::~GameFeatures() {}
GameFeatures::CombinedModDataChecker& GameFeatures::modDataChecker() const
{
return dynamic_cast<CombinedModDataChecker&>(*m_modDataChecker);
}
GameFeatures::CombinedModDataContent& GameFeatures::modDataContent() const
{
return dynamic_cast<CombinedModDataContent&>(*m_modDataContent);
}
void GameFeatures::updateCurrentFeatures(std::type_index const& index)
{
auto& features = m_allFeatures[index];
m_currentFeatures[index].clear();
// this can occur when starting MO2, just wait for the next update
if (!m_pluginContainer.managedGame()) {
return;
}
for (const auto& dataFeature : features) {
// registering plugin is disabled
if (!m_pluginContainer.isEnabled(dataFeature.plugin())) {
continue;
}
// games does not match
if (!dataFeature.games().isEmpty() &&
!dataFeature.games().contains(m_pluginContainer.managedGame()->gameName())) {
continue;
}
m_currentFeatures[index].push_back(dataFeature.feature());
}
// update mod data checker
if (index == ModDataCheckerIndex) {
auto& currentCheckers = m_currentFeatures[ModDataCheckerIndex];
std::vector<std::shared_ptr<ModDataChecker>> checkers;
checkers.reserve(currentCheckers.size());
std::transform(currentCheckers.begin(), currentCheckers.end(),
std::back_inserter(checkers), [](auto const& checker) {
return std::dynamic_pointer_cast<ModDataChecker>(checker);
});
modDataChecker().setCheckers(std::move(checkers));
emit modDataCheckerUpdated(gameFeature<ModDataChecker>().get());
}
// update mod data content
if (index == ModDataContentIndex) {
auto& currentContents = m_currentFeatures[ModDataContentIndex];
std::vector<std::shared_ptr<ModDataContent>> contents;
contents.reserve(currentContents.size());
std::transform(currentContents.begin(), currentContents.end(),
std::back_inserter(contents), [](auto const& checker) {
return std::dynamic_pointer_cast<ModDataContent>(checker);
});
modDataContent().setContents(std::move(contents));
emit modDataContentUpdated(gameFeature<ModDataContent>().get());
}
}
void GameFeatures::updateCurrentFeatures()
{
// TODO: this completely refilters everything currently, which should be ok since
// this should only be triggered by function that are uncommon (enabling/disabling
// plugin or changing the game), it should be possible to filter more finely by
// maintaining more information (e.g., only removing features from current when a
// plugin is disabled or unregistered)
//
for (auto& [info, _] : m_allFeatures) {
updateCurrentFeatures(info);
}
}
bool GameFeatures::registerGameFeature(MOBase::IPlugin* plugin,
QStringList const& games,
std::shared_ptr<MOBase::GameFeature> feature,
int priority)
{
auto& features = m_allFeatures[feature->typeInfo()];
if (std::find_if(features.begin(), features.end(), [&feature](const auto& data) {
return data.feature() == feature;
}) != features.end()) {
log::error("cannot register feature multiple time");
return false;
}
std::decay_t<decltype(features)>::iterator it;
if (dynamic_cast<IPluginGame*>(plugin)) {
it = features.end();
} else {
it = std::lower_bound(features.begin(), features.end(), priority,
[](const auto& feature, int priority) {
return feature.priority() > priority;
});
}
features.emplace(it, feature, plugin, games, std::numeric_limits<int>::min());
// TODO: only update if relevant
updateCurrentFeatures(feature->typeInfo());
return true;
}
// unregister game features
//
bool GameFeatures::unregisterGameFeature(std::shared_ptr<MOBase::GameFeature> feature)
{
bool removed = false;
for (auto& [_, features] : m_allFeatures) {
auto it =
std::find_if(features.begin(), features.end(), [&feature](const auto& data) {
return data.feature() == feature;
});
// the feature can only exist for one kind of features and cannot be duplicated
if (it != features.end()) {
features.erase(it);
removed = true;
break;
}
}
if (removed) {
updateCurrentFeatures();
}
return removed;
}
int GameFeatures::unregisterGameFeatures(MOBase::IPlugin* plugin,
std::type_info const& info)
{
auto& features = m_allFeatures[info];
const auto initialSize = features.size();
features.erase(std::remove_if(features.begin(), features.end(),
[plugin](const auto& feature) {
return feature.plugin() == plugin;
}),
features.end());
const int removed = features.size() - initialSize;
if (removed) {
updateCurrentFeatures();
}
return removed;
}
std::shared_ptr<GameFeature> GameFeatures::gameFeature(std::type_info const& info) const
{
if (info == ModDataCheckerIndex) {
return modDataChecker().isValid() ? m_modDataChecker : nullptr;
}
if (info == ModDataContentIndex) {
return modDataContent().isValid() ? m_modDataContent : nullptr;
}
auto it = m_currentFeatures.find(info);
if (it == m_currentFeatures.end() || it->second.empty()) {
return nullptr;
}
return it->second.front();
}
-117
View File
@@ -1,117 +0,0 @@
#ifndef MO2_GAME_FEATURES_H
#define MO2_GAME_FEATURES_H
#include <any>
#include <memory>
#include <typeinfo>
#include <unordered_map>
#include <vector>
#include <QObject>
#include "game_feature.h"
#include "moddatachecker.h"
#include "moddatacontent.h"
namespace MOBase
{
class IPlugin;
class IPluginGame;
} // namespace MOBase
class OrganizerCore;
class PluginContainer;
/**
* Class managing game features, either registered or from the game plugin.
*/
class GameFeatures : public QObject
{
Q_OBJECT
public:
/**
*
*/
GameFeatures(OrganizerCore* core, PluginContainer* plugins);
~GameFeatures();
// register game features
//
bool registerGameFeature(MOBase::IPlugin* plugin, QStringList const& games,
std::shared_ptr<MOBase::GameFeature> feature, int priority);
// unregister game features
//
bool unregisterGameFeature(std::shared_ptr<MOBase::GameFeature> feature);
int unregisterGameFeatures(MOBase::IPlugin* plugin, std::type_info const& info);
// retrieve a game feature
//
template <class T>
std::shared_ptr<T> gameFeature() const
{
return std::dynamic_pointer_cast<T>(gameFeature(typeid(T)));
}
signals:
void modDataCheckerUpdated(const MOBase::ModDataChecker* check);
void modDataContentUpdated(const MOBase::ModDataContent* check);
private:
friend class GameFeaturesProxy;
class GameFeatureWithData
{
// feature
std::shared_ptr<MOBase::GameFeature> m_feature;
// plugin that registered the feature
MOBase::IPlugin* m_plugin;
// games this plugin applies to - empty list indicates all games
QStringList m_games;
// priority of the plugin
int m_priority;
public:
GameFeatureWithData(std::shared_ptr<MOBase::GameFeature> feature,
MOBase::IPlugin* plugin, QStringList games, int priority)
: m_feature(feature), m_plugin(plugin), m_games(games), m_priority(priority)
{}
const auto& feature() const { return m_feature; }
auto* const plugin() const { return m_plugin; }
const auto& games() const { return m_games; }
auto priority() const { return m_priority; }
};
// retrieve a game feature from info
//
std::shared_ptr<MOBase::GameFeature> gameFeature(std::type_info const& index) const;
// update current features by filtering
//
void updateCurrentFeatures(std::type_index const& index);
void updateCurrentFeatures();
// implementation details
class CombinedModDataChecker;
class CombinedModDataContent;
CombinedModDataChecker& modDataChecker() const;
CombinedModDataContent& modDataContent() const;
PluginContainer& m_pluginContainer;
std::unordered_map<std::type_index, std::vector<GameFeatureWithData>> m_allFeatures;
std::unordered_map<std::type_index, std::vector<std::shared_ptr<MOBase::GameFeature>>>
m_currentFeatures;
std::shared_ptr<CombinedModDataChecker> m_modDataChecker;
std::shared_ptr<CombinedModDataContent> m_modDataContent;
};
#endif

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