summaryrefslogtreecommitdiffstats
path: root/src/core/file_sys/vfs.h
blob: cea4aa8b8177dfb7b9cc0bd90ff5185ee6d933f7 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.

#pragma once

#include <map>
#include <memory>
#include <string>
#include <string_view>
#include <type_traits>
#include <vector>
#include <boost/optional.hpp>
#include "common/common_types.h"

namespace FileSys {

class VfsDirectory;
class VfsFile;
class VfsFilesystem;

enum class Mode : u32;

// Convenience typedefs to use Vfs* interfaces
using VirtualFilesystem = std::shared_ptr<VfsFilesystem>;
using VirtualDir = std::shared_ptr<VfsDirectory>;
using VirtualFile = std::shared_ptr<VfsFile>;

// An enumeration representing what can be at the end of a path in a VfsFilesystem
enum class VfsEntryType {
    None,
    File,
    Directory,
};

// 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
// functionality, they will need to override.
class VfsFilesystem : NonCopyable {
public:
    explicit VfsFilesystem(VirtualDir root);
    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;
};

// A class representing a file in an abstract filesystem.
class VfsFile : NonCopyable {
public:
    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.
    virtual std::size_t GetSize() const = 0;
    // Resizes the file to new_size. Returns whether or not the operation was successful.
    virtual bool Resize(std::size_t new_size) = 0;
    // 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.
    virtual std::size_t Read(u8* data, std::size_t length, std::size_t offset = 0) const = 0;
    // The primary method of writing to the file. Writes length bytes from data starting at offset
    // into file. Returns number of bytes successfully written.
    virtual std::size_t Write(const u8* data, std::size_t length, std::size_t offset = 0) = 0;

    // Reads exactly one byte at the offset provided, returning boost::none on error.
    virtual boost::optional<u8> ReadByte(std::size_t offset = 0) const;
    // Reads size bytes starting at offset in file into a vector.
    virtual std::vector<u8> ReadBytes(std::size_t size, std::size_t offset = 0) const;
    // 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>
    std::size_t ReadArray(T* data, std::size_t number_elements, std::size_t offset = 0) const {
        static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");

        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>
    std::size_t ReadBytes(T* data, std::size_t size, std::size_t offset = 0) const {
        static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
        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>
    std::size_t ReadObject(T* data, std::size_t offset = 0) const {
        static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
        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.
    virtual bool WriteByte(u8 data, std::size_t offset = 0);
    // Writes a vector of bytes to offset in file and returns the number of bytes successfully
    // written.
    virtual std::size_t WriteBytes(const std::vector<u8>& data, std::size_t offset = 0);

    // 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>
    std::size_t WriteArray(const T* data, std::size_t number_elements, std::size_t offset = 0) {
        static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
        return Write(data, number_elements * sizeof(T), offset);
    }

    // Writes size bytes starting at memory location data to offset in file.
    // Returns the number of bytes written successfully.
    template <typename T>
    std::size_t WriteBytes(const T* data, std::size_t size, std::size_t offset = 0) {
        static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
        return Write(reinterpret_cast<const u8*>(data), size, offset);
    }

    // Writes one object of type T to offset in file.
    // Returns the number of bytes written successfully (sizeof(T)).
    template <typename T>
    std::size_t WriteObject(const T& data, std::size_t offset = 0) {
        static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
        return Write(&data, sizeof(T), offset);
    }

    // Renames the file to name. Returns whether or not the operation was successsful.
    virtual bool Rename(std::string_view name) = 0;

    // Returns the full path of this file as a string, recursively
    virtual std::string GetFullPath() const;
};

// A class representing a directory in an abstract filesystem.
class VfsDirectory : NonCopyable {
public:
    virtual ~VfsDirectory();

    // Retrives the file located at path as if the current directory was root. Returns nullptr if
    // not found.
    virtual std::shared_ptr<VfsFile> GetFileRelative(std::string_view path) const;
    // Calls GetFileRelative(path) on the root of the current directory.
    virtual std::shared_ptr<VfsFile> GetFileAbsolute(std::string_view path) const;

    // Retrives the directory located at path as if the current directory was root. Returns nullptr
    // if not found.
    virtual std::shared_ptr<VfsDirectory> GetDirectoryRelative(std::string_view path) const;
    // Calls GetDirectoryRelative(path) on the root of the current directory.
    virtual std::shared_ptr<VfsDirectory> GetDirectoryAbsolute(std::string_view path) const;

    // 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.
    virtual std::shared_ptr<VfsFile> GetFile(std::string_view name) const;

    // 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.
    virtual std::shared_ptr<VfsDirectory> GetSubdirectory(std::string_view name) const;

    // 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.
    virtual std::size_t GetSize() const;
    // 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.
    virtual std::shared_ptr<VfsDirectory> CreateSubdirectory(std::string_view name) = 0;
    // Creates a new file with name name. Returns a pointer to the new file or nullptr if the
    // operation failed.
    virtual std::shared_ptr<VfsFile> CreateFile(std::string_view name) = 0;

    // 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.
    virtual std::shared_ptr<VfsFile> CreateFileRelative(std::string_view path);

    // 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.
    virtual std::shared_ptr<VfsFile> CreateFileAbsolute(std::string_view path);

    // 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.
    virtual std::shared_ptr<VfsDirectory> CreateDirectoryRelative(std::string_view path);

    // 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.
    virtual std::shared_ptr<VfsDirectory> CreateDirectoryAbsolute(std::string_view path);

    // Deletes the subdirectory with name and returns true on success.
    virtual bool DeleteSubdirectory(std::string_view name) = 0;
    // Deletes all subdirectories and files of subdirectory with name recirsively and then deletes
    // the subdirectory. Returns true on success.
    virtual bool DeleteSubdirectoryRecursive(std::string_view name);
    // Returnes whether or not the file with name name was deleted successfully.
    virtual bool DeleteFile(std::string_view name) = 0;

    // Returns whether or not this directory was renamed to name.
    virtual bool Rename(std::string_view name) = 0;

    // Returns whether or not the file with name src was successfully copied to a new file with name
    // dest.
    virtual bool Copy(std::string_view src, std::string_view dest);

    // Gets all of the entries directly in the directory (files and dirs), returning a map between
    // item name -> type.
    virtual std::map<std::string, VfsEntryType, std::less<>> GetEntries() const;

    // Interprets the file with name file instead as a directory of type directory.
    // The directory must have a constructor that takes a single argument of type
    // std::shared_ptr<VfsFile>. Allows to reinterpret container files (i.e NCA, zip, XCI, etc) as a
    // subdirectory in one call.
    template <typename Directory>
    bool InterpretAsDirectory(std::string_view file) {
        auto file_p = GetFile(file);

        if (file_p == nullptr) {
            return false;
        }

        return ReplaceFileWithSubdirectory(file_p, std::make_shared<Directory>(file_p));
    }

    bool InterpretAsDirectory(const std::function<VirtualDir(VirtualFile)>& function,
                              const std::string& file) {
        auto file_p = GetFile(file);
        if (file_p == nullptr)
            return false;
        return ReplaceFileWithSubdirectory(file_p, function(file_p));
    }

    // Returns the full path of this directory as a string, recursively
    virtual std::string GetFullPath() const;

protected:
    // Backend for InterpretAsDirectory.
    // Removes all references to file and adds a reference to dir in the directory's implementation.
    virtual bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) = 0;
};

// A convenience partial-implementation of VfsDirectory that stubs out methods that should only work
// if writable. This is to avoid redundant empty methods everywhere.
class ReadOnlyVfsDirectory : public VfsDirectory {
public:
    bool IsWritable() const override;
    bool IsReadable() const override;
    std::shared_ptr<VfsDirectory> CreateSubdirectory(std::string_view name) override;
    std::shared_ptr<VfsFile> CreateFile(std::string_view name) override;
    bool DeleteSubdirectory(std::string_view name) override;
    bool DeleteFile(std::string_view name) override;
    bool Rename(std::string_view name) override;
};

// Compare the two files, byte-for-byte, in increments specificed by block_size
bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, size_t block_size = 0x1000);

// 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.
bool VfsRawCopy(const VirtualFile& src, const VirtualFile& dest, size_t block_size = 0x1000);

// 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.
bool VfsRawCopyD(const VirtualDir& src, const VirtualDir& dest, size_t block_size = 0x1000);

// 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);

} // namespace FileSys