1//************************************ bs::framework - Copyright 2018 Marko Pintera **************************************//
2//*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********//
3#pragma once
4
5#include "Prerequisites/BsPrerequisitesUtil.h"
6
7namespace bs
8{
9 /** @addtogroup Filesystem
10 * @{
11 */
12
13 /** Utility class for dealing with files. */
14 class BS_UTILITY_EXPORT FileSystem
15 {
16 public:
17 /**
18 * Opens a file and returns a data stream capable of reading or writing to that file.
19 *
20 * @param[in] fullPath Full path to a file.
21 * @param[in] readOnly (optional) If true, returned stream will only be readable.
22 */
23 static SPtr<DataStream> openFile(const Path& fullPath, bool readOnly = true);
24
25 /**
26 * Opens a file and returns a data stream capable of reading and writing to that file. If file doesn't exist new
27 * one will be created.
28 *
29 * @param[in] fullPath Full path to a file.
30 */
31 static SPtr<DataStream> createAndOpenFile(const Path& fullPath);
32
33 /**
34 * Returns the size of a file in bytes.
35 *
36 * @param[in] fullPath Full path to a file.
37 */
38 static UINT64 getFileSize(const Path& fullPath);
39
40 /**
41 * Deletes a file or a folder at the specified path.
42 *
43 * @param[in] fullPath Full path to a file or a folder..
44 * @param[in] recursively (optional) If true, folders will have their contents deleted as well. Otherwise an
45 * exception will be thrown for non-empty folders.
46 */
47 static void remove(const Path& fullPath, bool recursively = true);
48
49 /**
50 * Moves a file or a folder from one to another path. This can also be used as a rename operation.
51 *
52 * @param[in] oldPath Full path to the old file/folder.
53 * @param[in] newPath Full path to the new file/folder.
54 * @param[in] overwriteExisting (optional) If true, any existing file/folder at the new location will be
55 * overwritten, otherwise an exception will be thrown if a file/folder already exists.
56 */
57 static void move(const Path& oldPath, const Path& newPath, bool overwriteExisting = true);
58
59 /**
60 * Makes a copy of a file or a folder in the specified path.
61 *
62 * @param[in] oldPath Full path to the old file/folder.
63 * @param[in] newPath Full path to the new file/folder.
64 * @param[in] overwriteExisting (optional) If true, any existing file/folder at the new location will be
65 * overwritten, otherwise an exception will be thrown if a file/folder already exists.
66 */
67 static void copy(const Path& oldPath, const Path& newPath, bool overwriteExisting = true);
68
69 /**
70 * Creates a folder at the specified path.
71 *
72 * @param[in] fullPath Full path to a full folder to create.
73 */
74 static void createDir(const Path& fullPath);
75
76 /**
77 * Returns true if a file or a folder exists at the specified path.
78 *
79 * @param[in] fullPath Full path to a file or folder.
80 */
81 static bool exists(const Path& fullPath);
82
83 /**
84 * Returns true if a file exists at the specified path.
85 *
86 * @param[in] fullPath Full path to a file or folder.
87 */
88 static bool isFile(const Path& fullPath);
89
90 /**
91 * Returns true if a folder exists at the specified path.
92 *
93 * @param[in] fullPath Full path to a file or folder.
94 */
95 static bool isDirectory(const Path& fullPath);
96
97 /**
98 * Returns all files or folders located in the specified folder.
99 *
100 * @param[in] dirPath Full path to the folder to retrieve children files/folders from.
101 * @param[out] files Full paths to all files located directly in specified folder.
102 * @param[out] directories Full paths to all folders located directly in specified folder.
103 */
104 static void getChildren(const Path& dirPath, Vector<Path>& files, Vector<Path>& directories);
105
106 /**
107 * Iterates over all files and directories in the specified folder and calls the provided callback when a
108 * file/folder is iterated over.
109 *
110 * @param[in] dirPath Directory over which to iterate
111 * @param[in] fileCallback Callback to call whenever a file is found. If callback returns false iteration stops. Can be null.
112 * @param[in] dirCallback Callback to call whenever a directory is found. If callback returns false iteration stops. Can be null.
113 * @param[in] recursive If false then only the direct children of the provided folder will be iterated over,
114 * and if true then child directories will be recursively visited as well.
115 * @return True if iteration finished iterating over all files/folders, or false if it was
116 * interrupted by a callback returning false.
117 */
118 static bool iterate(const Path& dirPath, std::function<bool(const Path&)> fileCallback,
119 std::function<bool(const Path&)> dirCallback = nullptr, bool recursive = true);
120
121 /**
122 * Returns the last modified time of a file or a folder at the specified path.
123 *
124 * @param[in] fullPath Full path to a file or a folder.
125 */
126 static std::time_t getLastModifiedTime(const Path& fullPath);
127
128 /** Returns the path to the currently working directory. */
129 static Path getWorkingDirectoryPath();
130
131 /** Returns the path to a directory where temporary files may be stored. */
132 static Path getTempDirectoryPath();
133
134 private:
135 /** Copy a single file. Internal function used by copy(). */
136 static void copyFile(const Path& oldPath, const Path& newPath);
137 /** Remove a single file. Internal function used by remove(). */
138 static void removeFile(const Path& path);
139 /** Move a single file. Internal function used by move(). */
140 static void moveFile(const Path& oldPath, const Path& newPath);
141 };
142
143 /**
144 * Locks access to files on the same drive, allowing only one file to be read at a time, per drive. This prevents
145 * multiple threads accessing multiple files on the same drive at once, ruining performance on mechanical drives.
146 */
147 class BS_UTILITY_EXPORT FileScheduler final
148 {
149 public:
150 /**
151 * Locks access and doesn't allow other threads to get past this point until access is unlocked. Any scheduled
152 * file access should happen past this point.
153 */
154 static void lock(const Path& path)
155 {
156 // Note: File path should be analyzed and determined on which drive does the path belong to. Locks can then
157 // be issued on a per-drive basis, instead of having one global lock. This would allow multiple files to be
158 // accessed at the same time, as long as they're on different drives.
159 mMutex.lock();
160 }
161
162 /**
163 * Unlocks access and allows another thread to lock file access. Must be provided with the same file path as
164 * lock().
165 */
166 static void unlock(const Path& path)
167 {
168 mMutex.unlock();
169 }
170
171 /**
172 * Returns a lock object that immediately locks access (same as lock()), and then calls unlock() when it goes
173 * out of scope.
174 */
175 static Lock getLock(const Path& path)
176 {
177 return Lock(mMutex);
178 }
179
180 private:
181 static Mutex mMutex;
182 };
183
184 /** @} */
185}
186