Core: Rework VirtualDirectory to allow custom directory resolving
This commit is contained in:
parent
0494a72849
commit
36dd245564
|
|
@ -40,10 +40,10 @@ namespace Nz
|
||||||
template<typename T> std::shared_ptr<T> Load(std::string_view assetPath);
|
template<typename T> std::shared_ptr<T> Load(std::string_view assetPath);
|
||||||
template<typename T> std::shared_ptr<T> Load(std::string_view assetPath, typename T::Params params);
|
template<typename T> std::shared_ptr<T> Load(std::string_view assetPath, typename T::Params params);
|
||||||
|
|
||||||
inline const VirtualDirectoryPtr& Mount(std::string_view name, std::filesystem::path filepath);
|
const VirtualDirectoryPtr& Mount(std::string_view name, std::filesystem::path filepath);
|
||||||
inline const VirtualDirectoryPtr& Mount(std::string_view name, VirtualDirectoryPtr directory);
|
const VirtualDirectoryPtr& Mount(std::string_view name, VirtualDirectoryPtr directory);
|
||||||
|
|
||||||
inline void MountDefaultDirectories();
|
void MountDefaultDirectories();
|
||||||
|
|
||||||
template<typename T> std::shared_ptr<T> Open(std::string_view assetPath);
|
template<typename T> std::shared_ptr<T> Open(std::string_view assetPath);
|
||||||
template<typename T> std::shared_ptr<T> Open(std::string_view assetPath, typename T::Params params);
|
template<typename T> std::shared_ptr<T> Open(std::string_view assetPath, typename T::Params params);
|
||||||
|
|
|
||||||
|
|
@ -53,30 +53,6 @@ namespace Nz
|
||||||
return LoadImpl<T>(assetPath, params);
|
return LoadImpl<T>(assetPath, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline const VirtualDirectoryPtr& AppFilesystemComponent::Mount(std::string_view name, std::filesystem::path filepath)
|
|
||||||
{
|
|
||||||
return Mount(name, std::make_shared<VirtualDirectory>(std::move(filepath)));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline const VirtualDirectoryPtr& AppFilesystemComponent::Mount(std::string_view name, VirtualDirectoryPtr directory)
|
|
||||||
{
|
|
||||||
if (name.empty())
|
|
||||||
{
|
|
||||||
m_rootDirectory = std::move(directory);
|
|
||||||
return m_rootDirectory;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!m_rootDirectory)
|
|
||||||
m_rootDirectory = std::make_shared<VirtualDirectory>();
|
|
||||||
|
|
||||||
return m_rootDirectory->StoreDirectory(name, std::move(directory)).directory;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void AppFilesystemComponent::MountDefaultDirectories()
|
|
||||||
{
|
|
||||||
m_rootDirectory = std::make_shared<VirtualDirectory>(std::filesystem::current_path());
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
std::shared_ptr<T> AppFilesystemComponent::Open(std::string_view assetPath)
|
std::shared_ptr<T> AppFilesystemComponent::Open(std::string_view assetPath)
|
||||||
{
|
{
|
||||||
|
|
@ -154,19 +130,9 @@ namespace Nz
|
||||||
NazaraError(std::string(assetPath) + " is a directory");
|
NazaraError(std::string(assetPath) + " is a directory");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else if constexpr (std::is_same_v<Param, VirtualDirectory::DataPointerEntry>)
|
else if constexpr (std::is_same_v<Param, VirtualDirectory::FileEntry>)
|
||||||
{
|
{
|
||||||
resource = T::LoadFromMemory(arg.data, arg.size, params);
|
resource = T::LoadFromStream(*arg.stream, params);
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if constexpr (std::is_same_v<Param, VirtualDirectory::FileContentEntry>)
|
|
||||||
{
|
|
||||||
resource = T::LoadFromMemory(&arg.data[0], arg.data.size(), params);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if constexpr (std::is_same_v<Param, VirtualDirectory::PhysicalFileEntry>)
|
|
||||||
{
|
|
||||||
resource = T::LoadFromFile(arg.filePath, params);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -195,19 +161,9 @@ namespace Nz
|
||||||
NazaraError(std::string(assetPath) + " is a directory");
|
NazaraError(std::string(assetPath) + " is a directory");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else if constexpr (std::is_same_v<Param, VirtualDirectory::DataPointerEntry>)
|
else if constexpr (std::is_same_v<Param, VirtualDirectory::FileEntry>)
|
||||||
{
|
{
|
||||||
resource = T::OpenFromMemory(arg.data, arg.size, params);
|
resource = T::OpenFromStream(*arg.stream, params);
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if constexpr (std::is_same_v<Param, VirtualDirectory::FileContentEntry>)
|
|
||||||
{
|
|
||||||
resource = T::OpenFromMemory(&arg.data[0], arg.data.size(), params);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if constexpr (std::is_same_v<Param, VirtualDirectory::PhysicalFileEntry>)
|
|
||||||
{
|
|
||||||
resource = T::OpenFromFile(arg.filePath, params);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,8 @@
|
||||||
#define NAZARA_CORE_VIRTUALDIRECTORY_HPP
|
#define NAZARA_CORE_VIRTUALDIRECTORY_HPP
|
||||||
|
|
||||||
#include <Nazara/Prerequisites.hpp>
|
#include <Nazara/Prerequisites.hpp>
|
||||||
#include <filesystem>
|
#include <Nazara/Core/Stream.hpp>
|
||||||
#include <map>
|
#include <Nazara/Utils/FunctionRef.hpp>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
@ -19,23 +19,20 @@
|
||||||
namespace Nz
|
namespace Nz
|
||||||
{
|
{
|
||||||
class VirtualDirectory;
|
class VirtualDirectory;
|
||||||
|
class VirtualDirectoryResolver;
|
||||||
|
|
||||||
using VirtualDirectoryPtr = std::shared_ptr<VirtualDirectory>;
|
using VirtualDirectoryPtr = std::shared_ptr<VirtualDirectory>;
|
||||||
|
|
||||||
class VirtualDirectory : public std::enable_shared_from_this<VirtualDirectory>
|
class VirtualDirectory : public std::enable_shared_from_this<VirtualDirectory>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
struct DataPointerEntry;
|
|
||||||
struct DirectoryEntry;
|
struct DirectoryEntry;
|
||||||
struct FileContentEntry;
|
struct FileEntry;
|
||||||
struct PhysicalDirectoryEntry;
|
|
||||||
struct PhysicalFileEntry;
|
|
||||||
struct VirtualDirectoryEntry;
|
|
||||||
|
|
||||||
using Entry = std::variant<DataPointerEntry, FileContentEntry, PhysicalDirectoryEntry, PhysicalFileEntry, VirtualDirectoryEntry>;
|
using Entry = std::variant<DirectoryEntry, FileEntry>;
|
||||||
|
|
||||||
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::shared_ptr<VirtualDirectoryResolver> resolver, std::weak_ptr<VirtualDirectory> parentDirectory = {});
|
||||||
VirtualDirectory(const VirtualDirectory&) = delete;
|
VirtualDirectory(const VirtualDirectory&) = delete;
|
||||||
VirtualDirectory(VirtualDirectory&&) = delete;
|
VirtualDirectory(VirtualDirectory&&) = delete;
|
||||||
~VirtualDirectory() = default;
|
~VirtualDirectory() = default;
|
||||||
|
|
@ -49,33 +46,23 @@ namespace Nz
|
||||||
template<typename F> bool GetDirectoryEntry(std::string_view path, F&& callback);
|
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 const std::shared_ptr<VirtualDirectoryResolver>& GetResolver() const;
|
||||||
|
|
||||||
inline bool IsUprootAllowed() const;
|
inline bool IsUprootAllowed() const;
|
||||||
|
|
||||||
inline VirtualDirectoryEntry& StoreDirectory(std::string_view path, VirtualDirectoryPtr directory);
|
inline DirectoryEntry& StoreDirectory(std::string_view path, std::shared_ptr<VirtualDirectoryResolver> resolver);
|
||||||
inline PhysicalDirectoryEntry& StoreDirectory(std::string_view path, std::filesystem::path directoryPath);
|
inline DirectoryEntry& StoreDirectory(std::string_view path, VirtualDirectoryPtr directory);
|
||||||
inline FileContentEntry& StoreFile(std::string_view path, std::vector<UInt8> file);
|
inline FileEntry& StoreFile(std::string_view path, std::shared_ptr<Stream> stream);
|
||||||
inline PhysicalFileEntry& StoreFile(std::string_view path, std::filesystem::path filePath);
|
inline FileEntry& StoreFile(std::string_view path, ByteArray content);
|
||||||
inline DataPointerEntry& StoreFile(std::string_view path, const void* data, std::size_t size);
|
inline FileEntry& StoreFile(std::string_view path, const void* data, std::size_t size);
|
||||||
|
|
||||||
VirtualDirectory& operator=(const VirtualDirectory&) = delete;
|
VirtualDirectory& operator=(const VirtualDirectory&) = delete;
|
||||||
VirtualDirectory& operator=(VirtualDirectory&&) = delete;
|
VirtualDirectory& operator=(VirtualDirectory&&) = delete;
|
||||||
|
|
||||||
// File entries
|
// File entries
|
||||||
struct DataPointerEntry
|
struct FileEntry
|
||||||
{
|
{
|
||||||
const void* data;
|
std::shared_ptr<Stream> stream;
|
||||||
std::size_t size;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct FileContentEntry
|
|
||||||
{
|
|
||||||
std::vector<UInt8> data;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct PhysicalFileEntry
|
|
||||||
{
|
|
||||||
std::filesystem::path filePath;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Directory entries
|
// Directory entries
|
||||||
|
|
@ -84,17 +71,8 @@ namespace Nz
|
||||||
VirtualDirectoryPtr directory;
|
VirtualDirectoryPtr directory;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PhysicalDirectoryEntry : DirectoryEntry
|
|
||||||
{
|
|
||||||
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, bool allowResolve, 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);
|
||||||
|
|
||||||
template<typename T> T& StoreInternal(std::string name, T value);
|
template<typename T> T& StoreInternal(std::string name, T value);
|
||||||
|
|
@ -108,11 +86,28 @@ namespace Nz
|
||||||
Entry entry;
|
Entry entry;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::optional<std::filesystem::path> m_physicalPath;
|
std::shared_ptr<VirtualDirectoryResolver> m_resolver;
|
||||||
|
std::vector<std::string_view> m_cachedDirectoryParts;
|
||||||
std::vector<ContentEntry> m_content;
|
std::vector<ContentEntry> m_content;
|
||||||
std::weak_ptr<VirtualDirectory> m_parent;
|
std::weak_ptr<VirtualDirectory> m_parent;
|
||||||
bool m_isUprootAllowed;
|
bool m_isUprootAllowed;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class NAZARA_CORE_API VirtualDirectoryResolver
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
VirtualDirectoryResolver() = default;
|
||||||
|
VirtualDirectoryResolver(const VirtualDirectoryResolver&) = delete;
|
||||||
|
VirtualDirectoryResolver(VirtualDirectoryResolver&&) = delete;
|
||||||
|
virtual ~VirtualDirectoryResolver();
|
||||||
|
|
||||||
|
virtual void ForEach(std::weak_ptr<VirtualDirectory> parent, FunctionRef<bool (std::string_view name, VirtualDirectory::Entry&& entry)> callback) const = 0;
|
||||||
|
|
||||||
|
virtual std::optional<VirtualDirectory::Entry> Resolve(std::weak_ptr<VirtualDirectory> parent, const std::string_view* parts, std::size_t partCount) const = 0;
|
||||||
|
|
||||||
|
VirtualDirectoryResolver& operator=(const VirtualDirectoryResolver&) = delete;
|
||||||
|
VirtualDirectoryResolver& operator=(VirtualDirectoryResolver&&) = delete;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#include <Nazara/Core/VirtualDirectory.inl>
|
#include <Nazara/Core/VirtualDirectory.inl>
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,10 @@
|
||||||
|
|
||||||
#include <Nazara/Core/VirtualDirectory.hpp>
|
#include <Nazara/Core/VirtualDirectory.hpp>
|
||||||
#include <Nazara/Core/File.hpp>
|
#include <Nazara/Core/File.hpp>
|
||||||
|
#include <Nazara/Core/MemoryView.hpp>
|
||||||
|
#include <Nazara/Core/OwnedMemoryStream.hpp>
|
||||||
#include <Nazara/Core/StringExt.hpp>
|
#include <Nazara/Core/StringExt.hpp>
|
||||||
|
#include <Nazara/Utils/CallOnExit.hpp>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <Nazara/Core/Debug.hpp>
|
#include <Nazara/Core/Debug.hpp>
|
||||||
|
|
@ -17,8 +20,8 @@ namespace Nz
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
inline VirtualDirectory::VirtualDirectory(std::filesystem::path physicalPath, std::weak_ptr<VirtualDirectory> parentDirectory) :
|
inline VirtualDirectory::VirtualDirectory(std::shared_ptr<VirtualDirectoryResolver> resolver, std::weak_ptr<VirtualDirectory> parentDirectory) :
|
||||||
m_physicalPath(std::move(physicalPath)),
|
m_resolver(std::move(resolver)),
|
||||||
m_parent(std::move(parentDirectory)),
|
m_parent(std::move(parentDirectory)),
|
||||||
m_isUprootAllowed(false)
|
m_isUprootAllowed(false)
|
||||||
{
|
{
|
||||||
|
|
@ -39,11 +42,11 @@ namespace Nz
|
||||||
{
|
{
|
||||||
if (includeDots)
|
if (includeDots)
|
||||||
{
|
{
|
||||||
Entry ourselves = VirtualDirectoryEntry{ shared_from_this() };
|
Entry ourselves = DirectoryEntry{ 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 = VirtualDirectoryEntry{ { parent } };
|
Entry parentEntry = DirectoryEntry{ { parent } };
|
||||||
if (!CallbackReturn(callback, std::string_view(".."), parentEntry))
|
if (!CallbackReturn(callback, std::string_view(".."), parentEntry))
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -57,36 +60,20 @@ namespace Nz
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_physicalPath)
|
if (m_resolver)
|
||||||
{
|
{
|
||||||
for (auto&& physicalEntry : std::filesystem::directory_iterator(*m_physicalPath))
|
m_resolver->ForEach(weak_from_this(), [&](std::string_view filename, Entry&& entry)
|
||||||
{
|
{
|
||||||
std::string filename = physicalEntry.path().filename().generic_u8string();
|
// Check if this file/directory has been overridden by a virtual one
|
||||||
|
|
||||||
// Check if physical file/directory has been overridden by a virtual one
|
|
||||||
auto it = std::lower_bound(m_content.begin(), m_content.end(), filename, [](const ContentEntry& entry, std::string_view name)
|
auto it = std::lower_bound(m_content.begin(), m_content.end(), filename, [](const ContentEntry& entry, std::string_view name)
|
||||||
{
|
{
|
||||||
return entry.name < name;
|
return entry.name < name;
|
||||||
});
|
});
|
||||||
if (it != m_content.end() && it->name == filename)
|
if (it != m_content.end() && it->name == filename)
|
||||||
continue;
|
return true; //< this was already returned
|
||||||
|
|
||||||
std::filesystem::file_status status = physicalEntry.status();
|
return CallbackReturn(callback, filename, std::move(entry));
|
||||||
|
});
|
||||||
Entry entry;
|
|
||||||
if (std::filesystem::is_regular_file(status))
|
|
||||||
entry = PhysicalFileEntry{ physicalEntry.path() };
|
|
||||||
else if (std::filesystem::is_directory(status))
|
|
||||||
{
|
|
||||||
VirtualDirectoryPtr virtualDir = std::make_shared<VirtualDirectory>(physicalEntry.path(), weak_from_this());
|
|
||||||
entry = PhysicalDirectoryEntry{ { std::move(virtualDir) }, physicalEntry.path() };
|
|
||||||
}
|
|
||||||
else
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (!CallbackReturn(callback, std::string_view(filename), entry))
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -99,11 +86,11 @@ namespace Nz
|
||||||
{
|
{
|
||||||
using T = std::decay_t<decltype(entry)>;
|
using T = std::decay_t<decltype(entry)>;
|
||||||
|
|
||||||
if constexpr (std::is_same_v<T, VirtualDirectoryEntry> || std::is_same_v<T, PhysicalDirectoryEntry>)
|
if constexpr (std::is_same_v<T, DirectoryEntry>)
|
||||||
{
|
{
|
||||||
return CallbackReturn(callback, static_cast<const DirectoryEntry&>(entry));
|
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>)
|
else if constexpr (std::is_same_v<T, FileEntry>)
|
||||||
{
|
{
|
||||||
NazaraError("entry is a file");
|
NazaraError("entry is a file");
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -119,75 +106,70 @@ namespace Nz
|
||||||
assert(!path.empty());
|
assert(!path.empty());
|
||||||
|
|
||||||
VirtualDirectoryPtr currentDir = shared_from_this();
|
VirtualDirectoryPtr currentDir = shared_from_this();
|
||||||
std::optional<std::filesystem::path> physicalPathBase;
|
m_cachedDirectoryParts.clear();
|
||||||
std::vector<std::string> physicalDirectoryParts;
|
|
||||||
return SplitPath(path, [&](std::string_view dirName)
|
return SplitPath(path, [&](std::string_view dirName)
|
||||||
{
|
{
|
||||||
assert(!dirName.empty());
|
assert(!dirName.empty());
|
||||||
|
|
||||||
if (physicalPathBase)
|
// If we have directory parts, we're access this path across a resolver
|
||||||
|
if (!m_cachedDirectoryParts.empty())
|
||||||
{
|
{
|
||||||
// Special case when traversing directory
|
// Special case when traversing directory
|
||||||
if (dirName == ".." && !m_isUprootAllowed)
|
if (dirName == ".." && !currentDir->IsUprootAllowed())
|
||||||
{
|
{
|
||||||
// Don't allow to escape virtual directory
|
// Don't allow to escape virtual directory
|
||||||
if (!physicalDirectoryParts.empty())
|
if (!m_cachedDirectoryParts.empty())
|
||||||
physicalDirectoryParts.pop_back();
|
m_cachedDirectoryParts.pop_back();
|
||||||
else
|
|
||||||
physicalPathBase.reset();
|
|
||||||
}
|
}
|
||||||
else if (dirName != ".")
|
else if (dirName != ".")
|
||||||
physicalDirectoryParts.emplace_back(dirName);
|
m_cachedDirectoryParts.emplace_back(dirName);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return currentDir->GetEntryInternal(dirName, [&](const Entry& entry)
|
bool isFile = false;
|
||||||
|
bool foundDir = currentDir->GetEntryInternal(dirName, false, [&](const Entry& entry)
|
||||||
{
|
{
|
||||||
if (auto dirEntry = std::get_if<VirtualDirectoryEntry>(&entry))
|
if (auto dirEntry = std::get_if<DirectoryEntry>(&entry))
|
||||||
{
|
{
|
||||||
currentDir = dirEntry->directory;
|
currentDir = dirEntry->directory;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if (auto physDirEntry = std::get_if<PhysicalDirectoryEntry>(&entry))
|
|
||||||
{
|
|
||||||
assert(!physicalPathBase);
|
|
||||||
|
|
||||||
// We're traversing a physical directory
|
|
||||||
physicalPathBase = physDirEntry->filePath;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// not a directory
|
||||||
|
isFile = true;
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (foundDir)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (!isFile && m_resolver)
|
||||||
|
{
|
||||||
|
assert(m_cachedDirectoryParts.empty());
|
||||||
|
m_cachedDirectoryParts.push_back(dirName);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
},
|
},
|
||||||
[&](std::string_view name)
|
[&](std::string_view name)
|
||||||
{
|
{
|
||||||
if (physicalPathBase)
|
if (!m_cachedDirectoryParts.empty())
|
||||||
{
|
{
|
||||||
std::filesystem::path filePath = *physicalPathBase;
|
if (const auto& resolver = currentDir->GetResolver())
|
||||||
for (const auto& part : physicalDirectoryParts)
|
|
||||||
filePath /= part;
|
|
||||||
|
|
||||||
filePath /= name;
|
|
||||||
|
|
||||||
std::filesystem::file_status status = std::filesystem::status(filePath); //< FIXME: This will follow symlink, is this the intended behavior? (see symlink_status)
|
|
||||||
|
|
||||||
Entry entry;
|
|
||||||
if (std::filesystem::is_regular_file(status))
|
|
||||||
entry = PhysicalFileEntry{ std::move(filePath) };
|
|
||||||
else if (std::filesystem::is_directory(status))
|
|
||||||
{
|
{
|
||||||
VirtualDirectoryPtr virtualDir = std::make_shared<VirtualDirectory>(filePath, weak_from_this());
|
m_cachedDirectoryParts.push_back(name);
|
||||||
entry = PhysicalDirectoryEntry{ { std::move(virtualDir) }, std::move(filePath) };
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return false; //< either not known or of a special type
|
|
||||||
|
|
||||||
return CallbackReturn(callback, entry);
|
std::optional<Entry> entryOpt = m_resolver->Resolve({}, m_cachedDirectoryParts.data(), m_cachedDirectoryParts.size());
|
||||||
|
if (!entryOpt)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return CallbackReturn(callback, *std::move(entryOpt));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
return currentDir->GetEntryInternal(name, callback);
|
return currentDir->GetEntryInternal(name, true, callback);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -200,26 +182,65 @@ namespace Nz
|
||||||
{
|
{
|
||||||
using T = std::decay_t<decltype(entry)>;
|
using T = std::decay_t<decltype(entry)>;
|
||||||
|
|
||||||
using P1 = const void*;
|
if constexpr (std::is_same_v<T, FileEntry>)
|
||||||
using P2 = std::size_t;
|
{
|
||||||
|
Stream& stream = *entry.stream;
|
||||||
|
|
||||||
if constexpr (std::is_same_v<T, DataPointerEntry>)
|
if (stream.IsMemoryMapped())
|
||||||
{
|
{
|
||||||
return CallbackReturn(callback, static_cast<P1>(entry.data), SafeCast<P2>(entry.size));
|
const void* ptr = stream.GetMappedPointer();
|
||||||
}
|
UInt64 size = stream.GetSize();
|
||||||
else if constexpr (std::is_same_v<T, FileContentEntry>)
|
if (ptr && size > 0)
|
||||||
{
|
return CallbackReturn(callback, ptr, size);
|
||||||
return CallbackReturn(callback, static_cast<P1>(entry.data.data()), SafeCast<P2>(entry.data.size()));
|
}
|
||||||
}
|
|
||||||
else if constexpr (std::is_same_v<T, PhysicalFileEntry>)
|
|
||||||
{
|
|
||||||
std::optional<std::vector<UInt8>> source = File::ReadWhole(entry.filePath);
|
|
||||||
if (!source.has_value())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return CallbackReturn(callback, static_cast<P1>(source->data()), SafeCast<P2>(source->size()));
|
// Save and restore cursor position after the call
|
||||||
|
std::size_t cursorPos = stream.GetCursorPos();
|
||||||
|
CallOnExit restoreCursorPos([&] { stream.SetCursorPos(cursorPos); });
|
||||||
|
|
||||||
|
stream.SetCursorPos(0);
|
||||||
|
|
||||||
|
UInt64 fileSize = stream.GetSize();
|
||||||
|
|
||||||
|
std::vector<UInt8> data;
|
||||||
|
|
||||||
|
// Remember fileSize may be zero if file size isn't known in advance
|
||||||
|
if (fileSize > 0)
|
||||||
|
{
|
||||||
|
data.resize(fileSize);
|
||||||
|
if (stream.Read(&data[0], fileSize) != fileSize)
|
||||||
|
{
|
||||||
|
NazaraError("failed to read from stream");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
constexpr std::size_t blockSize = 4 * 1024;
|
||||||
|
|
||||||
|
while (!stream.EndOfStream())
|
||||||
|
{
|
||||||
|
std::size_t offset = data.size();
|
||||||
|
data.resize(offset + blockSize);
|
||||||
|
if (std::size_t readSize = stream.Read(&data[offset], blockSize); readSize != blockSize)
|
||||||
|
{
|
||||||
|
if (stream.EndOfStream())
|
||||||
|
{
|
||||||
|
data.resize(offset + readSize);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
NazaraError("failed to read from stream");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return CallbackReturn(callback, static_cast<const void*>(&data[0]), UInt64(data.size()));
|
||||||
}
|
}
|
||||||
else if constexpr (std::is_same_v<T, VirtualDirectoryEntry> || std::is_same_v<T, PhysicalDirectoryEntry>)
|
else if constexpr (std::is_same_v<T, DirectoryEntry>)
|
||||||
{
|
{
|
||||||
NazaraError("entry is a directory");
|
NazaraError("entry is a directory");
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -230,12 +251,17 @@ namespace Nz
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline const std::shared_ptr<VirtualDirectoryResolver>& VirtualDirectory::GetResolver() const
|
||||||
|
{
|
||||||
|
return m_resolver;
|
||||||
|
}
|
||||||
|
|
||||||
inline bool VirtualDirectory::IsUprootAllowed() const
|
inline bool VirtualDirectory::IsUprootAllowed() const
|
||||||
{
|
{
|
||||||
return m_isUprootAllowed;
|
return m_isUprootAllowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline auto VirtualDirectory::StoreDirectory(std::string_view path, VirtualDirectoryPtr directory) -> VirtualDirectoryEntry&
|
inline auto VirtualDirectory::StoreDirectory(std::string_view path, std::shared_ptr<VirtualDirectoryResolver> resolver) -> DirectoryEntry&
|
||||||
{
|
{
|
||||||
assert(!path.empty());
|
assert(!path.empty());
|
||||||
|
|
||||||
|
|
@ -247,29 +273,13 @@ 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), VirtualDirectoryEntry{ { std::move(directory) } });
|
DirectoryEntry entry;
|
||||||
}
|
entry.directory = std::make_shared<VirtualDirectory>(std::move(resolver), dir);
|
||||||
|
|
||||||
inline auto VirtualDirectory::StoreDirectory(std::string_view path, std::filesystem::path directoryPath) -> PhysicalDirectoryEntry&
|
|
||||||
{
|
|
||||||
assert(!path.empty());
|
|
||||||
|
|
||||||
std::shared_ptr<VirtualDirectory> dir;
|
|
||||||
std::string_view entryName;
|
|
||||||
if (!CreateOrRetrieveDirectory(path, dir, entryName))
|
|
||||||
throw std::runtime_error("invalid path");
|
|
||||||
|
|
||||||
if (entryName == "." || entryName == "..")
|
|
||||||
throw std::runtime_error("invalid entry name");
|
|
||||||
|
|
||||||
PhysicalDirectoryEntry entry;
|
|
||||||
entry.directory = std::make_shared<VirtualDirectory>(directoryPath, dir);
|
|
||||||
entry.filePath = std::move(directoryPath);
|
|
||||||
|
|
||||||
return dir->StoreInternal(std::string(entryName), std::move(entry));
|
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::StoreDirectory(std::string_view path, VirtualDirectoryPtr directory) -> DirectoryEntry&
|
||||||
{
|
{
|
||||||
assert(!path.empty());
|
assert(!path.empty());
|
||||||
|
|
||||||
|
|
@ -281,10 +291,10 @@ 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), FileContentEntry{ std::move(file) });
|
return dir->StoreInternal(std::string(entryName), DirectoryEntry{ { std::move(directory) } });
|
||||||
}
|
}
|
||||||
|
|
||||||
inline auto VirtualDirectory::StoreFile(std::string_view path, std::filesystem::path filePath) -> PhysicalFileEntry&
|
inline auto VirtualDirectory::StoreFile(std::string_view path, std::shared_ptr<Stream> stream) -> FileEntry&
|
||||||
{
|
{
|
||||||
assert(!path.empty());
|
assert(!path.empty());
|
||||||
|
|
||||||
|
|
@ -296,29 +306,24 @@ 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), PhysicalFileEntry{ std::move(filePath) });
|
return dir->StoreInternal(std::string(entryName), FileEntry{ std::move(stream) });
|
||||||
}
|
}
|
||||||
|
|
||||||
inline auto VirtualDirectory::StoreFile(std::string_view path, const void* data, std::size_t size) -> DataPointerEntry&
|
inline auto VirtualDirectory::StoreFile(std::string_view path, ByteArray content) -> FileEntry&
|
||||||
{
|
{
|
||||||
assert(!path.empty());
|
return StoreFile(path, std::make_shared<OwnedMemoryStream>(std::move(content)));
|
||||||
|
}
|
||||||
|
|
||||||
std::shared_ptr<VirtualDirectory> dir;
|
inline auto VirtualDirectory::StoreFile(std::string_view path, const void* data, std::size_t size) -> FileEntry&
|
||||||
std::string_view entryName;
|
{
|
||||||
if (!CreateOrRetrieveDirectory(path, dir, entryName))
|
return StoreFile(path, std::make_shared<MemoryView>(data, size));
|
||||||
throw std::runtime_error("invalid path");
|
|
||||||
|
|
||||||
if (entryName == "." || entryName == "..")
|
|
||||||
throw std::runtime_error("invalid entry name");
|
|
||||||
|
|
||||||
return dir->StoreInternal(std::string(entryName), DataPointerEntry{ data, size });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename F> bool VirtualDirectory::GetEntryInternal(std::string_view name, F&& callback)
|
template<typename F> bool VirtualDirectory::GetEntryInternal(std::string_view name, bool allowResolve, F&& callback)
|
||||||
{
|
{
|
||||||
if (name == ".")
|
if (name == ".")
|
||||||
{
|
{
|
||||||
Entry entry{ VirtualDirectoryEntry{ { shared_from_this() } } };
|
Entry entry{ DirectoryEntry{ { shared_from_this() } } };
|
||||||
return CallbackReturn(callback, entry);
|
return CallbackReturn(callback, entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -332,7 +337,7 @@ namespace Nz
|
||||||
|
|
||||||
if (parentEntry)
|
if (parentEntry)
|
||||||
{
|
{
|
||||||
Entry entry = VirtualDirectoryEntry{ { std::move(parentEntry) } };
|
Entry entry = DirectoryEntry{ { std::move(parentEntry) } };
|
||||||
return CallbackReturn(callback, entry);
|
return CallbackReturn(callback, entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -343,24 +348,14 @@ namespace Nz
|
||||||
});
|
});
|
||||||
if (it == m_content.end() || it->name != name)
|
if (it == m_content.end() || it->name != name)
|
||||||
{
|
{
|
||||||
// Virtual file not found, check if it has a physical one
|
// Virtual file not found, check if it the custom resolver knows about it
|
||||||
if (m_physicalPath)
|
if (m_resolver && allowResolve)
|
||||||
{
|
{
|
||||||
std::filesystem::path filePath = *m_physicalPath / name;
|
std::optional<Entry> entryOpt = m_resolver->Resolve(weak_from_this(), &name, 1);
|
||||||
std::filesystem::file_status status = std::filesystem::status(filePath); //< FIXME: This will follow symlink, is this the intended behavior? (see symlink_status)
|
if (!entryOpt)
|
||||||
|
return false;
|
||||||
|
|
||||||
Entry entry;
|
return CallbackReturn(callback, *std::move(entryOpt));
|
||||||
if (std::filesystem::is_regular_file(status))
|
|
||||||
entry = PhysicalFileEntry{ std::move(filePath) };
|
|
||||||
else if (std::filesystem::is_directory(status))
|
|
||||||
{
|
|
||||||
VirtualDirectoryPtr virtualDir = std::make_shared<VirtualDirectory>(filePath, weak_from_this());
|
|
||||||
entry = PhysicalDirectoryEntry{ { std::move(virtualDir) }, std::move(filePath) };
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return false; //< either not known or of a special type
|
|
||||||
|
|
||||||
return CallbackReturn(callback, entry);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -378,9 +373,9 @@ namespace Nz
|
||||||
{
|
{
|
||||||
assert(!dirName.empty());
|
assert(!dirName.empty());
|
||||||
|
|
||||||
bool dirFound = directory->GetEntryInternal(dirName, [&](const Entry& entry)
|
bool dirFound = directory->GetEntryInternal(dirName, true, [&](const Entry& entry)
|
||||||
{
|
{
|
||||||
if (auto dirEntry = std::get_if<VirtualDirectoryEntry>(&entry))
|
if (auto dirEntry = std::get_if<DirectoryEntry>(&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
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
// Copyright (C) 2023 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
|
||||||
|
// This file is part of the "Nazara Engine - Core module"
|
||||||
|
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef NAZARA_CORE_VIRTUALDIRECTORYFILESYSTEMRESOLVER_HPP
|
||||||
|
#define NAZARA_CORE_VIRTUALDIRECTORYFILESYSTEMRESOLVER_HPP
|
||||||
|
|
||||||
|
#include <Nazara/Prerequisites.hpp>
|
||||||
|
#include <Nazara/Core/Config.hpp>
|
||||||
|
#include <Nazara/Core/VirtualDirectory.hpp>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
namespace Nz
|
||||||
|
{
|
||||||
|
class NAZARA_CORE_API VirtualDirectoryFilesystemResolver : public VirtualDirectoryResolver
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
inline VirtualDirectoryFilesystemResolver(std::filesystem::path physicalPath, OpenModeFlags fileOpenMode = OpenMode::ReadOnly | OpenMode::Defer);
|
||||||
|
VirtualDirectoryFilesystemResolver(const VirtualDirectoryFilesystemResolver&) = delete;
|
||||||
|
VirtualDirectoryFilesystemResolver(VirtualDirectoryFilesystemResolver&&) = delete;
|
||||||
|
~VirtualDirectoryFilesystemResolver() = default;
|
||||||
|
|
||||||
|
void ForEach(std::weak_ptr<VirtualDirectory> parent, FunctionRef<bool(std::string_view name, VirtualDirectory::Entry&& entry)> callback) const override;
|
||||||
|
|
||||||
|
std::optional<VirtualDirectory::Entry> Resolve(std::weak_ptr<VirtualDirectory> parent, const std::string_view* parts, std::size_t partCount) const override;
|
||||||
|
|
||||||
|
VirtualDirectoryFilesystemResolver& operator=(const VirtualDirectoryFilesystemResolver&) = delete;
|
||||||
|
VirtualDirectoryFilesystemResolver& operator=(VirtualDirectoryFilesystemResolver&&) = delete;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::filesystem::path m_physicalPath;
|
||||||
|
OpenModeFlags m_fileOpenMode;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#include <Nazara/Core/VirtualDirectoryFilesystemResolver.inl>
|
||||||
|
|
||||||
|
#endif // NAZARA_CORE_VIRTUALDIRECTORYFILESYSTEMRESOLVER_HPP
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
// Copyright (C) 2023 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
|
||||||
|
// This file is part of the "Nazara Engine - Core module"
|
||||||
|
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||||
|
|
||||||
|
#include <Nazara/Core/VirtualDirectoryFilesystemResolver.hpp>
|
||||||
|
#include <Nazara/Core/Debug.hpp>
|
||||||
|
|
||||||
|
namespace Nz
|
||||||
|
{
|
||||||
|
inline VirtualDirectoryFilesystemResolver::VirtualDirectoryFilesystemResolver(std::filesystem::path physicalPath, OpenModeFlags fileOpenMode) :
|
||||||
|
m_physicalPath(std::move(physicalPath)),
|
||||||
|
m_fileOpenMode(fileOpenMode)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#include <Nazara/Core/DebugOff.hpp>
|
||||||
|
|
@ -3,8 +3,32 @@
|
||||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||||
|
|
||||||
#include <Nazara/Core/AppFilesystemComponent.hpp>
|
#include <Nazara/Core/AppFilesystemComponent.hpp>
|
||||||
|
#include <Nazara/Core/VirtualDirectoryFilesystemResolver.hpp>
|
||||||
#include <Nazara/Core/Debug.hpp>
|
#include <Nazara/Core/Debug.hpp>
|
||||||
|
|
||||||
namespace Nz
|
namespace Nz
|
||||||
{
|
{
|
||||||
|
const VirtualDirectoryPtr& AppFilesystemComponent::Mount(std::string_view name, std::filesystem::path filepath)
|
||||||
|
{
|
||||||
|
return Mount(name, std::make_shared<VirtualDirectory>(std::make_shared<VirtualDirectoryFilesystemResolver>(std::move(filepath))));
|
||||||
|
}
|
||||||
|
|
||||||
|
const VirtualDirectoryPtr& AppFilesystemComponent::Mount(std::string_view name, VirtualDirectoryPtr directory)
|
||||||
|
{
|
||||||
|
if (name.empty())
|
||||||
|
{
|
||||||
|
m_rootDirectory = std::move(directory);
|
||||||
|
return m_rootDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_rootDirectory)
|
||||||
|
m_rootDirectory = std::make_shared<VirtualDirectory>();
|
||||||
|
|
||||||
|
return m_rootDirectory->StoreDirectory(name, std::move(directory)).directory;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppFilesystemComponent::MountDefaultDirectories()
|
||||||
|
{
|
||||||
|
m_rootDirectory = std::make_shared<VirtualDirectory>(std::make_shared<VirtualDirectoryFilesystemResolver>(std::filesystem::current_path()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
// Copyright (C) 2023 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
|
||||||
|
// This file is part of the "Nazara Engine - Core module"
|
||||||
|
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||||
|
|
||||||
|
#include <Nazara/Core/VirtualDirectory.hpp>
|
||||||
|
#include <Nazara/Core/Debug.hpp>
|
||||||
|
|
||||||
|
namespace Nz
|
||||||
|
{
|
||||||
|
VirtualDirectoryResolver::~VirtualDirectoryResolver() = default;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
// Copyright (C) 2023 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
|
||||||
|
// This file is part of the "Nazara Engine - Core module"
|
||||||
|
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||||
|
|
||||||
|
#include <Nazara/Core/VirtualDirectoryFilesystemResolver.hpp>
|
||||||
|
#include <Nazara/Utils/Algorithm.hpp>
|
||||||
|
#include <Nazara/Core/Debug.hpp>
|
||||||
|
|
||||||
|
namespace Nz
|
||||||
|
{
|
||||||
|
void VirtualDirectoryFilesystemResolver::ForEach(std::weak_ptr<VirtualDirectory> parent, FunctionRef<bool(std::string_view name, VirtualDirectory::Entry&& entry)> callback) const
|
||||||
|
{
|
||||||
|
for (auto&& physicalEntry : std::filesystem::directory_iterator(m_physicalPath))
|
||||||
|
{
|
||||||
|
std::string filename = PathToString(physicalEntry.path().filename());
|
||||||
|
|
||||||
|
std::filesystem::file_status status = physicalEntry.status();
|
||||||
|
|
||||||
|
VirtualDirectory::Entry entry;
|
||||||
|
if (std::filesystem::is_regular_file(status))
|
||||||
|
{
|
||||||
|
if (!callback(filename, VirtualDirectory::FileEntry{ std::make_shared<File>(physicalEntry.path(), m_fileOpenMode) }))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (std::filesystem::is_directory(status))
|
||||||
|
{
|
||||||
|
VirtualDirectoryPtr virtualDir = std::make_shared<VirtualDirectory>(std::make_shared<VirtualDirectoryFilesystemResolver>(physicalEntry.path()), parent);
|
||||||
|
if (!callback(filename, VirtualDirectory::DirectoryEntry{ { std::move(virtualDir) } }))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<VirtualDirectory::Entry> VirtualDirectoryFilesystemResolver::Resolve(std::weak_ptr<VirtualDirectory> parent, const std::string_view* parts, std::size_t partCount) const
|
||||||
|
{
|
||||||
|
std::filesystem::path filePath = m_physicalPath;
|
||||||
|
for (std::size_t i = 0; i < partCount; ++i)
|
||||||
|
filePath /= parts[i];
|
||||||
|
|
||||||
|
std::filesystem::file_status status = std::filesystem::status(filePath); //< FIXME: This will follow symlink, is this the intended behavior? (see symlink_status)
|
||||||
|
|
||||||
|
VirtualDirectory::Entry entry;
|
||||||
|
if (std::filesystem::is_regular_file(status))
|
||||||
|
return VirtualDirectory::FileEntry{ std::make_shared<File>(std::move(filePath), m_fileOpenMode) };
|
||||||
|
else if (std::filesystem::is_directory(status))
|
||||||
|
{
|
||||||
|
VirtualDirectoryPtr virtualDir = std::make_shared<VirtualDirectory>(std::make_shared<VirtualDirectoryFilesystemResolver>(std::move(filePath)), parent);
|
||||||
|
return VirtualDirectory::DirectoryEntry{ { std::move(virtualDir) } };
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return std::nullopt; //< either not known or of a special type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
#include <Nazara/Core/File.hpp>
|
#include <Nazara/Core/File.hpp>
|
||||||
#include <Nazara/Core/StringExt.hpp>
|
#include <Nazara/Core/StringExt.hpp>
|
||||||
#include <Nazara/Core/VirtualDirectory.hpp>
|
#include <Nazara/Core/VirtualDirectory.hpp>
|
||||||
|
#include <Nazara/Core/VirtualDirectoryFilesystemResolver.hpp>
|
||||||
#include <Nazara/Core/Hash/SHA256.hpp>
|
#include <Nazara/Core/Hash/SHA256.hpp>
|
||||||
#include <catch2/catch_approx.hpp>
|
#include <catch2/catch_approx.hpp>
|
||||||
#include <catch2/catch_test_macros.hpp>
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
|
@ -65,12 +66,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::VirtualDirectoryEntry>(entry);
|
return std::holds_alternative<Nz::VirtualDirectory::DirectoryEntry>(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::VirtualDirectoryEntry>(entry);
|
return std::holds_alternative<Nz::VirtualDirectory::DirectoryEntry>(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*/)
|
||||||
|
|
@ -82,35 +83,28 @@ TEST_CASE("VirtualDirectory", "[Core][VirtualDirectory]")
|
||||||
std::mt19937 randGen(std::random_device{}());
|
std::mt19937 randGen(std::random_device{}());
|
||||||
auto GenerateRandomData = [&]
|
auto GenerateRandomData = [&]
|
||||||
{
|
{
|
||||||
std::vector<Nz::UInt8> randomData;
|
Nz::ByteArray randomData;
|
||||||
for (std::size_t i = 0; i < 256; ++i)
|
for (std::size_t i = 0; i < 256; ++i)
|
||||||
{
|
{
|
||||||
unsigned int data = randGen();
|
unsigned int data = randGen();
|
||||||
randomData.push_back(Nz::SafeCast<Nz::UInt8>((data & 0x000000FF) >> 0));
|
randomData.PushBack(Nz::SafeCast<Nz::UInt8>((data & 0x000000FF) >> 0));
|
||||||
randomData.push_back(Nz::SafeCast<Nz::UInt8>((data & 0x0000FF00) >> 8));
|
randomData.PushBack(Nz::SafeCast<Nz::UInt8>((data & 0x0000FF00) >> 8));
|
||||||
randomData.push_back(Nz::SafeCast<Nz::UInt8>((data & 0x00FF0000) >> 16));
|
randomData.PushBack(Nz::SafeCast<Nz::UInt8>((data & 0x00FF0000) >> 16));
|
||||||
randomData.push_back(Nz::SafeCast<Nz::UInt8>((data & 0xFF000000) >> 24));
|
randomData.PushBack(Nz::SafeCast<Nz::UInt8>((data & 0xFF000000) >> 24));
|
||||||
}
|
}
|
||||||
|
|
||||||
return randomData;
|
return randomData;
|
||||||
};
|
};
|
||||||
|
|
||||||
auto CheckFile = [&](std::string_view path, const std::vector<Nz::UInt8>& expectedData)
|
auto CheckFile = [&](std::string_view path, const Nz::ByteArray& expectedData)
|
||||||
{
|
{
|
||||||
return virtualDir->GetEntry(path, [&](const Nz::VirtualDirectory::Entry& entry)
|
return virtualDir->GetFileContent(path, [&](const void* data, std::size_t length)
|
||||||
{
|
{
|
||||||
if (!std::holds_alternative<Nz::VirtualDirectory::FileContentEntry>(entry))
|
return length == expectedData.size() && std::memcmp(data, &expectedData[0], length) == 0;
|
||||||
{
|
|
||||||
FAIL("Target is not a file");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto& contentEntry = std::get<Nz::VirtualDirectory::FileContentEntry>(entry);
|
|
||||||
return std::equal(expectedData.begin(), expectedData.end(), contentEntry.data.begin(), contentEntry.data.end());
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
auto CheckFileContent = [&](std::string_view path, const std::vector<Nz::UInt8>& expectedData)
|
auto CheckFileContent = [&](std::string_view path, const Nz::ByteArray& expectedData)
|
||||||
{
|
{
|
||||||
return virtualDir->GetFileContent(path, [&](const void* data, std::size_t size)
|
return virtualDir->GetFileContent(path, [&](const void* data, std::size_t size)
|
||||||
{
|
{
|
||||||
|
|
@ -120,7 +114,7 @@ TEST_CASE("VirtualDirectory", "[Core][VirtualDirectory]")
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::memcmp(expectedData.data(), data, expectedData.size()) == 0;
|
return std::memcmp(&expectedData[0], data, expectedData.size()) == 0;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -153,7 +147,7 @@ TEST_CASE("VirtualDirectory", "[Core][VirtualDirectory]")
|
||||||
struct File
|
struct File
|
||||||
{
|
{
|
||||||
std::string path;
|
std::string path;
|
||||||
std::vector<Nz::UInt8> data;
|
Nz::ByteArray data;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<File> files;
|
std::vector<File> files;
|
||||||
|
|
@ -188,7 +182,7 @@ TEST_CASE("VirtualDirectory", "[Core][VirtualDirectory]")
|
||||||
|
|
||||||
SECTION("Accessing filesystem using a VirtualDirectory")
|
SECTION("Accessing filesystem using a VirtualDirectory")
|
||||||
{
|
{
|
||||||
std::shared_ptr<Nz::VirtualDirectory> resourceDir = std::make_shared<Nz::VirtualDirectory>(GetAssetDir());
|
std::shared_ptr<Nz::VirtualDirectory> resourceDir = std::make_shared<Nz::VirtualDirectory>(std::make_shared<Nz::VirtualDirectoryFilesystemResolver>(GetAssetDir()));
|
||||||
|
|
||||||
WHEN("Iterating, it's not empty")
|
WHEN("Iterating, it's not empty")
|
||||||
{
|
{
|
||||||
|
|
@ -199,7 +193,7 @@ TEST_CASE("VirtualDirectory", "[Core][VirtualDirectory]")
|
||||||
CHECK_FALSE(name == "..");
|
CHECK_FALSE(name == "..");
|
||||||
|
|
||||||
INFO("Only physical files and directories are expected");
|
INFO("Only physical files and directories are expected");
|
||||||
CHECK((std::holds_alternative<Nz::VirtualDirectory::PhysicalDirectoryEntry>(entry) || std::holds_alternative<Nz::VirtualDirectory::PhysicalFileEntry>(entry)));
|
CHECK((std::holds_alternative<Nz::VirtualDirectory::DirectoryEntry>(entry) || std::holds_alternative<Nz::VirtualDirectory::FileEntry>(entry)));
|
||||||
empty = false;
|
empty = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -210,16 +204,14 @@ TEST_CASE("VirtualDirectory", "[Core][VirtualDirectory]")
|
||||||
{
|
{
|
||||||
return dir->GetEntry(filepath, [&](const Nz::VirtualDirectory::Entry& entry)
|
return dir->GetEntry(filepath, [&](const Nz::VirtualDirectory::Entry& entry)
|
||||||
{
|
{
|
||||||
REQUIRE(std::holds_alternative<Nz::VirtualDirectory::PhysicalFileEntry>(entry));
|
REQUIRE(std::holds_alternative<Nz::VirtualDirectory::FileEntry>(entry));
|
||||||
|
|
||||||
const auto& physFileEntry = std::get<Nz::VirtualDirectory::PhysicalFileEntry>(entry);
|
const auto& physFileEntry = std::get<Nz::VirtualDirectory::FileEntry>(entry);
|
||||||
|
|
||||||
Nz::File file(physFileEntry.filePath);
|
|
||||||
|
|
||||||
Nz::SHA256Hash hash;
|
Nz::SHA256Hash hash;
|
||||||
WHEN("We compute " << hash.GetHashName() << " of " << physFileEntry.filePath << " file")
|
WHEN("We compute " << hash.GetHashName() << " of " << physFileEntry.stream->GetPath() << " file")
|
||||||
{
|
{
|
||||||
CHECK(Nz::ToUpper(Nz::ComputeHash(hash, file).ToHex()) == expectedHash);
|
CHECK(Nz::ToUpper(Nz::ComputeHash(hash, *physFileEntry.stream).ToHex()) == expectedHash);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
@ -258,8 +250,8 @@ TEST_CASE("VirtualDirectory", "[Core][VirtualDirectory]")
|
||||||
|
|
||||||
auto CheckOurselves = [&](const auto& entry)
|
auto CheckOurselves = [&](const auto& entry)
|
||||||
{
|
{
|
||||||
REQUIRE(std::holds_alternative<Nz::VirtualDirectory::VirtualDirectoryEntry>(entry));
|
REQUIRE(std::holds_alternative<Nz::VirtualDirectory::DirectoryEntry>(entry));
|
||||||
const auto& dirEntry = std::get<Nz::VirtualDirectory::VirtualDirectoryEntry>(entry);
|
const auto& dirEntry = std::get<Nz::VirtualDirectory::DirectoryEntry>(entry);
|
||||||
return dirEntry.directory == resourceDir;
|
return dirEntry.directory == resourceDir;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -272,7 +264,7 @@ TEST_CASE("VirtualDirectory", "[Core][VirtualDirectory]")
|
||||||
}
|
}
|
||||||
AND_THEN("Overriding the physical file with another one")
|
AND_THEN("Overriding the physical file with another one")
|
||||||
{
|
{
|
||||||
resourceDir->StoreFile("Logo.png", GetAssetDir() / "Audio/ambience.ogg");
|
resourceDir->StoreFile("Logo.png", std::make_shared<Nz::File>(GetAssetDir() / "Audio/ambience.ogg", Nz::OpenMode::ReadOnly));
|
||||||
|
|
||||||
CHECK(CheckFileHash(resourceDir, "Audio/ambience.ogg", "49C486F44E43F023D54C9F375D902C21375DDB2748D3FA1863C9581D30E17F94"));
|
CHECK(CheckFileHash(resourceDir, "Audio/ambience.ogg", "49C486F44E43F023D54C9F375D902C21375DDB2748D3FA1863C9581D30E17F94"));
|
||||||
CHECK(CheckFileHash(resourceDir, "Logo.png", "49C486F44E43F023D54C9F375D902C21375DDB2748D3FA1863C9581D30E17F94"));
|
CHECK(CheckFileHash(resourceDir, "Logo.png", "49C486F44E43F023D54C9F375D902C21375DDB2748D3FA1863C9581D30E17F94"));
|
||||||
|
|
@ -289,7 +281,7 @@ TEST_CASE("VirtualDirectory", "[Core][VirtualDirectory]")
|
||||||
{
|
{
|
||||||
if (entryName == "GIF")
|
if (entryName == "GIF")
|
||||||
{
|
{
|
||||||
CHECK(std::holds_alternative<Nz::VirtualDirectory::PhysicalDirectoryEntry>(entry));
|
CHECK(std::holds_alternative<Nz::VirtualDirectory::DirectoryEntry>(entry));
|
||||||
found = true;
|
found = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -300,7 +292,7 @@ TEST_CASE("VirtualDirectory", "[Core][VirtualDirectory]")
|
||||||
|
|
||||||
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>(std::make_shared<Nz::VirtualDirectoryFilesystemResolver>(GetAssetDir() / "Audio"));
|
||||||
|
|
||||||
CHECK_FALSE(engineDir->IsUprootAllowed());
|
CHECK_FALSE(engineDir->IsUprootAllowed());
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue