file.h 16 KB

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