summaryrefslogtreecommitdiff
path: root/src/common/fs/file.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/fs/file.h')
-rw-r--r--src/common/fs/file.h450
1 files changed, 450 insertions, 0 deletions
diff --git a/src/common/fs/file.h b/src/common/fs/file.h
new file mode 100644
index 000000000..209f9664b
--- /dev/null
+++ b/src/common/fs/file.h
@@ -0,0 +1,450 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <cstdio>
8#include <filesystem>
9#include <fstream>
10#include <span>
11#include <type_traits>
12#include <vector>
13
14#include "common/concepts.h"
15#include "common/fs/fs_types.h"
16#include "common/fs/fs_util.h"
17
18namespace Common::FS {
19
20enum class SeekOrigin {
21 SetOrigin, // Seeks from the start of the file.
22 CurrentPosition, // Seeks from the current file pointer position.
23 End, // Seeks from the end of the file.
24};
25
26/**
27 * Opens a file stream at path with the specified open mode.
28 *
29 * @param file_stream Reference to file stream
30 * @param path Filesystem path
31 * @param open_mode File stream open mode
32 */
33template <typename FileStream>
34void OpenFileStream(FileStream& file_stream, const std::filesystem::path& path,
35 std::ios_base::openmode open_mode) {
36 file_stream.open(path, open_mode);
37}
38
39#ifdef _WIN32
40template <typename FileStream, typename Path>
41void OpenFileStream(FileStream& file_stream, const Path& path, std::ios_base::openmode open_mode) {
42 if constexpr (IsChar<typename Path::value_type>) {
43 file_stream.open(ToU8String(path), open_mode);
44 } else {
45 file_stream.open(std::filesystem::path{path}, open_mode);
46 }
47}
48#endif
49
50/**
51 * Reads an entire file at path and returns a string of the contents read from the file.
52 * If the filesystem object at path is not a file, this function returns an empty string.
53 *
54 * @param path Filesystem path
55 * @param type File type
56 *
57 * @returns A string of the contents read from the file.
58 */
59[[nodiscard]] std::string ReadStringFromFile(const std::filesystem::path& path, FileType type);
60
61#ifdef _WIN32
62template <typename Path>
63[[nodiscard]] std::string ReadStringFromFile(const Path& path, FileType type) {
64 if constexpr (IsChar<typename Path::value_type>) {
65 return ReadStringFromFile(ToU8String(path), type);
66 } else {
67 return ReadStringFromFile(std::filesystem::path{path}, type);
68 }
69}
70#endif
71
72/**
73 * Writes a string to a file at path and returns the number of characters successfully written.
74 * If an file already exists at path, its contents will be erased.
75 * If the filesystem object at path is not a file, this function returns 0.
76 *
77 * @param path Filesystem path
78 * @param type File type
79 *
80 * @returns Number of characters successfully written.
81 */
82[[nodiscard]] size_t WriteStringToFile(const std::filesystem::path& path, FileType type,
83 std::string_view string);
84
85#ifdef _WIN32
86template <typename Path>
87[[nodiscard]] size_t WriteStringToFile(const Path& path, FileType type, std::string_view string) {
88 if constexpr (IsChar<typename Path::value_type>) {
89 return WriteStringToFile(ToU8String(path), type, string);
90 } else {
91 return WriteStringToFile(std::filesystem::path{path}, type, string);
92 }
93}
94#endif
95
96/**
97 * Appends a string to a file at path and returns the number of characters successfully written.
98 * If a file does not exist at path, WriteStringToFile is called instead.
99 * If the filesystem object at path is not a file, this function returns 0.
100 *
101 * @param path Filesystem path
102 * @param type File type
103 *
104 * @returns Number of characters successfully written.
105 */
106[[nodiscard]] size_t AppendStringToFile(const std::filesystem::path& path, FileType type,
107 std::string_view string);
108
109#ifdef _WIN32
110template <typename Path>
111[[nodiscard]] size_t AppendStringToFile(const Path& path, FileType type, std::string_view string) {
112 if constexpr (IsChar<typename Path::value_type>) {
113 return AppendStringToFile(ToU8String(path), type, string);
114 } else {
115 return AppendStringToFile(std::filesystem::path{path}, type, string);
116 }
117}
118#endif
119
120class IOFile final : NonCopyable {
121public:
122 IOFile();
123
124 explicit IOFile(const std::string& path, FileAccessMode mode,
125 FileType type = FileType::BinaryFile,
126 FileShareFlag flag = FileShareFlag::ShareReadOnly);
127
128 explicit IOFile(std::string_view path, FileAccessMode mode,
129 FileType type = FileType::BinaryFile,
130 FileShareFlag flag = FileShareFlag::ShareReadOnly);
131
132 /**
133 * An IOFile is a lightweight wrapper on C Library file operations.
134 * Automatically closes an open file on the destruction of an IOFile object.
135 *
136 * @param path Filesystem path
137 * @param mode File access mode
138 * @param type File type, default is BinaryFile. Use TextFile to open the file as a text file
139 * @param flag (Windows only) File-share access flag, default is ShareReadOnly
140 */
141 explicit IOFile(const std::filesystem::path& path, FileAccessMode mode,
142 FileType type = FileType::BinaryFile,
143 FileShareFlag flag = FileShareFlag::ShareReadOnly);
144
145 virtual ~IOFile();
146
147 IOFile(IOFile&& other) noexcept;
148 IOFile& operator=(IOFile&& other) noexcept;
149
150 /**
151 * Gets the path of the file.
152 *
153 * @returns The path of the file.
154 */
155 [[nodiscard]] std::filesystem::path GetPath() const;
156
157 /**
158 * Gets the access mode of the file.
159 *
160 * @returns The access mode of the file.
161 */
162 [[nodiscard]] FileAccessMode GetAccessMode() const;
163
164 /**
165 * Gets the type of the file.
166 *
167 * @returns The type of the file.
168 */
169 [[nodiscard]] FileType GetType() const;
170
171 /**
172 * Opens a file at path with the specified file access mode.
173 * This function behaves differently depending on the FileAccessMode.
174 * These behaviors are documented in each enum value of FileAccessMode.
175 *
176 * @param path Filesystem path
177 * @param mode File access mode
178 * @param type File type, default is BinaryFile. Use TextFile to open the file as a text file
179 * @param flag (Windows only) File-share access flag, default is ShareReadOnly
180 */
181 void Open(const std::filesystem::path& path, FileAccessMode mode,
182 FileType type = FileType::BinaryFile,
183 FileShareFlag flag = FileShareFlag::ShareReadOnly);
184
185#ifdef _WIN32
186 template <typename Path>
187 [[nodiscard]] void Open(const Path& path, FileAccessMode mode,
188 FileType type = FileType::BinaryFile,
189 FileShareFlag flag = FileShareFlag::ShareReadOnly) {
190 using ValueType = typename Path::value_type;
191 if constexpr (IsChar<ValueType>) {
192 Open(ToU8String(path), mode, type, flag);
193 } else {
194 Open(std::filesystem::path{path}, mode, type, flag);
195 }
196 }
197#endif
198
199 /// Closes the file if it is opened.
200 void Close();
201
202 /**
203 * Checks whether the file is open.
204 * Use this to check whether the calls to Open() or Close() succeeded.
205 *
206 * @returns True if the file is open, false otherwise.
207 */
208 [[nodiscard]] bool IsOpen() const;
209
210 /**
211 * Helper function which deduces the value type of a contiguous STL container used in ReadSpan.
212 * If T is not a contiguous STL container as defined by the concept IsSTLContainer, this calls
213 * ReadObject and T must be a trivially copyable object.
214 *
215 * See ReadSpan for more details if T is a contiguous container.
216 * See ReadObject for more details if T is a trivially copyable object.
217 *
218 * @tparam T Contiguous container or trivially copyable object
219 *
220 * @param data Container of T::value_type data or reference to object
221 *
222 * @returns Count of T::value_type data or objects successfully read.
223 */
224 template <typename T>
225 [[nodiscard]] size_t Read(T& data) const {
226 if constexpr (IsSTLContainer<T>) {
227 using ContiguousType = typename T::value_type;
228 static_assert(std::is_trivially_copyable_v<ContiguousType>,
229 "Data type must be trivially copyable.");
230 return ReadSpan<ContiguousType>(data);
231 } else {
232 return ReadObject(data) ? 1 : 0;
233 }
234 }
235
236 /**
237 * Helper function which deduces the value type of a contiguous STL container used in WriteSpan.
238 * If T is not a contiguous STL container as defined by the concept IsSTLContainer, this calls
239 * WriteObject and T must be a trivially copyable object.
240 *
241 * See WriteSpan for more details if T is a contiguous container.
242 * See WriteObject for more details if T is a trivially copyable object.
243 *
244 * @tparam T Contiguous container or trivially copyable object
245 *
246 * @param data Container of T::value_type data or const reference to object
247 *
248 * @returns Count of T::value_type data or objects successfully written.
249 */
250 template <typename T>
251 [[nodiscard]] size_t Write(const T& data) const {
252 if constexpr (IsSTLContainer<T>) {
253 using ContiguousType = typename T::value_type;
254 static_assert(std::is_trivially_copyable_v<ContiguousType>,
255 "Data type must be trivially copyable.");
256 return WriteSpan<ContiguousType>(data);
257 } else {
258 static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
259 return WriteObject(data) ? 1 : 0;
260 }
261 }
262
263 /**
264 * Reads a span of T data from a file sequentially.
265 * This function reads from the current position of the file pointer and
266 * advances it by the (count of T * sizeof(T)) bytes successfully read.
267 *
268 * Failures occur when:
269 * - The file is not open
270 * - The opened file lacks read permissions
271 * - Attempting to read beyond the end-of-file
272 *
273 * @tparam T Data type
274 *
275 * @param data Span of T data
276 *
277 * @returns Count of T data successfully read.
278 */
279 template <typename T>
280 [[nodiscard]] size_t ReadSpan(std::span<T> data) const {
281 static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
282
283 if (!IsOpen()) {
284 return 0;
285 }
286
287 return std::fread(data.data(), sizeof(T), data.size(), file);
288 }
289
290 /**
291 * Writes a span of T data to a file sequentially.
292 * This function writes from the current position of the file pointer and
293 * advances it by the (count of T * sizeof(T)) bytes successfully written.
294 *
295 * Failures occur when:
296 * - The file is not open
297 * - The opened file lacks write permissions
298 *
299 * @tparam T Data type
300 *
301 * @param data Span of T data
302 *
303 * @returns Count of T data successfully written.
304 */
305 template <typename T>
306 [[nodiscard]] size_t WriteSpan(std::span<const T> data) const {
307 static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
308
309 if (!IsOpen()) {
310 return 0;
311 }
312
313 return std::fwrite(data.data(), sizeof(T), data.size(), file);
314 }
315
316 /**
317 * Reads a T object from a file sequentially.
318 * This function reads from the current position of the file pointer and
319 * advances it by the sizeof(T) bytes successfully read.
320 *
321 * Failures occur when:
322 * - The file is not open
323 * - The opened file lacks read permissions
324 * - Attempting to read beyond the end-of-file
325 *
326 * @tparam T Data type
327 *
328 * @param object Reference to object
329 *
330 * @returns True if the object is successfully read from the file, false otherwise.
331 */
332 template <typename T>
333 [[nodiscard]] bool ReadObject(T& object) const {
334 static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
335 static_assert(!std::is_pointer_v<T>, "T must not be a pointer to an object.");
336
337 if (!IsOpen()) {
338 return false;
339 }
340
341 return std::fread(&object, sizeof(T), 1, file) == 1;
342 }
343
344 /**
345 * Writes a T object to a file sequentially.
346 * This function writes from the current position of the file pointer and
347 * advances it by the sizeof(T) bytes successfully written.
348 *
349 * Failures occur when:
350 * - The file is not open
351 * - The opened file lacks write permissions
352 *
353 * @tparam T Data type
354 *
355 * @param object Const reference to object
356 *
357 * @returns True if the object is successfully written to the file, false otherwise.
358 */
359 template <typename T>
360 [[nodiscard]] bool WriteObject(const T& object) const {
361 static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
362 static_assert(!std::is_pointer_v<T>, "T must not be a pointer to an object.");
363
364 if (!IsOpen()) {
365 return false;
366 }
367
368 return std::fwrite(&object, sizeof(T), 1, file) == 1;
369 }
370
371 /**
372 * Specialized function to read a string of a given length from a file sequentially.
373 * This function writes from the current position of the file pointer and
374 * advances it by the number of characters successfully read.
375 * The size of the returned string may not match length if not all bytes are successfully read.
376 *
377 * @param length Length of the string
378 *
379 * @returns A string read from the file.
380 */
381 [[nodiscard]] std::string ReadString(size_t length) const;
382
383 /**
384 * Specialized function to write a string to a file sequentially.
385 * This function writes from the current position of the file pointer and
386 * advances it by the number of characters successfully written.
387 *
388 * @param string Span of const char backed std::string or std::string_view
389 *
390 * @returns Number of characters successfully written.
391 */
392 [[nodiscard]] size_t WriteString(std::span<const char> string) const;
393
394 /**
395 * Flushes any unwritten buffered data into the file.
396 *
397 * @returns True if the flush was successful, false otherwise.
398 */
399 [[nodiscard]] bool Flush() const;
400
401 /**
402 * Resizes the file to a given size.
403 * If the file is resized to a smaller size, the remainder of the file is discarded.
404 * If the file is resized to a larger size, the new area appears as if zero-filled.
405 *
406 * Failures occur when:
407 * - The file is not open
408 *
409 * @param size File size in bytes
410 *
411 * @returns True if the file resize succeeded, false otherwise.
412 */
413 [[nodiscard]] bool SetSize(u64 size) const;
414
415 /**
416 * Gets the size of the file.
417 *
418 * Failures occur when:
419 * - The file is not open
420 *
421 * @returns The file size in bytes of the file. Returns 0 on failure.
422 */
423 [[nodiscard]] u64 GetSize() const;
424
425 /**
426 * Moves the current position of the file pointer with the specified offset and seek origin.
427 *
428 * @param offset Offset from seek origin
429 * @param origin Seek origin
430 *
431 * @returns True if the file pointer has moved to the specified offset, false otherwise.
432 */
433 [[nodiscard]] bool Seek(s64 offset, SeekOrigin origin = SeekOrigin::SetOrigin) const;
434
435 /**
436 * Gets the current position of the file pointer.
437 *
438 * @returns The current position of the file pointer.
439 */
440 [[nodiscard]] s64 Tell() const;
441
442private:
443 std::filesystem::path file_path;
444 FileAccessMode file_access_mode;
445 FileType file_type;
446
447 std::FILE* file = nullptr;
448};
449
450} // namespace Common::FS