From 2229dfd6e5675ae2fcf1b5324e5e526f139013fa Mon Sep 17 00:00:00 2001 From: SirLynix Date: Thu, 7 Jul 2022 08:50:03 +0200 Subject: [PATCH] Core/VirtualDirectory: Handle physical directories as virtual directories --- include/Nazara/Core/VirtualDirectory.hpp | 26 +++++---- include/Nazara/Core/VirtualDirectory.inl | 63 +++++++++++++++++----- tests/Engine/Core/VirtualDirectoryTest.cpp | 26 +++++++-- 3 files changed, 89 insertions(+), 26 deletions(-) diff --git a/include/Nazara/Core/VirtualDirectory.hpp b/include/Nazara/Core/VirtualDirectory.hpp index 57d46f6f7..7c1a76944 100644 --- a/include/Nazara/Core/VirtualDirectory.hpp +++ b/include/Nazara/Core/VirtualDirectory.hpp @@ -30,8 +30,9 @@ namespace Nz struct FileContentEntry; struct PhysicalDirectoryEntry; struct PhysicalFileEntry; + struct VirtualDirectoryEntry; - using Entry = std::variant; + using Entry = std::variant; inline VirtualDirectory(std::weak_ptr parentDirectory = {}); inline VirtualDirectory(std::filesystem::path physicalPath, std::weak_ptr parentDirectory = {}); @@ -45,12 +46,13 @@ namespace Nz template void Foreach(F&& callback, bool includeDots = false); + template bool GetDirectoryEntry(std::string_view path, F&& callback); template bool GetEntry(std::string_view path, F&& callback); template bool GetFileContent(std::string_view path, F&& callback); inline bool IsUprootAllowed() const; - inline DirectoryEntry& StoreDirectory(std::string_view path, VirtualDirectoryPtr directory); + inline VirtualDirectoryEntry& StoreDirectory(std::string_view path, VirtualDirectoryPtr directory); inline PhysicalDirectoryEntry& StoreDirectory(std::string_view path, std::filesystem::path directoryPath); inline FileContentEntry& StoreFile(std::string_view path, std::vector file); inline PhysicalFileEntry& StoreFile(std::string_view path, std::filesystem::path filePath); @@ -59,32 +61,38 @@ namespace Nz VirtualDirectory& operator=(const VirtualDirectory&) = delete; VirtualDirectory& operator=(VirtualDirectory&&) = delete; + // File entries struct DataPointerEntry { const void* data; std::size_t size; }; - struct DirectoryEntry - { - VirtualDirectoryPtr directory; - }; - struct FileContentEntry { std::vector data; }; - struct PhysicalDirectoryEntry + struct PhysicalFileEntry { std::filesystem::path filePath; }; - struct PhysicalFileEntry + // Directory entries + struct DirectoryEntry + { + VirtualDirectoryPtr directory; + }; + + struct PhysicalDirectoryEntry : DirectoryEntry { std::filesystem::path filePath; }; + struct VirtualDirectoryEntry : DirectoryEntry + { + }; + private: template bool GetEntryInternal(std::string_view name, F&& callback); inline bool CreateOrRetrieveDirectory(std::string_view path, std::shared_ptr& directory, std::string_view& entryName); diff --git a/include/Nazara/Core/VirtualDirectory.inl b/include/Nazara/Core/VirtualDirectory.inl index 23e29fb83..c835efa3b 100644 --- a/include/Nazara/Core/VirtualDirectory.inl +++ b/include/Nazara/Core/VirtualDirectory.inl @@ -39,11 +39,11 @@ namespace Nz { if (includeDots) { - Entry ourselves = DirectoryEntry{ shared_from_this() }; + Entry ourselves = VirtualDirectoryEntry{ shared_from_this() }; callback(std::string_view("."), ourselves); if (VirtualDirectoryPtr parent = m_parent.lock()) { - Entry parentEntry = DirectoryEntry{ parent }; + Entry parentEntry = VirtualDirectoryEntry{ parent }; if (!CallbackReturn(callback, std::string_view(".."), parentEntry)) return; } @@ -77,7 +77,10 @@ namespace Nz if (std::filesystem::is_regular_file(status)) entry = PhysicalFileEntry{ physicalEntry.path() }; else if (std::filesystem::is_directory(status)) - entry = PhysicalDirectoryEntry{ physicalEntry.path() }; + { + VirtualDirectoryPtr virtualDir = std::make_shared(physicalEntry.path(), weak_from_this()); + entry = PhysicalDirectoryEntry{ std::move(virtualDir), physicalEntry.path() }; + } else continue; @@ -87,6 +90,30 @@ namespace Nz } } + template + bool VirtualDirectory::GetDirectoryEntry(std::string_view path, F&& callback) + { + return GetEntry(path, [&](const Entry& entry) + { + return std::visit([&](auto&& entry) + { + using T = std::decay_t; + + if constexpr (std::is_same_v || std::is_same_v) + { + return CallbackReturn(callback, static_cast(entry)); + } + else if constexpr (std::is_same_v || std::is_same_v || std::is_same_v) + { + NazaraError("entry is a file"); + return false; + } + else + static_assert(AlwaysFalse(), "incomplete visitor"); + }, entry); + }); + } + template bool VirtualDirectory::GetEntry(std::string_view path, F&& callback) { assert(!path.empty()); @@ -117,7 +144,7 @@ namespace Nz return currentDir->GetEntryInternal(dirName, [&](const Entry& entry) { - if (auto dirEntry = std::get_if(&entry)) + if (auto dirEntry = std::get_if(&entry)) { currentDir = dirEntry->directory; return true; @@ -150,7 +177,10 @@ namespace Nz if (std::filesystem::is_regular_file(status)) entry = PhysicalFileEntry{ std::move(filePath) }; else if (std::filesystem::is_directory(status)) - entry = PhysicalDirectoryEntry{ std::move(filePath) }; + { + VirtualDirectoryPtr virtualDir = std::make_shared(filePath, weak_from_this()); + entry = PhysicalDirectoryEntry{ std::move(virtualDir), std::move(filePath) }; + } else return false; //< either not known or of a special type @@ -189,7 +219,7 @@ namespace Nz return CallbackReturn(callback, static_cast(source->data()), SafeCast(source->size())); } - else if constexpr (std::is_same_v || std::is_same_v) + else if constexpr (std::is_same_v || std::is_same_v) { NazaraError("entry is a directory"); return false; @@ -205,7 +235,7 @@ namespace Nz return m_isUprootAllowed; } - inline auto VirtualDirectory::StoreDirectory(std::string_view path, VirtualDirectoryPtr directory) -> DirectoryEntry& + inline auto VirtualDirectory::StoreDirectory(std::string_view path, VirtualDirectoryPtr directory) -> VirtualDirectoryEntry& { assert(!path.empty()); @@ -217,7 +247,7 @@ namespace Nz if (entryName == "." || entryName == "..") throw std::runtime_error("invalid entry name"); - return dir->StoreInternal(std::string(entryName), DirectoryEntry{ std::move(directory) }); + return dir->StoreInternal(std::string(entryName), VirtualDirectoryEntry{ std::move(directory) }); } inline auto VirtualDirectory::StoreDirectory(std::string_view path, std::filesystem::path directoryPath) -> PhysicalDirectoryEntry& @@ -232,7 +262,11 @@ namespace Nz if (entryName == "." || entryName == "..") throw std::runtime_error("invalid entry name"); - return dir->StoreInternal(std::string(entryName), PhysicalDirectoryEntry{ std::move(directoryPath) }); + PhysicalDirectoryEntry entry; + entry.directory = std::make_shared(directoryPath, dir); + entry.filePath = std::move(directoryPath); + + return dir->StoreInternal(std::string(entryName), std::move(entry)); } inline auto VirtualDirectory::StoreFile(std::string_view path, std::vector file) -> FileContentEntry& @@ -284,7 +318,7 @@ namespace Nz { if (name == ".") { - Entry entry{ DirectoryEntry{ shared_from_this() } }; + Entry entry{ VirtualDirectoryEntry{ shared_from_this() } }; return CallbackReturn(callback, entry); } @@ -298,7 +332,7 @@ namespace Nz if (parentEntry) { - Entry entry = DirectoryEntry{ std::move(parentEntry) }; + Entry entry = VirtualDirectoryEntry{ std::move(parentEntry) }; return CallbackReturn(callback, entry); } } @@ -319,7 +353,10 @@ namespace Nz if (std::filesystem::is_regular_file(status)) entry = PhysicalFileEntry{ std::move(filePath) }; else if (std::filesystem::is_directory(status)) - entry = PhysicalDirectoryEntry{ std::move(filePath) }; + { + VirtualDirectoryPtr virtualDir = std::make_shared(filePath, weak_from_this()); + entry = PhysicalDirectoryEntry{ std::move(virtualDir), std::move(filePath) }; + } else return false; //< either not known or of a special type @@ -343,7 +380,7 @@ namespace Nz bool dirFound = directory->GetEntryInternal(dirName, [&](const Entry& entry) { - if (auto dirEntry = std::get_if(&entry)) + if (auto dirEntry = std::get_if(&entry)) directory = dirEntry->directory; else allowCreation = false; //< does exist but is not a directory diff --git a/tests/Engine/Core/VirtualDirectoryTest.cpp b/tests/Engine/Core/VirtualDirectoryTest.cpp index f46d239d2..4bfd25774 100644 --- a/tests/Engine/Core/VirtualDirectoryTest.cpp +++ b/tests/Engine/Core/VirtualDirectoryTest.cpp @@ -64,12 +64,12 @@ TEST_CASE("VirtualDirectory", "[Core][VirtualDirectory]") CHECK(virtualDir->GetEntry("Foo", [](const Nz::VirtualDirectory::Entry& entry) { - return std::holds_alternative(entry); + return std::holds_alternative(entry); })); CHECK(virtualDir->GetEntry("Foo/Bar", [](const Nz::VirtualDirectory::Entry& entry) { - return std::holds_alternative(entry); + return std::holds_alternative(entry); })); CHECK_FALSE(virtualDir->GetEntry("Foo/Bar/File.bin", [](const Nz::VirtualDirectory::Entry& /*entry*/) @@ -257,8 +257,8 @@ TEST_CASE("VirtualDirectory", "[Core][VirtualDirectory]") auto CheckOurselves = [&](const auto& entry) { - REQUIRE(std::holds_alternative(entry)); - const auto& dirEntry = std::get(entry); + REQUIRE(std::holds_alternative(entry)); + const auto& dirEntry = std::get(entry); return dirEntry.directory == resourceDir; }; @@ -279,6 +279,24 @@ TEST_CASE("VirtualDirectory", "[Core][VirtualDirectory]") CHECK(CheckFileContentHash(resourceDir, "Logo.png", "49C486F44E43F023D54C9F375D902C21375DDB2748D3FA1863C9581D30E17F94")); } + WHEN("Accessing physical folder as a virtual folder") + { + CHECK(resourceDir->GetDirectoryEntry("Utility", [&](const Nz::VirtualDirectory::DirectoryEntry& directoryEntry) + { + bool found = false; + directoryEntry.directory->Foreach([&](std::string_view entryName, const Nz::VirtualDirectory::Entry& entry) + { + if (entryName == "GIF") + { + CHECK(std::holds_alternative(entry)); + found = true; + } + }); + + return found; + })); + } + WHEN("Testing uproot escape") { std::shared_ptr engineDir = std::make_shared(GetAssetDir() / "Audio");