| | |
| | |
| | |
| |
|
| | #pragma once |
| |
|
| | #include <array> |
| | #include <cstdio> |
| | #include <functional> |
| | #include <ios> |
| | #include <limits> |
| | #include <optional> |
| | #include <string> |
| | #include <string_view> |
| | #include <type_traits> |
| | #include <vector> |
| | #include <boost/serialization/split_member.hpp> |
| | #include <boost/serialization/string.hpp> |
| | #include <boost/serialization/wrapper.hpp> |
| | #include "common/common_types.h" |
| | #ifdef _MSC_VER |
| | #include "common/string_util.h" |
| | #endif |
| |
|
| | namespace FileUtil { |
| |
|
| | |
| | enum class UserPath { |
| | CacheDir, |
| | CheatsDir, |
| | ConfigDir, |
| | DLLDir, |
| | DumpDir, |
| | LoadDir, |
| | LogDir, |
| | NANDDir, |
| | RootDir, |
| | SDMCDir, |
| | ShaderDir, |
| | StatesDir, |
| | SysDataDir, |
| | UserDir, |
| | }; |
| |
|
| | |
| | std::string SerializePath(const std::string& input, bool is_saving); |
| |
|
| | |
| | struct Path : public boost::serialization::wrapper_traits<const Path> { |
| | std::string& str; |
| |
|
| | explicit Path(std::string& _str) : str(_str) {} |
| |
|
| | static const Path make(std::string& str) { |
| | return Path(str); |
| | } |
| |
|
| | template <class Archive> |
| | void save(Archive& ar, const unsigned int) const { |
| | auto s_path = SerializePath(str, true); |
| | ar << s_path; |
| | } |
| | template <class Archive> |
| | void load(Archive& ar, const unsigned int) const { |
| | ar >> str; |
| | str = SerializePath(str, false); |
| | } |
| |
|
| | BOOST_SERIALIZATION_SPLIT_MEMBER(); |
| | friend class boost::serialization::access; |
| | }; |
| |
|
| | |
| | struct FSTEntry { |
| | bool isDirectory; |
| | u64 size; |
| | std::string physicalName; |
| | std::string virtualName; |
| | std::vector<FSTEntry> children; |
| |
|
| | private: |
| | template <class Archive> |
| | void serialize(Archive& ar, const unsigned int) { |
| | ar& isDirectory; |
| | ar& size; |
| | ar& Path::make(physicalName); |
| | ar& Path::make(virtualName); |
| | ar& children; |
| | } |
| | friend class boost::serialization::access; |
| | }; |
| |
|
| | |
| | [[nodiscard]] bool Exists(const std::string& filename); |
| |
|
| | |
| | [[nodiscard]] bool IsDirectory(const std::string& filename); |
| |
|
| | |
| | [[nodiscard]] u64 GetSize(const std::string& filename); |
| |
|
| | |
| | [[nodiscard]] u64 GetSize(int fd); |
| |
|
| | |
| | [[nodiscard]] u64 GetSize(FILE* f); |
| |
|
| | |
| | bool CreateDir(const std::string& filename); |
| |
|
| | |
| | bool CreateFullPath(const std::string& fullPath); |
| |
|
| | |
| | |
| | bool Delete(const std::string& filename); |
| |
|
| | |
| | bool DeleteDir(const std::string& filename); |
| |
|
| | |
| | bool Rename(const std::string& srcFilename, const std::string& destFilename); |
| |
|
| | |
| | bool Copy(const std::string& srcFilename, const std::string& destFilename); |
| |
|
| | |
| | bool CreateEmptyFile(const std::string& filename); |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | using DirectoryEntryCallable = std::function<bool( |
| | u64* num_entries_out, const std::string& directory, const std::string& virtual_name)>; |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | bool ForeachDirectoryEntry(u64* num_entries_out, const std::string& directory, |
| | DirectoryEntryCallable callback); |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | u64 ScanDirectoryTree(const std::string& directory, FSTEntry& parent_entry, |
| | unsigned int recursion = 0); |
| |
|
| | |
| | |
| | |
| | |
| | |
| | void GetAllFilesFromNestedEntries(FSTEntry& directory, std::vector<FSTEntry>& output); |
| |
|
| | |
| | bool DeleteDirRecursively(const std::string& directory, unsigned int recursion = 256); |
| |
|
| | |
| | [[nodiscard]] std::optional<std::string> GetCurrentDir(); |
| |
|
| | |
| | void CopyDir(const std::string& source_path, const std::string& dest_path); |
| |
|
| | |
| | bool SetCurrentDir(const std::string& directory); |
| |
|
| | void SetUserPath(const std::string& path = ""); |
| |
|
| | void SetCurrentRomPath(const std::string& path); |
| |
|
| | |
| | |
| | [[nodiscard]] const std::string& GetUserPath(UserPath path); |
| |
|
| | |
| | |
| | [[nodiscard]] const std::string& GetDefaultUserPath(UserPath path); |
| |
|
| | |
| | void UpdateUserPath(UserPath path, const std::string& filename); |
| |
|
| | #ifdef __APPLE__ |
| | [[nodiscard]] std::optional<std::string> GetBundleDirectory(); |
| | #endif |
| |
|
| | #ifdef _WIN32 |
| | [[nodiscard]] const std::string& GetExeDirectory(); |
| | [[nodiscard]] std::string AppDataRoamingDirectory(); |
| | #endif |
| |
|
| | std::size_t WriteStringToFile(bool text_file, const std::string& filename, std::string_view str); |
| |
|
| | std::size_t ReadFileToString(bool text_file, const std::string& filename, std::string& str); |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | void SplitFilename83(const std::string& filename, std::array<char, 9>& short_name, |
| | std::array<char, 4>& extension); |
| |
|
| | |
| | |
| | [[nodiscard]] std::vector<std::string> SplitPathComponents(std::string_view filename); |
| |
|
| | |
| | [[nodiscard]] std::string_view GetParentPath(std::string_view path); |
| |
|
| | |
| | [[nodiscard]] std::string_view GetPathWithoutTop(std::string_view path); |
| |
|
| | |
| | [[nodiscard]] std::string_view GetFilename(std::string_view path); |
| |
|
| | |
| | [[nodiscard]] std::string_view GetExtensionFromFilename(std::string_view name); |
| |
|
| | |
| | [[nodiscard]] std::string_view RemoveTrailingSlash(std::string_view path); |
| |
|
| | |
| | template <typename T> |
| | [[nodiscard]] std::vector<T> SliceVector(const std::vector<T>& vector, std::size_t first, |
| | std::size_t last) { |
| | if (first >= last) { |
| | return {}; |
| | } |
| | last = std::min<std::size_t>(last, vector.size()); |
| | return std::vector<T>(vector.begin() + first, vector.begin() + first + last); |
| | } |
| |
|
| | enum class DirectorySeparator { |
| | ForwardSlash, |
| | BackwardSlash, |
| | PlatformDefault, |
| | }; |
| |
|
| | |
| | |
| | [[nodiscard]] std::string SanitizePath( |
| | std::string_view path, |
| | DirectorySeparator directory_separator = DirectorySeparator::ForwardSlash); |
| |
|
| | |
| | |
| | |
| | class IOFile : public NonCopyable { |
| | public: |
| | IOFile(); |
| |
|
| | |
| | |
| | |
| | IOFile(const std::string& filename, const char openmode[], int flags = 0); |
| |
|
| | ~IOFile(); |
| |
|
| | IOFile(IOFile&& other) noexcept; |
| | IOFile& operator=(IOFile&& other) noexcept; |
| |
|
| | void Swap(IOFile& other) noexcept; |
| |
|
| | bool Close(); |
| |
|
| | template <typename T> |
| | std::size_t ReadArray(T* data, std::size_t length) { |
| | static_assert(std::is_trivially_copyable_v<T>, |
| | "Given array does not consist of trivially copyable objects"); |
| |
|
| | std::size_t items_read = ReadImpl(data, length, sizeof(T)); |
| | if (items_read != length) |
| | m_good = false; |
| |
|
| | return items_read; |
| | } |
| |
|
| | template <typename T> |
| | std::size_t ReadAtArray(T* data, std::size_t length, std::size_t offset) { |
| | static_assert(std::is_trivially_copyable_v<T>, |
| | "Given array does not consist of trivially copyable objects"); |
| |
|
| | std::size_t items_read = ReadAtImpl(data, length, sizeof(T), offset); |
| | if (items_read != length) |
| | m_good = false; |
| |
|
| | return items_read; |
| | } |
| |
|
| | template <typename T> |
| | std::size_t WriteArray(const T* data, std::size_t length) { |
| | static_assert(std::is_trivially_copyable_v<T>, |
| | "Given array does not consist of trivially copyable objects"); |
| |
|
| | std::size_t items_written = WriteImpl(data, length, sizeof(T)); |
| | if (items_written != length) |
| | m_good = false; |
| |
|
| | return items_written; |
| | } |
| |
|
| | template <typename T> |
| | std::size_t ReadBytes(T* data, std::size_t length) { |
| | static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable"); |
| | return ReadArray(reinterpret_cast<char*>(data), length); |
| | } |
| |
|
| | template <typename T> |
| | std::size_t ReadAtBytes(T* data, std::size_t length, std::size_t offset) { |
| | static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable"); |
| | return ReadAtArray(reinterpret_cast<char*>(data), length, offset); |
| | } |
| |
|
| | template <typename T> |
| | std::size_t WriteBytes(const T* data, std::size_t length) { |
| | static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable"); |
| | return WriteArray(reinterpret_cast<const char*>(data), length); |
| | } |
| |
|
| | template <typename T> |
| | std::size_t WriteObject(const T& object) { |
| | static_assert(!std::is_pointer_v<T>, "WriteObject arguments must not be a pointer"); |
| | return WriteArray(&object, 1); |
| | } |
| |
|
| | std::size_t WriteString(std::string_view str) { |
| | return WriteArray(str.data(), str.length()); |
| | } |
| |
|
| | [[nodiscard]] bool IsOpen() const { |
| | return nullptr != m_file; |
| | } |
| |
|
| | |
| | [[nodiscard]] bool IsGood() const { |
| | return m_good; |
| | } |
| | [[nodiscard]] int GetFd() const { |
| | #ifdef ANDROID |
| | return m_fd; |
| | #else |
| | if (m_file == nullptr) |
| | return -1; |
| | return fileno(m_file); |
| | #endif |
| | } |
| | [[nodiscard]] explicit operator bool() const { |
| | return IsGood(); |
| | } |
| |
|
| | bool Seek(s64 off, int origin); |
| | [[nodiscard]] u64 Tell() const; |
| | [[nodiscard]] u64 GetSize() const; |
| | bool Resize(u64 size); |
| | bool Flush(); |
| |
|
| | |
| | void Clear() { |
| | m_good = true; |
| | std::clearerr(m_file); |
| | } |
| |
|
| | private: |
| | std::size_t ReadImpl(void* data, std::size_t length, std::size_t data_size); |
| | std::size_t ReadAtImpl(void* data, std::size_t length, std::size_t data_size, |
| | std::size_t offset); |
| | std::size_t WriteImpl(const void* data, std::size_t length, std::size_t data_size); |
| |
|
| | bool Open(); |
| |
|
| | std::FILE* m_file = nullptr; |
| | int m_fd = -1; |
| | bool m_good = true; |
| |
|
| | std::string filename; |
| | std::string openmode; |
| | u32 flags; |
| |
|
| | template <class Archive> |
| | void serialize(Archive& ar, const unsigned int) { |
| | ar& Path::make(filename); |
| | ar& openmode; |
| | ar& flags; |
| | u64 pos; |
| | if (Archive::is_saving::value) { |
| | pos = Tell(); |
| | } |
| | ar& pos; |
| | if (Archive::is_loading::value) { |
| | Open(); |
| | Seek(pos, SEEK_SET); |
| | } |
| | } |
| | friend class boost::serialization::access; |
| | }; |
| |
|
| | template <std::ios_base::openmode o, typename T> |
| | void OpenFStream(T& fstream, const std::string& filename); |
| | } |
| |
|
| | |
| | template <typename T> |
| | void OpenFStream(T& fstream, const std::string& filename, std::ios_base::openmode openmode) { |
| | #ifdef _MSC_VER |
| | fstream.open(Common::UTF8ToUTF16W(filename), openmode); |
| | #else |
| | fstream.open(filename, openmode); |
| | #endif |
| | } |
| |
|