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