From 3f250dc402427407588af71412d124377de7a582 Mon Sep 17 00:00:00 2001 From: Jeremy Rimpo Date: Tue, 25 Jun 2024 20:34:23 -0500 Subject: [PATCH 1/5] WIP: Support for archive file previews - Should account for alternates - Extracts files and requests preview from plugins that claim support --- src/filetree.cpp | 8 ++--- src/modinfodialog.cpp | 6 +--- src/modinfodialogimages.cpp | 2 +- src/organizercore.cpp | 66 ++++++++++++++++++++++++++++++------- src/previewgenerator.cpp | 21 ++++++++++-- src/previewgenerator.h | 4 ++- 6 files changed, 80 insertions(+), 27 deletions(-) diff --git a/src/filetree.cpp b/src/filetree.cpp index f043448f4..6d397f24e 100644 --- a/src/filetree.cpp +++ b/src/filetree.cpp @@ -246,16 +246,12 @@ 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())) { + if (m_plugins.previewGenerator().previewSupported(fi.suffix().toLower(), + item->isFromArchive())) { preview(item); return; } diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index d0e8d1ad2..1798e9870 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -42,12 +42,8 @@ const int max_scan_for_context_menu = 50; bool canPreviewFile(const PluginContainer& pluginContainer, bool isArchive, const QString& filename) { - if (isArchive) { - return false; - } - const auto ext = QFileInfo(filename).suffix().toLower(); - return pluginContainer.previewGenerator().previewSupported(ext); + return pluginContainer.previewGenerator().previewSupported(ext, isArchive); } bool isExecutableFilename(const QString& filename) diff --git a/src/modinfodialogimages.cpp b/src/modinfodialogimages.cpp index 1042eacaa..7b250e1e8 100644 --- a/src/modinfodialogimages.cpp +++ b/src/modinfodialogimages.cpp @@ -262,7 +262,7 @@ void ImagesTab::select(std::size_t i, Visibility v) ui->imagesPath->setText(QDir::toNativeSeparators(f->path())); ui->imagesExplore->setEnabled(true); if (plugin().previewGenerator().previewSupported( - QFileInfo(f->path()).suffix().toLower())) + QFileInfo(f->path()).suffix().toLower(), false)) ui->previewPluginButton->setEnabled(true); else ui->previewPluginButton->setEnabled(false); diff --git a/src/organizercore.cpp b/src/organizercore.cpp index f471ead19..ac7e5fc22 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -71,6 +71,8 @@ #include #include +#include "bs_archive.h" + #include "organizerproxy.h" using namespace MOShared; @@ -1053,7 +1055,7 @@ bool OrganizerCore::previewFileWithAlternatives(QWidget* parent, QString fileNam // set up preview dialog PreviewDialog preview(fileName, parent); - auto addFunc = [&](int originId) { + auto addFunc = [&](int originId, std::wstring archiveName = L"") { FilesOrigin& origin = directoryStructure()->getOriginByID(originId); QString filePath = QDir::fromNativeSeparators(ToQString(origin.getPath())) + "/" + fileName; @@ -1066,14 +1068,32 @@ bool OrganizerCore::previewFileWithAlternatives(QWidget* parent, QString fileNam } else { preview.addVariant(ToQString(origin.getName()), wid); } + } else if (archiveName != L"") { + auto archiveFile = directoryStructure()->searchFile(archiveName); + if (archiveFile.get() != nullptr) { + try { + libbsarch::bs_archive archiveLoader; + archiveLoader.load_from_disk(archiveFile->getFullPath()); + libbsarch::memory_blob fileData = archiveLoader.extract_to_memory(fileName.toStdWString()); + QByteArray convertedFileData((char*)(fileData.data), fileData.size); + QWidget* wid = m_PluginContainer->previewGenerator().genArchivePreview( + convertedFileData, filePath); + if (wid == nullptr) { + reportError(tr("failed to generate preview for %1").arg(filePath)); + } else { + preview.addVariant(ToQString(origin.getName()), wid); + } + } catch (std::exception& e) { + } + } } }; if (selectedOrigin == -1) { // don't bother with the vector of origins, just add them as they come - addFunc(file->getOrigin()); + addFunc(file->getOrigin(), file->isFromArchive() ? file->getArchive().name() : L""); for (const auto& alt : file->getAlternatives()) { - addFunc(alt.originID()); + addFunc(alt.originID(), alt.isFromArchive() ? alt.archive().name() : L""); } } else { std::vector origins; @@ -1121,20 +1141,42 @@ bool OrganizerCore::previewFileWithAlternatives(QWidget* parent, QString fileNam bool OrganizerCore::previewFile(QWidget* parent, const QString& originName, const QString& path) { - if (!QFile::exists(path)) { - reportError(tr("File '%1' not found.").arg(path)); - return false; - } - PreviewDialog preview(path, parent); + auto fileIndex = directoryStructure()->searchFile(path.toStdWString())->getIndex(); + auto file = directoryStructure() + ->getOriginByName(originName.toStdWString()) + .findFile(fileIndex); + + if (QFile::exists(path)) { + QWidget* wid = m_PluginContainer->previewGenerator().genPreview(path); + if (wid == nullptr) { + reportError(tr("Failed to generate preview for %1").arg(path)); + return false; + } - QWidget* wid = m_PluginContainer->previewGenerator().genPreview(path); - if (wid == nullptr) { - reportError(tr("Failed to generate preview for %1").arg(path)); + preview.addVariant(originName, wid); + } else if (file->isFromArchive()) { + auto archive = directoryStructure()->searchFile(file->getArchive().name()); + if (archive.get() != nullptr) { + try { + libbsarch::bs_archive archiveLoader; + archiveLoader.load_from_disk(archive->getFullPath()); + auto fileData = archiveLoader.extract_to_memory(path.toStdWString()); + QByteArray convertedFileData((char*)(fileData.data), fileData.size); + QWidget* wid = m_PluginContainer->previewGenerator().genArchivePreview( + convertedFileData, path); + if (wid == nullptr) { + reportError(tr("failed to generate preview for %1").arg(path)); + } else { + preview.addVariant(originName, wid); + } + } catch (std::exception& e) { + } + } + } else { return false; } - preview.addVariant(originName, wid); preview.exec(); return true; diff --git a/src/previewgenerator.cpp b/src/previewgenerator.cpp index ba73c0650..9ab96868d 100644 --- a/src/previewgenerator.cpp +++ b/src/previewgenerator.cpp @@ -35,12 +35,15 @@ PreviewGenerator::PreviewGenerator(const PluginContainer& pluginContainer) m_MaxSize = QGuiApplication::primaryScreen()->size() * 0.8; } -bool PreviewGenerator::previewSupported(const QString& fileExtension) const +bool PreviewGenerator::previewSupported(const QString& fileExtension, const bool& isArchive) const { auto& previews = m_PluginContainer.plugins(); for (auto* preview : previews) { if (preview->supportedExtensions().contains(fileExtension)) { - return true; + if (!isArchive) + return true; + if (preview->supportsArchives()) + return true; } } return false; @@ -58,3 +61,17 @@ QWidget* PreviewGenerator::genPreview(const QString& fileName) const } return nullptr; } + +QWidget* PreviewGenerator::genArchivePreview(const QByteArray& fileData, const QString& fileName) const +{ + const QString ext = QFileInfo(fileName).suffix().toLower(); + auto& previews = m_PluginContainer.plugins(); + for (auto* preview : previews) { + if (m_PluginContainer.isEnabled(preview) && + preview->supportedExtensions().contains(ext) && preview->supportsArchives() + ) { + return preview->genDataPreview(fileData, fileName, m_MaxSize); + } + } + return nullptr; +} diff --git a/src/previewgenerator.h b/src/previewgenerator.h index dd0d2cd4f..15d652f62 100644 --- a/src/previewgenerator.h +++ b/src/previewgenerator.h @@ -33,10 +33,12 @@ class PreviewGenerator public: PreviewGenerator(const PluginContainer& pluginContainer); - bool previewSupported(const QString& fileExtension) const; + bool previewSupported(const QString& fileExtension, const bool& isArchive) const; QWidget* genPreview(const QString& fileName) const; + QWidget* genArchivePreview(const QByteArray& fileData, const QString& fileName) const; + private: const PluginContainer& m_PluginContainer; QSize m_MaxSize; From 8ddf23f34bf30e2f1b06a697456bbfa53ee0eb7f Mon Sep 17 00:00:00 2001 From: Jeremy Rimpo Date: Tue, 25 Jun 2024 20:34:50 -0500 Subject: [PATCH 2/5] libbsarch and DDS header --- src/CMakeLists.txt | 3 ++- src/organizercore.cpp | 3 ++- src/previewgenerator.cpp | 9 +++++---- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 10459e7e1..fc4658aa9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -7,8 +7,9 @@ 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 - Qt::WebEngineWidgets Qt::WebSockets) + libbsarch 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" diff --git a/src/organizercore.cpp b/src/organizercore.cpp index ac7e5fc22..5d915a32d 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -1074,7 +1074,8 @@ bool OrganizerCore::previewFileWithAlternatives(QWidget* parent, QString fileNam try { libbsarch::bs_archive archiveLoader; archiveLoader.load_from_disk(archiveFile->getFullPath()); - libbsarch::memory_blob fileData = archiveLoader.extract_to_memory(fileName.toStdWString()); + libbsarch::memory_blob fileData = + archiveLoader.extract_to_memory(fileName.toStdWString()); QByteArray convertedFileData((char*)(fileData.data), fileData.size); QWidget* wid = m_PluginContainer->previewGenerator().genArchivePreview( convertedFileData, filePath); diff --git a/src/previewgenerator.cpp b/src/previewgenerator.cpp index 9ab96868d..dad41c2fa 100644 --- a/src/previewgenerator.cpp +++ b/src/previewgenerator.cpp @@ -35,7 +35,8 @@ PreviewGenerator::PreviewGenerator(const PluginContainer& pluginContainer) m_MaxSize = QGuiApplication::primaryScreen()->size() * 0.8; } -bool PreviewGenerator::previewSupported(const QString& fileExtension, const bool& isArchive) const +bool PreviewGenerator::previewSupported(const QString& fileExtension, + const bool& isArchive) const { auto& previews = m_PluginContainer.plugins(); for (auto* preview : previews) { @@ -62,14 +63,14 @@ QWidget* PreviewGenerator::genPreview(const QString& fileName) const return nullptr; } -QWidget* PreviewGenerator::genArchivePreview(const QByteArray& fileData, const QString& fileName) const +QWidget* PreviewGenerator::genArchivePreview(const QByteArray& fileData, + const QString& fileName) const { const QString ext = QFileInfo(fileName).suffix().toLower(); auto& previews = m_PluginContainer.plugins(); for (auto* preview : previews) { if (m_PluginContainer.isEnabled(preview) && - preview->supportedExtensions().contains(ext) && preview->supportsArchives() - ) { + preview->supportedExtensions().contains(ext) && preview->supportsArchives()) { return preview->genDataPreview(fileData, fileName, m_MaxSize); } } From 93f0a265958ad606ccfd4b7c82622e6319b725d8 Mon Sep 17 00:00:00 2001 From: Jeremy Rimpo Date: Sat, 6 Jul 2024 20:43:03 -0500 Subject: [PATCH 3/5] Revert previewFile changes - Takes a true absolute path to a file - Not simple to add alternate origins or DDS files here - Used mostly by the mod info file tree --- src/organizercore.cpp | 40 +++++++++------------------------------- 1 file changed, 9 insertions(+), 31 deletions(-) diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 5d915a32d..0f6a14551 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -1142,42 +1142,20 @@ bool OrganizerCore::previewFileWithAlternatives(QWidget* parent, QString fileNam bool OrganizerCore::previewFile(QWidget* parent, const QString& originName, const QString& path) { + if (!QFile::exists(path)) { + reportError(tr("File '%1' not found.").arg(path)); + return false; + } + PreviewDialog preview(path, parent); - auto fileIndex = directoryStructure()->searchFile(path.toStdWString())->getIndex(); - auto file = directoryStructure() - ->getOriginByName(originName.toStdWString()) - .findFile(fileIndex); - - if (QFile::exists(path)) { - QWidget* wid = m_PluginContainer->previewGenerator().genPreview(path); - if (wid == nullptr) { - reportError(tr("Failed to generate preview for %1").arg(path)); - return false; - } - preview.addVariant(originName, wid); - } else if (file->isFromArchive()) { - auto archive = directoryStructure()->searchFile(file->getArchive().name()); - if (archive.get() != nullptr) { - try { - libbsarch::bs_archive archiveLoader; - archiveLoader.load_from_disk(archive->getFullPath()); - auto fileData = archiveLoader.extract_to_memory(path.toStdWString()); - QByteArray convertedFileData((char*)(fileData.data), fileData.size); - QWidget* wid = m_PluginContainer->previewGenerator().genArchivePreview( - convertedFileData, path); - if (wid == nullptr) { - reportError(tr("failed to generate preview for %1").arg(path)); - } else { - preview.addVariant(originName, wid); - } - } catch (std::exception& e) { - } - } - } else { + QWidget* wid = m_PluginContainer->previewGenerator().genPreview(path); + if (wid == nullptr) { + reportError(tr("Failed to generate preview for %1").arg(path)); return false; } + preview.addVariant(originName, wid); preview.exec(); return true; From 6fe3650a338090a9d059f4442b13990c772d7f2b Mon Sep 17 00:00:00 2001 From: Jeremy Rimpo Date: Thu, 11 Jul 2024 02:16:44 -0500 Subject: [PATCH 4/5] Add DirectXTex depends --- src/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fc4658aa9..7188c03b8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -7,7 +7,7 @@ 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 - libbsarch Qt::WebEngineWidgets Qt::WebSockets) + DirectXTex libbsarch Qt::WebEngineWidgets Qt::WebSockets) target_link_libraries(organizer PUBLIC Shlwapi) target_include_directories(organizer PUBLIC ${DDS_ROOT}) mo2_install_target(organizer) From 53fd4ddd4ec57893202705efd0c2cccdf1aa4bcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Thu, 11 Jul 2024 16:10:54 +0200 Subject: [PATCH 5/5] Add missing directxtex dependency in CI. --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 89f3bc7c3..c58575748 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,5 +16,5 @@ jobs: 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 + boost boost-di sip pyqt pybind11 ss licenses explorerpp DirectXTex mo2-dependencies: usvfs cmake_common uibase githubpp bsatk esptk archive lootcli game_gamebryo