| 
									
										
										
										
											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 05:30:24 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-26 11:24:08 +00:00
										 |  |  | #include <algorithm>
 | 
					
						
							| 
									
										
										
										
											2014-02-21 01:47:53 +01:00
										 |  |  | #include <cstddef>
 | 
					
						
							| 
									
										
										
										
											2015-12-07 19:53:42 -05:00
										 |  |  | #include <limits>
 | 
					
						
							| 
									
										
										
										
											2015-12-06 23:15:51 -05:00
										 |  |  | #include <memory>
 | 
					
						
							| 
									
										
										
										
											2014-02-21 01:47:53 +01:00
										 |  |  | #include <string>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-17 05:18:15 -05:00
										 |  |  | #include "Common/CDUtils.h"
 | 
					
						
							| 
									
										
										
										
											2014-02-21 01:47:53 +01:00
										 |  |  | #include "Common/CommonTypes.h"
 | 
					
						
							| 
									
										
										
										
											2014-02-17 05:18:15 -05:00
										 |  |  | #include "Common/FileUtil.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "DiscIO/Blob.h"
 | 
					
						
							|  |  |  | #include "DiscIO/CISOBlob.h"
 | 
					
						
							|  |  |  | #include "DiscIO/CompressedBlob.h"
 | 
					
						
							|  |  |  | #include "DiscIO/DriveBlob.h"
 | 
					
						
							|  |  |  | #include "DiscIO/FileBlob.h"
 | 
					
						
							|  |  |  | #include "DiscIO/WbfsBlob.h"
 | 
					
						
							| 
									
										
										
										
											2008-12-08 05:30:24 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | namespace DiscIO | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void SectorReader::SetSectorSize(int blocksize) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-04-26 11:24:08 +00:00
										 |  |  | 	m_block_size = std::max(blocksize, 0); | 
					
						
							| 
									
										
										
										
											2015-12-07 19:53:42 -05:00
										 |  |  | 	for (auto& cache_entry : m_cache) | 
					
						
							| 
									
										
										
										
											2016-04-26 11:24:08 +00:00
										 |  |  | 	{ | 
					
						
							|  |  |  | 		cache_entry.Reset(); | 
					
						
							|  |  |  | 		cache_entry.data.resize(m_chunk_blocks * m_block_size); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2015-12-07 19:53:42 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-26 11:24:08 +00:00
										 |  |  | void SectorReader::SetChunkSize(int block_cnt) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	m_chunk_blocks = std::max(block_cnt, 1); | 
					
						
							|  |  |  | 	// Clear cache and resize the data arrays
 | 
					
						
							|  |  |  | 	SetSectorSize(m_block_size); | 
					
						
							| 
									
										
										
										
											2008-12-08 05:30:24 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-12 15:33:41 -04:00
										 |  |  | SectorReader::~SectorReader() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2008-12-08 05:30:24 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-26 11:24:08 +00:00
										 |  |  | const SectorReader::Cache* SectorReader::FindCacheLine(u64 block_num) | 
					
						
							| 
									
										
										
										
											2008-12-08 05:30:24 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-04-26 11:24:08 +00:00
										 |  |  | 	auto itr = std::find_if(m_cache.begin(), m_cache.end(), [&](const Cache& entry) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		return entry.Contains(block_num); | 
					
						
							|  |  |  | 	}); | 
					
						
							|  |  |  | 	if (itr == m_cache.end()) | 
					
						
							|  |  |  | 		return nullptr; | 
					
						
							| 
									
										
										
										
											2015-12-07 19:53:42 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-26 11:24:08 +00:00
										 |  |  | 	itr->MarkUsed(); | 
					
						
							|  |  |  | 	return &*itr; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | SectorReader::Cache* SectorReader::GetEmptyCacheLine() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	Cache* oldest = &m_cache[0]; | 
					
						
							|  |  |  | 	// Find the Least Recently Used cache line to replace.
 | 
					
						
							|  |  |  | 	for (auto& cache_entry : m_cache) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		if (cache_entry.IsLessRecentlyUsedThan(*oldest)) | 
					
						
							|  |  |  | 			oldest = &cache_entry; | 
					
						
							|  |  |  | 		cache_entry.ShiftLRU(); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	oldest->Reset(); | 
					
						
							|  |  |  | 	return oldest; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const SectorReader::Cache* SectorReader::GetCacheLine(u64 block_num) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (auto entry = FindCacheLine(block_num)) | 
					
						
							|  |  |  | 		return entry; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Cache miss. Fault in the missing entry.
 | 
					
						
							|  |  |  | 	Cache* cache = GetEmptyCacheLine(); | 
					
						
							|  |  |  | 	// We only read aligned chunks, this avoids duplicate overlapping entries.
 | 
					
						
							|  |  |  | 	u64 chunk_idx = block_num / m_chunk_blocks; | 
					
						
							|  |  |  | 	u32 blocks_read = ReadChunk(cache->data.data(), chunk_idx); | 
					
						
							|  |  |  | 	if (!blocks_read) | 
					
						
							|  |  |  | 		return nullptr; | 
					
						
							|  |  |  | 	cache->Fill(chunk_idx * m_chunk_blocks, blocks_read); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Secondary check for out-of-bounds read.
 | 
					
						
							|  |  |  | 	// If we got less than m_chunk_blocks, we may still have missed.
 | 
					
						
							|  |  |  | 	// We do this after the cache fill since the cache line itself is
 | 
					
						
							|  |  |  | 	// fine, the problem is being asked to read past the end of the disk.
 | 
					
						
							|  |  |  | 	return cache->Contains(block_num) ? cache : nullptr; | 
					
						
							| 
									
										
										
										
											2008-12-08 05:30:24 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool SectorReader::Read(u64 offset, u64 size, u8* out_ptr) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u64 remain = size; | 
					
						
							| 
									
										
										
										
											2016-04-26 11:24:08 +00:00
										 |  |  | 	u64 block  = 0; | 
					
						
							|  |  |  | 	u32 position_in_block = static_cast<u32>(offset % m_block_size); | 
					
						
							| 
									
										
										
										
											2008-12-08 05:30:24 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	while (remain > 0) | 
					
						
							|  |  |  | 	{ | 
					
						
							| 
									
										
										
										
											2016-04-26 11:24:08 +00:00
										 |  |  | 		block = offset / m_block_size; | 
					
						
							| 
									
										
										
										
											2008-12-08 05:30:24 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-26 11:24:08 +00:00
										 |  |  | 		const Cache* cache = GetCacheLine(block); | 
					
						
							|  |  |  | 		if (!cache) | 
					
						
							|  |  |  | 			return false; | 
					
						
							| 
									
										
										
										
											2015-09-16 21:06:54 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-26 11:24:08 +00:00
										 |  |  | 		// Cache entries are aligned chunks, we may not want to read from the start
 | 
					
						
							|  |  |  | 		u32 read_offset = static_cast<u32>(block - cache->block_idx) * m_block_size + position_in_block; | 
					
						
							|  |  |  | 		u32 can_read = m_block_size * cache->num_blocks - read_offset; | 
					
						
							|  |  |  | 		u32 was_read = static_cast<u32>(std::min<u64>(can_read, remain)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		std::copy(cache->data.begin() + read_offset, | 
					
						
							|  |  |  | 		          cache->data.begin() + read_offset + was_read, | 
					
						
							|  |  |  | 		          out_ptr); | 
					
						
							| 
									
										
										
										
											2014-03-12 15:33:41 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-26 11:24:08 +00:00
										 |  |  | 		offset  += was_read; | 
					
						
							|  |  |  | 		out_ptr += was_read; | 
					
						
							|  |  |  | 		remain  -= was_read; | 
					
						
							|  |  |  | 		position_in_block = 0; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-12-08 05:30:24 +00:00
										 |  |  | 	return true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-26 11:24:08 +00:00
										 |  |  | // Crap default implementation if not overridden.
 | 
					
						
							|  |  |  | bool SectorReader::ReadMultipleAlignedBlocks(u64 block_num, u64 cnt_blocks, u8* out_ptr) | 
					
						
							| 
									
										
										
										
											2009-02-22 16:48:54 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-04-26 11:24:08 +00:00
										 |  |  | 	for (u64 i = 0; i < cnt_blocks; ++i) | 
					
						
							| 
									
										
										
										
											2009-02-22 16:48:54 +00:00
										 |  |  | 	{ | 
					
						
							| 
									
										
										
										
											2016-04-26 11:24:08 +00:00
										 |  |  | 		if (!GetBlock(block_num + i, out_ptr)) | 
					
						
							|  |  |  | 			return false; | 
					
						
							|  |  |  | 		out_ptr += m_block_size; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return true; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2015-12-07 19:53:42 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-26 11:24:08 +00:00
										 |  |  | u32 SectorReader::ReadChunk(u8* buffer, u64 chunk_num) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u64 block_num  = chunk_num * m_chunk_blocks; | 
					
						
							|  |  |  | 	u32 cnt_blocks = m_chunk_blocks; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// If we are reading the end of a disk, there may not be enough blocks to
 | 
					
						
							|  |  |  | 	// read a whole chunk. We need to clamp down in that case.
 | 
					
						
							|  |  |  | 	u64 end_block = GetDataSize() / m_block_size; | 
					
						
							|  |  |  | 	if (end_block) | 
					
						
							|  |  |  | 		cnt_blocks = static_cast<u32>(std::min<u64>(m_chunk_blocks, end_block - block_num)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (ReadMultipleAlignedBlocks(block_num, cnt_blocks, buffer)) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		if (cnt_blocks < m_chunk_blocks) | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			std::fill(buffer + cnt_blocks * m_block_size, | 
					
						
							|  |  |  | 			          buffer + m_chunk_blocks * m_block_size, | 
					
						
							|  |  |  | 			          0u); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return cnt_blocks; | 
					
						
							| 
									
										
										
										
											2009-02-22 16:48:54 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2014-03-12 15:33:41 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-26 11:24:08 +00:00
										 |  |  | 	// end_block may be zero on real disks if we fail to get the media size.
 | 
					
						
							|  |  |  | 	// We have to fallback to probing the disk instead.
 | 
					
						
							|  |  |  | 	if (!end_block) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		for (u32 i = 0; i < cnt_blocks; ++i) | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			if (!GetBlock(block_num + i, buffer)) | 
					
						
							|  |  |  | 			{ | 
					
						
							|  |  |  | 				std::fill(buffer, buffer + (cnt_blocks - i) * m_block_size, 0u); | 
					
						
							|  |  |  | 				return i; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			buffer += m_block_size; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return cnt_blocks; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return 0; | 
					
						
							| 
									
										
										
										
											2009-02-22 16:48:54 +00:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2008-12-08 05:30:24 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-06 23:15:51 -05:00
										 |  |  | std::unique_ptr<IBlobReader> CreateBlobReader(const std::string& filename) | 
					
						
							| 
									
										
										
										
											2008-12-08 05:30:24 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2014-03-12 15:33:41 -04:00
										 |  |  | 	if (cdio_is_cdrom(filename)) | 
					
						
							| 
									
										
										
										
											2009-02-21 23:44:40 +00:00
										 |  |  | 		return DriveReader::Create(filename); | 
					
						
							| 
									
										
										
										
											2008-12-08 05:30:24 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (!File::Exists(filename)) | 
					
						
							| 
									
										
										
										
											2014-03-09 21:14:26 +01:00
										 |  |  | 		return nullptr; | 
					
						
							| 
									
										
										
										
											2008-12-08 05:30:24 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-04 00:09:01 +10:00
										 |  |  | 	if (IsWbfsBlob(filename)) | 
					
						
							|  |  |  | 		return WbfsFileReader::Create(filename); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-09-26 14:46:53 +02:00
										 |  |  | 	if (IsGCZBlob(filename)) | 
					
						
							| 
									
										
										
										
											2008-12-08 05:30:24 +00:00
										 |  |  | 		return CompressedBlobReader::Create(filename); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-12-29 14:42:20 +00:00
										 |  |  | 	if (IsCISOBlob(filename)) | 
					
						
							|  |  |  | 		return CISOFileReader::Create(filename); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-24 19:57:29 +00:00
										 |  |  | 	// Still here? Assume plain file - since we know it exists due to the File::Exists check above.
 | 
					
						
							| 
									
										
										
										
											2008-12-08 05:30:24 +00:00
										 |  |  | 	return PlainFileReader::Create(filename); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | }  // namespace
 |