mirror of
				https://github.com/dolphin-emu/dolphin.git
				synced 2025-10-25 01:19:19 +00:00 
			
		
		
		
	SPDX standardizes how source code conveys its copyright and licensing information. See https://spdx.github.io/spdx-spec/1-rationale/ . SPDX tags are adopted in many large projects, including things like the Linux kernel.
		
			
				
	
	
		
			160 lines
		
	
	
	
		
			4.7 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			160 lines
		
	
	
	
		
			4.7 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| // Copyright 2008 Dolphin Emulator Project
 | |
| // SPDX-License-Identifier: GPL-2.0-or-later
 | |
| 
 | |
| #include <algorithm>
 | |
| #include <cstdio>
 | |
| #include <cstring>
 | |
| #include <memory>
 | |
| #include <string>
 | |
| #include <vector>
 | |
| 
 | |
| #include "Common/CommonTypes.h"
 | |
| #include "Common/IOFile.h"
 | |
| #include "Common/Logging/Log.h"
 | |
| #include "DiscIO/Blob.h"
 | |
| #include "DiscIO/DriveBlob.h"
 | |
| 
 | |
| #ifdef _WIN32
 | |
| #include "Common/StringUtil.h"
 | |
| #else
 | |
| #include <stdio.h>  // fileno
 | |
| #include <sys/ioctl.h>
 | |
| #if defined __linux__
 | |
| #include <linux/fs.h>  // BLKGETSIZE64
 | |
| #elif defined __FreeBSD__
 | |
| #include <sys/disk.h>  // DIOCGMEDIASIZE
 | |
| #elif defined __APPLE__
 | |
| #include <sys/disk.h>  // DKIOCGETBLOCKCOUNT / DKIOCGETBLOCKSIZE
 | |
| #endif
 | |
| #endif
 | |
| 
 | |
| namespace DiscIO
 | |
| {
 | |
| DriveReader::DriveReader(const std::string& drive)
 | |
| {
 | |
|   // 32 sectors is roughly the optimal amount a CD Drive can read in
 | |
|   // a single IO cycle. Larger values yield no performance improvement
 | |
|   // and just cause IO stalls from the read delay. Smaller values allow
 | |
|   // the OS IO and seeking overhead to ourstrip the time actually spent
 | |
|   // transferring bytes from the media.
 | |
|   SetChunkSize(32);  // 32*2048 = 64KiB
 | |
|   SetSectorSize(2048);
 | |
| #ifdef _WIN32
 | |
|   auto const path = UTF8ToTStr(std::string("\\\\.\\") + drive);
 | |
|   m_disc_handle = CreateFile(path.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
 | |
|                              nullptr, OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, nullptr);
 | |
|   if (IsOK())
 | |
|   {
 | |
|     // Do a test read to make sure everything is OK, since it seems you can get
 | |
|     // handles to empty drives.
 | |
|     DWORD not_used;
 | |
|     std::vector<u8> buffer(GetSectorSize());
 | |
|     if (!ReadFile(m_disc_handle, buffer.data(), GetSectorSize(), ¬_used, nullptr))
 | |
|     {
 | |
|       // OK, something is wrong.
 | |
|       CloseHandle(m_disc_handle);
 | |
|       m_disc_handle = INVALID_HANDLE_VALUE;
 | |
|     }
 | |
|   }
 | |
|   if (IsOK())
 | |
|   {
 | |
|     // Initialize m_size by querying the volume capacity.
 | |
|     STORAGE_READ_CAPACITY storage_size;
 | |
|     storage_size.Version = sizeof(storage_size);
 | |
|     DWORD bytes = 0;
 | |
|     DeviceIoControl(m_disc_handle, IOCTL_STORAGE_READ_CAPACITY, nullptr, 0, &storage_size,
 | |
|                     sizeof(storage_size), &bytes, nullptr);
 | |
|     m_size = bytes ? storage_size.DiskLength.QuadPart : 0;
 | |
| 
 | |
| #ifdef _LOCKDRIVE  // Do we want to lock the drive?
 | |
|     // Lock the compact disc in the CD-ROM drive to prevent accidental
 | |
|     // removal while reading from it.
 | |
|     m_lock_cdrom.PreventMediaRemoval = TRUE;
 | |
|     DeviceIoControl(m_disc_handle, IOCTL_CDROM_MEDIA_REMOVAL, &m_lock_cdrom, sizeof(m_lock_cdrom),
 | |
|                     nullptr, 0, &dwNotUsed, nullptr);
 | |
| #endif
 | |
| #else
 | |
|   m_file.Open(drive, "rb");
 | |
|   if (m_file)
 | |
|   {
 | |
|     int fd = fileno(m_file.GetHandle());
 | |
| #if defined __linux__
 | |
|     // NOTE: Doesn't matter if it fails, m_size was initialized to zero
 | |
|     ioctl(fd, BLKGETSIZE64, &m_size);  // u64*
 | |
| #elif defined __FreeBSD__
 | |
|     off_t size = 0;
 | |
|     ioctl(fd, DIOCGMEDIASIZE, &size);  // off_t*
 | |
|     m_size = size;
 | |
| #elif defined __APPLE__
 | |
|     u64 count = 0;
 | |
|     u32 block_size = 0;
 | |
|     ioctl(fd, DKIOCGETBLOCKCOUNT, &count);      // u64*
 | |
|     ioctl(fd, DKIOCGETBLOCKSIZE, &block_size);  // u32*
 | |
|     m_size = count * block_size;
 | |
| #endif
 | |
| #endif
 | |
|   }
 | |
|   else
 | |
|   {
 | |
|     NOTICE_LOG_FMT(DISCIO, "Load from DVD backup failed or no disc in drive {}", drive);
 | |
|   }
 | |
| }
 | |
| 
 | |
| DriveReader::~DriveReader()
 | |
| {
 | |
| #ifdef _WIN32
 | |
| #ifdef _LOCKDRIVE  // Do we want to lock the drive?
 | |
|   // Unlock the disc in the CD-ROM drive.
 | |
|   m_lock_cdrom.PreventMediaRemoval = FALSE;
 | |
|   DeviceIoControl(m_disc_handle, IOCTL_CDROM_MEDIA_REMOVAL, &m_lock_cdrom, sizeof(m_lock_cdrom),
 | |
|                   nullptr, 0, &dwNotUsed, nullptr);
 | |
| #endif
 | |
|   if (m_disc_handle != INVALID_HANDLE_VALUE)
 | |
|   {
 | |
|     CloseHandle(m_disc_handle);
 | |
|     m_disc_handle = INVALID_HANDLE_VALUE;
 | |
|   }
 | |
| #else
 | |
|   m_file.Close();
 | |
| #endif
 | |
| }
 | |
| 
 | |
| std::unique_ptr<DriveReader> DriveReader::Create(const std::string& drive)
 | |
| {
 | |
|   auto reader = std::unique_ptr<DriveReader>(new DriveReader(drive));
 | |
| 
 | |
|   if (!reader->IsOK())
 | |
|     reader.reset();
 | |
| 
 | |
|   return reader;
 | |
| }
 | |
| 
 | |
| bool DriveReader::GetBlock(u64 block_num, u8* out_ptr)
 | |
| {
 | |
|   return DriveReader::ReadMultipleAlignedBlocks(block_num, 1, out_ptr);
 | |
| }
 | |
| 
 | |
| bool DriveReader::ReadMultipleAlignedBlocks(u64 block_num, u64 num_blocks, u8* out_ptr)
 | |
| {
 | |
| #ifdef _WIN32
 | |
|   LARGE_INTEGER offset;
 | |
|   offset.QuadPart = GetSectorSize() * block_num;
 | |
|   DWORD bytes_read;
 | |
|   if (!SetFilePointerEx(m_disc_handle, offset, nullptr, FILE_BEGIN) ||
 | |
|       !ReadFile(m_disc_handle, out_ptr, static_cast<DWORD>(GetSectorSize() * num_blocks),
 | |
|                 &bytes_read, nullptr))
 | |
|   {
 | |
|     ERROR_LOG_FMT(DISCIO, "Disc Read Error");
 | |
|     return false;
 | |
|   }
 | |
|   return bytes_read == GetSectorSize() * num_blocks;
 | |
| #else
 | |
|   m_file.Seek(GetSectorSize() * block_num, SEEK_SET);
 | |
|   if (m_file.ReadBytes(out_ptr, num_blocks * GetSectorSize()))
 | |
|     return true;
 | |
|   m_file.Clear();
 | |
|   return false;
 | |
| #endif
 | |
| }
 | |
| 
 | |
| }  // namespace DiscIO
 |