| 
									
										
										
										
											2015-05-24 06:55:12 +02:00
										 |  |  | // Copyright 2008 Dolphin Emulator Project
 | 
					
						
							| 
									
										
										
										
											2015-05-18 01:08:10 +02:00
										 |  |  | // Licensed under GPLv2+
 | 
					
						
							| 
									
										
										
										
											2013-04-17 23:09:55 -04:00
										 |  |  | // Refer to the license.txt file included.
 | 
					
						
							| 
									
										
										
										
											2008-12-08 04:46:09 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-10 13:54:46 -05:00
										 |  |  | #pragma once
 | 
					
						
							| 
									
										
										
										
											2008-12-08 04:46:09 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | // BLOB
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Blobs in Dolphin are read only Binary Large OBjects. For example, a typical DVD image.
 | 
					
						
							|  |  |  | // Often, you may want to store these things in a highly compressed format, but still
 | 
					
						
							|  |  |  | // allow random access. Or you may store them on an odd device, like raw on a DVD.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Always read your BLOBs using an interface returned by CreateBlobReader(). It will
 | 
					
						
							| 
									
										
										
										
											2009-02-22 16:48:54 +00:00
										 |  |  | // detect whether the file is a compressed blob, or just a big hunk of data, or a drive, and
 | 
					
						
							| 
									
										
										
										
											2008-12-08 04:46:09 +00:00
										 |  |  | // automatically do the right thing.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-07 19:53:42 -05:00
										 |  |  | #include <array>
 | 
					
						
							| 
									
										
										
										
											2015-12-06 23:15:51 -05:00
										 |  |  | #include <memory>
 | 
					
						
							| 
									
										
										
										
											2014-03-12 15:33:41 -04:00
										 |  |  | #include <string>
 | 
					
						
							| 
									
										
										
										
											2017-03-03 14:43:52 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-17 05:18:15 -05:00
										 |  |  | #include "Common/CommonTypes.h"
 | 
					
						
							| 
									
										
										
										
											2017-03-03 14:43:52 -05:00
										 |  |  | #include "Common/Swap.h"
 | 
					
						
							| 
									
										
										
										
											2008-12-08 04:46:09 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | namespace DiscIO | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-07-06 20:33:05 +02:00
										 |  |  | // Increment CACHE_REVISION (ISOFile.cpp & GameFile.cpp) if the enum below is modified
 | 
					
						
							| 
									
										
										
										
											2015-09-27 14:01:12 +02:00
										 |  |  | enum class BlobType | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   PLAIN, | 
					
						
							|  |  |  |   DRIVE, | 
					
						
							|  |  |  |   DIRECTORY, | 
					
						
							|  |  |  |   GCZ, | 
					
						
							|  |  |  |   CISO, | 
					
						
							| 
									
										
										
										
											2016-12-17 14:48:49 +01:00
										 |  |  |   WBFS, | 
					
						
							|  |  |  |   TGC | 
					
						
							| 
									
										
										
										
											2015-09-27 14:01:12 +02:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-12-08 04:46:09 +00:00
										 |  |  | class IBlobReader | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | public: | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   virtual ~IBlobReader() {} | 
					
						
							|  |  |  |   virtual BlobType GetBlobType() const = 0; | 
					
						
							|  |  |  |   virtual u64 GetRawSize() const = 0; | 
					
						
							|  |  |  |   virtual u64 GetDataSize() const = 0; | 
					
						
							|  |  |  |   // NOT thread-safe - can't call this from multiple threads.
 | 
					
						
							|  |  |  |   virtual bool Read(u64 offset, u64 size, u8* out_ptr) = 0; | 
					
						
							| 
									
										
										
										
											2008-12-08 04:46:09 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | protected: | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   IBlobReader() {} | 
					
						
							| 
									
										
										
										
											2008-12-08 04:46:09 +00:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-26 11:24:08 +00:00
										 |  |  | // Provides caching and byte-operation-to-block-operations facilities.
 | 
					
						
							|  |  |  | // Used for compressed blob and direct drive reading.
 | 
					
						
							|  |  |  | // NOTE: GetDataSize() is expected to be evenly divisible by the sector size.
 | 
					
						
							| 
									
										
										
										
											2008-12-08 04:46:09 +00:00
										 |  |  | class SectorReader : public IBlobReader | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | public: | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   virtual ~SectorReader() = 0; | 
					
						
							| 
									
										
										
										
											2009-02-22 16:48:54 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   bool Read(u64 offset, u64 size, u8* out_ptr) override; | 
					
						
							| 
									
										
										
										
											2014-09-01 15:48:02 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | protected: | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   void SetSectorSize(int blocksize); | 
					
						
							|  |  |  |   int GetSectorSize() const { return m_block_size; } | 
					
						
							|  |  |  |   // Set the chunk size -> the number of blocks to read at a time.
 | 
					
						
							|  |  |  |   // Default value is 1 but that is too low for physical devices
 | 
					
						
							|  |  |  |   // like CDROMs. Setting this to a higher value helps reduce seeking
 | 
					
						
							|  |  |  |   // and IO overhead by batching reads. Do not set it too high either
 | 
					
						
							|  |  |  |   // as large reads are slow and will take too long to resolve.
 | 
					
						
							|  |  |  |   void SetChunkSize(int blocks); | 
					
						
							|  |  |  |   int GetChunkSize() const { return m_chunk_blocks; } | 
					
						
							|  |  |  |   // Read a single block/sector.
 | 
					
						
							|  |  |  |   virtual bool GetBlock(u64 block_num, u8* out) = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Read multiple contiguous blocks.
 | 
					
						
							|  |  |  |   // Default implementation just calls GetBlock in a loop, it should be
 | 
					
						
							|  |  |  |   // overridden in derived classes where possible.
 | 
					
						
							|  |  |  |   virtual bool ReadMultipleAlignedBlocks(u64 block_num, u64 num_blocks, u8* out_ptr); | 
					
						
							| 
									
										
										
										
											2016-04-26 11:24:08 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | private: | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   struct Cache | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     std::vector<u8> data; | 
					
						
							|  |  |  |     u64 block_idx = 0; | 
					
						
							|  |  |  |     u32 num_blocks = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // [Pseudo-] Least Recently Used Shift Register
 | 
					
						
							|  |  |  |     // When an empty cache line is needed, the line with the lowest value
 | 
					
						
							|  |  |  |     // is taken and reset; the LRU register is then shifted down 1 place
 | 
					
						
							|  |  |  |     // on all lines (low bit discarded). When a line is used, the high bit
 | 
					
						
							|  |  |  |     // is set marking it as most recently used.
 | 
					
						
							|  |  |  |     u32 lru_sreg = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     void Reset() | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       block_idx = 0; | 
					
						
							|  |  |  |       num_blocks = 0; | 
					
						
							|  |  |  |       lru_sreg = 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     void Fill(u64 block, u32 count) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       block_idx = block; | 
					
						
							|  |  |  |       num_blocks = count; | 
					
						
							|  |  |  |       // NOTE: Setting only the high bit means the newest line will
 | 
					
						
							|  |  |  |       //   be selected for eviction if every line in the cache was
 | 
					
						
							|  |  |  |       //   touched. This gives MRU behavior which is probably
 | 
					
						
							|  |  |  |       //   desirable in that case.
 | 
					
						
							|  |  |  |       MarkUsed(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     bool Contains(u64 block) const { return block >= block_idx && block - block_idx < num_blocks; } | 
					
						
							|  |  |  |     void MarkUsed() { lru_sreg |= 0x80000000; } | 
					
						
							|  |  |  |     void ShiftLRU() { lru_sreg >>= 1; } | 
					
						
							|  |  |  |     bool IsLessRecentlyUsedThan(const Cache& other) const { return lru_sreg < other.lru_sreg; } | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Gets the cache line that contains the given block, or nullptr.
 | 
					
						
							|  |  |  |   // NOTE: The cache record only lasts until it expires (next GetEmptyCacheLine)
 | 
					
						
							|  |  |  |   const Cache* FindCacheLine(u64 block_num); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Finds the least recently used cache line, resets and returns it.
 | 
					
						
							|  |  |  |   Cache* GetEmptyCacheLine(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Combines FindCacheLine with GetEmptyCacheLine and ReadChunk.
 | 
					
						
							|  |  |  |   // Always returns a valid cache line (loading the data if needed).
 | 
					
						
							|  |  |  |   // May return nullptr only if the cache missed and the read failed.
 | 
					
						
							|  |  |  |   const Cache* GetCacheLine(u64 block_num); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Read all bytes from a chunk of blocks into a buffer.
 | 
					
						
							|  |  |  |   // Returns the number of blocks read (may be less than m_chunk_blocks
 | 
					
						
							|  |  |  |   // if chunk_num is the last chunk on the disk and the disk size is not
 | 
					
						
							|  |  |  |   // evenly divisible into chunks). Returns zero if it fails.
 | 
					
						
							|  |  |  |   u32 ReadChunk(u8* buffer, u64 chunk_num); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   static constexpr int CACHE_LINES = 32; | 
					
						
							|  |  |  |   u32 m_block_size = 0;    // Bytes in a sector/block
 | 
					
						
							|  |  |  |   u32 m_chunk_blocks = 1;  // Number of sectors/blocks in a chunk
 | 
					
						
							|  |  |  |   std::array<Cache, CACHE_LINES> m_cache; | 
					
						
							| 
									
										
										
										
											2008-12-08 04:46:09 +00:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-03 13:42:26 +02:00
										 |  |  | class CBlobBigEndianReader | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | public: | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   CBlobBigEndianReader(IBlobReader& reader) : m_reader(reader) {} | 
					
						
							|  |  |  |   template <typename T> | 
					
						
							|  |  |  |   bool ReadSwapped(u64 offset, T* buffer) const | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     T temp; | 
					
						
							|  |  |  |     if (!m_reader.Read(offset, sizeof(T), reinterpret_cast<u8*>(&temp))) | 
					
						
							|  |  |  |       return false; | 
					
						
							|  |  |  |     *buffer = Common::FromBigEndian(temp); | 
					
						
							|  |  |  |     return true; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2015-10-03 13:42:26 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | private: | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   IBlobReader& m_reader; | 
					
						
							| 
									
										
										
										
											2015-10-03 13:42:26 +02:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-12-08 04:46:09 +00:00
										 |  |  | // Factory function - examines the path to choose the right type of IBlobReader, and returns one.
 | 
					
						
							| 
									
										
										
										
											2015-12-06 23:15:51 -05:00
										 |  |  | std::unique_ptr<IBlobReader> CreateBlobReader(const std::string& filename); | 
					
						
							| 
									
										
										
										
											2008-12-08 04:46:09 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-11-27 07:53:28 -08:00
										 |  |  | typedef bool (*CompressCB)(const std::string& text, float percent, void* arg); | 
					
						
							| 
									
										
										
										
											2008-12-08 04:46:09 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-21 12:50:15 +01:00
										 |  |  | bool CompressFileToBlob(const std::string& infile_path, const std::string& outfile_path, | 
					
						
							|  |  |  |                         u32 sub_type = 0, int sector_size = 16384, CompressCB callback = nullptr, | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |                         void* arg = nullptr); | 
					
						
							| 
									
										
										
										
											2016-12-21 12:50:15 +01:00
										 |  |  | bool DecompressBlobToFile(const std::string& infile_path, const std::string& outfile_path, | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |                           CompressCB callback = nullptr, void* arg = nullptr); | 
					
						
							| 
									
										
										
										
											2008-12-08 04:46:09 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | }  // namespace
 |