1 /* Copyright (C) 2017 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 namespace ERR
35 {
36 	const Status VFS_DIR_NOT_FOUND   = -110100;
37 	const Status VFS_FILE_NOT_FOUND  = -110101;
38 	const Status VFS_ALREADY_MOUNTED = -110102;
39 }
40 
41 // (recursive mounting and mounting archives are no longer optional since they don't hurt)
42 enum VfsMountFlags
43 {
44 	/**
45 	 * all real directories mounted during this operation will be watched
46 	 * for changes. this flag is provided to avoid watches in output-only
47 	 * directories, e.g. screenshots/ (only causes unnecessary overhead).
48 	 **/
49 	VFS_MOUNT_WATCH = 1,
50 
51 	/**
52 	 * anything mounted from here should be included when building archives.
53 	 **/
54 	VFS_MOUNT_ARCHIVABLE = 2,
55 
56 	/**
57 	 * return ERR::VFS_DIR_NOT_FOUND if the given real path doesn't exist.
58 	 * (the default behavior is to create all real directories in the path)
59 	 **/
60 	VFS_MOUNT_MUST_EXIST = 4,
61 
62 	/**
63 	 * keep the files named "*.DELETED" visible in the VFS directories.
64 	 * the standard behavior of hiding the file with the same name minus the
65 	 * ".DELETED" suffix will still apply.
66 	 * (the default behavior is to hide both the suffixed and unsuffixed files)
67 	 **/
68 	VFS_MOUNT_KEEP_DELETED = 8,
69 
70 	/**
71 	 * mark a directory replaceable, so that when writing a file to this path
72 	 * new real directories will be created instead of reusing already existing
73 	 * ones mounted at a subpath of the VFS path.
74 	 * (the default behaviour is to write to the real directory associated
75 	 * with the VFS directory that was last mounted to this path (or subpath))
76 	 **/
77 	VFS_MOUNT_REPLACEABLE = 16
78 };
79 
80 // (member functions are thread-safe after the instance has been
81 // constructed - each acquires a pthread mutex.)
82 struct IVFS
83 {
~IVFSIVFS84 	virtual ~IVFS() {}
85 
86 	/**
87 	 * mount a directory into the VFS.
88 	 *
89 	 * @param mountPoint (will be created if it does not already exist)
90 	 * @param path real directory path
91 	 * @param flags
92 	 * @param priority
93 	 * @return Status.
94 	 *
95 	 * if files are encountered that already exist in the VFS (sub)directories,
96 	 * the most recent / highest priority/precedence version is preferred.
97 	 *
98 	 * if files with archive extensions are seen, their contents are added
99 	 * as well.
100 	 **/
101 	virtual Status Mount(const VfsPath& mountPoint, const OsPath& path, size_t flags = 0, size_t priority = 0) = 0;
102 
103 	/**
104 	 * Retrieve information about a file (similar to POSIX stat).
105 	 *
106 	 * @param pathname
107 	 * @param pfileInfo receives information about the file. Passing NULL
108 	 *		  suppresses warnings if the file doesn't exist.
109 	 *
110 	 * @return Status.
111 	 **/
112 	virtual Status GetFileInfo(const VfsPath& pathname, CFileInfo* pfileInfo) const = 0;
113 
114 	/**
115 	 * Retrieve mount priority for a file.
116 	 *
117 	 * @param pathname
118 	 * @param ppriority receives priority value, if the file can be found.
119 	 *
120 	 * @return Status.
121 	 **/
122 	virtual Status GetFilePriority(const VfsPath& pathname, size_t* ppriority) const = 0;
123 
124 	/**
125 	 * Retrieve lists of all files and subdirectories in a directory.
126 	 *
127 	 * @return Status.
128 	 *
129 	 * Rationale:
130 	 * - this interface avoids having to lock the directory while an
131 	 *   iterator is extant.
132 	 * - we cannot efficiently provide routines for returning files and
133 	 *   subdirectories separately due to the underlying POSIX interface.
134 	 **/
135 	virtual Status GetDirectoryEntries(const VfsPath& path, CFileInfos* fileInfos, DirectoryNames* subdirectoryNames) const = 0;
136 
137 	/**
138 	 * Create a file with the given contents.
139 	 * @param pathname
140 	 * @param fileContents
141 	 * @param size [bytes] of the contents, will match that of the file.
142 	 * @return Status.
143 	 **/
144 	virtual Status CreateFile(const VfsPath& pathname, const shared_ptr<u8>& fileContents, size_t size) = 0;
145 
146 	/**
147 	 * Replace a file with the given contents.
148 	 *
149 	 * @see CreateFile
150 	 *
151 	 * Used to replace a file if it is already present (even if the file is not
152 	 * in the attached vfs directory). Calls CreateFile if the file doesn't yet
153 	 * exist.
154 	  **/
155 	virtual Status ReplaceFile(const VfsPath& pathname, const shared_ptr<u8>& fileContents, size_t size) = 0;
156 
157 	/**
158 	 * Read an entire file into memory.
159 	 *
160 	 * @param pathname
161 	 * @param fileContents receives a smart pointer to the contents.
162 	 * @param size receives the size [bytes] of the file contents.
163 	 * @return Status.
164 	 **/
165 	virtual Status LoadFile(const VfsPath& pathname, shared_ptr<u8>& fileContents, size_t& size) = 0;
166 
167 	/**
168 	 * @return a string representation of all files and directories.
169 	 **/
170 	virtual std::wstring TextRepresentation() const = 0;
171 
172 	/**
173 	 * retrieve the real (POSIX) pathname underlying a VFS file.
174 	 *
175 	 * this is useful for passing paths to external libraries.
176 	 **/
177 	virtual Status GetRealPath(const VfsPath& pathname, OsPath& realPathname) = 0;
178 
179 	/**
180 	 * retrieve the real (POSIX) pathname underlying a VFS directory.
181 	 *
182 	 * this is useful for passing paths to external libraries.
183 	 **/
184 	virtual Status GetDirectoryRealPath(const VfsPath& pathname, OsPath& realPathname) = 0;
185 
186 	/**
187 	 * retrieve the VFS pathname that corresponds to a real file.
188 	 *
189 	 * this is useful for reacting to file change notifications.
190 	 *
191 	 * the current implementation requires time proportional to the
192 	 * number of directories; this could be accelerated by only checking
193 	 * directories below a mount point with a matching real path.
194 	 **/
195 	virtual Status GetVirtualPath(const OsPath& realPathname, VfsPath& pathname) = 0;
196 
197 	/**
198 	 * remove file from the virtual directory listing.
199 	 **/
200 	virtual Status RemoveFile(const VfsPath& pathname) = 0;
201 
202 	/**
203 	 * request the directory be re-populated when it is next accessed.
204 	 * useful for synchronizing with the underlying filesystem after
205 	 * files have been created or their metadata changed.
206 	 **/
207 	virtual Status RepopulateDirectory(const VfsPath& path) = 0;
208 
209 	/**
210 	 * empty the contents of the filesystem.
211 	 * this is typically only necessary when changing the set of
212 	 * mounted directories, e.g. when switching mods.
213 	 * NB: open files are not affected.
214 	 **/
215 	virtual void Clear() = 0;
216 };
217 
218 typedef shared_ptr<IVFS> PIVFS;
219 
220 /**
221  * create an instance of a Virtual File System.
222  *
223  * note: there is no limitation to a single instance, it may make sense
224  * to create and destroy VFS instances during each unit test.
225  **/
226 LIB_API PIVFS CreateVfs();
227 
228 #endif	// #ifndef INCLUDED_VFS
229