mirror of
https://github.com/PCSX2/pcsx2.git
synced 2026-01-31 01:15:24 +01:00
314 lines
6.6 KiB
C++
314 lines
6.6 KiB
C++
// SPDX-FileCopyrightText: 2002-2026 PCSX2 Dev Team
|
|
// SPDX-License-Identifier: GPL-3.0+
|
|
|
|
#include "CDVD/BlockdumpFileReader.h"
|
|
#include "CDVD/ChdFileReader.h"
|
|
#include "CDVD/CsoFileReader.h"
|
|
#include "CDVD/FlatFileReader.h"
|
|
#include "CDVD/GzippedFileReader.h"
|
|
#include "CDVD/IsoFileFormats.h"
|
|
#include "Config.h"
|
|
#include "Host.h"
|
|
|
|
#include "common/Assertions.h"
|
|
#include "common/Console.h"
|
|
#include "common/Error.h"
|
|
#include "common/Path.h"
|
|
#include "common/StringUtil.h"
|
|
|
|
#include "fmt/format.h"
|
|
|
|
static const char* nameFromType(int type)
|
|
{
|
|
switch (type)
|
|
{
|
|
case ISOTYPE_CD:
|
|
return "CD";
|
|
case ISOTYPE_DVD:
|
|
return "DVD";
|
|
case ISOTYPE_AUDIO:
|
|
return "Audio CD";
|
|
case ISOTYPE_DVDDL:
|
|
return "DVD9 (dual-layer)";
|
|
case ISOTYPE_ILLEGAL:
|
|
return "Illegal media";
|
|
default:
|
|
return "Unknown or corrupt";
|
|
}
|
|
}
|
|
|
|
static std::unique_ptr<ThreadedFileReader> GetFileReader(const std::string& path)
|
|
{
|
|
const std::string_view extension = Path::GetExtension(path);
|
|
|
|
if (StringUtil::compareNoCase(extension, "chd"))
|
|
return std::make_unique<ChdFileReader>();
|
|
|
|
if (StringUtil::compareNoCase(extension, "cso") || StringUtil::compareNoCase(extension, "zso"))
|
|
return std::make_unique<CsoFileReader>();
|
|
|
|
if (StringUtil::compareNoCase(extension, "gz"))
|
|
return std::make_unique<GzippedFileReader>();
|
|
|
|
if (StringUtil::compareNoCase(extension, "dump"))
|
|
return std::make_unique<BlockdumpFileReader>();
|
|
|
|
return std::make_unique<FlatFileReader>();
|
|
}
|
|
|
|
int InputIsoFile::ReadSync(u8* dst, uint lsn)
|
|
{
|
|
if (lsn >= m_blocks)
|
|
{
|
|
ERROR_LOG("isoFile error: Block index is past the end of file! ({} >= {}).", lsn, m_blocks);
|
|
return -1;
|
|
}
|
|
|
|
return m_reader->ReadSync(dst + m_blockofs, lsn, 1);
|
|
}
|
|
|
|
void InputIsoFile::BeginRead2(uint lsn)
|
|
{
|
|
m_current_lsn = lsn;
|
|
|
|
if (lsn >= m_blocks)
|
|
{
|
|
// While this usually indicates that the ISO is corrupted, some games do attempt
|
|
// to read past the end of the disc, so don't error here.
|
|
ERROR_LOG("isoFile error: Block index is past the end of file! ({} >= {}).", lsn, m_blocks);
|
|
return;
|
|
}
|
|
|
|
// same sector?
|
|
if (lsn == m_read_lsn)
|
|
return;
|
|
|
|
m_read_lsn = lsn;
|
|
|
|
m_reader->BeginRead(m_readbuffer, m_read_lsn, 1);
|
|
m_read_inprogress = true;
|
|
}
|
|
|
|
int InputIsoFile::FinishRead3(u8* dst, uint mode)
|
|
{
|
|
// Do nothing for out of bounds disc sector reads. It prevents some games
|
|
// from hanging (All-Star Baseball 2005, Hello Kitty: Roller Rescue,
|
|
// Hot Wheels: Beat That! (NTSC), Ratchet & Clank 3 (PAL),
|
|
// Test Drive: Eve of Destruction, etc.).
|
|
if (m_current_lsn >= m_blocks)
|
|
return 0;
|
|
|
|
if (m_read_inprogress)
|
|
{
|
|
const int ret = m_reader->FinishRead();
|
|
m_read_inprogress = false;
|
|
|
|
if (ret <= 0)
|
|
{
|
|
m_read_lsn = -1;
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
int _offset = 0;
|
|
int length = 0;
|
|
|
|
switch (mode)
|
|
{
|
|
case CDVD_MODE_2352:
|
|
_offset = 0;
|
|
length = 2352;
|
|
break;
|
|
case CDVD_MODE_2340:
|
|
_offset = 12;
|
|
length = 2340;
|
|
break;
|
|
case CDVD_MODE_2328:
|
|
_offset = 24;
|
|
length = 2328;
|
|
break;
|
|
case CDVD_MODE_2048:
|
|
_offset = 24;
|
|
length = 2048;
|
|
break;
|
|
}
|
|
|
|
int end1 = m_blockofs + m_blocksize;
|
|
int end2 = _offset + length;
|
|
int end = std::min(end1, end2);
|
|
|
|
int diff = m_blockofs - _offset;
|
|
int ndiff = 0;
|
|
if (diff > 0)
|
|
{
|
|
std::memset(dst, 0, diff);
|
|
_offset = m_blockofs;
|
|
}
|
|
else
|
|
{
|
|
ndiff = -diff;
|
|
diff = 0;
|
|
}
|
|
|
|
length = end - _offset;
|
|
|
|
std::memcpy(dst + diff, m_readbuffer + ndiff, length);
|
|
|
|
if (m_type == ISOTYPE_CD && diff >= 12)
|
|
{
|
|
lsn_to_msf(dst + diff - 12, m_current_lsn);
|
|
dst[diff - 9] = 2;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
InputIsoFile::InputIsoFile()
|
|
{
|
|
_init();
|
|
}
|
|
|
|
InputIsoFile::~InputIsoFile()
|
|
{
|
|
Close();
|
|
}
|
|
|
|
void InputIsoFile::_init()
|
|
{
|
|
m_type = ISOTYPE_ILLEGAL;
|
|
m_flags = 0;
|
|
|
|
m_offset = 0;
|
|
m_blockofs = 0;
|
|
m_blocksize = 0;
|
|
m_blocks = 0;
|
|
|
|
m_read_inprogress = false;
|
|
m_current_lsn = -1;
|
|
m_read_lsn = -1;
|
|
m_reader.reset();
|
|
}
|
|
|
|
bool InputIsoFile::Open(std::string srcfile, Error* error)
|
|
{
|
|
Close();
|
|
m_filename = std::move(srcfile);
|
|
m_reader = GetFileReader(m_filename);
|
|
if (!m_reader->Open(m_filename, error))
|
|
{
|
|
m_reader.reset();
|
|
return false;
|
|
}
|
|
|
|
if (!Detect())
|
|
{
|
|
Error::SetStringFmt(error, "Unable to identify the ISO image type for '{}'", Path::GetFileName(m_filename));
|
|
Close();
|
|
return false;
|
|
}
|
|
|
|
m_blocks = m_reader->GetBlockCount();
|
|
|
|
Console.WriteLn(Color_StrongBlue, "isoFile open ok: %s", m_filename.c_str());
|
|
|
|
Console.WriteLn(" Image type = %s", nameFromType(m_type));
|
|
//Console.WriteLn(" Fileparts = %u", m_numparts); // Pointless print, it's 1 unless it says otherwise above
|
|
DevCon.WriteLn(" blocks = %u", m_blocks);
|
|
DevCon.WriteLn(" offset = %d", m_offset);
|
|
DevCon.WriteLn(" blocksize = %u", m_blocksize);
|
|
DevCon.WriteLn(" blockoffset = %d", m_blockofs);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool InputIsoFile::Precache(ProgressCallback* progress, Error* error)
|
|
{
|
|
return m_reader->Precache(progress, error);
|
|
}
|
|
|
|
void InputIsoFile::Close()
|
|
{
|
|
if (m_reader)
|
|
{
|
|
m_reader->Close();
|
|
m_reader.reset();
|
|
}
|
|
|
|
_init();
|
|
}
|
|
|
|
bool InputIsoFile::IsOpened() const
|
|
{
|
|
return m_reader != nullptr;
|
|
}
|
|
|
|
bool InputIsoFile::tryIsoType(u32 size, u32 offset, u32 blockofs)
|
|
{
|
|
u8 buf[2456];
|
|
|
|
m_blocksize = size;
|
|
m_offset = offset;
|
|
m_blockofs = blockofs;
|
|
|
|
m_reader->SetDataOffset(offset);
|
|
m_reader->SetBlockSize(size);
|
|
|
|
if (ReadSync(buf, 16) < 0)
|
|
return false;
|
|
|
|
if (strncmp((char*)(buf + 25), "CD001", 5)) // Not ISO 9660 compliant
|
|
return false;
|
|
|
|
m_type = (*(u16*)(buf + 190) == 2048) ? ISOTYPE_CD : ISOTYPE_DVD;
|
|
|
|
return true; // We can deal with this.
|
|
}
|
|
|
|
// based on florin's CDVDbin detection code :)
|
|
// Parameter:
|
|
//
|
|
//
|
|
// Returns true if the image is valid/known/supported, or false if not (type == ISOTYPE_ILLEGAL).
|
|
bool InputIsoFile::Detect(bool readType)
|
|
{
|
|
m_type = ISOTYPE_ILLEGAL;
|
|
|
|
// First sanity check: no sane CD image has less than 16 sectors, since that's what
|
|
// we need simply to contain a TOC. So if the file size is not large enough to
|
|
// accommodate that, it is NOT a CD image --->
|
|
|
|
int sectors = m_reader->GetBlockCount();
|
|
|
|
if (sectors < 17)
|
|
return false;
|
|
|
|
m_blocks = 17;
|
|
|
|
if (tryIsoType(2048, 0, 24))
|
|
return true; // ISO 2048
|
|
if (tryIsoType(2336, 0, 16))
|
|
return true; // RAW 2336
|
|
if (tryIsoType(2352, 0, 0))
|
|
return true; // RAW 2352
|
|
if (tryIsoType(2448, 0, 0))
|
|
return true; // RAWQ 2448
|
|
|
|
if (tryIsoType(2048, 150 * 2048, 24))
|
|
return true; // NERO ISO 2048
|
|
if (tryIsoType(2352, 150 * 2048, 0))
|
|
return true; // NERO RAW 2352
|
|
if (tryIsoType(2448, 150 * 2048, 0))
|
|
return true; // NERO RAWQ 2448
|
|
|
|
m_offset = 0;
|
|
m_blocksize = CD_FRAMESIZE_RAW;
|
|
m_blockofs = 0;
|
|
m_type = ISOTYPE_AUDIO;
|
|
|
|
m_reader->SetDataOffset(m_offset);
|
|
m_reader->SetBlockSize(m_blocksize);
|
|
|
|
//BUG: This also detects a memory-card-file as a valid Audio-CD ISO... -avih
|
|
return true;
|
|
}
|