123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208 |
- // Copyright 2008 Dolphin Emulator Project
- // SPDX-License-Identifier: GPL-2.0-or-later
- #pragma once
- // 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
- // detect whether the file is a compressed blob, or just a big hunk of data, or a drive, and
- // automatically do the right thing.
- #include <array>
- #include <functional>
- #include <memory>
- #include <optional>
- #include <string>
- #include <vector>
- #include "Common/CommonTypes.h"
- #include "Common/Swap.h"
- namespace DiscIO
- {
- enum class WIARVZCompressionType : u32;
- // Increment CACHE_REVISION (GameFileCache.cpp) if the enum below is modified
- enum class BlobType
- {
- PLAIN,
- DRIVE,
- DIRECTORY,
- GCZ,
- CISO,
- WBFS,
- TGC,
- WIA,
- RVZ,
- MOD_DESCRIPTOR,
- NFS,
- SPLIT_PLAIN,
- };
- // If you convert an ISO file to another format and then call GetDataSize on it, what is the result?
- enum class DataSizeType
- {
- // The result is the same as for the ISO.
- Accurate,
- // The result is not larger than for the ISO. (It's usually a little smaller than for the ISO.)
- // Reads to offsets that are larger than the result will return some kind of "blank" data.
- LowerBound,
- // The result is not smaller than for the ISO. (It's usually much larger than for the ISO.)
- UpperBound,
- };
- std::string GetName(BlobType blob_type, bool translate);
- class BlobReader
- {
- public:
- virtual ~BlobReader() {}
- virtual BlobType GetBlobType() const = 0;
- virtual std::unique_ptr<BlobReader> CopyReader() const = 0;
- virtual u64 GetRawSize() const = 0;
- virtual u64 GetDataSize() const = 0;
- virtual DataSizeType GetDataSizeType() const = 0;
- // Returns 0 if the format does not use blocks
- virtual u64 GetBlockSize() const = 0;
- virtual bool HasFastRandomAccessInBlock() const = 0;
- virtual std::string GetCompressionMethod() const = 0;
- virtual std::optional<int> GetCompressionLevel() const = 0;
- // NOT thread-safe - can't call this from multiple threads.
- virtual bool Read(u64 offset, u64 size, u8* out_ptr) = 0;
- template <typename T>
- std::optional<T> ReadSwapped(u64 offset)
- {
- T temp;
- if (!Read(offset, sizeof(T), reinterpret_cast<u8*>(&temp)))
- return std::nullopt;
- return Common::FromBigEndian(temp);
- }
- virtual bool SupportsReadWiiDecrypted(u64 offset, u64 size, u64 partition_data_offset) const
- {
- return false;
- }
- virtual bool ReadWiiDecrypted(u64 offset, u64 size, u8* out_ptr, u64 partition_data_offset)
- {
- return false;
- }
- protected:
- BlobReader() {}
- };
- // 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.
- class SectorReader : public BlobReader
- {
- public:
- virtual ~SectorReader() = 0;
- bool Read(u64 offset, u64 size, u8* out_ptr) override;
- protected:
- 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);
- private:
- 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;
- };
- // Factory function - examines the path to choose the right type of BlobReader, and returns one.
- std::unique_ptr<BlobReader> CreateBlobReader(const std::string& filename);
- using CompressCB = std::function<bool(const std::string& text, float percent)>;
- bool ConvertToGCZ(BlobReader* infile, const std::string& infile_path,
- const std::string& outfile_path, u32 sub_type, int sector_size,
- CompressCB callback);
- bool ConvertToPlain(BlobReader* infile, const std::string& infile_path,
- const std::string& outfile_path, CompressCB callback);
- bool ConvertToWIAOrRVZ(BlobReader* infile, const std::string& infile_path,
- const std::string& outfile_path, bool rvz,
- WIARVZCompressionType compression_type, int compression_level,
- int chunk_size, CompressCB callback);
- } // namespace DiscIO
|