File: Blob.h

package info (click to toggle)
dolphin-emu 5.0%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 28,976 kB
  • ctags: 35,666
  • sloc: cpp: 213,139; java: 6,252; asm: 2,277; xml: 1,998; ansic: 1,514; python: 462; sh: 279; pascal: 247; makefile: 124; perl: 97
file content (190 lines) | stat: -rw-r--r-- 5,574 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
// 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 <string>
#include "Common/CommonFuncs.h"
#include "Common/CommonTypes.h"

namespace DiscIO
{

// Increment CACHE_REVISION if the enum below is modified (ISOFile.cpp & GameFile.cpp)
enum class BlobType
{
	PLAIN,
	DRIVE,
	DIRECTORY,
	GCZ,
	CISO,
	WBFS
};

class IBlobReader
{
public:
	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;

protected:
	IBlobReader() {}
};


// 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 IBlobReader
{
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;
};

class CBlobBigEndianReader
{
public:
	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;
	}

private:
	IBlobReader& m_reader;
};

// Factory function - examines the path to choose the right type of IBlobReader, and returns one.
std::unique_ptr<IBlobReader> CreateBlobReader(const std::string& filename);

typedef bool (*CompressCB)(const std::string& text, float percent, void* arg);

bool CompressFileToBlob(const std::string& infile, const std::string& outfile, u32 sub_type = 0, int sector_size = 16384,
		CompressCB callback = nullptr, void *arg = nullptr);
bool DecompressBlobToFile(const std::string& infile, const std::string& outfile,
		CompressCB callback = nullptr, void *arg = nullptr);

}  // namespace