Line data Source code
1 : /* Copyright (C) 2022 Wildfire Games.
2 : *
3 : * Permission is hereby granted, free of charge, to any person obtaining
4 : * a copy of this software and associated documentation files (the
5 : * "Software"), to deal in the Software without restriction, including
6 : * without limitation the rights to use, copy, modify, merge, publish,
7 : * distribute, sublicense, and/or sell copies of the Software, and to
8 : * permit persons to whom the Software is furnished to do so, subject to
9 : * the following conditions:
10 : *
11 : * The above copyright notice and this permission notice shall be included
12 : * in all copies or substantial portions of the Software.
13 : *
14 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 : * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 : * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17 : * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18 : * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19 : * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20 : * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 : */
22 :
23 : /*
24 : * Virtual File System API - allows transparent access to files in
25 : * archives, modding via multiple mount points and hotloading.
26 : */
27 :
28 : #ifndef INCLUDED_VFS
29 : #define INCLUDED_VFS
30 :
31 : #include "lib/file/file_system.h" // CFileInfo
32 : #include "lib/file/vfs/vfs_path.h"
33 :
34 : constexpr size_t VFS_MIN_PRIORITY = 0;
35 : constexpr size_t VFS_MAX_PRIORITY = std::numeric_limits<size_t>::max();
36 :
37 : namespace ERR
38 : {
39 : const Status VFS_DIR_NOT_FOUND = -110100;
40 : const Status VFS_FILE_NOT_FOUND = -110101;
41 : const Status VFS_ALREADY_MOUNTED = -110102;
42 : }
43 :
44 : // (recursive mounting and mounting archives are no longer optional since they don't hurt)
45 : enum VfsMountFlags
46 : {
47 : /**
48 : * all real directories mounted during this operation will be watched
49 : * for changes. this flag is provided to avoid watches in output-only
50 : * directories, e.g. screenshots/ (only causes unnecessary overhead).
51 : **/
52 : VFS_MOUNT_WATCH = 1,
53 :
54 : /**
55 : * anything mounted from here should be included when building archives.
56 : **/
57 : VFS_MOUNT_ARCHIVABLE = 2,
58 :
59 : /**
60 : * return ERR::VFS_DIR_NOT_FOUND if the given real path doesn't exist.
61 : * (the default behavior is to create all real directories in the path)
62 : **/
63 : VFS_MOUNT_MUST_EXIST = 4,
64 :
65 : /**
66 : * keep the files named "*.DELETED" visible in the VFS directories.
67 : * the standard behavior of hiding the file with the same name minus the
68 : * ".DELETED" suffix will still apply.
69 : * (the default behavior is to hide both the suffixed and unsuffixed files)
70 : **/
71 : VFS_MOUNT_KEEP_DELETED = 8
72 : };
73 :
74 : // (member functions are thread-safe after the instance has been
75 : // constructed - each acquires a mutex.)
76 83 : struct IVFS
77 : {
78 83 : virtual ~IVFS() {}
79 :
80 : /**
81 : * mount a directory into the VFS.
82 : *
83 : * @param mountPoint (will be created if it does not already exist)
84 : * @param path real directory path
85 : * @param flags
86 : * @param priority
87 : * @return Status.
88 : *
89 : * if files are encountered that already exist in the VFS (sub)directories,
90 : * the most recent / highest priority/precedence version is preferred.
91 : *
92 : * Note that the 'real directory' associated with a VFS Path
93 : * will be relative to the highest priority subdirectory in the path,
94 : * and that in case of equal priority, the order is _undefined_,
95 : * and will depend on the exact order of populate() calls.
96 : *
97 : * if files with archive extensions are seen, their contents are added
98 : * as well.
99 : **/
100 : virtual Status Mount(const VfsPath& mountPoint, const OsPath& path, size_t flags = 0, size_t priority = 0) = 0;
101 :
102 : /**
103 : * Retrieve information about a file (similar to POSIX stat).
104 : *
105 : * @param pathname
106 : * @param pfileInfo receives information about the file. Passing NULL
107 : * suppresses warnings if the file doesn't exist.
108 : *
109 : * @return Status.
110 : **/
111 : virtual Status GetFileInfo(const VfsPath& pathname, CFileInfo* pfileInfo) const = 0;
112 :
113 : /**
114 : * Retrieve mount priority for a file.
115 : *
116 : * @param pathname
117 : * @param ppriority receives priority value, if the file can be found.
118 : *
119 : * @return Status.
120 : **/
121 : virtual Status GetFilePriority(const VfsPath& pathname, size_t* ppriority) const = 0;
122 :
123 : /**
124 : * Retrieve lists of all files and subdirectories in a directory.
125 : *
126 : * @return Status.
127 : *
128 : * Rationale:
129 : * - this interface avoids having to lock the directory while an
130 : * iterator is extant.
131 : * - we cannot efficiently provide routines for returning files and
132 : * subdirectories separately due to the underlying POSIX interface.
133 : **/
134 : virtual Status GetDirectoryEntries(const VfsPath& path, CFileInfos* fileInfos, DirectoryNames* subdirectoryNames) const = 0;
135 :
136 : /**
137 : * Create a file with the given contents.
138 : * @param pathname
139 : * @param fileContents
140 : * @param size [bytes] of the contents, will match that of the file.
141 : * @return Status.
142 : **/
143 : virtual Status CreateFile(const VfsPath& pathname, const std::shared_ptr<u8>& fileContents, size_t size) = 0;
144 :
145 : /**
146 : * Read an entire file into memory.
147 : *
148 : * @param pathname
149 : * @param fileContents receives a smart pointer to the contents.
150 : * @param size receives the size [bytes] of the file contents.
151 : * @return Status.
152 : **/
153 : virtual Status LoadFile(const VfsPath& pathname, std::shared_ptr<u8>& fileContents, size_t& size) = 0;
154 :
155 : /**
156 : * @return a string representation of all files and directories.
157 : **/
158 : virtual std::wstring TextRepresentation() const = 0;
159 :
160 : /**
161 : * Retrieve the POSIX pathname a VFS file was loaded from.
162 : * This is distinct from the current 'real' path, since that depends on the parent directory's real path,
163 : * which may have been overwritten by a mod or another call to Mount().
164 : *
165 : * This is used by the caching to split by mod, and you also ought to call this to delete a file.
166 : * (note that deleting has other issues, see below).
167 : **/
168 : virtual Status GetOriginalPath(const VfsPath& filename, OsPath& loadedPathname) = 0;
169 :
170 : /**
171 : * Retrieve the real (POSIX) pathname underlying a VFS file.
172 : * This is useful for passing paths to external libraries.
173 : * Note that this returns the real path relative to the highest priority VFS subpath.
174 : * @param createMissingDirectories - if true, create subdirectories on the file system as required.
175 : * (this defaults to true because, in general, this function is then used to create new files).
176 : **/
177 : virtual Status GetRealPath(const VfsPath& pathname, OsPath& realPathname, bool createMissingDirectories = true) = 0;
178 :
179 : /**
180 : * Retrieve the real (POSIX) pathname underlying a VFS directory.
181 : * This is useful for passing paths to external libraries.
182 : * Note that this returns the real path relative to the highest priority VFS subpath.
183 : * @param createMissingDirectories - if true, create subdirectories on the file system as required.
184 : * (this defaults to true because, in general, this function is then used to create new files).
185 : **/
186 : virtual Status GetDirectoryRealPath(const VfsPath& pathname, OsPath& realPathname, bool createMissingDirectories = true) = 0;
187 :
188 : /**
189 : * retrieve the VFS pathname that corresponds to a real file.
190 : *
191 : * this is useful for reacting to file change notifications.
192 : *
193 : * the current implementation requires time proportional to the
194 : * number of directories; this could be accelerated by only checking
195 : * directories below a mount point with a matching real path.
196 : **/
197 : virtual Status GetVirtualPath(const OsPath& realPathname, VfsPath& pathname) = 0;
198 :
199 : /**
200 : * remove file from the virtual directory listing.
201 : **/
202 : virtual Status RemoveFile(const VfsPath& pathname) = 0;
203 :
204 : /**
205 : * request the directory be re-populated when it is next accessed.
206 : * useful for synchronizing with the underlying filesystem after
207 : * files have been created or their metadata changed.
208 : **/
209 : virtual Status RepopulateDirectory(const VfsPath& path) = 0;
210 :
211 : /**
212 : * empty the contents of the filesystem.
213 : * this is typically only necessary when changing the set of
214 : * mounted directories, e.g. when switching mods.
215 : * NB: open files are not affected.
216 : **/
217 : virtual void Clear() = 0;
218 : };
219 :
220 : typedef std::shared_ptr<IVFS> PIVFS;
221 :
222 : /**
223 : * create an instance of a Virtual File System.
224 : *
225 : * note: there is no limitation to a single instance, it may make sense
226 : * to create and destroy VFS instances during each unit test.
227 : **/
228 : PIVFS CreateVfs();
229 :
230 : #endif // #ifndef INCLUDED_VFS
|