From 068e12bbb2cd774af89715105241ba7d3e3d0dcd Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Fri, 1 May 2020 18:03:27 +0200 Subject: Move to the new filetree for the installation manager. --- src/archivefiletree.cpp | 252 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 252 insertions(+) create mode 100644 src/archivefiletree.cpp (limited to 'src/archivefiletree.cpp') diff --git a/src/archivefiletree.cpp b/src/archivefiletree.cpp new file mode 100644 index 00000000..b629cc8e --- /dev/null +++ b/src/archivefiletree.cpp @@ -0,0 +1,252 @@ +/* +Copyright (C) MO2 Team. All rights reserved. + +This file is part of Mod Organizer. + +Mod Organizer is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Mod Organizer is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Mod Organizer. If not, see . +*/ + +// For QObject::tr: +#include + +#include "archivefiletree.h" + +#include "log.h" + +using namespace MOBase; + +/** + * We use custom file entries to store the index. + */ +class ArchiveFileEntry : public virtual FileTreeEntry { +public: + + /** + * @brief Create a new entry corresponding to a file. + * + * @param parent The tree containing this file. + * @param name The name of this file. + * @param index The index of the file in the archive. + * @param time The modification time of this file. + */ + ArchiveFileEntry(std::shared_ptr parent, QString name, int index, QDateTime time) : + FileTreeEntry(parent, name, time), m_Index(index) { + } + + /** + * @brief Create a new entry corresponding to a directory. + * + * @param parent The tree containing this directory. + * @param name The name of this directory. + * @param index The index of the directory in the archive, or -1. + */ + ArchiveFileEntry(std::shared_ptr parent, QString name, int index) : + FileTreeEntry(parent, name), m_Index(index) { + } + + // No private since we are in an implementation file: + const int m_Index; +}; + + +/** + * + */ +class ArchiveFileTreeImpl: public virtual ArchiveFileTree, public virtual ArchiveFileEntry { +public: + + using File = std::tuple; + +public: // Public for make_shared (but not accessible by other since not exposed in .h): + + ArchiveFileTreeImpl(std::shared_ptr parent, QString name, int index, std::vector&& files) + : FileTreeEntry(parent, name), ArchiveFileEntry(parent, name, index), IFileTree(), m_Files(std::move(files)) { } + +public: // Overrides: + + /** + * @override + */ + std::shared_ptr addFile(QString path, QDateTime time = QDateTime()) override { + // Cannot add file to an archive. + throw UnsupportedOperationException(QObject::tr("Cannot create file within an archive.")); + } + + /** + * + */ + static void mapToArchive(IFileTree const& tree, QString path, FileData* const* data) + { + if (path.length() > 0) { + // when using a long windows path (starting with \\?\) we apparently can have redundant + // . components in the path. This wasn't a problem with "regular" path names. + if (path == ".") { + path.clear(); + } + else { + path.append("\\"); + } + } + + for (auto const& entry : tree) { + if (entry->isDir()) { + const ArchiveFileTreeImpl& archiveEntry = dynamic_cast(*entry); + QString tmp = path + archiveEntry.name(); + if (archiveEntry.m_Index != -1) { + data[archiveEntry.m_Index]->addOutputFileName(tmp); + } + mapToArchive(*archiveEntry.astree(), tmp, data); + } + else { + const ArchiveFileEntry& archiveFileEntry = dynamic_cast(*entry); + data[archiveFileEntry.m_Index]->addOutputFileName(path + archiveFileEntry.name()); + } + } + } + + /** + * + */ + void mapToArchive(Archive* archive) const override { + FileData* const* data; + size_t size; + archive->getFileList(data, size); + mapToArchive(*this, "", data); + } + +protected: + + /* + * Overriding this to create custom FileTreeEntry with index set to -1. No need to + * override makeFile() since we addFile is overriden. Note that this will not be + * used to create existing tree since we do this manually in doPopulate. + * + * @override + */ + virtual std::shared_ptr makeDirectory( + std::shared_ptr parent, QString name) const override { + return std::make_shared(parent, name, -1, std::vector{}); + } + + virtual void doPopulate(std::shared_ptr parent, std::vector>& entries) const override { + + // Sort by name: + std::sort(std::begin(m_Files), std::end(m_Files), + [](const auto& a, const auto& b) { + return std::get<0>(a)[0].compare(std::get<0>(b)[0], Qt::CaseInsensitive) < 0; }); + + // We know that the files are sorted: + QString currentName = ""; + int currentIndex; + std::vector currentFiles; + for (auto& p : m_Files) { + + // At the start or if we have reset, just retrieve the current name and index - The + // index might not be valid in this case (e.g., if the path is a/b, the index is the + // one for a/b while we would want the one for a, but we correct that later): + if (currentName == "") { + currentName = std::get<0>(p)[0]; + currentIndex = std::get<2>(p); + } + + // If the name is different, we need to create a directory from what we have + // accumulated: + if (currentName != std::get<0>(p)[0]) { + // No index here since this is not an empty tree: + entries.push_back(std::make_shared(parent, currentName, currentIndex, std::move(currentFiles))); + currentFiles.clear(); // Back to a valid state. + + // Retrieve the next index: + currentIndex = std::get<2>(p); + } + + // We can always override the current name: + currentName = std::get<0>(p)[0]; + + // If the current path contains only one components: + if (std::get<0>(p).size() == 1) { + // If it is not a directory, then it is a file in directly under this tree: + if (!std::get<1>(p)) { + entries.push_back( + std::make_shared(parent, currentName, std::get<2>(p), QDateTime())); + currentName = ""; + } + // Otherwize, it is the actual "file" corresponding to the directory, so we can retrieve + // the index here: + currentIndex = std::get<2>(p); + } + else { + currentFiles.push_back({ + QStringList(std::get<0>(p).begin() + 1, std::get<0>(p).end()), std::get<1>(p), std::get<2>(p) + }); + } + } + + if (currentName != "") { + entries.push_back(std::make_shared(parent, currentName, currentIndex, std::move(currentFiles))); + } + } + +private: + + mutable std::vector m_Files; +}; + +std::shared_ptr ArchiveFileTree::makeTree(Archive* archive) { + + FileData* const* data; + size_t size; + archive->getFileList(data, size); + + std::vector files; + files.reserve(size); + + for (size_t i = 0; i < size; ++i) { + files.push_back(std::make_tuple(data[i]->getFileName().replace("\\", "/").split("/", Qt::SkipEmptyParts), data[i]->isDirectory(), (int) i)); + } + + auto tree = std::make_shared(nullptr, "", -1, std::move(files)); + return tree; +} + +/** + * @brief Recursive function for the ArchiveFileTree::mapToArchive method. Need a template + * here because iterators from a vector of entries are not exactly the same as the iterators + * returned by a IFileTree. + * + */ +template +void mapToArchive(FileData* const* data, It begin, It end) { + for (auto it = begin; it != end; ++it) { + auto entry = *it; + auto* aentry = dynamic_cast(entry.get()); + + if (aentry->m_Index != -1) { + data[aentry->m_Index]->addOutputFileName(aentry->path()); + } + + if (entry->isDir()) { + auto tree = entry->astree(); + mapToArchive(data, tree->begin(), tree->end()); + } + } +} + +void ArchiveFileTree::mapToArchive(Archive* archive, std::vector> const& entries) { + FileData* const* data; + size_t size; + archive->getFileList(data, size); + + ::mapToArchive(data, entries.cbegin(), entries.cend()); +} -- cgit v1.3.1 From f405a51bf63373403cb0682dff243a90219af2bb Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Thu, 7 May 2020 16:22:07 +0200 Subject: Override astree() in ArchiveFileTreeImpl to avoid VS warnings. --- src/archivefiletree.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'src/archivefiletree.cpp') diff --git a/src/archivefiletree.cpp b/src/archivefiletree.cpp index b629cc8e..ffee7612 100644 --- a/src/archivefiletree.cpp +++ b/src/archivefiletree.cpp @@ -73,6 +73,16 @@ public: // Public for make_shared (but not accessible by other since not exposed ArchiveFileTreeImpl(std::shared_ptr parent, QString name, int index, std::vector&& files) : FileTreeEntry(parent, name), ArchiveFileEntry(parent, name, index), IFileTree(), m_Files(std::move(files)) { } +public: // Override to avoid VS warnings: + + virtual std::shared_ptr astree() override { + return IFileTree::astree(); + } + + virtual std::shared_ptr astree() const override { + return IFileTree::astree(); + } + public: // Overrides: /** -- cgit v1.3.1 From 411b73e0aa68899d04b309b443aebf8de4b1a057 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Thu, 7 May 2020 19:18:58 +0200 Subject: Fix issue with some archives not being extracted correctly. --- src/archivefiletree.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src/archivefiletree.cpp') diff --git a/src/archivefiletree.cpp b/src/archivefiletree.cpp index ffee7612..77b17112 100644 --- a/src/archivefiletree.cpp +++ b/src/archivefiletree.cpp @@ -158,7 +158,7 @@ protected: // We know that the files are sorted: QString currentName = ""; - int currentIndex; + int currentIndex = -1; std::vector currentFiles; for (auto& p : m_Files) { @@ -167,7 +167,6 @@ protected: // one for a/b while we would want the one for a, but we correct that later): if (currentName == "") { currentName = std::get<0>(p)[0]; - currentIndex = std::get<2>(p); } // If the name is different, we need to create a directory from what we have @@ -178,7 +177,7 @@ protected: currentFiles.clear(); // Back to a valid state. // Retrieve the next index: - currentIndex = std::get<2>(p); + currentIndex = -1; } // We can always override the current name: -- cgit v1.3.1 From b2bd13e3db1d1469c59527fb90e763a8e42dd1f8 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Thu, 7 May 2020 22:09:40 +0200 Subject: Fix another issue with some archives not being loading properly. --- src/archivefiletree.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'src/archivefiletree.cpp') diff --git a/src/archivefiletree.cpp b/src/archivefiletree.cpp index 77b17112..c95cc3cd 100644 --- a/src/archivefiletree.cpp +++ b/src/archivefiletree.cpp @@ -191,9 +191,11 @@ protected: std::make_shared(parent, currentName, std::get<2>(p), QDateTime())); currentName = ""; } - // Otherwize, it is the actual "file" corresponding to the directory, so we can retrieve - // the index here: - currentIndex = std::get<2>(p); + else { + // Otherwize, it is the actual "file" corresponding to the directory, so we can retrieve + // the index here: + currentIndex = std::get<2>(p); + } } else { currentFiles.push_back({ -- cgit v1.3.1 From 572fa7050f3dddf7a1190e07a2692a5ae9d07766 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Fri, 8 May 2020 21:43:27 +0200 Subject: Minor comment updates. --- src/archivefiletree.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'src/archivefiletree.cpp') diff --git a/src/archivefiletree.cpp b/src/archivefiletree.cpp index c95cc3cd..96b7e42b 100644 --- a/src/archivefiletree.cpp +++ b/src/archivefiletree.cpp @@ -162,9 +162,7 @@ protected: std::vector currentFiles; for (auto& p : m_Files) { - // At the start or if we have reset, just retrieve the current name and index - The - // index might not be valid in this case (e.g., if the path is a/b, the index is the - // one for a/b while we would want the one for a, but we correct that later): + // At the start or if we have reset, just retrieve the current name: if (currentName == "") { currentName = std::get<0>(p)[0]; } @@ -172,11 +170,14 @@ protected: // If the name is different, we need to create a directory from what we have // accumulated: if (currentName != std::get<0>(p)[0]) { - // No index here since this is not an empty tree: + + // 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(parent, currentName, currentIndex, std::move(currentFiles))); + currentFiles.clear(); // Back to a valid state. - // Retrieve the next index: + // Reset the index: currentIndex = -1; } @@ -185,6 +186,7 @@ protected: // If the current path contains only one components: if (std::get<0>(p).size() == 1) { + // If it is not a directory, then it is a file in directly under this tree: if (!std::get<1>(p)) { entries.push_back( @@ -192,7 +194,7 @@ protected: currentName = ""; } else { - // Otherwize, it is the actual "file" corresponding to the directory, so we can retrieve + // Otherwize, it is the actual "file" corresponding to the directory we are listing, so we can retrieve // the index here: currentIndex = std::get<2>(p); } -- cgit v1.3.1 From 0a3dade905e4112b8ba2eb29b3781ee03b243ea6 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Tue, 12 May 2020 19:32:31 +0200 Subject: Update after const_cast removal in uibase. --- src/archivefiletree.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src/archivefiletree.cpp') diff --git a/src/archivefiletree.cpp b/src/archivefiletree.cpp index 96b7e42b..6bd2b820 100644 --- a/src/archivefiletree.cpp +++ b/src/archivefiletree.cpp @@ -40,7 +40,7 @@ public: * @param index The index of the file in the archive. * @param time The modification time of this file. */ - ArchiveFileEntry(std::shared_ptr parent, QString name, int index, QDateTime time) : + ArchiveFileEntry(std::shared_ptr parent, QString name, int index, QDateTime time) : FileTreeEntry(parent, name, time), m_Index(index) { } @@ -51,7 +51,7 @@ public: * @param name The name of this directory. * @param index The index of the directory in the archive, or -1. */ - ArchiveFileEntry(std::shared_ptr parent, QString name, int index) : + ArchiveFileEntry(std::shared_ptr parent, QString name, int index) : FileTreeEntry(parent, name), m_Index(index) { } @@ -70,7 +70,7 @@ public: public: // Public for make_shared (but not accessible by other since not exposed in .h): - ArchiveFileTreeImpl(std::shared_ptr parent, QString name, int index, std::vector&& files) + ArchiveFileTreeImpl(std::shared_ptr parent, QString name, int index, std::vector&& files) : FileTreeEntry(parent, name), ArchiveFileEntry(parent, name, index), IFileTree(), m_Files(std::move(files)) { } public: // Override to avoid VS warnings: @@ -145,11 +145,11 @@ protected: * @override */ virtual std::shared_ptr makeDirectory( - std::shared_ptr parent, QString name) const override { + std::shared_ptr parent, QString name) const override { return std::make_shared(parent, name, -1, std::vector{}); } - virtual void doPopulate(std::shared_ptr parent, std::vector>& entries) const override { + virtual void doPopulate(std::shared_ptr parent, std::vector>& entries) const override { // Sort by name: std::sort(std::begin(m_Files), std::end(m_Files), -- cgit v1.3.1 From 04698417a93a51acc80112feab170f3aff7572b7 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Sat, 16 May 2020 14:00:24 +0200 Subject: Add implementation of clone() and doClone() for the archive filetree. --- src/archivefiletree.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) (limited to 'src/archivefiletree.cpp') diff --git a/src/archivefiletree.cpp b/src/archivefiletree.cpp index 6bd2b820..0b715f4f 100644 --- a/src/archivefiletree.cpp +++ b/src/archivefiletree.cpp @@ -55,6 +55,10 @@ public: FileTreeEntry(parent, name), m_Index(index) { } + virtual std::shared_ptr clone() const override { + return std::make_shared(nullptr, name(), m_Index); + } + // No private since we are in an implementation file: const int m_Index; }; @@ -70,7 +74,7 @@ public: public: // Public for make_shared (but not accessible by other since not exposed in .h): - ArchiveFileTreeImpl(std::shared_ptr parent, QString name, int index, std::vector&& files) + ArchiveFileTreeImpl(std::shared_ptr parent, QString name, int index, std::vector files) : FileTreeEntry(parent, name), ArchiveFileEntry(parent, name, index), IFileTree(), m_Files(std::move(files)) { } public: // Override to avoid VS warnings: @@ -83,6 +87,12 @@ public: // Override to avoid VS warnings: return IFileTree::astree(); } +protected: + + virtual std::shared_ptr clone() const override { + return IFileTree::clone(); + } + public: // Overrides: /** @@ -211,6 +221,10 @@ protected: } } + virtual std::shared_ptr doClone() const override { + return std::make_shared(nullptr, name(), m_Index, m_Files); + } + private: mutable std::vector m_Files; -- cgit v1.3.1