CDVD: Use ThreadedFileReader for uncompressed ISOs

This commit is contained in:
Stenzek 2024-05-16 20:36:43 +10:00 committed by Connor McLaughlin
parent f0ae33d61e
commit 29e9125b15
18 changed files with 151 additions and 443 deletions

View File

@ -53,7 +53,6 @@ else()
if(UNIX AND NOT APPLE)
if(LINUX)
check_lib(AIO aio libaio.h)
check_lib(LIBUDEV libudev libudev.h)
endif()

View File

@ -5,13 +5,6 @@
#include "common/Pcsx2Defs.h"
#ifdef _WIN32
#include "common/RedtapeWindows.h"
#elif defined(__linux__)
#include <libaio.h>
#elif defined(__POSIX__)
#include <aio.h>
#endif
#include <memory>
#include <string>
@ -20,16 +13,12 @@ class Error;
class AsyncFileReader
{
protected:
AsyncFileReader()
: m_dataoffset(0)
, m_blocksize(0)
{
}
AsyncFileReader() = default;
std::string m_filename;
u32 m_dataoffset;
u32 m_blocksize;
u32 m_dataoffset = 0;
u32 m_blocksize = 2048;
public:
virtual ~AsyncFileReader(){};
@ -52,46 +41,3 @@ public:
const std::string& GetFilename() const { return m_filename; }
u32 GetBlockSize() const { return m_blocksize; }
};
class FlatFileReader final : public AsyncFileReader
{
DeclareNoncopyableObject(FlatFileReader);
#ifdef _WIN32
HANDLE hOverlappedFile;
OVERLAPPED asyncOperationContext;
HANDLE hEvent;
bool asyncInProgress;
#elif defined(__linux__)
int m_fd; // FIXME don't know if overlap as an equivalent on linux
io_context_t m_aio_context;
#elif defined(__POSIX__)
int m_fd; // TODO OSX don't know if overlap as an equivalent on OSX
struct aiocb m_aiocb;
bool m_async_read_in_progress;
#endif
bool shareWrite;
public:
FlatFileReader(bool shareWrite = false);
~FlatFileReader() override;
bool Open(std::string filenae, Error* error) override;
int ReadSync(void* pBuffer, u32 sector, u32 count) override;
void BeginRead(void* pBuffer, u32 sector, u32 count) override;
int FinishRead() override;
void CancelRead() override;
void Close() override;
u32 GetBlockCount() const override;
void SetBlockSize(u32 bytes) override { m_blocksize = bytes; }
void SetDataOffset(u32 bytes) override { m_dataoffset = bytes; }
};

View File

@ -39,7 +39,7 @@ BlockdumpFileReader::BlockdumpFileReader() = default;
BlockdumpFileReader::~BlockdumpFileReader()
{
Close();
pxAssert(!m_file);
}
bool BlockdumpFileReader::Open2(std::string filename, Error* error)

View File

@ -23,6 +23,11 @@
#include "fmt/core.h"
// TODO: FIXME! Should be platform specific.
#ifdef _WIN32
#include "common/RedtapeWindows.h"
#endif
#define ENABLE_TIMESTAMPS
const CDVD_API* CDVD = nullptr;

View File

@ -18,15 +18,11 @@ static constexpr u32 MAX_PARENTS = 32; // Surely someone wouldn't be insane enou
static std::vector<std::pair<std::string, chd_header>> s_chd_hash_cache; // <filename, header>
static std::recursive_mutex s_chd_hash_cache_mutex;
ChdFileReader::ChdFileReader()
{
m_blocksize = 2048;
ChdFile = nullptr;
}
ChdFileReader::ChdFileReader() = default;
ChdFileReader::~ChdFileReader()
{
Close();
pxAssert(!ChdFile);
}
static chd_file* OpenCHD(const std::string& filename, FileSystem::ManagedCFilePtr fp, Error* error, u32 recursion_level)

View File

@ -26,7 +26,7 @@ public:
private:
bool ParseTOC(u64* out_frame_count);
chd_file* ChdFile;
u64 file_size;
u32 hunk_size;
chd_file* ChdFile = nullptr;
u64 file_size = 0;
u32 hunk_size = 0;
};

View File

@ -4,6 +4,7 @@
#include "AsyncFileReader.h"
#include "CsoFileReader.h"
#include "common/Assertions.h"
#include "common/Console.h"
#include "common/FileSystem.h"
#include "common/Error.h"
@ -29,14 +30,11 @@ struct CsoHeader
static const u32 CSO_READ_BUFFER_SIZE = 256 * 1024;
CsoFileReader::CsoFileReader()
{
m_blocksize = 2048;
}
CsoFileReader::CsoFileReader() = default;
CsoFileReader::~CsoFileReader()
{
Close();
pxAssert(m_src);
}
bool CsoFileReader::ValidateHeader(const CsoHeader& hdr, Error* error)

View File

@ -0,0 +1,85 @@
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
// SPDX-License-Identifier: LGPL-3.0+
#include "FlatFileReader.h"
#include "common/Assertions.h"
#include "common/Console.h"
#include "common/FileSystem.h"
#include "common/Error.h"
#include <cerrno>
#include <cstring>
static constexpr size_t CHUNK_SIZE = 128 * 1024;
FlatFileReader::FlatFileReader() = default;
FlatFileReader::~FlatFileReader()
{
pxAssert(!m_file);
}
bool FlatFileReader::Open2(std::string filename, Error* error)
{
m_filename = std::move(filename);
if (!(m_file = FileSystem::OpenCFile(m_filename.c_str(), "rb", error)))
return false;
const s64 filesize = FileSystem::FSize64(m_file);
if (filesize <= 0)
{
Error::SetStringView(error, "Failed to determine file size.");
Close2();
return false;
}
m_file_size = static_cast<u64>(filesize);
return true;
}
ThreadedFileReader::Chunk FlatFileReader::ChunkForOffset(u64 offset)
{
ThreadedFileReader::Chunk chunk = {};
if (offset >= m_file_size)
{
chunk.chunkID = -1;
}
else
{
chunk.chunkID = offset / CHUNK_SIZE;
chunk.length = static_cast<u32>(std::min<u64>(m_file_size - offset, CHUNK_SIZE));
chunk.offset = static_cast<u64>(chunk.chunkID) * CHUNK_SIZE;
}
return chunk;
}
int FlatFileReader::ReadChunk(void* dst, s64 blockID)
{
if (blockID < 0)
return -1;
const u64 file_offset = static_cast<u64>(blockID) * CHUNK_SIZE;
if (FileSystem::FSeek64(m_file, file_offset, SEEK_SET) != 0)
return -1;
const u32 read_size = static_cast<u32>(std::min<u64>(m_file_size - file_offset, CHUNK_SIZE));
return (std::fread(dst, read_size, 1, m_file) == 1) ? static_cast<int>(read_size) : 0;
}
void FlatFileReader::Close2()
{
if (!m_file)
return;
std::fclose(m_file);
m_file = nullptr;
m_file_size = 0;
}
u32 FlatFileReader::GetBlockCount() const
{
return static_cast<u32>(m_file_size / m_blocksize);
}

View File

@ -0,0 +1,29 @@
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
// SPDX-License-Identifier: LGPL-3.0+
#pragma once
#include "CDVD/ThreadedFileReader.h"
#include <cstdio>
class FlatFileReader final : public ThreadedFileReader
{
DeclareNoncopyableObject(FlatFileReader);
std::FILE* m_file = nullptr;
u64 m_file_size = 0;
public:
FlatFileReader();
~FlatFileReader() override;
bool Open2(std::string filename, Error* error) override;
Chunk ChunkForOffset(u64 offset) override;
int ReadChunk(void* dst, s64 blockID) override;
void Close2() override;
u32 GetBlockCount() const override;
};

View File

@ -2,6 +2,7 @@
// SPDX-License-Identifier: LGPL-3.0+
#include "CDVD/BlockdumpFileReader.h"
#include "CDVD/FlatFileReader.h"
#include "CDVD/IsoFileFormats.h"
#include "Config.h"
#include "Host.h"
@ -203,16 +204,11 @@ bool InputIsoFile::Open(std::string srcfile, Error* error, bool testOnly)
// First try using a compressed reader. If it works, go with it.
m_reader = CompressedFileReader::GetNewReader(m_filename);
isCompressed = m_reader != NULL;
isCompressed = m_reader != nullptr;
// If it wasn't compressed, let's open it has a FlatFileReader.
if (!isCompressed)
{
// Allow write sharing of the iso based on the ini settings.
// Mostly useful for romhacking, where the disc is frequently
// changed and the emulator would block modifications
m_reader = new FlatFileReader(EmuConfig.CdvdShareWrite);
}
m_reader = new FlatFileReader();
if (!m_reader->Open(m_filename, error))
return false;
@ -274,15 +270,19 @@ bool InputIsoFile::Open(std::string srcfile, Error* error, bool testOnly)
void InputIsoFile::Close()
{
delete m_reader;
m_reader = NULL;
if (m_reader)
{
m_reader->Close();
delete m_reader;
m_reader = nullptr;
}
_init();
}
bool InputIsoFile::IsOpened() const
{
return m_reader != NULL;
return m_reader != nullptr;
}
bool InputIsoFile::tryIsoType(u32 size, u32 offset, u32 blockofs)

View File

@ -211,6 +211,7 @@ set(pcsx2CDVDSources
CDVD/CDVDdiscReader.cpp
CDVD/CDVDisoReader.cpp
CDVD/CDVDdiscThread.cpp
CDVD/FlatFileReader.cpp
CDVD/InputIsoFile.cpp
CDVD/IsoHasher.cpp
CDVD/IsoReader.cpp
@ -235,6 +236,7 @@ set(pcsx2CDVDHeaders
CDVD/CompressedFileReader.h
CDVD/ChdFileReader.h
CDVD/CsoFileReader.h
CDVD/FlatFileReader.h
CDVD/GzippedFileReader.h
CDVD/ThreadedFileReader.h
CDVD/IsoFileFormats.h
@ -852,19 +854,16 @@ endif()
set(pcsx2LinuxSources
CDVD/Linux/DriveUtility.cpp
CDVD/Linux/IOCtlSrc.cpp
Linux/LnxFlatFileReader.cpp
)
set(pcsx2OSXSources
CDVD/Darwin/DriveUtility.cpp
CDVD/Darwin/IOCtlSrc.cpp
Darwin/DarwinFlatFileReader.cpp
)
set(pcsx2FreeBSDSources
CDVD/Darwin/DriveUtility.cpp
CDVD/Darwin/IOCtlSrc.cpp
Darwin/DarwinFlatFileReader.cpp
)
# Linux headers
@ -931,7 +930,6 @@ set(pcsx2RecordingHeaders
set(pcsx2WindowsSources
CDVD/Windows/DriveUtility.cpp
CDVD/Windows/IOCtlSrc.cpp
windows/FlatFileReaderWindows.cpp
windows/Optimus.cpp
)
@ -1065,7 +1063,6 @@ if(LINUX)
)
target_link_libraries(PCSX2_FLAGS INTERFACE
PkgConfig::AIO
PkgConfig::LIBUDEV
)
endif()

View File

@ -1110,7 +1110,6 @@ struct Pcsx2Config
bool
CdvdVerboseReads : 1, // enables cdvd read activity verbosely dumped to the console
CdvdDumpBlocks : 1, // enables cdvd block dumping
CdvdShareWrite : 1, // allows the iso to be modified while it's loaded
EnablePatches : 1, // enables patch detection and application
EnableCheats : 1, // enables cheat detection and application
EnablePINE : 1, // enables inter-process communication

View File

@ -1,126 +0,0 @@
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team
// SPDX-License-Identifier: LGPL-3.0+
#include "AsyncFileReader.h"
#include "common/Console.h"
#include "common/FileSystem.h"
#include <unistd.h>
#include <fcntl.h>
// The aio module has been reported to cause issues with FreeBSD 10.3, so let's
// disable it for 10.3 and earlier and hope FreeBSD 11 and onwards is fine.
// Note: It may be worth checking whether aio provides any performance benefit.
#if defined(__FreeBSD__) && __FreeBSD__ < 11
#define DISABLE_AIO
#warning AIO has been disabled.
#endif
FlatFileReader::FlatFileReader(bool shareWrite)
: shareWrite(shareWrite)
{
m_blocksize = 2048;
m_fd = -1;
m_async_read_in_progress = false;
}
FlatFileReader::~FlatFileReader()
{
Close();
}
bool FlatFileReader::Open(std::string filename, Error* error)
{
m_filename = std::move(filename);
m_fd = FileSystem::OpenFDFile(m_filename.c_str(), O_RDONLY, 0, error);
return (m_fd != -1);
}
int FlatFileReader::ReadSync(void* pBuffer, u32 sector, u32 count)
{
BeginRead(pBuffer, sector, count);
return FinishRead();
}
void FlatFileReader::BeginRead(void* pBuffer, u32 sector, u32 count)
{
u64 offset = sector * (u64)m_blocksize + m_dataoffset;
u32 bytesToRead = count * m_blocksize;
m_async_read_in_progress = false;
#ifndef DISABLE_AIO
m_aiocb = {0};
m_aiocb.aio_fildes = m_fd;
m_aiocb.aio_offset = offset;
m_aiocb.aio_nbytes = bytesToRead;
m_aiocb.aio_buf = pBuffer;
if (aio_read(&m_aiocb) == 0)
{
m_async_read_in_progress = true;
}
else
{
switch (errno)
{
#if defined(__FreeBSD__)
case ENOSYS:
Console.Error("AIO read failed: Check the aio kernel module is loaded");
break;
#endif
case EAGAIN:
Console.Warning("AIO read failed: Out of resources. Will read synchronously");
break;
default:
Console.Error("AIO read failed: error code %d\n", errno);
break;
}
}
#endif
if (!m_async_read_in_progress)
{
m_aiocb.aio_nbytes = pread(m_fd, pBuffer, bytesToRead, offset);
if (m_aiocb.aio_nbytes != bytesToRead)
m_aiocb.aio_nbytes = -1;
}
}
int FlatFileReader::FinishRead()
{
if (!m_async_read_in_progress)
return m_aiocb.aio_nbytes == (size_t)-1 ? -1 : 1;
m_async_read_in_progress = true;
struct aiocb* aiocb_list[] = {&m_aiocb};
while (aio_suspend(aiocb_list, 1, nullptr) == -1 && errno == EINTR)
;
return aio_return(&m_aiocb);
}
void FlatFileReader::CancelRead()
{
if (m_async_read_in_progress)
{
aio_cancel(m_fd, &m_aiocb);
m_async_read_in_progress = false;
}
}
void FlatFileReader::Close()
{
CancelRead();
if (m_fd != -1)
close(m_fd);
m_fd = -1;
}
u32 FlatFileReader::GetBlockCount() const
{
return static_cast<u32>(FileSystem::GetPathFileSize(m_filename.c_str()) / m_blocksize);
}

View File

@ -1,95 +0,0 @@
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team
// SPDX-License-Identifier: LGPL-3.0+
#include "AsyncFileReader.h"
#include "common/FileSystem.h"
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
FlatFileReader::FlatFileReader(bool shareWrite)
: shareWrite(shareWrite)
{
m_blocksize = 2048;
m_fd = -1;
m_aio_context = 0;
}
FlatFileReader::~FlatFileReader()
{
Close();
}
bool FlatFileReader::Open(std::string filename, Error* error)
{
m_filename = std::move(filename);
int err = io_setup(64, &m_aio_context);
if (err)
return false;
m_fd = FileSystem::OpenFDFile(m_filename.c_str(), O_RDONLY, 0, error);
return (m_fd != -1);
}
int FlatFileReader::ReadSync(void* pBuffer, u32 sector, u32 count)
{
BeginRead(pBuffer, sector, count);
return FinishRead();
}
void FlatFileReader::BeginRead(void* pBuffer, u32 sector, u32 count)
{
u64 offset;
offset = sector * (s64)m_blocksize + m_dataoffset;
u32 bytesToRead = count * m_blocksize;
struct iocb iocb;
struct iocb* iocbs = &iocb;
io_prep_pread(&iocb, m_fd, pBuffer, bytesToRead, offset);
io_submit(m_aio_context, 1, &iocbs);
}
int FlatFileReader::FinishRead()
{
struct io_event event;
int nevents = io_getevents(m_aio_context, 1, 1, &event, NULL);
if (nevents < 1)
return -1;
return event.res;
}
void FlatFileReader::CancelRead()
{
// Will be done when m_aio_context context is destroyed
// Note: io_cancel exists but need the iocb structure as parameter
// int io_cancel(aio_context_t ctx_id, struct iocb *iocb,
// struct io_event *result);
}
void FlatFileReader::Close()
{
if (m_fd != -1)
close(m_fd);
io_destroy(m_aio_context);
m_fd = -1;
m_aio_context = 0;
}
u32 FlatFileReader::GetBlockCount() const
{
struct stat sysStatData;
if (fstat(m_fd, &sysStatData) < 0)
return 0;
return static_cast<u32>(sysStatData.st_size / m_blocksize);
}

View File

@ -1709,7 +1709,6 @@ void Pcsx2Config::LoadSaveCore(SettingsWrapper& wrap)
SettingsWrapBitBool(CdvdVerboseReads);
SettingsWrapBitBool(CdvdDumpBlocks);
SettingsWrapBitBool(CdvdShareWrite);
SettingsWrapBitBool(EnablePatches);
SettingsWrapBitBool(EnableCheats);
SettingsWrapBitBool(EnablePINE);

View File

@ -115,6 +115,7 @@
<ClCompile Include="CDVD\ChunksCache.cpp" />
<ClCompile Include="CDVD\CompressedFileReader.cpp" />
<ClCompile Include="CDVD\CsoFileReader.cpp" />
<ClCompile Include="CDVD\FlatFileReader.cpp" />
<ClCompile Include="CDVD\GzippedFileReader.cpp" />
<ClCompile Include="CDVD\IsoReader.cpp" />
<ClCompile Include="CDVD\IsoHasher.cpp" />
@ -345,10 +346,6 @@
<ClCompile Include="VMManager.cpp" />
<ClCompile Include="windows\Optimus.cpp" />
<ClCompile Include="Pcsx2Config.cpp" />
<ClCompile Include="windows\FlatFileReaderWindows.cpp" />
<ClCompile Include="Darwin\DarwinFlatFileReader.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="SaveState.cpp" />
<ClCompile Include="SourceLog.cpp" />
<ClCompile Include="Elfheader.cpp" />
@ -472,6 +469,7 @@
<ClInclude Include="CDVD\CompressedFileReaderUtils.h" />
<ClInclude Include="CDVD\CsoFileReader.h" />
<ClInclude Include="CDVD\ChdFileReader.h" />
<ClInclude Include="CDVD\FlatFileReader.h" />
<ClInclude Include="CDVD\GzippedFileReader.h" />
<ClInclude Include="CDVD\IsoReader.h" />
<ClInclude Include="CDVD\IsoHasher.h" />

View File

@ -713,12 +713,6 @@
<ClCompile Include="Gif_Logger.cpp">
<Filter>System\Ps2\GS</Filter>
</ClCompile>
<ClCompile Include="windows\FlatFileReaderWindows.cpp">
<Filter>System\ISO</Filter>
</ClCompile>
<ClCompile Include="Darwin\DarwinFlatFileReader.cpp">
<Filter>System\ISO</Filter>
</ClCompile>
<ClCompile Include="CDVD\InputIsoFile.cpp">
<Filter>System\ISO</Filter>
</ClCompile>
@ -1404,6 +1398,9 @@
<ClCompile Include="Host\CubebAudioStream.cpp">
<Filter>Misc\Host</Filter>
</ClCompile>
<ClCompile Include="CDVD\FlatFileReader.cpp">
<Filter>System\ISO</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Patch.h">
@ -2321,6 +2318,9 @@
<ClInclude Include="CDVD\BlockdumpFileReader.h">
<Filter>System\ISO</Filter>
</ClInclude>
<ClInclude Include="CDVD\FlatFileReader.h">
<Filter>System\ISO</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<CustomBuildStep Include="rdebug\deci2.h">

View File

@ -1,122 +0,0 @@
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team
// SPDX-License-Identifier: LGPL-3.0+
#include "AsyncFileReader.h"
#include "common/FileSystem.h"
#include "common/Error.h"
FlatFileReader::FlatFileReader(bool shareWrite) : shareWrite(shareWrite)
{
m_blocksize = 2048;
hOverlappedFile = INVALID_HANDLE_VALUE;
hEvent = INVALID_HANDLE_VALUE;
asyncInProgress = false;
}
FlatFileReader::~FlatFileReader()
{
Close();
}
bool FlatFileReader::Open(std::string filename, Error* error)
{
m_filename = std::move(filename);
hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
DWORD shareMode = FILE_SHARE_READ;
if (shareWrite)
shareMode |= FILE_SHARE_WRITE;
hOverlappedFile = CreateFile(
FileSystem::GetWin32Path(m_filename).c_str(),
GENERIC_READ,
shareMode,
NULL,
OPEN_EXISTING,
FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_OVERLAPPED,
NULL);
if (hOverlappedFile == INVALID_HANDLE_VALUE)
{
Error::SetWin32(error, GetLastError());
return false;
}
return true;
}
int FlatFileReader::ReadSync(void* pBuffer, u32 sector, u32 count)
{
//LARGE_INTEGER offset;
//offset.QuadPart = sector * (__int64)m_blocksize;
//
//DWORD bytesToRead = count * m_blocksize;
//DWORD bytes;
//if(!ReadFile(hOverlappedFile, pBuffer, bytesToRead, &bytes, NULL))
// return -1;
//return bytes;
BeginRead(pBuffer, sector, count);
return FinishRead();
}
void FlatFileReader::BeginRead(void* pBuffer, u32 sector, u32 count)
{
LARGE_INTEGER offset;
offset.QuadPart = sector * (s64)m_blocksize + m_dataoffset;
DWORD bytesToRead = count * m_blocksize;
ZeroMemory(&asyncOperationContext, sizeof(asyncOperationContext));
asyncOperationContext.hEvent = hEvent;
asyncOperationContext.Offset = offset.LowPart;
asyncOperationContext.OffsetHigh = offset.HighPart;
ReadFile(hOverlappedFile, pBuffer, bytesToRead, NULL, &asyncOperationContext);
asyncInProgress = true;
}
int FlatFileReader::FinishRead()
{
DWORD bytes;
if(!GetOverlappedResult(hOverlappedFile, &asyncOperationContext, &bytes, TRUE))
{
asyncInProgress = false;
return -1;
}
asyncInProgress = false;
return bytes;
}
void FlatFileReader::CancelRead()
{
CancelIo(hOverlappedFile);
}
void FlatFileReader::Close()
{
if(asyncInProgress)
CancelRead();
if(hOverlappedFile != INVALID_HANDLE_VALUE)
CloseHandle(hOverlappedFile);
if(hEvent != INVALID_HANDLE_VALUE)
CloseHandle(hEvent);
hOverlappedFile = INVALID_HANDLE_VALUE;
hEvent = INVALID_HANDLE_VALUE;
}
u32 FlatFileReader::GetBlockCount(void) const
{
LARGE_INTEGER fileSize;
fileSize.LowPart = GetFileSize(hOverlappedFile, reinterpret_cast<DWORD*>(&fileSize.HighPart));
return static_cast<u32>(fileSize.QuadPart / m_blocksize);
}