2022-04-23 04:59:50 -04:00
|
|
|
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2021-05-25 19:32:56 -04:00
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include <cstdio>
|
|
|
|
#include <filesystem>
|
|
|
|
#include <span>
|
|
|
|
#include <type_traits>
|
|
|
|
|
|
|
|
#include "common/concepts.h"
|
|
|
|
#include "common/fs/fs_types.h"
|
|
|
|
#include "common/fs/fs_util.h"
|
|
|
|
|
|
|
|
namespace Common::FS {
|
|
|
|
|
|
|
|
enum class SeekOrigin {
|
|
|
|
SetOrigin, // Seeks from the start of the file.
|
|
|
|
CurrentPosition, // Seeks from the current file pointer position.
|
|
|
|
End, // Seeks from the end of the file.
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Opens a file stream at path with the specified open mode.
|
|
|
|
*
|
|
|
|
* @param file_stream Reference to file stream
|
|
|
|
* @param path Filesystem path
|
|
|
|
* @param open_mode File stream open mode
|
|
|
|
*/
|
|
|
|
template <typename FileStream>
|
|
|
|
void OpenFileStream(FileStream& file_stream, const std::filesystem::path& path,
|
|
|
|
std::ios_base::openmode open_mode) {
|
|
|
|
file_stream.open(path, open_mode);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
template <typename FileStream, typename Path>
|
|
|
|
void OpenFileStream(FileStream& file_stream, const Path& path, std::ios_base::openmode open_mode) {
|
|
|
|
if constexpr (IsChar<typename Path::value_type>) {
|
|
|
|
file_stream.open(ToU8String(path), open_mode);
|
|
|
|
} else {
|
|
|
|
file_stream.open(std::filesystem::path{path}, open_mode);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reads an entire file at path and returns a string of the contents read from the file.
|
2021-06-19 03:49:11 -04:00
|
|
|
* If the filesystem object at path is not a regular file, this function returns an empty string.
|
2021-05-25 19:32:56 -04:00
|
|
|
*
|
|
|
|
* @param path Filesystem path
|
|
|
|
* @param type File type
|
|
|
|
*
|
|
|
|
* @returns A string of the contents read from the file.
|
|
|
|
*/
|
|
|
|
[[nodiscard]] std::string ReadStringFromFile(const std::filesystem::path& path, FileType type);
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
template <typename Path>
|
|
|
|
[[nodiscard]] std::string ReadStringFromFile(const Path& path, FileType type) {
|
|
|
|
if constexpr (IsChar<typename Path::value_type>) {
|
|
|
|
return ReadStringFromFile(ToU8String(path), type);
|
|
|
|
} else {
|
|
|
|
return ReadStringFromFile(std::filesystem::path{path}, type);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Writes a string to a file at path and returns the number of characters successfully written.
|
2021-06-13 10:55:59 -04:00
|
|
|
* If a file already exists at path, its contents will be erased.
|
2021-06-19 03:49:11 -04:00
|
|
|
* If a file does not exist at path, it creates and opens a new empty file for writing.
|
|
|
|
* If the filesystem object at path exists and is not a regular file, this function returns 0.
|
2021-05-25 19:32:56 -04:00
|
|
|
*
|
|
|
|
* @param path Filesystem path
|
|
|
|
* @param type File type
|
|
|
|
*
|
|
|
|
* @returns Number of characters successfully written.
|
|
|
|
*/
|
|
|
|
[[nodiscard]] size_t WriteStringToFile(const std::filesystem::path& path, FileType type,
|
|
|
|
std::string_view string);
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
template <typename Path>
|
|
|
|
[[nodiscard]] size_t WriteStringToFile(const Path& path, FileType type, std::string_view string) {
|
|
|
|
if constexpr (IsChar<typename Path::value_type>) {
|
|
|
|
return WriteStringToFile(ToU8String(path), type, string);
|
|
|
|
} else {
|
|
|
|
return WriteStringToFile(std::filesystem::path{path}, type, string);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Appends a string to a file at path and returns the number of characters successfully written.
|
2021-06-19 03:49:11 -04:00
|
|
|
* If a file does not exist at path, it creates and opens a new empty file for appending.
|
|
|
|
* If the filesystem object at path exists and is not a regular file, this function returns 0.
|
2021-05-25 19:32:56 -04:00
|
|
|
*
|
|
|
|
* @param path Filesystem path
|
|
|
|
* @param type File type
|
|
|
|
*
|
|
|
|
* @returns Number of characters successfully written.
|
|
|
|
*/
|
|
|
|
[[nodiscard]] size_t AppendStringToFile(const std::filesystem::path& path, FileType type,
|
|
|
|
std::string_view string);
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
template <typename Path>
|
|
|
|
[[nodiscard]] size_t AppendStringToFile(const Path& path, FileType type, std::string_view string) {
|
|
|
|
if constexpr (IsChar<typename Path::value_type>) {
|
|
|
|
return AppendStringToFile(ToU8String(path), type, string);
|
|
|
|
} else {
|
|
|
|
return AppendStringToFile(std::filesystem::path{path}, type, string);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2021-05-28 01:49:38 -04:00
|
|
|
class IOFile final {
|
2021-05-25 19:32:56 -04:00
|
|
|
public:
|
|
|
|
IOFile();
|
|
|
|
|
|
|
|
explicit IOFile(const std::string& path, FileAccessMode mode,
|
|
|
|
FileType type = FileType::BinaryFile,
|
|
|
|
FileShareFlag flag = FileShareFlag::ShareReadOnly);
|
|
|
|
|
|
|
|
explicit IOFile(std::string_view path, FileAccessMode mode,
|
|
|
|
FileType type = FileType::BinaryFile,
|
|
|
|
FileShareFlag flag = FileShareFlag::ShareReadOnly);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* An IOFile is a lightweight wrapper on C Library file operations.
|
|
|
|
* Automatically closes an open file on the destruction of an IOFile object.
|
|
|
|
*
|
|
|
|
* @param path Filesystem path
|
|
|
|
* @param mode File access mode
|
|
|
|
* @param type File type, default is BinaryFile. Use TextFile to open the file as a text file
|
|
|
|
* @param flag (Windows only) File-share access flag, default is ShareReadOnly
|
|
|
|
*/
|
|
|
|
explicit IOFile(const std::filesystem::path& path, FileAccessMode mode,
|
|
|
|
FileType type = FileType::BinaryFile,
|
|
|
|
FileShareFlag flag = FileShareFlag::ShareReadOnly);
|
|
|
|
|
2021-05-28 01:47:50 -04:00
|
|
|
~IOFile();
|
2021-05-25 19:32:56 -04:00
|
|
|
|
2021-05-28 01:49:38 -04:00
|
|
|
IOFile(const IOFile&) = delete;
|
|
|
|
IOFile& operator=(const IOFile&) = delete;
|
|
|
|
|
2021-05-25 19:32:56 -04:00
|
|
|
IOFile(IOFile&& other) noexcept;
|
|
|
|
IOFile& operator=(IOFile&& other) noexcept;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets the path of the file.
|
|
|
|
*
|
|
|
|
* @returns The path of the file.
|
|
|
|
*/
|
|
|
|
[[nodiscard]] std::filesystem::path GetPath() const;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets the access mode of the file.
|
|
|
|
*
|
|
|
|
* @returns The access mode of the file.
|
|
|
|
*/
|
|
|
|
[[nodiscard]] FileAccessMode GetAccessMode() const;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets the type of the file.
|
|
|
|
*
|
|
|
|
* @returns The type of the file.
|
|
|
|
*/
|
|
|
|
[[nodiscard]] FileType GetType() const;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Opens a file at path with the specified file access mode.
|
|
|
|
* This function behaves differently depending on the FileAccessMode.
|
|
|
|
* These behaviors are documented in each enum value of FileAccessMode.
|
|
|
|
*
|
|
|
|
* @param path Filesystem path
|
|
|
|
* @param mode File access mode
|
|
|
|
* @param type File type, default is BinaryFile. Use TextFile to open the file as a text file
|
|
|
|
* @param flag (Windows only) File-share access flag, default is ShareReadOnly
|
|
|
|
*/
|
|
|
|
void Open(const std::filesystem::path& path, FileAccessMode mode,
|
|
|
|
FileType type = FileType::BinaryFile,
|
|
|
|
FileShareFlag flag = FileShareFlag::ShareReadOnly);
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
template <typename Path>
|
2022-02-01 02:19:16 -05:00
|
|
|
void Open(const Path& path, FileAccessMode mode, FileType type = FileType::BinaryFile,
|
|
|
|
FileShareFlag flag = FileShareFlag::ShareReadOnly) {
|
2021-05-25 19:32:56 -04:00
|
|
|
using ValueType = typename Path::value_type;
|
|
|
|
if constexpr (IsChar<ValueType>) {
|
|
|
|
Open(ToU8String(path), mode, type, flag);
|
|
|
|
} else {
|
|
|
|
Open(std::filesystem::path{path}, mode, type, flag);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/// Closes the file if it is opened.
|
|
|
|
void Close();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Checks whether the file is open.
|
|
|
|
* Use this to check whether the calls to Open() or Close() succeeded.
|
|
|
|
*
|
|
|
|
* @returns True if the file is open, false otherwise.
|
|
|
|
*/
|
|
|
|
[[nodiscard]] bool IsOpen() const;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Helper function which deduces the value type of a contiguous STL container used in ReadSpan.
|
2022-10-26 00:41:54 -04:00
|
|
|
* If T is not a contiguous container as defined by the concept IsContiguousContainer, this
|
|
|
|
* calls ReadObject and T must be a trivially copyable object.
|
2021-05-25 19:32:56 -04:00
|
|
|
*
|
|
|
|
* See ReadSpan for more details if T is a contiguous container.
|
|
|
|
* See ReadObject for more details if T is a trivially copyable object.
|
|
|
|
*
|
|
|
|
* @tparam T Contiguous container or trivially copyable object
|
|
|
|
*
|
|
|
|
* @param data Container of T::value_type data or reference to object
|
|
|
|
*
|
|
|
|
* @returns Count of T::value_type data or objects successfully read.
|
|
|
|
*/
|
|
|
|
template <typename T>
|
|
|
|
[[nodiscard]] size_t Read(T& data) const {
|
2022-10-26 00:41:54 -04:00
|
|
|
if constexpr (IsContiguousContainer<T>) {
|
2021-05-25 19:32:56 -04:00
|
|
|
using ContiguousType = typename T::value_type;
|
|
|
|
static_assert(std::is_trivially_copyable_v<ContiguousType>,
|
|
|
|
"Data type must be trivially copyable.");
|
|
|
|
return ReadSpan<ContiguousType>(data);
|
|
|
|
} else {
|
|
|
|
return ReadObject(data) ? 1 : 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Helper function which deduces the value type of a contiguous STL container used in WriteSpan.
|
2022-10-26 00:41:54 -04:00
|
|
|
* If T is not a contiguous STL container as defined by the concept IsContiguousContainer, this
|
|
|
|
* calls WriteObject and T must be a trivially copyable object.
|
2021-05-25 19:32:56 -04:00
|
|
|
*
|
|
|
|
* See WriteSpan for more details if T is a contiguous container.
|
|
|
|
* See WriteObject for more details if T is a trivially copyable object.
|
|
|
|
*
|
|
|
|
* @tparam T Contiguous container or trivially copyable object
|
|
|
|
*
|
|
|
|
* @param data Container of T::value_type data or const reference to object
|
|
|
|
*
|
|
|
|
* @returns Count of T::value_type data or objects successfully written.
|
|
|
|
*/
|
|
|
|
template <typename T>
|
|
|
|
[[nodiscard]] size_t Write(const T& data) const {
|
2022-10-26 00:41:54 -04:00
|
|
|
if constexpr (IsContiguousContainer<T>) {
|
2021-05-25 19:32:56 -04:00
|
|
|
using ContiguousType = typename T::value_type;
|
|
|
|
static_assert(std::is_trivially_copyable_v<ContiguousType>,
|
|
|
|
"Data type must be trivially copyable.");
|
|
|
|
return WriteSpan<ContiguousType>(data);
|
|
|
|
} else {
|
|
|
|
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
|
|
|
|
return WriteObject(data) ? 1 : 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reads a span of T data from a file sequentially.
|
|
|
|
* This function reads from the current position of the file pointer and
|
|
|
|
* advances it by the (count of T * sizeof(T)) bytes successfully read.
|
|
|
|
*
|
|
|
|
* Failures occur when:
|
|
|
|
* - The file is not open
|
|
|
|
* - The opened file lacks read permissions
|
|
|
|
* - Attempting to read beyond the end-of-file
|
|
|
|
*
|
|
|
|
* @tparam T Data type
|
|
|
|
*
|
|
|
|
* @param data Span of T data
|
|
|
|
*
|
|
|
|
* @returns Count of T data successfully read.
|
|
|
|
*/
|
|
|
|
template <typename T>
|
|
|
|
[[nodiscard]] size_t ReadSpan(std::span<T> data) const {
|
|
|
|
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
|
|
|
|
|
|
|
|
if (!IsOpen()) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return std::fread(data.data(), sizeof(T), data.size(), file);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Writes a span of T data to a file sequentially.
|
|
|
|
* This function writes from the current position of the file pointer and
|
|
|
|
* advances it by the (count of T * sizeof(T)) bytes successfully written.
|
|
|
|
*
|
|
|
|
* Failures occur when:
|
|
|
|
* - The file is not open
|
|
|
|
* - The opened file lacks write permissions
|
|
|
|
*
|
|
|
|
* @tparam T Data type
|
|
|
|
*
|
|
|
|
* @param data Span of T data
|
|
|
|
*
|
|
|
|
* @returns Count of T data successfully written.
|
|
|
|
*/
|
|
|
|
template <typename T>
|
|
|
|
[[nodiscard]] size_t WriteSpan(std::span<const T> data) const {
|
|
|
|
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
|
|
|
|
|
|
|
|
if (!IsOpen()) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return std::fwrite(data.data(), sizeof(T), data.size(), file);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reads a T object from a file sequentially.
|
|
|
|
* This function reads from the current position of the file pointer and
|
|
|
|
* advances it by the sizeof(T) bytes successfully read.
|
|
|
|
*
|
|
|
|
* Failures occur when:
|
|
|
|
* - The file is not open
|
|
|
|
* - The opened file lacks read permissions
|
|
|
|
* - Attempting to read beyond the end-of-file
|
|
|
|
*
|
|
|
|
* @tparam T Data type
|
|
|
|
*
|
|
|
|
* @param object Reference to object
|
|
|
|
*
|
|
|
|
* @returns True if the object is successfully read from the file, false otherwise.
|
|
|
|
*/
|
|
|
|
template <typename T>
|
|
|
|
[[nodiscard]] bool ReadObject(T& object) const {
|
|
|
|
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
|
|
|
|
static_assert(!std::is_pointer_v<T>, "T must not be a pointer to an object.");
|
|
|
|
|
|
|
|
if (!IsOpen()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return std::fread(&object, sizeof(T), 1, file) == 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Writes a T object to a file sequentially.
|
|
|
|
* This function writes from the current position of the file pointer and
|
|
|
|
* advances it by the sizeof(T) bytes successfully written.
|
|
|
|
*
|
|
|
|
* Failures occur when:
|
|
|
|
* - The file is not open
|
|
|
|
* - The opened file lacks write permissions
|
|
|
|
*
|
|
|
|
* @tparam T Data type
|
|
|
|
*
|
|
|
|
* @param object Const reference to object
|
|
|
|
*
|
|
|
|
* @returns True if the object is successfully written to the file, false otherwise.
|
|
|
|
*/
|
|
|
|
template <typename T>
|
|
|
|
[[nodiscard]] bool WriteObject(const T& object) const {
|
|
|
|
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
|
|
|
|
static_assert(!std::is_pointer_v<T>, "T must not be a pointer to an object.");
|
|
|
|
|
|
|
|
if (!IsOpen()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return std::fwrite(&object, sizeof(T), 1, file) == 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Specialized function to read a string of a given length from a file sequentially.
|
|
|
|
* This function writes from the current position of the file pointer and
|
|
|
|
* advances it by the number of characters successfully read.
|
|
|
|
* The size of the returned string may not match length if not all bytes are successfully read.
|
|
|
|
*
|
|
|
|
* @param length Length of the string
|
|
|
|
*
|
|
|
|
* @returns A string read from the file.
|
|
|
|
*/
|
|
|
|
[[nodiscard]] std::string ReadString(size_t length) const;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Specialized function to write a string to a file sequentially.
|
|
|
|
* This function writes from the current position of the file pointer and
|
|
|
|
* advances it by the number of characters successfully written.
|
|
|
|
*
|
|
|
|
* @param string Span of const char backed std::string or std::string_view
|
|
|
|
*
|
|
|
|
* @returns Number of characters successfully written.
|
|
|
|
*/
|
|
|
|
[[nodiscard]] size_t WriteString(std::span<const char> string) const;
|
|
|
|
|
|
|
|
/**
|
2021-07-05 12:32:14 -04:00
|
|
|
* Attempts to flush any unwritten buffered data into the file.
|
2021-05-25 19:32:56 -04:00
|
|
|
*
|
|
|
|
* @returns True if the flush was successful, false otherwise.
|
|
|
|
*/
|
2021-06-19 03:48:02 -04:00
|
|
|
bool Flush() const;
|
2021-05-25 19:32:56 -04:00
|
|
|
|
2021-07-05 12:32:14 -04:00
|
|
|
/**
|
|
|
|
* Attempts to commit the file into the disk.
|
|
|
|
* Note that this is an expensive operation as this forces the operating system to write
|
|
|
|
* the contents of the file associated with the file descriptor into the disk.
|
|
|
|
*
|
|
|
|
* @returns True if the commit was successful, false otherwise.
|
|
|
|
*/
|
|
|
|
bool Commit() const;
|
|
|
|
|
2021-05-25 19:32:56 -04:00
|
|
|
/**
|
|
|
|
* Resizes the file to a given size.
|
|
|
|
* If the file is resized to a smaller size, the remainder of the file is discarded.
|
|
|
|
* If the file is resized to a larger size, the new area appears as if zero-filled.
|
|
|
|
*
|
|
|
|
* Failures occur when:
|
|
|
|
* - The file is not open
|
|
|
|
*
|
|
|
|
* @param size File size in bytes
|
|
|
|
*
|
|
|
|
* @returns True if the file resize succeeded, false otherwise.
|
|
|
|
*/
|
|
|
|
[[nodiscard]] bool SetSize(u64 size) const;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets the size of the file.
|
|
|
|
*
|
|
|
|
* Failures occur when:
|
|
|
|
* - The file is not open
|
|
|
|
*
|
|
|
|
* @returns The file size in bytes of the file. Returns 0 on failure.
|
|
|
|
*/
|
|
|
|
[[nodiscard]] u64 GetSize() const;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Moves the current position of the file pointer with the specified offset and seek origin.
|
|
|
|
*
|
|
|
|
* @param offset Offset from seek origin
|
|
|
|
* @param origin Seek origin
|
|
|
|
*
|
|
|
|
* @returns True if the file pointer has moved to the specified offset, false otherwise.
|
|
|
|
*/
|
|
|
|
[[nodiscard]] bool Seek(s64 offset, SeekOrigin origin = SeekOrigin::SetOrigin) const;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets the current position of the file pointer.
|
|
|
|
*
|
|
|
|
* @returns The current position of the file pointer.
|
|
|
|
*/
|
|
|
|
[[nodiscard]] s64 Tell() const;
|
|
|
|
|
|
|
|
private:
|
|
|
|
std::filesystem::path file_path;
|
2021-05-28 01:46:26 -04:00
|
|
|
FileAccessMode file_access_mode{};
|
|
|
|
FileType file_type{};
|
2021-05-25 19:32:56 -04:00
|
|
|
|
|
|
|
std::FILE* file = nullptr;
|
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace Common::FS
|