| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453 |
- // Copyright 2021 yuzu Emulator Project
- // Licensed under GPLv2 or any later version
- // Refer to the license.txt file included.
- #pragma once
- #include <cstdio>
- #include <filesystem>
- #include <fstream>
- #include <span>
- #include <type_traits>
- #include <vector>
- #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.
- * If the filesystem object at path is not a file, this function returns an empty string.
- *
- * @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.
- * If an file already exists at path, its contents will be erased.
- * If the filesystem object at path is not a file, this function returns 0.
- *
- * @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.
- * If a file does not exist at path, WriteStringToFile is called instead.
- * If the filesystem object at path is not a file, this function returns 0.
- *
- * @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
- class IOFile final {
- 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);
- ~IOFile();
- IOFile(const IOFile&) = delete;
- IOFile& operator=(const IOFile&) = delete;
- 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>
- [[nodiscard]] void Open(const Path& path, FileAccessMode mode,
- FileType type = FileType::BinaryFile,
- FileShareFlag flag = FileShareFlag::ShareReadOnly) {
- 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.
- * If T is not a contiguous STL container as defined by the concept IsSTLContainer, this calls
- * ReadObject and T must be a trivially copyable object.
- *
- * 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 {
- if constexpr (IsSTLContainer<T>) {
- 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.
- * If T is not a contiguous STL container as defined by the concept IsSTLContainer, this calls
- * WriteObject and T must be a trivially copyable object.
- *
- * 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 {
- if constexpr (IsSTLContainer<T>) {
- 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;
- /**
- * Flushes any unwritten buffered data into the file.
- *
- * @returns True if the flush was successful, false otherwise.
- */
- [[nodiscard]] bool Flush() const;
- /**
- * 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;
- FileAccessMode file_access_mode{};
- FileType file_type{};
- std::FILE* file = nullptr;
- };
- } // namespace Common::FS
|