file.h 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  1. // Copyright 2021 yuzu Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #pragma once
  5. #include <cstdio>
  6. #include <filesystem>
  7. #include <fstream>
  8. #include <span>
  9. #include <type_traits>
  10. #include <vector>
  11. #include "common/concepts.h"
  12. #include "common/fs/fs_types.h"
  13. #include "common/fs/fs_util.h"
  14. namespace Common::FS {
  15. enum class SeekOrigin {
  16. SetOrigin, // Seeks from the start of the file.
  17. CurrentPosition, // Seeks from the current file pointer position.
  18. End, // Seeks from the end of the file.
  19. };
  20. /**
  21. * Opens a file stream at path with the specified open mode.
  22. *
  23. * @param file_stream Reference to file stream
  24. * @param path Filesystem path
  25. * @param open_mode File stream open mode
  26. */
  27. template <typename FileStream>
  28. void OpenFileStream(FileStream& file_stream, const std::filesystem::path& path,
  29. std::ios_base::openmode open_mode) {
  30. file_stream.open(path, open_mode);
  31. }
  32. #ifdef _WIN32
  33. template <typename FileStream, typename Path>
  34. void OpenFileStream(FileStream& file_stream, const Path& path, std::ios_base::openmode open_mode) {
  35. if constexpr (IsChar<typename Path::value_type>) {
  36. file_stream.open(ToU8String(path), open_mode);
  37. } else {
  38. file_stream.open(std::filesystem::path{path}, open_mode);
  39. }
  40. }
  41. #endif
  42. /**
  43. * Reads an entire file at path and returns a string of the contents read from the file.
  44. * If the filesystem object at path is not a file, this function returns an empty string.
  45. *
  46. * @param path Filesystem path
  47. * @param type File type
  48. *
  49. * @returns A string of the contents read from the file.
  50. */
  51. [[nodiscard]] std::string ReadStringFromFile(const std::filesystem::path& path, FileType type);
  52. #ifdef _WIN32
  53. template <typename Path>
  54. [[nodiscard]] std::string ReadStringFromFile(const Path& path, FileType type) {
  55. if constexpr (IsChar<typename Path::value_type>) {
  56. return ReadStringFromFile(ToU8String(path), type);
  57. } else {
  58. return ReadStringFromFile(std::filesystem::path{path}, type);
  59. }
  60. }
  61. #endif
  62. /**
  63. * Writes a string to a file at path and returns the number of characters successfully written.
  64. * If an file already exists at path, its contents will be erased.
  65. * If the filesystem object at path is not a file, this function returns 0.
  66. *
  67. * @param path Filesystem path
  68. * @param type File type
  69. *
  70. * @returns Number of characters successfully written.
  71. */
  72. [[nodiscard]] size_t WriteStringToFile(const std::filesystem::path& path, FileType type,
  73. std::string_view string);
  74. #ifdef _WIN32
  75. template <typename Path>
  76. [[nodiscard]] size_t WriteStringToFile(const Path& path, FileType type, std::string_view string) {
  77. if constexpr (IsChar<typename Path::value_type>) {
  78. return WriteStringToFile(ToU8String(path), type, string);
  79. } else {
  80. return WriteStringToFile(std::filesystem::path{path}, type, string);
  81. }
  82. }
  83. #endif
  84. /**
  85. * Appends a string to a file at path and returns the number of characters successfully written.
  86. * If a file does not exist at path, WriteStringToFile is called instead.
  87. * If the filesystem object at path is not a file, this function returns 0.
  88. *
  89. * @param path Filesystem path
  90. * @param type File type
  91. *
  92. * @returns Number of characters successfully written.
  93. */
  94. [[nodiscard]] size_t AppendStringToFile(const std::filesystem::path& path, FileType type,
  95. std::string_view string);
  96. #ifdef _WIN32
  97. template <typename Path>
  98. [[nodiscard]] size_t AppendStringToFile(const Path& path, FileType type, std::string_view string) {
  99. if constexpr (IsChar<typename Path::value_type>) {
  100. return AppendStringToFile(ToU8String(path), type, string);
  101. } else {
  102. return AppendStringToFile(std::filesystem::path{path}, type, string);
  103. }
  104. }
  105. #endif
  106. class IOFile final {
  107. public:
  108. IOFile();
  109. explicit IOFile(const std::string& path, FileAccessMode mode,
  110. FileType type = FileType::BinaryFile,
  111. FileShareFlag flag = FileShareFlag::ShareReadOnly);
  112. explicit IOFile(std::string_view path, FileAccessMode mode,
  113. FileType type = FileType::BinaryFile,
  114. FileShareFlag flag = FileShareFlag::ShareReadOnly);
  115. /**
  116. * An IOFile is a lightweight wrapper on C Library file operations.
  117. * Automatically closes an open file on the destruction of an IOFile object.
  118. *
  119. * @param path Filesystem path
  120. * @param mode File access mode
  121. * @param type File type, default is BinaryFile. Use TextFile to open the file as a text file
  122. * @param flag (Windows only) File-share access flag, default is ShareReadOnly
  123. */
  124. explicit IOFile(const std::filesystem::path& path, FileAccessMode mode,
  125. FileType type = FileType::BinaryFile,
  126. FileShareFlag flag = FileShareFlag::ShareReadOnly);
  127. ~IOFile();
  128. IOFile(const IOFile&) = delete;
  129. IOFile& operator=(const IOFile&) = delete;
  130. IOFile(IOFile&& other) noexcept;
  131. IOFile& operator=(IOFile&& other) noexcept;
  132. /**
  133. * Gets the path of the file.
  134. *
  135. * @returns The path of the file.
  136. */
  137. [[nodiscard]] std::filesystem::path GetPath() const;
  138. /**
  139. * Gets the access mode of the file.
  140. *
  141. * @returns The access mode of the file.
  142. */
  143. [[nodiscard]] FileAccessMode GetAccessMode() const;
  144. /**
  145. * Gets the type of the file.
  146. *
  147. * @returns The type of the file.
  148. */
  149. [[nodiscard]] FileType GetType() const;
  150. /**
  151. * Opens a file at path with the specified file access mode.
  152. * This function behaves differently depending on the FileAccessMode.
  153. * These behaviors are documented in each enum value of FileAccessMode.
  154. *
  155. * @param path Filesystem path
  156. * @param mode File access mode
  157. * @param type File type, default is BinaryFile. Use TextFile to open the file as a text file
  158. * @param flag (Windows only) File-share access flag, default is ShareReadOnly
  159. */
  160. void Open(const std::filesystem::path& path, FileAccessMode mode,
  161. FileType type = FileType::BinaryFile,
  162. FileShareFlag flag = FileShareFlag::ShareReadOnly);
  163. #ifdef _WIN32
  164. template <typename Path>
  165. [[nodiscard]] void Open(const Path& path, FileAccessMode mode,
  166. FileType type = FileType::BinaryFile,
  167. FileShareFlag flag = FileShareFlag::ShareReadOnly) {
  168. using ValueType = typename Path::value_type;
  169. if constexpr (IsChar<ValueType>) {
  170. Open(ToU8String(path), mode, type, flag);
  171. } else {
  172. Open(std::filesystem::path{path}, mode, type, flag);
  173. }
  174. }
  175. #endif
  176. /// Closes the file if it is opened.
  177. void Close();
  178. /**
  179. * Checks whether the file is open.
  180. * Use this to check whether the calls to Open() or Close() succeeded.
  181. *
  182. * @returns True if the file is open, false otherwise.
  183. */
  184. [[nodiscard]] bool IsOpen() const;
  185. /**
  186. * Helper function which deduces the value type of a contiguous STL container used in ReadSpan.
  187. * If T is not a contiguous STL container as defined by the concept IsSTLContainer, this calls
  188. * ReadObject and T must be a trivially copyable object.
  189. *
  190. * See ReadSpan for more details if T is a contiguous container.
  191. * See ReadObject for more details if T is a trivially copyable object.
  192. *
  193. * @tparam T Contiguous container or trivially copyable object
  194. *
  195. * @param data Container of T::value_type data or reference to object
  196. *
  197. * @returns Count of T::value_type data or objects successfully read.
  198. */
  199. template <typename T>
  200. [[nodiscard]] size_t Read(T& data) const {
  201. if constexpr (IsSTLContainer<T>) {
  202. using ContiguousType = typename T::value_type;
  203. static_assert(std::is_trivially_copyable_v<ContiguousType>,
  204. "Data type must be trivially copyable.");
  205. return ReadSpan<ContiguousType>(data);
  206. } else {
  207. return ReadObject(data) ? 1 : 0;
  208. }
  209. }
  210. /**
  211. * Helper function which deduces the value type of a contiguous STL container used in WriteSpan.
  212. * If T is not a contiguous STL container as defined by the concept IsSTLContainer, this calls
  213. * WriteObject and T must be a trivially copyable object.
  214. *
  215. * See WriteSpan for more details if T is a contiguous container.
  216. * See WriteObject for more details if T is a trivially copyable object.
  217. *
  218. * @tparam T Contiguous container or trivially copyable object
  219. *
  220. * @param data Container of T::value_type data or const reference to object
  221. *
  222. * @returns Count of T::value_type data or objects successfully written.
  223. */
  224. template <typename T>
  225. [[nodiscard]] size_t Write(const T& data) const {
  226. if constexpr (IsSTLContainer<T>) {
  227. using ContiguousType = typename T::value_type;
  228. static_assert(std::is_trivially_copyable_v<ContiguousType>,
  229. "Data type must be trivially copyable.");
  230. return WriteSpan<ContiguousType>(data);
  231. } else {
  232. static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
  233. return WriteObject(data) ? 1 : 0;
  234. }
  235. }
  236. /**
  237. * Reads a span of T data from a file sequentially.
  238. * This function reads from the current position of the file pointer and
  239. * advances it by the (count of T * sizeof(T)) bytes successfully read.
  240. *
  241. * Failures occur when:
  242. * - The file is not open
  243. * - The opened file lacks read permissions
  244. * - Attempting to read beyond the end-of-file
  245. *
  246. * @tparam T Data type
  247. *
  248. * @param data Span of T data
  249. *
  250. * @returns Count of T data successfully read.
  251. */
  252. template <typename T>
  253. [[nodiscard]] size_t ReadSpan(std::span<T> data) const {
  254. static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
  255. if (!IsOpen()) {
  256. return 0;
  257. }
  258. return std::fread(data.data(), sizeof(T), data.size(), file);
  259. }
  260. /**
  261. * Writes a span of T data to a file sequentially.
  262. * This function writes from the current position of the file pointer and
  263. * advances it by the (count of T * sizeof(T)) bytes successfully written.
  264. *
  265. * Failures occur when:
  266. * - The file is not open
  267. * - The opened file lacks write permissions
  268. *
  269. * @tparam T Data type
  270. *
  271. * @param data Span of T data
  272. *
  273. * @returns Count of T data successfully written.
  274. */
  275. template <typename T>
  276. [[nodiscard]] size_t WriteSpan(std::span<const T> data) const {
  277. static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
  278. if (!IsOpen()) {
  279. return 0;
  280. }
  281. return std::fwrite(data.data(), sizeof(T), data.size(), file);
  282. }
  283. /**
  284. * Reads a T object from a file sequentially.
  285. * This function reads from the current position of the file pointer and
  286. * advances it by the sizeof(T) bytes successfully read.
  287. *
  288. * Failures occur when:
  289. * - The file is not open
  290. * - The opened file lacks read permissions
  291. * - Attempting to read beyond the end-of-file
  292. *
  293. * @tparam T Data type
  294. *
  295. * @param object Reference to object
  296. *
  297. * @returns True if the object is successfully read from the file, false otherwise.
  298. */
  299. template <typename T>
  300. [[nodiscard]] bool ReadObject(T& object) const {
  301. static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
  302. static_assert(!std::is_pointer_v<T>, "T must not be a pointer to an object.");
  303. if (!IsOpen()) {
  304. return false;
  305. }
  306. return std::fread(&object, sizeof(T), 1, file) == 1;
  307. }
  308. /**
  309. * Writes a T object to a file sequentially.
  310. * This function writes from the current position of the file pointer and
  311. * advances it by the sizeof(T) bytes successfully written.
  312. *
  313. * Failures occur when:
  314. * - The file is not open
  315. * - The opened file lacks write permissions
  316. *
  317. * @tparam T Data type
  318. *
  319. * @param object Const reference to object
  320. *
  321. * @returns True if the object is successfully written to the file, false otherwise.
  322. */
  323. template <typename T>
  324. [[nodiscard]] bool WriteObject(const T& object) const {
  325. static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
  326. static_assert(!std::is_pointer_v<T>, "T must not be a pointer to an object.");
  327. if (!IsOpen()) {
  328. return false;
  329. }
  330. return std::fwrite(&object, sizeof(T), 1, file) == 1;
  331. }
  332. /**
  333. * Specialized function to read a string of a given length from a file sequentially.
  334. * This function writes from the current position of the file pointer and
  335. * advances it by the number of characters successfully read.
  336. * The size of the returned string may not match length if not all bytes are successfully read.
  337. *
  338. * @param length Length of the string
  339. *
  340. * @returns A string read from the file.
  341. */
  342. [[nodiscard]] std::string ReadString(size_t length) const;
  343. /**
  344. * Specialized function to write a string to a file sequentially.
  345. * This function writes from the current position of the file pointer and
  346. * advances it by the number of characters successfully written.
  347. *
  348. * @param string Span of const char backed std::string or std::string_view
  349. *
  350. * @returns Number of characters successfully written.
  351. */
  352. [[nodiscard]] size_t WriteString(std::span<const char> string) const;
  353. /**
  354. * Flushes any unwritten buffered data into the file.
  355. *
  356. * @returns True if the flush was successful, false otherwise.
  357. */
  358. [[nodiscard]] bool Flush() const;
  359. /**
  360. * Resizes the file to a given size.
  361. * If the file is resized to a smaller size, the remainder of the file is discarded.
  362. * If the file is resized to a larger size, the new area appears as if zero-filled.
  363. *
  364. * Failures occur when:
  365. * - The file is not open
  366. *
  367. * @param size File size in bytes
  368. *
  369. * @returns True if the file resize succeeded, false otherwise.
  370. */
  371. [[nodiscard]] bool SetSize(u64 size) const;
  372. /**
  373. * Gets the size of the file.
  374. *
  375. * Failures occur when:
  376. * - The file is not open
  377. *
  378. * @returns The file size in bytes of the file. Returns 0 on failure.
  379. */
  380. [[nodiscard]] u64 GetSize() const;
  381. /**
  382. * Moves the current position of the file pointer with the specified offset and seek origin.
  383. *
  384. * @param offset Offset from seek origin
  385. * @param origin Seek origin
  386. *
  387. * @returns True if the file pointer has moved to the specified offset, false otherwise.
  388. */
  389. [[nodiscard]] bool Seek(s64 offset, SeekOrigin origin = SeekOrigin::SetOrigin) const;
  390. /**
  391. * Gets the current position of the file pointer.
  392. *
  393. * @returns The current position of the file pointer.
  394. */
  395. [[nodiscard]] s64 Tell() const;
  396. private:
  397. std::filesystem::path file_path;
  398. FileAccessMode file_access_mode{};
  399. FileType file_type{};
  400. std::FILE* file = nullptr;
  401. };
  402. } // namespace Common::FS