summaryrefslogtreecommitdiff
path: root/src/common/fs/file.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/fs/file.cpp')
-rw-r--r--src/common/fs/file.cpp392
1 files changed, 392 insertions, 0 deletions
diff --git a/src/common/fs/file.cpp b/src/common/fs/file.cpp
new file mode 100644
index 000000000..9f3de1cb0
--- /dev/null
+++ b/src/common/fs/file.cpp
@@ -0,0 +1,392 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/fs/file.h"
6#include "common/fs/fs.h"
7#include "common/fs/path_util.h"
8#include "common/logging/log.h"
9
10#ifdef _WIN32
11#include <io.h>
12#include <share.h>
13#else
14#include <unistd.h>
15#endif
16
17#ifdef _MSC_VER
18#define fileno _fileno
19#define fseeko _fseeki64
20#define ftello _ftelli64
21#endif
22
23namespace Common::FS {
24
25namespace fs = std::filesystem;
26
27namespace {
28
29#ifdef _WIN32
30
31/**
32 * Converts the file access mode and file type enums to a file access mode wide string.
33 *
34 * @param mode File access mode
35 * @param type File type
36 *
37 * @returns A pointer to a wide string representing the file access mode.
38 */
39[[nodiscard]] constexpr const wchar_t* AccessModeToWStr(FileAccessMode mode, FileType type) {
40 switch (type) {
41 case FileType::BinaryFile:
42 switch (mode) {
43 case FileAccessMode::Read:
44 return L"rb";
45 case FileAccessMode::Write:
46 return L"wb";
47 case FileAccessMode::Append:
48 return L"ab";
49 case FileAccessMode::ReadWrite:
50 return L"r+b";
51 case FileAccessMode::ReadAppend:
52 return L"a+b";
53 }
54 break;
55 case FileType::TextFile:
56 switch (mode) {
57 case FileAccessMode::Read:
58 return L"r";
59 case FileAccessMode::Write:
60 return L"w";
61 case FileAccessMode::Append:
62 return L"a";
63 case FileAccessMode::ReadWrite:
64 return L"r+";
65 case FileAccessMode::ReadAppend:
66 return L"a+";
67 }
68 break;
69 }
70
71 return L"";
72}
73
74/**
75 * Converts the file-share access flag enum to a Windows defined file-share access flag.
76 *
77 * @param flag File-share access flag
78 *
79 * @returns Windows defined file-share access flag.
80 */
81[[nodiscard]] constexpr int ToWindowsFileShareFlag(FileShareFlag flag) {
82 switch (flag) {
83 case FileShareFlag::ShareNone:
84 default:
85 return _SH_DENYRW;
86 case FileShareFlag::ShareReadOnly:
87 return _SH_DENYWR;
88 case FileShareFlag::ShareWriteOnly:
89 return _SH_DENYRD;
90 case FileShareFlag::ShareReadWrite:
91 return _SH_DENYNO;
92 }
93}
94
95#else
96
97/**
98 * Converts the file access mode and file type enums to a file access mode string.
99 *
100 * @param mode File access mode
101 * @param type File type
102 *
103 * @returns A pointer to a string representing the file access mode.
104 */
105[[nodiscard]] constexpr const char* AccessModeToStr(FileAccessMode mode, FileType type) {
106 switch (type) {
107 case FileType::BinaryFile:
108 switch (mode) {
109 case FileAccessMode::Read:
110 return "rb";
111 case FileAccessMode::Write:
112 return "wb";
113 case FileAccessMode::Append:
114 return "ab";
115 case FileAccessMode::ReadWrite:
116 return "r+b";
117 case FileAccessMode::ReadAppend:
118 return "a+b";
119 }
120 break;
121 case FileType::TextFile:
122 switch (mode) {
123 case FileAccessMode::Read:
124 return "r";
125 case FileAccessMode::Write:
126 return "w";
127 case FileAccessMode::Append:
128 return "a";
129 case FileAccessMode::ReadWrite:
130 return "r+";
131 case FileAccessMode::ReadAppend:
132 return "a+";
133 }
134 break;
135 }
136
137 return "";
138}
139
140#endif
141
142/**
143 * Converts the seek origin enum to a seek origin integer.
144 *
145 * @param origin Seek origin
146 *
147 * @returns Seek origin integer.
148 */
149[[nodiscard]] constexpr int ToSeekOrigin(SeekOrigin origin) {
150 switch (origin) {
151 case SeekOrigin::SetOrigin:
152 default:
153 return SEEK_SET;
154 case SeekOrigin::CurrentPosition:
155 return SEEK_CUR;
156 case SeekOrigin::End:
157 return SEEK_END;
158 }
159}
160
161} // Anonymous namespace
162
163std::string ReadStringFromFile(const std::filesystem::path& path, FileType type) {
164 if (!IsFile(path)) {
165 return "";
166 }
167
168 IOFile io_file{path, FileAccessMode::Read, type};
169
170 return io_file.ReadString(io_file.GetSize());
171}
172
173size_t WriteStringToFile(const std::filesystem::path& path, FileType type,
174 std::string_view string) {
175 if (!IsFile(path)) {
176 return 0;
177 }
178
179 IOFile io_file{path, FileAccessMode::Write, type};
180
181 return io_file.WriteString(string);
182}
183
184size_t AppendStringToFile(const std::filesystem::path& path, FileType type,
185 std::string_view string) {
186 if (!Exists(path)) {
187 return WriteStringToFile(path, type, string);
188 }
189
190 if (!IsFile(path)) {
191 return 0;
192 }
193
194 IOFile io_file{path, FileAccessMode::Append, type};
195
196 return io_file.WriteString(string);
197}
198
199IOFile::IOFile() = default;
200
201IOFile::IOFile(const std::string& path, FileAccessMode mode, FileType type, FileShareFlag flag) {
202 Open(path, mode, type, flag);
203}
204
205IOFile::IOFile(std::string_view path, FileAccessMode mode, FileType type, FileShareFlag flag) {
206 Open(path, mode, type, flag);
207}
208
209IOFile::IOFile(const fs::path& path, FileAccessMode mode, FileType type, FileShareFlag flag) {
210 Open(path, mode, type, flag);
211}
212
213IOFile::~IOFile() {
214 Close();
215}
216
217IOFile::IOFile(IOFile&& other) noexcept {
218 std::swap(file_path, other.file_path);
219 std::swap(file_access_mode, other.file_access_mode);
220 std::swap(file_type, other.file_type);
221 std::swap(file, other.file);
222}
223
224IOFile& IOFile::operator=(IOFile&& other) noexcept {
225 std::swap(file_path, other.file_path);
226 std::swap(file_access_mode, other.file_access_mode);
227 std::swap(file_type, other.file_type);
228 std::swap(file, other.file);
229 return *this;
230}
231
232fs::path IOFile::GetPath() const {
233 return file_path;
234}
235
236FileAccessMode IOFile::GetAccessMode() const {
237 return file_access_mode;
238}
239
240FileType IOFile::GetType() const {
241 return file_type;
242}
243
244void IOFile::Open(const fs::path& path, FileAccessMode mode, FileType type, FileShareFlag flag) {
245 Close();
246
247 file_path = path;
248 file_access_mode = mode;
249 file_type = type;
250
251 errno = 0;
252
253#ifdef _WIN32
254 if (flag != FileShareFlag::ShareNone) {
255 file = _wfsopen(path.c_str(), AccessModeToWStr(mode, type), ToWindowsFileShareFlag(flag));
256 } else {
257 _wfopen_s(&file, path.c_str(), AccessModeToWStr(mode, type));
258 }
259#else
260 file = std::fopen(path.c_str(), AccessModeToStr(mode, type));
261#endif
262
263 if (!IsOpen()) {
264 const auto ec = std::error_code{errno, std::generic_category()};
265 LOG_ERROR(Common_Filesystem, "Failed to open the file at path={}, ec_message={}",
266 PathToUTF8String(file_path), ec.message());
267 }
268}
269
270void IOFile::Close() {
271 if (!IsOpen()) {
272 return;
273 }
274
275 errno = 0;
276
277 const auto close_result = std::fclose(file) == 0;
278
279 if (!close_result) {
280 const auto ec = std::error_code{errno, std::generic_category()};
281 LOG_ERROR(Common_Filesystem, "Failed to close the file at path={}, ec_message={}",
282 PathToUTF8String(file_path), ec.message());
283 }
284
285 file = nullptr;
286}
287
288bool IOFile::IsOpen() const {
289 return file != nullptr;
290}
291
292std::string IOFile::ReadString(size_t length) const {
293 std::vector<char> string_buffer(length);
294
295 const auto chars_read = ReadSpan<char>(string_buffer);
296 const auto string_size = chars_read != length ? chars_read : length;
297
298 return std::string{string_buffer.data(), string_size};
299}
300
301size_t IOFile::WriteString(std::span<const char> string) const {
302 return WriteSpan(string);
303}
304
305bool IOFile::Flush() const {
306 if (!IsOpen()) {
307 return false;
308 }
309
310 errno = 0;
311
312 const auto flush_result = std::fflush(file) == 0;
313
314 if (!flush_result) {
315 const auto ec = std::error_code{errno, std::generic_category()};
316 LOG_ERROR(Common_Filesystem, "Failed to flush the file at path={}, ec_message={}",
317 PathToUTF8String(file_path), ec.message());
318 }
319
320 return flush_result;
321}
322
323bool IOFile::SetSize(u64 size) const {
324 if (!IsOpen()) {
325 return false;
326 }
327
328 errno = 0;
329
330#ifdef _WIN32
331 const auto set_size_result = _chsize_s(fileno(file), static_cast<s64>(size)) == 0;
332#else
333 const auto set_size_result = ftruncate(fileno(file), static_cast<s64>(size)) == 0;
334#endif
335
336 if (!set_size_result) {
337 const auto ec = std::error_code{errno, std::generic_category()};
338 LOG_ERROR(Common_Filesystem, "Failed to resize the file at path={}, size={}, ec_message={}",
339 PathToUTF8String(file_path), size, ec.message());
340 }
341
342 return set_size_result;
343}
344
345u64 IOFile::GetSize() const {
346 if (!IsOpen()) {
347 return 0;
348 }
349
350 std::error_code ec;
351
352 const auto file_size = fs::file_size(file_path, ec);
353
354 if (ec) {
355 LOG_ERROR(Common_Filesystem, "Failed to retrieve the file size of path={}, ec_message={}",
356 PathToUTF8String(file_path), ec.message());
357 return 0;
358 }
359
360 return file_size;
361}
362
363bool IOFile::Seek(s64 offset, SeekOrigin origin) const {
364 if (!IsOpen()) {
365 return false;
366 }
367
368 errno = 0;
369
370 const auto seek_result = fseeko(file, offset, ToSeekOrigin(origin)) == 0;
371
372 if (!seek_result) {
373 const auto ec = std::error_code{errno, std::generic_category()};
374 LOG_ERROR(Common_Filesystem,
375 "Failed to seek the file at path={}, offset={}, origin={}, ec_message={}",
376 PathToUTF8String(file_path), offset, origin, ec.message());
377 }
378
379 return seek_result;
380}
381
382s64 IOFile::Tell() const {
383 if (!IsOpen()) {
384 return 0;
385 }
386
387 errno = 0;
388
389 return ftello(file);
390}
391
392} // namespace Common::FS