forked from dolphin-emu/dolphin
		
	The original reason I wanted to do this was so that we can replace the Android-specific code with this in the future, but of course, just deduplicating between DolphinWX and DolphinQt2 is nice too. Fixes: - DolphinQt2 showing the wrong size for split WBFS disc images. - DolphinQt2 being case sensitive when checking if a file is a DOL/ELF. - DolphinQt2 not detecting when a Wii banner has become available after the game list cache was created. Removes: - DolphinWX's ability to load PNGs as custom banners. But it was already rather broken (see https://bugs.dolphin-emu.org/issues/10365 and https://bugs.dolphin-emu.org/issues/10366). The reason I removed this was because PNG decoding relied on wx code and we don't have any good non-wx/Qt code for loading PNG files right now (let's not use SOIL), but we should be able to use libpng directly to implement PNG loading in the future. - DolphinQt2's ability to ignore a cached game if the last modified time differs. We currently don't have a non-wx/Qt way to get the time.
		
			
				
	
	
		
			169 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			169 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
// Copyright 2008 Dolphin Emulator Project
 | 
						|
// Licensed under GPLv2+
 | 
						|
// Refer to the license.txt file included.
 | 
						|
 | 
						|
#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 <memory>
 | 
						|
#include <optional>
 | 
						|
#include <string>
 | 
						|
#include <vector>
 | 
						|
 | 
						|
#include "Common/CommonTypes.h"
 | 
						|
#include "Common/Swap.h"
 | 
						|
 | 
						|
namespace DiscIO
 | 
						|
{
 | 
						|
// Increment CACHE_REVISION (GameFileCache.cpp) if the enum below is modified
 | 
						|
enum class BlobType
 | 
						|
{
 | 
						|
  PLAIN,
 | 
						|
  DRIVE,
 | 
						|
  DIRECTORY,
 | 
						|
  GCZ,
 | 
						|
  CISO,
 | 
						|
  WBFS,
 | 
						|
  TGC
 | 
						|
};
 | 
						|
 | 
						|
class BlobReader
 | 
						|
{
 | 
						|
public:
 | 
						|
  virtual ~BlobReader() {}
 | 
						|
  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;
 | 
						|
  template <typename T>
 | 
						|
  std::optional<T> ReadSwapped(u64 offset)
 | 
						|
  {
 | 
						|
    T temp;
 | 
						|
    if (!Read(offset, sizeof(T), reinterpret_cast<u8*>(&temp)))
 | 
						|
      return {};
 | 
						|
    return Common::FromBigEndian(temp);
 | 
						|
  }
 | 
						|
 | 
						|
  virtual bool SupportsReadWiiDecrypted() const { return false; }
 | 
						|
  virtual bool ReadWiiDecrypted(u64 offset, u64 size, u8* out_ptr, u64 partition_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);
 | 
						|
 | 
						|
typedef bool (*CompressCB)(const std::string& text, float percent, void* arg);
 | 
						|
 | 
						|
bool CompressFileToBlob(const std::string& infile_path, const std::string& outfile_path,
 | 
						|
                        u32 sub_type = 0, int sector_size = 16384, CompressCB callback = nullptr,
 | 
						|
                        void* arg = nullptr);
 | 
						|
bool DecompressBlobToFile(const std::string& infile_path, const std::string& outfile_path,
 | 
						|
                          CompressCB callback = nullptr, void* arg = nullptr);
 | 
						|
 | 
						|
}  // namespace
 |