// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include #include #include #include #include "common/concepts.h" #include "common/types.h" namespace Common::FS { enum class FileAccessMode { /** * If the file at path exists, it opens the file for reading. * If the file at path does not exist, it fails to open the file. */ Read = 1 << 0, /** * If the file at path exists, the existing contents of the file are erased. * The empty file is then opened for writing. * If the file at path does not exist, it creates and opens a new empty file for writing. */ Write = 1 << 1, /** * If the file at path exists, it opens the file for reading and writing. * If the file at path does not exist, it fails to open the file. */ ReadWrite = Read | Write, /** * If the file at path exists, it opens the file for appending. * If the file at path does not exist, it creates and opens a new empty file for appending. */ Append = 1 << 2, /** * If the file at path exists, it opens the file for both reading and appending. * If the file at path does not exist, it creates and opens a new empty file for both * reading and appending. */ ReadAppend = Read | Append, }; enum class FileType { BinaryFile, TextFile, }; enum class FileShareFlag { ShareNone, // Provides exclusive access to the file. ShareReadOnly, // Provides read only shared access to the file. ShareWriteOnly, // Provides write only shared access to the file. ShareReadWrite, // Provides read and write shared access to the file. }; enum class SeekOrigin : u32 { SetOrigin, // Seeks from the start of the file. CurrentPosition, // Seeks from the current file pointer position. End, // Seeks from the end of the file. }; 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); 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; std::filesystem::path GetPath() const { return file_path; } FileAccessMode GetAccessMode() const { return file_access_mode; } FileType GetType() const { return file_type; } bool IsOpen() const { return file != nullptr; } uintptr_t GetFileMapping(); void Open(const std::filesystem::path& path, FileAccessMode mode, FileType type = FileType::BinaryFile, FileShareFlag flag = FileShareFlag::ShareReadOnly); void Close(); bool Flush() const; bool Commit() const; bool SetSize(u64 size) const; u64 GetSize() const; bool Seek(s64 offset, SeekOrigin origin = SeekOrigin::SetOrigin) const; s64 Tell() const; template size_t Read(T& data) const { if constexpr (IsContiguousContainer) { using ContiguousType = typename T::value_type; static_assert(std::is_trivially_copyable_v, "Data type must be trivially copyable."); return ReadSpan(data); } else { return ReadObject(data) ? 1 : 0; } } template size_t Write(const T& data) const { if constexpr (IsContiguousContainer) { using ContiguousType = typename T::value_type; static_assert(std::is_trivially_copyable_v, "Data type must be trivially copyable."); return WriteSpan(data); } else { static_assert(std::is_trivially_copyable_v, "Data type must be trivially copyable."); return WriteObject(data) ? 1 : 0; } } template size_t ReadSpan(std::span data) const { static_assert(std::is_trivially_copyable_v, "Data type must be trivially copyable."); if (!IsOpen()) { return 0; } return ReadRaw(data.data(), data.size()); } template size_t ReadRaw(void* data, size_t size) const { return std::fread(data, sizeof(T), size, file); } template size_t WriteSpan(std::span data) const { static_assert(std::is_trivially_copyable_v, "Data type must be trivially copyable."); if (!IsOpen()) { return 0; } return std::fwrite(data.data(), sizeof(T), data.size(), file); } template bool ReadObject(T& object) const { static_assert(std::is_trivially_copyable_v, "Data type must be trivially copyable."); static_assert(!std::is_pointer_v, "T must not be a pointer to an object."); if (!IsOpen()) { return false; } return std::fread(&object, sizeof(T), 1, file) == 1; } template size_t WriteRaw(void* data, size_t size) const { return std::fwrite(data, sizeof(T), size, file); } template bool WriteObject(const T& object) const { static_assert(std::is_trivially_copyable_v, "Data type must be trivially copyable."); static_assert(!std::is_pointer_v, "T must not be a pointer to an object."); if (!IsOpen()) { return false; } return std::fwrite(&object, sizeof(T), 1, file) == 1; } std::string ReadString(size_t length) const; size_t WriteString(std::span string) const { return WriteSpan(string); } static void WriteBytes(const std::filesystem::path path, std::span vec) { IOFile out(path, FileAccessMode::Write); out.Write(vec); } private: std::filesystem::path file_path; FileAccessMode file_access_mode{}; FileType file_type{}; std::FILE* file = nullptr; uintptr_t file_mapping = 0; }; } // namespace Common::FS