Skip to content

Commit

Permalink
refactor(Vfs): replace deprecated vdfs.hh with Vfs.hh
Browse files Browse the repository at this point in the history
The VDF-implementation in `vdfs.hh` is very basic, very inflexible implementation which only supported loading VDF-files. The implementation completely ignored the original premise of the virtual file system in the ZenGin: unifying file system access.

The new implementation which can be found in `Vfs.hh` keeps this in mind and is designed from the ground up as a full virtual file system. It allows for mounting VDF-files, host file structures and more. Many of the unnecessary features of VDF-files (like catalog offset) have been removed so that the implementation can be slim and flexible.

Users of the old `vdfs.hh` implementation should migrate to the new `Vfs.hh`-implementation since the old implementation will be removed in phoenix 2.0
  • Loading branch information
lmichaelis committed May 3, 2023
1 parent bee00d1 commit 78a1c82
Show file tree
Hide file tree
Showing 8 changed files with 685 additions and 121 deletions.
4 changes: 2 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ set(PHOENIX_SOURCES
source/softskin_mesh.cc
source/texture.cc
source/vdfs.cc
source/Vfs.cc
source/vobs/camera.cc
source/vobs/light.cc
source/vobs/misc.cc
Expand All @@ -96,7 +97,6 @@ set(PHOENIX_TESTS
tests/test_animation.cc
tests/test_archive.cc
tests/test_buffer.cc
tests/test_compat.cc
tests/test_font.cc
tests/test_material.cc
tests/test_messages.cc
Expand All @@ -109,7 +109,7 @@ set(PHOENIX_TESTS
tests/test_save_game.cc
tests/test_script.cc
tests/test_texture.cc
tests/test_vdfs.cc
tests/TestVfs.cc
tests/test_vobs_g1.cc
tests/test_vobs_g2.cc
tests/test_world.cc)
Expand Down
173 changes: 173 additions & 0 deletions include/phoenix/Vfs.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
// Copyright © 2023 Luis Michaelis <me@lmichaelis.de>
// SPDX-License-Identifier: MIT
#pragma once
#include "Api.hh"
#include "buffer.hh"
#include "math.hh"

#include <filesystem>
#include <set>
#include <string>
#include <string_view>
#include <variant>

namespace phoenix {
class VfsBrokenDiskError : public error {
public:
PHOENIX_INTERNAL explicit VfsBrokenDiskError(std::string const& signature);
};

class VfsFileExistsError : public error {
public:
PHOENIX_INTERNAL explicit VfsFileExistsError(std::string const& name);
};

class VfsNotFoundError : public error {
public:
PHOENIX_INTERNAL explicit VfsNotFoundError(std::string const& name);
};

enum class VfsNodeType {
DIRECTORY = 1,
FILE = 2,
};

class VfsNode;

struct VfsNodeComparator {
using is_transparent = std::true_type;

PHOENIX_API [[nodiscard]] bool operator()(const VfsNode& a, const VfsNode& b) const noexcept;
PHOENIX_API [[nodiscard]] bool operator()(const VfsNode& a, std::string_view b) const noexcept;
PHOENIX_API [[nodiscard]] bool operator()(std::string_view a, const VfsNode& b) const noexcept;
};

class VfsNode {
public:
PHOENIX_API [[nodiscard]] VfsNodeType type() const noexcept;
PHOENIX_API [[nodiscard]] std::time_t time() const noexcept;
PHOENIX_API [[nodiscard]] std::string const& name() const noexcept;

PHOENIX_API [[nodiscard]] std::vector<VfsNode> const& children() const;
PHOENIX_API [[nodiscard]] VfsNode const* child(std::string_view name) const;
PHOENIX_API [[nodiscard]] VfsNode* child(std::string_view name);

PHOENIX_API VfsNode* create(VfsNode node);
PHOENIX_API bool remove(std::string_view name);

PHOENIX_API [[nodiscard]] buffer open() const;

PHOENIX_API [[nodiscard]] static VfsNode directory(std::string_view name);
PHOENIX_API [[nodiscard]] static VfsNode file(std::string_view name, buffer dev);

PHOENIX_API [[nodiscard]] static VfsNode directory(std::string_view name, std::time_t ts);
PHOENIX_API [[nodiscard]] static VfsNode file(std::string_view name, buffer dev, std::time_t ts);

protected:
PHOENIX_API explicit VfsNode(std::string_view name, std::time_t ts);
PHOENIX_API explicit VfsNode(std::string_view name, buffer dev, std::time_t ts);

private:
std::string _m_name;
std::time_t _m_time;
std::variant<std::vector<VfsNode>, buffer> _m_data;
};

enum class VfsOverwriteBehavior {
NONE = 0, ///< Overwrite no conflicting nodes.
ALL = 1, ///< Overwrite all conflicting nodes.
NEWER = 2, ///< Overwrite newer conflicting nodes.
OLDER = 3, ///< Overwrite older conflicting nodes.
};

/// \brief An implementation of the virtual file system.
class Vfs {
public:
PHOENIX_API Vfs();

/// \brief Get the root node of the file system structure.
/// \return The root node of the file system structure.
PHOENIX_API [[nodiscard]] VfsNode const& root() const noexcept;

/// \brief Create all missing directories in the given path.
/// \param path The path of the directory to create.
/// \return The newly created directory.
/// \throws VfsFileExistsError if a part of the given path already exists and is a file.
PHOENIX_API VfsNode& mkdir(std::string_view path);

/// \brief Delete the file or directory at the given path
/// \param path The path of the node to delete.
/// \return `true` if removal was successful and `false` if not (ie. the file could not be found).
PHOENIX_API bool remove(std::string_view path);

/// \brief Mount the given file system node into the given directory.
///
/// When the given \p node is a directory, it is merged with any existing directory with
/// the same name in the given \p parent path.
///
/// \param node The file system node to mount.
/// \param parent The path of the parent node to mount into.
/// \param overwrite The behavior of the system when conflicting files are found.
/// \throws VfsNotFoundError if the given \p parent node could not be found.
/// \throws VfsFileExistsError if the given parent node is not a directory.
PHOENIX_API void mount(VfsNode node, //
std::string_view parent,
VfsOverwriteBehavior overwrite = VfsOverwriteBehavior::ALL);

/// \brief Mount the disk file at the given host path into the file system.
///
/// The disk contents are mounted at the root node of the file system and existing
/// directories are merged together.
///
/// \param path The path of the disk to mount.
/// \param overwrite The behavior of the system when conflicting files are found.
/// \throws VfsBrokenDiskError if the disk file is corrupted or invalid and thus, can't be loaded.
/// \see #mount_disk(buffer, VfsOverwriteBehavior)
PHOENIX_API void mount_disk(std::filesystem::path const& host,
VfsOverwriteBehavior overwrite = VfsOverwriteBehavior::OLDER);

/// \brief Mount the disk file in the given buffer into the file system.
///
/// The disk contents are mounted at the root node of the file system and existing
/// directories are merged together.
///
/// \param buf A buffer containing the disk file contents.
/// \param overwrite The behavior of the system when conflicting files are found.
/// \throws VfsBrokenDiskError if the disk file is corrupted or invalid and thus, can't be loaded.
PHOENIX_API void mount_disk(buffer buf, VfsOverwriteBehavior overwrite = VfsOverwriteBehavior::OLDER);

/// \brief Mount a file or directory from the host file system into the Vfs.
/// \note If a path to a directory is provided, only its children are mounted, not the directory itself.
/// \param host The path of the file or directory to mount.
/// \param parent The path of the parent node to mount into.
/// \param overwrite The behavior of the system when conflicting files are found.
/// \throws VfsNotFoundError if the given \p parent node could not be found.
/// \throws VfsFileExistsError if the given parent node is not a directory.
PHOENIX_API void mount_host(std::filesystem::path const& host,
std::string_view parent,
VfsOverwriteBehavior overwrite = VfsOverwriteBehavior::ALL);

/// \brief Resolve the given path in the Vfs to a file system node.
/// \param path The path to the node to resolve.
/// \return The node at the given path or `nullptr` if the path could not be resolved.
PHOENIX_API [[nodiscard]] VfsNode const* resolve(std::string_view path) const noexcept;

/// \brief Resolve the given path in the Vfs to a file system node.
/// \param path The path to the node to resolve.
/// \return The node at the given path or `nullptr` if the path could not be resolved.
PHOENIX_API [[nodiscard]] VfsNode* resolve(std::string_view path) noexcept;

/// \brief Find the first node with the given name in the Vfs.
/// \param name The name of the node to find.
/// \return The node with the given name or `nullptr` if no node with the given name was found.
PHOENIX_API [[nodiscard]] VfsNode const* find(std::string_view name) const noexcept;

/// \brief Find the first node with the given name in the Vfs.
/// \param name The name of the node to find.
/// \return The node with the given name or `nullptr` if no node with the given name was found.
PHOENIX_API [[nodiscard]] VfsNode* find(std::string_view name) noexcept;

private:
VfsNode _m_root;
};
} // namespace phoenix
3 changes: 3 additions & 0 deletions include/phoenix/vdfs.hh
Original file line number Diff line number Diff line change
Expand Up @@ -189,16 +189,19 @@ namespace phoenix {
/// \brief Opens the file at the given \p path as a VDF file.
/// \param comment The comment on the file. Note that comments are trimmed to 256 characters.
/// \param timestamp The timestamp of the archive.
PHOENIX_DEPRECATED("scheduled for removal; use Vfs instead")
PHOENIX_API explicit vdf_file(std::string_view comment, std::time_t timestamp = -1);

/// \brief Reads the header and catalog from a file and creates a vdf_file from it.
/// \param path The path of the file to read from.
/// \return The vdf_file.
PHOENIX_DEPRECATED("scheduled for removal; use Vfs instead")
PHOENIX_API static vdf_file open(const std::filesystem::path& path);

/// \brief Reads the header and catalog from a buffer and creates a vdf_file from it.
/// \param path The buffer to read from.
/// \return The vdf_file.
PHOENIX_DEPRECATED("scheduled for removal; use Vfs instead")
PHOENIX_API static vdf_file open(phoenix::buffer& buf);

/// \brief Searches the VDF file for the first entry with the given name.
Expand Down
Loading

0 comments on commit 78a1c82

Please sign in to comment.