2018-07-19 04:07:11 +03:00
|
|
|
// Copyright 2018 yuzu emulator team
|
|
|
|
// Licensed under GPLv2 or any later version
|
|
|
|
// Refer to the license.txt file included.
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
2018-10-30 07:03:25 +03:00
|
|
|
#include <functional>
|
2018-09-20 04:54:14 +03:00
|
|
|
#include <map>
|
2018-07-19 04:07:11 +03:00
|
|
|
#include <memory>
|
2018-10-30 07:03:25 +03:00
|
|
|
#include <optional>
|
2018-07-19 04:07:11 +03:00
|
|
|
#include <string>
|
2018-07-22 08:23:29 +03:00
|
|
|
#include <string_view>
|
2018-07-19 04:07:11 +03:00
|
|
|
#include <type_traits>
|
|
|
|
#include <vector>
|
2018-10-30 07:03:25 +03:00
|
|
|
|
2018-07-19 04:07:11 +03:00
|
|
|
#include "common/common_types.h"
|
2018-09-24 03:41:24 +03:00
|
|
|
#include "core/file_sys/vfs_types.h"
|
2018-07-19 04:07:11 +03:00
|
|
|
|
|
|
|
namespace FileSys {
|
2018-08-03 18:46:30 +03:00
|
|
|
|
2018-08-21 17:48:24 +03:00
|
|
|
enum class Mode : u32;
|
|
|
|
|
2018-08-03 18:46:30 +03:00
|
|
|
// An enumeration representing what can be at the end of a path in a VfsFilesystem
|
|
|
|
enum class VfsEntryType {
|
|
|
|
None,
|
|
|
|
File,
|
|
|
|
Directory,
|
|
|
|
};
|
|
|
|
|
2018-08-09 04:44:59 +03:00
|
|
|
// A class representing an abstract filesystem. A default implementation given the root VirtualDir
|
|
|
|
// is provided for convenience, but if the Vfs implementation has any additional state or
|
2018-08-03 18:46:30 +03:00
|
|
|
// functionality, they will need to override.
|
2018-08-12 23:37:38 +03:00
|
|
|
class VfsFilesystem : NonCopyable {
|
|
|
|
public:
|
2018-08-12 23:47:08 +03:00
|
|
|
explicit VfsFilesystem(VirtualDir root);
|
2018-08-03 18:46:30 +03:00
|
|
|
virtual ~VfsFilesystem();
|
|
|
|
|
|
|
|
// Gets the friendly name for the filesystem.
|
|
|
|
virtual std::string GetName() const;
|
|
|
|
|
|
|
|
// Return whether or not the user has read permissions on this filesystem.
|
|
|
|
virtual bool IsReadable() const;
|
|
|
|
// Return whether or not the user has write permission on this filesystem.
|
|
|
|
virtual bool IsWritable() const;
|
|
|
|
|
|
|
|
// Determine if the entry at path is non-existant, a file, or a directory.
|
|
|
|
virtual VfsEntryType GetEntryType(std::string_view path) const;
|
|
|
|
|
|
|
|
// Opens the file with path relative to root. If it doesn't exist, returns nullptr.
|
|
|
|
virtual VirtualFile OpenFile(std::string_view path, Mode perms);
|
|
|
|
// Creates a new, empty file at path
|
|
|
|
virtual VirtualFile CreateFile(std::string_view path, Mode perms);
|
|
|
|
// Copies the file from old_path to new_path, returning the new file on success and nullptr on
|
|
|
|
// failure.
|
|
|
|
virtual VirtualFile CopyFile(std::string_view old_path, std::string_view new_path);
|
|
|
|
// Moves the file from old_path to new_path, returning the moved file on success and nullptr on
|
|
|
|
// failure.
|
|
|
|
virtual VirtualFile MoveFile(std::string_view old_path, std::string_view new_path);
|
|
|
|
// Deletes the file with path relative to root, returing true on success.
|
|
|
|
virtual bool DeleteFile(std::string_view path);
|
|
|
|
|
|
|
|
// Opens the directory with path relative to root. If it doesn't exist, returns nullptr.
|
|
|
|
virtual VirtualDir OpenDirectory(std::string_view path, Mode perms);
|
|
|
|
// Creates a new, empty directory at path
|
|
|
|
virtual VirtualDir CreateDirectory(std::string_view path, Mode perms);
|
|
|
|
// Copies the directory from old_path to new_path, returning the new directory on success and
|
|
|
|
// nullptr on failure.
|
|
|
|
virtual VirtualDir CopyDirectory(std::string_view old_path, std::string_view new_path);
|
|
|
|
// Moves the directory from old_path to new_path, returning the moved directory on success and
|
|
|
|
// nullptr on failure.
|
|
|
|
virtual VirtualDir MoveDirectory(std::string_view old_path, std::string_view new_path);
|
|
|
|
// Deletes the directory with path relative to root, returing true on success.
|
|
|
|
virtual bool DeleteDirectory(std::string_view path);
|
|
|
|
|
|
|
|
protected:
|
|
|
|
// Root directory in default implementation.
|
|
|
|
VirtualDir root;
|
|
|
|
};
|
2018-07-19 04:07:11 +03:00
|
|
|
|
|
|
|
// A class representing a file in an abstract filesystem.
|
2018-08-12 23:37:38 +03:00
|
|
|
class VfsFile : NonCopyable {
|
|
|
|
public:
|
2018-07-19 04:07:11 +03:00
|
|
|
virtual ~VfsFile();
|
|
|
|
|
|
|
|
// Retrieves the file name.
|
|
|
|
virtual std::string GetName() const = 0;
|
|
|
|
// Retrieves the extension of the file name.
|
|
|
|
virtual std::string GetExtension() const;
|
|
|
|
// Retrieves the size of the file.
|
2018-09-15 16:21:06 +03:00
|
|
|
virtual std::size_t GetSize() const = 0;
|
2018-07-19 04:07:11 +03:00
|
|
|
// Resizes the file to new_size. Returns whether or not the operation was successful.
|
2018-09-15 16:21:06 +03:00
|
|
|
virtual bool Resize(std::size_t new_size) = 0;
|
2018-07-19 04:07:11 +03:00
|
|
|
// Gets a pointer to the directory containing this file, returning nullptr if there is none.
|
|
|
|
virtual std::shared_ptr<VfsDirectory> GetContainingDirectory() const = 0;
|
|
|
|
|
|
|
|
// Returns whether or not the file can be written to.
|
|
|
|
virtual bool IsWritable() const = 0;
|
|
|
|
// Returns whether or not the file can be read from.
|
|
|
|
virtual bool IsReadable() const = 0;
|
|
|
|
|
|
|
|
// The primary method of reading from the file. Reads length bytes into data starting at offset
|
|
|
|
// into file. Returns number of bytes successfully read.
|
2018-09-15 16:21:06 +03:00
|
|
|
virtual std::size_t Read(u8* data, std::size_t length, std::size_t offset = 0) const = 0;
|
2018-07-19 04:07:11 +03:00
|
|
|
// The primary method of writing to the file. Writes length bytes from data starting at offset
|
|
|
|
// into file. Returns number of bytes successfully written.
|
2018-09-15 16:21:06 +03:00
|
|
|
virtual std::size_t Write(const u8* data, std::size_t length, std::size_t offset = 0) = 0;
|
2018-07-19 04:07:11 +03:00
|
|
|
|
2018-10-30 07:03:25 +03:00
|
|
|
// Reads exactly one byte at the offset provided, returning std::nullopt on error.
|
|
|
|
virtual std::optional<u8> ReadByte(std::size_t offset = 0) const;
|
2018-07-19 04:07:11 +03:00
|
|
|
// Reads size bytes starting at offset in file into a vector.
|
2018-09-15 16:21:06 +03:00
|
|
|
virtual std::vector<u8> ReadBytes(std::size_t size, std::size_t offset = 0) const;
|
2018-07-19 04:07:11 +03:00
|
|
|
// Reads all the bytes from the file into a vector. Equivalent to 'file->Read(file->GetSize(),
|
|
|
|
// 0)'
|
|
|
|
virtual std::vector<u8> ReadAllBytes() const;
|
|
|
|
|
|
|
|
// Reads an array of type T, size number_elements starting at offset.
|
|
|
|
// Returns the number of bytes (sizeof(T)*number_elements) read successfully.
|
|
|
|
template <typename T>
|
2018-09-15 16:21:06 +03:00
|
|
|
std::size_t ReadArray(T* data, std::size_t number_elements, std::size_t offset = 0) const {
|
2018-07-21 04:45:20 +03:00
|
|
|
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
|
2018-07-19 04:07:11 +03:00
|
|
|
|
|
|
|
return Read(reinterpret_cast<u8*>(data), number_elements * sizeof(T), offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reads size bytes into the memory starting at data starting at offset into the file.
|
|
|
|
// Returns the number of bytes read successfully.
|
|
|
|
template <typename T>
|
2018-09-15 16:21:06 +03:00
|
|
|
std::size_t ReadBytes(T* data, std::size_t size, std::size_t offset = 0) const {
|
2018-07-21 04:45:20 +03:00
|
|
|
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
|
2018-07-19 04:07:11 +03:00
|
|
|
return Read(reinterpret_cast<u8*>(data), size, offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reads one object of type T starting at offset in file.
|
|
|
|
// Returns the number of bytes read successfully (sizeof(T)).
|
|
|
|
template <typename T>
|
2018-09-15 16:21:06 +03:00
|
|
|
std::size_t ReadObject(T* data, std::size_t offset = 0) const {
|
2018-07-21 04:45:20 +03:00
|
|
|
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
|
2018-07-19 04:07:11 +03:00
|
|
|
return Read(reinterpret_cast<u8*>(data), sizeof(T), offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Writes exactly one byte to offset in file and retuns whether or not the byte was written
|
|
|
|
// successfully.
|
2018-09-15 16:21:06 +03:00
|
|
|
virtual bool WriteByte(u8 data, std::size_t offset = 0);
|
2018-07-19 04:07:11 +03:00
|
|
|
// Writes a vector of bytes to offset in file and returns the number of bytes successfully
|
|
|
|
// written.
|
2018-09-15 16:21:06 +03:00
|
|
|
virtual std::size_t WriteBytes(const std::vector<u8>& data, std::size_t offset = 0);
|
2018-07-19 04:07:11 +03:00
|
|
|
|
|
|
|
// Writes an array of type T, size number_elements to offset in file.
|
|
|
|
// Returns the number of bytes (sizeof(T)*number_elements) written successfully.
|
|
|
|
template <typename T>
|
2018-09-15 16:21:06 +03:00
|
|
|
std::size_t WriteArray(const T* data, std::size_t number_elements, std::size_t offset = 0) {
|
2018-07-21 04:45:20 +03:00
|
|
|
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
|
2018-12-07 04:23:44 +03:00
|
|
|
return Write(reinterpret_cast<const u8*>(data), number_elements * sizeof(T), offset);
|
2018-07-19 04:07:11 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Writes size bytes starting at memory location data to offset in file.
|
|
|
|
// Returns the number of bytes written successfully.
|
|
|
|
template <typename T>
|
2018-09-15 16:21:06 +03:00
|
|
|
std::size_t WriteBytes(const T* data, std::size_t size, std::size_t offset = 0) {
|
2018-07-21 04:45:20 +03:00
|
|
|
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
|
2018-07-21 04:40:13 +03:00
|
|
|
return Write(reinterpret_cast<const u8*>(data), size, offset);
|
2018-07-19 04:07:11 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Writes one object of type T to offset in file.
|
|
|
|
// Returns the number of bytes written successfully (sizeof(T)).
|
|
|
|
template <typename T>
|
2018-09-15 16:21:06 +03:00
|
|
|
std::size_t WriteObject(const T& data, std::size_t offset = 0) {
|
2018-07-21 04:45:20 +03:00
|
|
|
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
|
2018-12-07 04:23:44 +03:00
|
|
|
return Write(reinterpret_cast<const u8*>(&data), sizeof(T), offset);
|
2018-07-19 04:07:11 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Renames the file to name. Returns whether or not the operation was successsful.
|
2018-07-22 08:23:29 +03:00
|
|
|
virtual bool Rename(std::string_view name) = 0;
|
2018-07-28 01:14:03 +03:00
|
|
|
|
|
|
|
// Returns the full path of this file as a string, recursively
|
|
|
|
virtual std::string GetFullPath() const;
|
2018-07-19 04:07:11 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
// A class representing a directory in an abstract filesystem.
|
2018-08-12 23:37:38 +03:00
|
|
|
class VfsDirectory : NonCopyable {
|
|
|
|
public:
|
2018-07-19 04:07:11 +03:00
|
|
|
virtual ~VfsDirectory();
|
|
|
|
|
|
|
|
// Retrives the file located at path as if the current directory was root. Returns nullptr if
|
|
|
|
// not found.
|
2018-07-22 08:23:29 +03:00
|
|
|
virtual std::shared_ptr<VfsFile> GetFileRelative(std::string_view path) const;
|
2018-07-19 04:07:11 +03:00
|
|
|
// Calls GetFileRelative(path) on the root of the current directory.
|
2018-07-22 08:23:29 +03:00
|
|
|
virtual std::shared_ptr<VfsFile> GetFileAbsolute(std::string_view path) const;
|
2018-07-19 04:07:11 +03:00
|
|
|
|
|
|
|
// Retrives the directory located at path as if the current directory was root. Returns nullptr
|
|
|
|
// if not found.
|
2018-07-22 08:23:29 +03:00
|
|
|
virtual std::shared_ptr<VfsDirectory> GetDirectoryRelative(std::string_view path) const;
|
2018-07-19 04:07:11 +03:00
|
|
|
// Calls GetDirectoryRelative(path) on the root of the current directory.
|
2018-07-22 08:23:29 +03:00
|
|
|
virtual std::shared_ptr<VfsDirectory> GetDirectoryAbsolute(std::string_view path) const;
|
2018-07-19 04:07:11 +03:00
|
|
|
|
|
|
|
// Returns a vector containing all of the files in this directory.
|
|
|
|
virtual std::vector<std::shared_ptr<VfsFile>> GetFiles() const = 0;
|
|
|
|
// Returns the file with filename matching name. Returns nullptr if directory dosen't have a
|
|
|
|
// file with name.
|
2018-07-22 08:23:29 +03:00
|
|
|
virtual std::shared_ptr<VfsFile> GetFile(std::string_view name) const;
|
2018-07-19 04:07:11 +03:00
|
|
|
|
|
|
|
// Returns a vector containing all of the subdirectories in this directory.
|
|
|
|
virtual std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const = 0;
|
|
|
|
// Returns the directory with name matching name. Returns nullptr if directory dosen't have a
|
|
|
|
// directory with name.
|
2018-07-22 08:23:29 +03:00
|
|
|
virtual std::shared_ptr<VfsDirectory> GetSubdirectory(std::string_view name) const;
|
2018-07-19 04:07:11 +03:00
|
|
|
|
|
|
|
// Returns whether or not the directory can be written to.
|
|
|
|
virtual bool IsWritable() const = 0;
|
|
|
|
// Returns whether of not the directory can be read from.
|
|
|
|
virtual bool IsReadable() const = 0;
|
|
|
|
|
|
|
|
// Returns whether or not the directory is the root of the current file tree.
|
|
|
|
virtual bool IsRoot() const;
|
|
|
|
|
|
|
|
// Returns the name of the directory.
|
|
|
|
virtual std::string GetName() const = 0;
|
|
|
|
// Returns the total size of all files and subdirectories in this directory.
|
2018-09-15 16:21:06 +03:00
|
|
|
virtual std::size_t GetSize() const;
|
2018-07-19 04:07:11 +03:00
|
|
|
// Returns the parent directory of this directory. Returns nullptr if this directory is root or
|
|
|
|
// has no parent.
|
|
|
|
virtual std::shared_ptr<VfsDirectory> GetParentDirectory() const = 0;
|
|
|
|
|
|
|
|
// Creates a new subdirectory with name name. Returns a pointer to the new directory or nullptr
|
|
|
|
// if the operation failed.
|
2018-07-22 08:23:29 +03:00
|
|
|
virtual std::shared_ptr<VfsDirectory> CreateSubdirectory(std::string_view name) = 0;
|
2018-07-19 04:07:11 +03:00
|
|
|
// Creates a new file with name name. Returns a pointer to the new file or nullptr if the
|
|
|
|
// operation failed.
|
2018-07-22 08:23:29 +03:00
|
|
|
virtual std::shared_ptr<VfsFile> CreateFile(std::string_view name) = 0;
|
2018-07-19 04:07:11 +03:00
|
|
|
|
|
|
|
// Creates a new file at the path relative to this directory. Also creates directories if
|
|
|
|
// they do not exist and is supported by this implementation. Returns nullptr on any failure.
|
2018-07-22 08:23:29 +03:00
|
|
|
virtual std::shared_ptr<VfsFile> CreateFileRelative(std::string_view path);
|
2018-07-19 04:07:11 +03:00
|
|
|
|
|
|
|
// Creates a new file at the path relative to root of this directory. Also creates directories
|
|
|
|
// if they do not exist and is supported by this implementation. Returns nullptr on any failure.
|
2018-07-22 08:23:29 +03:00
|
|
|
virtual std::shared_ptr<VfsFile> CreateFileAbsolute(std::string_view path);
|
2018-07-19 04:07:11 +03:00
|
|
|
|
|
|
|
// Creates a new directory at the path relative to this directory. Also creates directories if
|
|
|
|
// they do not exist and is supported by this implementation. Returns nullptr on any failure.
|
2018-07-22 08:23:29 +03:00
|
|
|
virtual std::shared_ptr<VfsDirectory> CreateDirectoryRelative(std::string_view path);
|
2018-07-19 04:07:11 +03:00
|
|
|
|
|
|
|
// Creates a new directory at the path relative to root of this directory. Also creates
|
|
|
|
// directories if they do not exist and is supported by this implementation. Returns nullptr on
|
|
|
|
// any failure.
|
2018-07-22 08:23:29 +03:00
|
|
|
virtual std::shared_ptr<VfsDirectory> CreateDirectoryAbsolute(std::string_view path);
|
2018-07-19 04:07:11 +03:00
|
|
|
|
2018-11-30 22:44:13 +03:00
|
|
|
// Deletes the subdirectory with the given name and returns true on success.
|
2018-07-22 08:23:29 +03:00
|
|
|
virtual bool DeleteSubdirectory(std::string_view name) = 0;
|
2018-11-30 22:44:13 +03:00
|
|
|
|
|
|
|
// Deletes all subdirectories and files within the provided directory and then deletes
|
|
|
|
// the directory itself. Returns true on success.
|
2018-07-22 08:23:29 +03:00
|
|
|
virtual bool DeleteSubdirectoryRecursive(std::string_view name);
|
2018-11-30 22:44:13 +03:00
|
|
|
|
|
|
|
// Deletes all subdirectories and files within the provided directory.
|
|
|
|
// Unlike DeleteSubdirectoryRecursive, this does not delete the provided directory.
|
|
|
|
virtual bool CleanSubdirectoryRecursive(std::string_view name);
|
|
|
|
|
|
|
|
// Returns whether or not the file with name name was deleted successfully.
|
2018-07-22 08:23:29 +03:00
|
|
|
virtual bool DeleteFile(std::string_view name) = 0;
|
2018-07-19 04:07:11 +03:00
|
|
|
|
|
|
|
// Returns whether or not this directory was renamed to name.
|
2018-07-22 08:23:29 +03:00
|
|
|
virtual bool Rename(std::string_view name) = 0;
|
2018-07-19 04:07:11 +03:00
|
|
|
|
|
|
|
// Returns whether or not the file with name src was successfully copied to a new file with name
|
|
|
|
// dest.
|
2018-07-22 08:23:29 +03:00
|
|
|
virtual bool Copy(std::string_view src, std::string_view dest);
|
2018-07-19 04:07:11 +03:00
|
|
|
|
2018-09-20 04:54:14 +03:00
|
|
|
// Gets all of the entries directly in the directory (files and dirs), returning a map between
|
|
|
|
// item name -> type.
|
2018-09-24 04:50:16 +03:00
|
|
|
virtual std::map<std::string, VfsEntryType, std::less<>> GetEntries() const;
|
2018-09-20 04:54:14 +03:00
|
|
|
|
2018-07-28 01:14:03 +03:00
|
|
|
// Returns the full path of this directory as a string, recursively
|
|
|
|
virtual std::string GetFullPath() const;
|
2018-07-19 04:07:11 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
// A convenience partial-implementation of VfsDirectory that stubs out methods that should only work
|
|
|
|
// if writable. This is to avoid redundant empty methods everywhere.
|
2018-08-12 23:37:38 +03:00
|
|
|
class ReadOnlyVfsDirectory : public VfsDirectory {
|
|
|
|
public:
|
2018-07-19 04:07:11 +03:00
|
|
|
bool IsWritable() const override;
|
|
|
|
bool IsReadable() const override;
|
2018-07-22 08:23:29 +03:00
|
|
|
std::shared_ptr<VfsDirectory> CreateSubdirectory(std::string_view name) override;
|
|
|
|
std::shared_ptr<VfsFile> CreateFile(std::string_view name) override;
|
2018-12-01 07:44:08 +03:00
|
|
|
std::shared_ptr<VfsFile> CreateFileAbsolute(std::string_view path) override;
|
|
|
|
std::shared_ptr<VfsFile> CreateFileRelative(std::string_view path) override;
|
|
|
|
std::shared_ptr<VfsDirectory> CreateDirectoryAbsolute(std::string_view path) override;
|
|
|
|
std::shared_ptr<VfsDirectory> CreateDirectoryRelative(std::string_view path) override;
|
2018-07-22 08:23:29 +03:00
|
|
|
bool DeleteSubdirectory(std::string_view name) override;
|
2018-12-01 07:44:08 +03:00
|
|
|
bool DeleteSubdirectoryRecursive(std::string_view name) override;
|
2018-11-30 22:44:13 +03:00
|
|
|
bool CleanSubdirectoryRecursive(std::string_view name) override;
|
2018-07-22 08:23:29 +03:00
|
|
|
bool DeleteFile(std::string_view name) override;
|
|
|
|
bool Rename(std::string_view name) override;
|
2018-07-19 04:07:11 +03:00
|
|
|
};
|
2018-07-28 01:14:03 +03:00
|
|
|
|
2018-09-26 00:56:14 +03:00
|
|
|
// Compare the two files, byte-for-byte, in increments specified by block_size
|
|
|
|
bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2,
|
|
|
|
std::size_t block_size = 0x1000);
|
2018-07-28 06:55:23 +03:00
|
|
|
|
2018-07-28 01:14:03 +03:00
|
|
|
// A method that copies the raw data between two different implementations of VirtualFile. If you
|
|
|
|
// are using the same implementation, it is probably better to use the Copy method in the parent
|
|
|
|
// directory of src/dest.
|
2018-09-26 00:56:14 +03:00
|
|
|
bool VfsRawCopy(const VirtualFile& src, const VirtualFile& dest, std::size_t block_size = 0x1000);
|
2018-09-20 04:55:47 +03:00
|
|
|
|
2018-09-24 04:50:16 +03:00
|
|
|
// A method that performs a similar function to VfsRawCopy above, but instead copies entire
|
|
|
|
// directories. It suffers the same performance penalties as above and an implementation-specific
|
|
|
|
// Copy should always be preferred.
|
2018-09-26 00:56:14 +03:00
|
|
|
bool VfsRawCopyD(const VirtualDir& src, const VirtualDir& dest, std::size_t block_size = 0x1000);
|
2018-07-28 01:14:03 +03:00
|
|
|
|
2018-08-17 00:05:30 +03:00
|
|
|
// Checks if the directory at path relative to rel exists. If it does, returns that. If it does not
|
|
|
|
// it attempts to create it and returns the new dir or nullptr on failure.
|
|
|
|
VirtualDir GetOrCreateDirectoryRelative(const VirtualDir& rel, std::string_view path);
|
|
|
|
|
2018-07-19 04:07:11 +03:00
|
|
|
} // namespace FileSys
|