Blob.h 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. // Copyright 2008 Dolphin Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #pragma once
  4. // BLOB
  5. // Blobs in Dolphin are read only Binary Large OBjects. For example, a typical DVD image.
  6. // Often, you may want to store these things in a highly compressed format, but still
  7. // allow random access. Or you may store them on an odd device, like raw on a DVD.
  8. // Always read your BLOBs using an interface returned by CreateBlobReader(). It will
  9. // detect whether the file is a compressed blob, or just a big hunk of data, or a drive, and
  10. // automatically do the right thing.
  11. #include <array>
  12. #include <functional>
  13. #include <memory>
  14. #include <optional>
  15. #include <string>
  16. #include <vector>
  17. #include "Common/CommonTypes.h"
  18. #include "Common/Swap.h"
  19. namespace DiscIO
  20. {
  21. enum class WIARVZCompressionType : u32;
  22. // Increment CACHE_REVISION (GameFileCache.cpp) if the enum below is modified
  23. enum class BlobType
  24. {
  25. PLAIN,
  26. DRIVE,
  27. DIRECTORY,
  28. GCZ,
  29. CISO,
  30. WBFS,
  31. TGC,
  32. WIA,
  33. RVZ,
  34. MOD_DESCRIPTOR,
  35. NFS,
  36. SPLIT_PLAIN,
  37. };
  38. // If you convert an ISO file to another format and then call GetDataSize on it, what is the result?
  39. enum class DataSizeType
  40. {
  41. // The result is the same as for the ISO.
  42. Accurate,
  43. // The result is not larger than for the ISO. (It's usually a little smaller than for the ISO.)
  44. // Reads to offsets that are larger than the result will return some kind of "blank" data.
  45. LowerBound,
  46. // The result is not smaller than for the ISO. (It's usually much larger than for the ISO.)
  47. UpperBound,
  48. };
  49. std::string GetName(BlobType blob_type, bool translate);
  50. class BlobReader
  51. {
  52. public:
  53. virtual ~BlobReader() {}
  54. virtual BlobType GetBlobType() const = 0;
  55. virtual std::unique_ptr<BlobReader> CopyReader() const = 0;
  56. virtual u64 GetRawSize() const = 0;
  57. virtual u64 GetDataSize() const = 0;
  58. virtual DataSizeType GetDataSizeType() const = 0;
  59. // Returns 0 if the format does not use blocks
  60. virtual u64 GetBlockSize() const = 0;
  61. virtual bool HasFastRandomAccessInBlock() const = 0;
  62. virtual std::string GetCompressionMethod() const = 0;
  63. virtual std::optional<int> GetCompressionLevel() const = 0;
  64. // NOT thread-safe - can't call this from multiple threads.
  65. virtual bool Read(u64 offset, u64 size, u8* out_ptr) = 0;
  66. template <typename T>
  67. std::optional<T> ReadSwapped(u64 offset)
  68. {
  69. T temp;
  70. if (!Read(offset, sizeof(T), reinterpret_cast<u8*>(&temp)))
  71. return std::nullopt;
  72. return Common::FromBigEndian(temp);
  73. }
  74. virtual bool SupportsReadWiiDecrypted(u64 offset, u64 size, u64 partition_data_offset) const
  75. {
  76. return false;
  77. }
  78. virtual bool ReadWiiDecrypted(u64 offset, u64 size, u8* out_ptr, u64 partition_data_offset)
  79. {
  80. return false;
  81. }
  82. protected:
  83. BlobReader() {}
  84. };
  85. // Provides caching and byte-operation-to-block-operations facilities.
  86. // Used for compressed blob and direct drive reading.
  87. // NOTE: GetDataSize() is expected to be evenly divisible by the sector size.
  88. class SectorReader : public BlobReader
  89. {
  90. public:
  91. virtual ~SectorReader() = 0;
  92. bool Read(u64 offset, u64 size, u8* out_ptr) override;
  93. protected:
  94. void SetSectorSize(int blocksize);
  95. int GetSectorSize() const { return m_block_size; }
  96. // Set the chunk size -> the number of blocks to read at a time.
  97. // Default value is 1 but that is too low for physical devices
  98. // like CDROMs. Setting this to a higher value helps reduce seeking
  99. // and IO overhead by batching reads. Do not set it too high either
  100. // as large reads are slow and will take too long to resolve.
  101. void SetChunkSize(int blocks);
  102. int GetChunkSize() const { return m_chunk_blocks; }
  103. // Read a single block/sector.
  104. virtual bool GetBlock(u64 block_num, u8* out) = 0;
  105. // Read multiple contiguous blocks.
  106. // Default implementation just calls GetBlock in a loop, it should be
  107. // overridden in derived classes where possible.
  108. virtual bool ReadMultipleAlignedBlocks(u64 block_num, u64 num_blocks, u8* out_ptr);
  109. private:
  110. struct Cache
  111. {
  112. std::vector<u8> data;
  113. u64 block_idx = 0;
  114. u32 num_blocks = 0;
  115. // [Pseudo-] Least Recently Used Shift Register
  116. // When an empty cache line is needed, the line with the lowest value
  117. // is taken and reset; the LRU register is then shifted down 1 place
  118. // on all lines (low bit discarded). When a line is used, the high bit
  119. // is set marking it as most recently used.
  120. u32 lru_sreg = 0;
  121. void Reset()
  122. {
  123. block_idx = 0;
  124. num_blocks = 0;
  125. lru_sreg = 0;
  126. }
  127. void Fill(u64 block, u32 count)
  128. {
  129. block_idx = block;
  130. num_blocks = count;
  131. // NOTE: Setting only the high bit means the newest line will
  132. // be selected for eviction if every line in the cache was
  133. // touched. This gives MRU behavior which is probably
  134. // desirable in that case.
  135. MarkUsed();
  136. }
  137. bool Contains(u64 block) const { return block >= block_idx && block - block_idx < num_blocks; }
  138. void MarkUsed() { lru_sreg |= 0x80000000; }
  139. void ShiftLRU() { lru_sreg >>= 1; }
  140. bool IsLessRecentlyUsedThan(const Cache& other) const { return lru_sreg < other.lru_sreg; }
  141. };
  142. // Gets the cache line that contains the given block, or nullptr.
  143. // NOTE: The cache record only lasts until it expires (next GetEmptyCacheLine)
  144. const Cache* FindCacheLine(u64 block_num);
  145. // Finds the least recently used cache line, resets and returns it.
  146. Cache* GetEmptyCacheLine();
  147. // Combines FindCacheLine with GetEmptyCacheLine and ReadChunk.
  148. // Always returns a valid cache line (loading the data if needed).
  149. // May return nullptr only if the cache missed and the read failed.
  150. const Cache* GetCacheLine(u64 block_num);
  151. // Read all bytes from a chunk of blocks into a buffer.
  152. // Returns the number of blocks read (may be less than m_chunk_blocks
  153. // if chunk_num is the last chunk on the disk and the disk size is not
  154. // evenly divisible into chunks). Returns zero if it fails.
  155. u32 ReadChunk(u8* buffer, u64 chunk_num);
  156. static constexpr int CACHE_LINES = 32;
  157. u32 m_block_size = 0; // Bytes in a sector/block
  158. u32 m_chunk_blocks = 1; // Number of sectors/blocks in a chunk
  159. std::array<Cache, CACHE_LINES> m_cache;
  160. };
  161. // Factory function - examines the path to choose the right type of BlobReader, and returns one.
  162. std::unique_ptr<BlobReader> CreateBlobReader(const std::string& filename);
  163. using CompressCB = std::function<bool(const std::string& text, float percent)>;
  164. bool ConvertToGCZ(BlobReader* infile, const std::string& infile_path,
  165. const std::string& outfile_path, u32 sub_type, int sector_size,
  166. CompressCB callback);
  167. bool ConvertToPlain(BlobReader* infile, const std::string& infile_path,
  168. const std::string& outfile_path, CompressCB callback);
  169. bool ConvertToWIAOrRVZ(BlobReader* infile, const std::string& infile_path,
  170. const std::string& outfile_path, bool rvz,
  171. WIARVZCompressionType compression_type, int compression_level,
  172. int chunk_size, CompressCB callback);
  173. } // namespace DiscIO