| |
| |
| |
|
|
| #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 |
| } |
|
|