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 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 10459e7e1..7188c03b8 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) + DirectXTex 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/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..0f6a14551 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,33 @@ 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; diff --git a/src/previewgenerator.cpp b/src/previewgenerator.cpp index ba73c0650..dad41c2fa 100644 --- a/src/previewgenerator.cpp +++ b/src/previewgenerator.cpp @@ -35,12 +35,16 @@ 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 +62,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;