Core/VirtualDirectory: Handle physical directories as virtual directories

This commit is contained in:
SirLynix 2022-07-07 08:50:03 +02:00
parent e7f4714747
commit 2229dfd6e5
3 changed files with 89 additions and 26 deletions

View File

@ -30,8 +30,9 @@ namespace Nz
struct FileContentEntry; struct FileContentEntry;
struct PhysicalDirectoryEntry; struct PhysicalDirectoryEntry;
struct PhysicalFileEntry; struct PhysicalFileEntry;
struct VirtualDirectoryEntry;
using Entry = std::variant<DataPointerEntry, DirectoryEntry, FileContentEntry, PhysicalDirectoryEntry, PhysicalFileEntry>; using Entry = std::variant<DataPointerEntry, FileContentEntry, PhysicalDirectoryEntry, PhysicalFileEntry, VirtualDirectoryEntry>;
inline VirtualDirectory(std::weak_ptr<VirtualDirectory> parentDirectory = {}); inline VirtualDirectory(std::weak_ptr<VirtualDirectory> parentDirectory = {});
inline VirtualDirectory(std::filesystem::path physicalPath, std::weak_ptr<VirtualDirectory> parentDirectory = {}); inline VirtualDirectory(std::filesystem::path physicalPath, std::weak_ptr<VirtualDirectory> parentDirectory = {});
@ -45,12 +46,13 @@ namespace Nz
template<typename F> void Foreach(F&& callback, bool includeDots = false); template<typename F> void Foreach(F&& callback, bool includeDots = false);
template<typename F> bool GetDirectoryEntry(std::string_view path, F&& callback);
template<typename F> bool GetEntry(std::string_view path, F&& callback); template<typename F> bool GetEntry(std::string_view path, F&& callback);
template<typename F> bool GetFileContent(std::string_view path, F&& callback); template<typename F> bool GetFileContent(std::string_view path, F&& callback);
inline bool IsUprootAllowed() const; 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 PhysicalDirectoryEntry& StoreDirectory(std::string_view path, std::filesystem::path directoryPath);
inline FileContentEntry& StoreFile(std::string_view path, std::vector<UInt8> file); inline FileContentEntry& StoreFile(std::string_view path, std::vector<UInt8> file);
inline PhysicalFileEntry& StoreFile(std::string_view path, std::filesystem::path filePath); inline PhysicalFileEntry& StoreFile(std::string_view path, std::filesystem::path filePath);
@ -59,32 +61,38 @@ namespace Nz
VirtualDirectory& operator=(const VirtualDirectory&) = delete; VirtualDirectory& operator=(const VirtualDirectory&) = delete;
VirtualDirectory& operator=(VirtualDirectory&&) = delete; VirtualDirectory& operator=(VirtualDirectory&&) = delete;
// File entries
struct DataPointerEntry struct DataPointerEntry
{ {
const void* data; const void* data;
std::size_t size; std::size_t size;
}; };
struct DirectoryEntry
{
VirtualDirectoryPtr directory;
};
struct FileContentEntry struct FileContentEntry
{ {
std::vector<UInt8> data; std::vector<UInt8> data;
}; };
struct PhysicalDirectoryEntry struct PhysicalFileEntry
{ {
std::filesystem::path filePath; std::filesystem::path filePath;
}; };
struct PhysicalFileEntry // Directory entries
struct DirectoryEntry
{
VirtualDirectoryPtr directory;
};
struct PhysicalDirectoryEntry : DirectoryEntry
{ {
std::filesystem::path filePath; std::filesystem::path filePath;
}; };
struct VirtualDirectoryEntry : DirectoryEntry
{
};
private: private:
template<typename F> bool GetEntryInternal(std::string_view name, F&& callback); template<typename F> bool GetEntryInternal(std::string_view name, F&& callback);
inline bool CreateOrRetrieveDirectory(std::string_view path, std::shared_ptr<VirtualDirectory>& directory, std::string_view& entryName); inline bool CreateOrRetrieveDirectory(std::string_view path, std::shared_ptr<VirtualDirectory>& directory, std::string_view& entryName);

View File

@ -39,11 +39,11 @@ namespace Nz
{ {
if (includeDots) if (includeDots)
{ {
Entry ourselves = DirectoryEntry{ shared_from_this() }; Entry ourselves = VirtualDirectoryEntry{ shared_from_this() };
callback(std::string_view("."), ourselves); callback(std::string_view("."), ourselves);
if (VirtualDirectoryPtr parent = m_parent.lock()) if (VirtualDirectoryPtr parent = m_parent.lock())
{ {
Entry parentEntry = DirectoryEntry{ parent }; Entry parentEntry = VirtualDirectoryEntry{ parent };
if (!CallbackReturn(callback, std::string_view(".."), parentEntry)) if (!CallbackReturn(callback, std::string_view(".."), parentEntry))
return; return;
} }
@ -77,7 +77,10 @@ namespace Nz
if (std::filesystem::is_regular_file(status)) if (std::filesystem::is_regular_file(status))
entry = PhysicalFileEntry{ physicalEntry.path() }; entry = PhysicalFileEntry{ physicalEntry.path() };
else if (std::filesystem::is_directory(status)) else if (std::filesystem::is_directory(status))
entry = PhysicalDirectoryEntry{ physicalEntry.path() }; {
VirtualDirectoryPtr virtualDir = std::make_shared<VirtualDirectory>(physicalEntry.path(), weak_from_this());
entry = PhysicalDirectoryEntry{ std::move(virtualDir), physicalEntry.path() };
}
else else
continue; continue;
@ -87,6 +90,30 @@ namespace Nz
} }
} }
template<typename F>
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<decltype(entry)>;
if constexpr (std::is_same_v<T, VirtualDirectoryEntry> || std::is_same_v<T, PhysicalDirectoryEntry>)
{
return CallbackReturn(callback, static_cast<const DirectoryEntry&>(entry));
}
else if constexpr (std::is_same_v<T, DataPointerEntry> || std::is_same_v<T, FileContentEntry> || std::is_same_v<T, PhysicalFileEntry>)
{
NazaraError("entry is a file");
return false;
}
else
static_assert(AlwaysFalse<T>(), "incomplete visitor");
}, entry);
});
}
template<typename F> bool VirtualDirectory::GetEntry(std::string_view path, F&& callback) template<typename F> bool VirtualDirectory::GetEntry(std::string_view path, F&& callback)
{ {
assert(!path.empty()); assert(!path.empty());
@ -117,7 +144,7 @@ namespace Nz
return currentDir->GetEntryInternal(dirName, [&](const Entry& entry) return currentDir->GetEntryInternal(dirName, [&](const Entry& entry)
{ {
if (auto dirEntry = std::get_if<DirectoryEntry>(&entry)) if (auto dirEntry = std::get_if<VirtualDirectoryEntry>(&entry))
{ {
currentDir = dirEntry->directory; currentDir = dirEntry->directory;
return true; return true;
@ -150,7 +177,10 @@ namespace Nz
if (std::filesystem::is_regular_file(status)) if (std::filesystem::is_regular_file(status))
entry = PhysicalFileEntry{ std::move(filePath) }; entry = PhysicalFileEntry{ std::move(filePath) };
else if (std::filesystem::is_directory(status)) else if (std::filesystem::is_directory(status))
entry = PhysicalDirectoryEntry{ std::move(filePath) }; {
VirtualDirectoryPtr virtualDir = std::make_shared<VirtualDirectory>(filePath, weak_from_this());
entry = PhysicalDirectoryEntry{ std::move(virtualDir), std::move(filePath) };
}
else else
return false; //< either not known or of a special type return false; //< either not known or of a special type
@ -189,7 +219,7 @@ namespace Nz
return CallbackReturn(callback, static_cast<P1>(source->data()), SafeCast<P2>(source->size())); return CallbackReturn(callback, static_cast<P1>(source->data()), SafeCast<P2>(source->size()));
} }
else if constexpr (std::is_same_v<T, DirectoryEntry> || std::is_same_v<T, PhysicalDirectoryEntry>) else if constexpr (std::is_same_v<T, VirtualDirectoryEntry> || std::is_same_v<T, PhysicalDirectoryEntry>)
{ {
NazaraError("entry is a directory"); NazaraError("entry is a directory");
return false; return false;
@ -205,7 +235,7 @@ namespace Nz
return m_isUprootAllowed; 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()); assert(!path.empty());
@ -217,7 +247,7 @@ namespace Nz
if (entryName == "." || entryName == "..") if (entryName == "." || entryName == "..")
throw std::runtime_error("invalid entry name"); 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& inline auto VirtualDirectory::StoreDirectory(std::string_view path, std::filesystem::path directoryPath) -> PhysicalDirectoryEntry&
@ -232,7 +262,11 @@ namespace Nz
if (entryName == "." || entryName == "..") if (entryName == "." || entryName == "..")
throw std::runtime_error("invalid entry name"); throw std::runtime_error("invalid entry name");
return dir->StoreInternal(std::string(entryName), PhysicalDirectoryEntry{ std::move(directoryPath) }); PhysicalDirectoryEntry entry;
entry.directory = std::make_shared<VirtualDirectory>(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<UInt8> file) -> FileContentEntry& inline auto VirtualDirectory::StoreFile(std::string_view path, std::vector<UInt8> file) -> FileContentEntry&
@ -284,7 +318,7 @@ namespace Nz
{ {
if (name == ".") if (name == ".")
{ {
Entry entry{ DirectoryEntry{ shared_from_this() } }; Entry entry{ VirtualDirectoryEntry{ shared_from_this() } };
return CallbackReturn(callback, entry); return CallbackReturn(callback, entry);
} }
@ -298,7 +332,7 @@ namespace Nz
if (parentEntry) if (parentEntry)
{ {
Entry entry = DirectoryEntry{ std::move(parentEntry) }; Entry entry = VirtualDirectoryEntry{ std::move(parentEntry) };
return CallbackReturn(callback, entry); return CallbackReturn(callback, entry);
} }
} }
@ -319,7 +353,10 @@ namespace Nz
if (std::filesystem::is_regular_file(status)) if (std::filesystem::is_regular_file(status))
entry = PhysicalFileEntry{ std::move(filePath) }; entry = PhysicalFileEntry{ std::move(filePath) };
else if (std::filesystem::is_directory(status)) else if (std::filesystem::is_directory(status))
entry = PhysicalDirectoryEntry{ std::move(filePath) }; {
VirtualDirectoryPtr virtualDir = std::make_shared<VirtualDirectory>(filePath, weak_from_this());
entry = PhysicalDirectoryEntry{ std::move(virtualDir), std::move(filePath) };
}
else else
return false; //< either not known or of a special type return false; //< either not known or of a special type
@ -343,7 +380,7 @@ namespace Nz
bool dirFound = directory->GetEntryInternal(dirName, [&](const Entry& entry) bool dirFound = directory->GetEntryInternal(dirName, [&](const Entry& entry)
{ {
if (auto dirEntry = std::get_if<DirectoryEntry>(&entry)) if (auto dirEntry = std::get_if<VirtualDirectoryEntry>(&entry))
directory = dirEntry->directory; directory = dirEntry->directory;
else else
allowCreation = false; //< does exist but is not a directory allowCreation = false; //< does exist but is not a directory

View File

@ -64,12 +64,12 @@ TEST_CASE("VirtualDirectory", "[Core][VirtualDirectory]")
CHECK(virtualDir->GetEntry("Foo", [](const Nz::VirtualDirectory::Entry& entry) CHECK(virtualDir->GetEntry("Foo", [](const Nz::VirtualDirectory::Entry& entry)
{ {
return std::holds_alternative<Nz::VirtualDirectory::DirectoryEntry>(entry); return std::holds_alternative<Nz::VirtualDirectory::VirtualDirectoryEntry>(entry);
})); }));
CHECK(virtualDir->GetEntry("Foo/Bar", [](const Nz::VirtualDirectory::Entry& entry) CHECK(virtualDir->GetEntry("Foo/Bar", [](const Nz::VirtualDirectory::Entry& entry)
{ {
return std::holds_alternative<Nz::VirtualDirectory::DirectoryEntry>(entry); return std::holds_alternative<Nz::VirtualDirectory::VirtualDirectoryEntry>(entry);
})); }));
CHECK_FALSE(virtualDir->GetEntry("Foo/Bar/File.bin", [](const Nz::VirtualDirectory::Entry& /*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) auto CheckOurselves = [&](const auto& entry)
{ {
REQUIRE(std::holds_alternative<Nz::VirtualDirectory::DirectoryEntry>(entry)); REQUIRE(std::holds_alternative<Nz::VirtualDirectory::VirtualDirectoryEntry>(entry));
const auto& dirEntry = std::get<Nz::VirtualDirectory::DirectoryEntry>(entry); const auto& dirEntry = std::get<Nz::VirtualDirectory::VirtualDirectoryEntry>(entry);
return dirEntry.directory == resourceDir; return dirEntry.directory == resourceDir;
}; };
@ -279,6 +279,24 @@ TEST_CASE("VirtualDirectory", "[Core][VirtualDirectory]")
CHECK(CheckFileContentHash(resourceDir, "Logo.png", "49C486F44E43F023D54C9F375D902C21375DDB2748D3FA1863C9581D30E17F94")); 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<Nz::VirtualDirectory::PhysicalDirectoryEntry>(entry));
found = true;
}
});
return found;
}));
}
WHEN("Testing uproot escape") WHEN("Testing uproot escape")
{ {
std::shared_ptr<Nz::VirtualDirectory> engineDir = std::make_shared<Nz::VirtualDirectory>(GetAssetDir() / "Audio"); std::shared_ptr<Nz::VirtualDirectory> engineDir = std::make_shared<Nz::VirtualDirectory>(GetAssetDir() / "Audio");