[libromdata] ISO: Get the sector size from the underlying disc reader classes if not reading directly from a file.

The SparseDiscReader changes in v2.3 broke this, so anything that uses
an ISO-9660 format showed 2048-byte sectors, even if this wasn't the case.

SparseDiscReader subclasses now have a set of CD-ROM specific fields to
set, e.g. CD-ROM sector size, mode, and subchannels. For multi-track
images, this should be set for the "main" data track.

Updated the following classes to set these fields:
- CdiReader (uses Track 02 for Dreamcast games)
- GdiReader (uses Track 03 for Dreamcast games)
- Cdrom2352Reader (always uses 2352-byte sectors, but gets mode from
  the first sector)
- CisoPspReader (always uses MODE1/2048)

GameCube and Wii disc reading classes don't set these, since they aren't
CD-ROM formats with multiple sector sizes.
This commit is contained in:
David Korth 2025-04-26 10:48:52 -04:00
parent 8e78811e8a
commit 5a738f8277
11 changed files with 296 additions and 77 deletions

View File

@ -10,6 +10,11 @@
* Affects: v2.5
* rpcli: SCSI inquiry was accidentally broken during a code cleanup.
* Affects: v2.3 - v2.5
* ISO: Get the sector size from the underlying disc reader classes if
not reading directly from a file.
* The SparseDiscReader changes in v2.3 broke this, so anything that
uses an ISO-9660 format showed 2048-byte sectors, even if this
wasn't the case.
* Other changes:
* Added support for localsearch-3.8, the new name of Tracker.

View File

@ -16,12 +16,15 @@
// Other rom-properties libraries
#include "librpbase/Achievements.hpp"
#include "librpbase/disc/PartitionFile.hpp"
#include "librpbase/disc/SparseDiscReader.hpp"
#include "libi18n/i18n.h"
using namespace LibRpBase;
using namespace LibRpFile;
using namespace LibRpText;
// C++ STL classes
#include <typeinfo>
using std::array;
using std::string;
using std::vector;
@ -69,6 +72,9 @@ public:
// Sector offset
// Usually 0 (for 2048) or 16 (for 2352 or 2448).
// NOTE: If SparseDiscReader is used, this will almost
// always be 2048. Query SparseDiscReader to get the
// actual sector size.
unsigned int sector_offset;
// UDF version
@ -783,7 +789,33 @@ int ISO::loadFieldData(void)
// TODO: ascii_to_utf8()?
// Sector size
d->fields.addField_string_numeric(C_("ISO", "Sector Size"), d->sector_size);
// NOTE: Need to check for a SparseDiscReader first, since if one's
// in use, ISO will always think the disc has 2048-byte sectors.
unsigned int sector_size = 0;
const SparseDiscReader *sdr = dynamic_cast<const SparseDiscReader*>(d->file.get());
if (!sdr) {
// Not a SparseDiscReader.
// If this is a PartitionFile, check the underlying IDiscReader.
PartitionFile *const pf = dynamic_cast<PartitionFile*>(d->file.get());
if (pf) {
IDiscReaderPtr dr = pf->getIDiscReader();
sdr = dynamic_cast<const SparseDiscReader*>(dr.get());
}
}
if (sdr) {
// Get the sector size from the SparseDiscReader.
// TODO: Also mode and subchannels?
if (sdr->hasCdromInfo()) {
sector_size = sdr->cdromSectorSize();
}
} else {
// Use the ISO-9660 sector size.
sector_size = d->sector_size;
}
if (sector_size > 0) {
d->fields.addField_string_numeric(C_("ISO", "Sector Size"), sector_size);
}
switch (d->discType) {
case ISOPrivate::DiscType::ISO9660:

View File

@ -59,6 +59,8 @@ public:
// SectorReadMode to sector size map
static const array<uint32_t, static_cast<uint32_t>(SectorReadMode::Max)> sectorReadModeToSizeMap;
// SectorReadMode to CD-ROM mode map
static const array<uint8_t, static_cast<size_t>(SectorReadMode::Max)> sectorReadModeToCdromModeMap;
// Block range mapping
// NOTE: This currently *only* contains data tracks.
@ -117,9 +119,15 @@ public:
/** CdiReaderPrivate **/
// SectorReadMode to sector size map
const array<uint32_t, static_cast<uint32_t>(CdiReaderPrivate::SectorReadMode::Max)> CdiReaderPrivate::sectorReadModeToSizeMap = {{
const array<uint32_t, static_cast<size_t>(CdiReaderPrivate::SectorReadMode::Max)> CdiReaderPrivate::sectorReadModeToSizeMap = {{
2048, 2336, 2352, 2352+16, 2352+96
}};
// SectorReadMode to CD-ROM mode map
const array<uint8_t, static_cast<size_t>(CdiReaderPrivate::SectorReadMode::Max)> CdiReaderPrivate::sectorReadModeToCdromModeMap = {{
1, 2, 0,
// subchannel modes (TODO)
1, 1,
}};
CdiReaderPrivate::CdiReaderPrivate(CdiReader *q)
: super(q)
@ -387,6 +395,33 @@ int CdiReaderPrivate::parseCdiFile(void)
// Done parsing the CDI.
// TODO: Sort by LBA?
// Set the SparseDiscReader CD-ROM sector size values.
// NOTE: Could be multiple sector size values, but we'll use the
// one from Track 02 if available; otherwise, Track 01.
BlockRange *sdrBlockRange = nullptr;
for (unsigned int i = 3; i > 0; i--) {
if (trackMappings.size() >= i && trackMappings[i-1] >= 0) {
sdrBlockRange = &blockRanges[trackMappings[i-1]];
break;
}
}
if (sdrBlockRange) {
unsigned int sectorSize = sdrBlockRange->sectorSize;
// TODO: Subchannel modes. Assuming Mode 1 for these for now.
this->hasCdromInfo = true;
this->cdromSectorMode = sectorReadModeToCdromModeMap[static_cast<size_t>(sdrBlockRange->sectorReadMode)];
if (sectorSize > 2352) {
// Subchannels are present.
this->cdromSectorSize = 2352;
this->cdromSubchannelSize = sectorSize - 2352;
} else {
// No subchannels.
this->cdromSectorSize = sectorSize;
}
}
return 0;
}

View File

@ -35,6 +35,10 @@ private:
typedef SparseDiscReaderPrivate super;
RP_DISABLE_COPY(Cdrom2352ReaderPrivate)
public:
// CD-ROM sync magic magic
static const array<uint8_t, 12> CDROM_2352_MAGIC;
public:
// Physical block size
// Supported block sizes: 2352 (raw), 2448 (raw+subchan)
@ -46,6 +50,10 @@ public:
/** Cdrom2352ReaderPrivate **/
// CD-ROM sync magic magic
const array<uint8_t, 12> Cdrom2352ReaderPrivate::CDROM_2352_MAGIC =
{{0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00}};
Cdrom2352ReaderPrivate::Cdrom2352ReaderPrivate(Cdrom2352Reader *q, unsigned int physBlockSize)
: super(q)
, physBlockSize(physBlockSize)
@ -77,11 +85,35 @@ Cdrom2352Reader::Cdrom2352Reader(const IRpFilePtr &file, unsigned int physBlockS
const off64_t fileSize = m_file->size();
if (fileSize <= 0 || fileSize % d->physBlockSize != 0) {
// Invalid disc size.
m_file.reset();
m_lastError = EIO;
m_file.reset();
return;
}
// Read the first sector to determine the CD-ROM mode.
CDROM_2352_Sector_t sector;
size_t sz_read = m_file->seekAndRead(0, &sector, sizeof(sector));
if (sz_read != sizeof(sector)) {
// Read error.
m_lastError = m_file->lastError();
m_file.reset();
return;
}
// Check the CD-ROM sync magic.
if (memcmp(sector.sync, Cdrom2352ReaderPrivate::CDROM_2352_MAGIC.data(), Cdrom2352ReaderPrivate::CDROM_2352_MAGIC.size()) != 0) {
// Sync magic is incorrect.
m_lastError = EIO;
m_file.reset();
return;
}
// Get the CD-ROM information.
d->hasCdromInfo = true;
d->cdromSectorMode = sector.mode;
d->cdromSectorSize = 2352;
d->cdromSubchannelSize = 0;
// Disc parameters.
// NOTE: A 32-bit block count allows for ~8 TiB with 2048-byte sectors.
d->blockCount = static_cast<unsigned int>(fileSize / 2352LL);
@ -105,13 +137,8 @@ int Cdrom2352Reader::isDiscSupported_static(const uint8_t *pHeader, size_t szHea
return -1;
}
// CD-ROM sync magic magic
static constexpr array<uint8_t, 12> CDROM_2352_MAGIC =
{{0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00}};
// Check the CD-ROM sync magic.
if (!memcmp(pHeader, CDROM_2352_MAGIC.data(), CDROM_2352_MAGIC.size()))
{
if (!memcmp(pHeader, Cdrom2352ReaderPrivate::CDROM_2352_MAGIC.data(), Cdrom2352ReaderPrivate::CDROM_2352_MAGIC.size())) {
// Valid CD-ROM sync magic.
return 0;
}

View File

@ -482,6 +482,12 @@ CisoPspReader::CisoPspReader(const IRpFilePtr &file)
d->z_buffer.resize(cache_size);
d->blockCacheIdx = ~0U;
// PSP disc images are always ISO-9660 Mode 1.
d->hasCdromInfo = true;
d->cdromSectorMode = 1;
d->cdromSectorSize = 2048;
d->cdromSubchannelSize = 0;
// Reset the disc position.
d->pos = 0;
}

View File

@ -242,6 +242,25 @@ int GdiReaderPrivate::parseGdiFile(char *gdibuf)
// Done parsing the GDI.
// TODO: Sort by LBA?
// Set the SparseDiscReader CD-ROM sector size values.
// NOTE: Could be multiple sector size values, but we'll use the
// one from Track 03 if available; otherwise, Track 02 or Track 01.
BlockRange *sdrBlockRange = nullptr;
for (unsigned int i = 3; i > 0; i--) {
if (trackMappings.size() >= i && trackMappings[i-1] >= 0) {
sdrBlockRange = &blockRanges[trackMappings[i-1]];
break;
}
}
if (sdrBlockRange) {
// TODO: Do any DC games use Mode 2 or subchannels?
this->hasCdromInfo = true;
this->cdromSectorMode = 1;
this->cdromSectorSize = sdrBlockRange->sectorSize;
this->cdromSubchannelSize = 0;
}
return 0;
}

View File

@ -147,7 +147,7 @@ off64_t PartitionFile::tell(void)
return m_pos;
}
/** File properties. **/
/** File properties **/
/**
* Get the file size.

View File

@ -2,7 +2,7 @@
* ROM Properties Page shell extension. (librpbase) *
* PartitionFile.hpp: IRpFile implementation for IPartition. *
* *
* Copyright (c) 2016-2024 by David Korth. *
* Copyright (c) 2016-2025 by David Korth. *
* SPDX-License-Identifier: GPL-2.0-or-later *
***************************************************************************/
@ -15,80 +15,92 @@ namespace LibRpBase {
class PartitionFile final : public LibRpFile::IRpFile
{
public:
/**
* Open a file from an IPartition.
* NOTE: These files are read-only.
*
* @param partition [in] IPartition (or IDiscReader) object
* @param offset [in] File starting offset
* @param size [in] File size
*/
PartitionFile(const IDiscReaderPtr& partition, off64_t offset, off64_t size);
public:
/**
* Open a file from an IPartition.
* NOTE: These files are read-only.
*
* @param partition [in] IPartition (or IDiscReader) object
* @param offset [in] File starting offset
* @param size [in] File size
*/
PartitionFile(const IDiscReaderPtr& partition, off64_t offset, off64_t size);
private:
typedef IRpFile super;
RP_DISABLE_COPY(PartitionFile)
private:
typedef IRpFile super;
RP_DISABLE_COPY(PartitionFile)
public:
/**
* Is the file open?
* This usually only returns false if an error occurred.
* @return True if the file is open; false if it isn't.
*/
bool isOpen(void) const final;
public:
/**
* Is the file open?
* This usually only returns false if an error occurred.
* @return True if the file is open; false if it isn't.
*/
bool isOpen(void) const final;
/**
* Close the file.
*/
void close(void) final;
/**
* Close the file.
*/
void close(void) final;
/**
* Read data from the file.
* @param ptr Output data buffer.
* @param size Amount of data to read, in bytes.
* @return Number of bytes read.
*/
ATTR_ACCESS_SIZE(write_only, 2, 3)
size_t read(void *ptr, size_t size) final;
/**
* Read data from the file.
* @param ptr Output data buffer.
* @param size Amount of data to read, in bytes.
* @return Number of bytes read.
*/
ATTR_ACCESS_SIZE(write_only, 2, 3)
size_t read(void *ptr, size_t size) final;
/**
* Write data to the file.
* (NOTE: Not valid for PartitionFile; this will always return 0.)
* @param ptr Input data buffer.
* @param size Amount of data to read, in bytes.
* @return Number of bytes written.
*/
ATTR_ACCESS_SIZE(read_only, 2, 3)
size_t write(const void *ptr, size_t size) final;
/**
* Write data to the file.
* (NOTE: Not valid for PartitionFile; this will always return 0.)
* @param ptr Input data buffer.
* @param size Amount of data to read, in bytes.
* @return Number of bytes written.
*/
ATTR_ACCESS_SIZE(read_only, 2, 3)
size_t write(const void *ptr, size_t size) final;
/**
* Set the file position.
* @param pos File position.
* @return 0 on success; -1 on error.
*/
int seek(off64_t pos) final;
/**
* Set the file position.
* @param pos File position.
* @return 0 on success; -1 on error.
*/
int seek(off64_t pos) final;
/**
* Get the file position.
* @return File position, or -1 on error.
*/
off64_t tell(void) final;
/**
* Get the file position.
* @return File position, or -1 on error.
*/
off64_t tell(void) final;
public:
/** File properties. **/
public:
/** File properties **/
/**
* Get the file size.
* @return File size, or negative on error.
*/
off64_t size(void) final;
/**
* Get the file size.
* @return File size, or negative on error.
*/
off64_t size(void) final;
protected:
IDiscReaderPtr m_partition;
off64_t m_offset; // File starting offset.
off64_t m_size; // File size.
off64_t m_pos; // Current position.
public:
/** PartitionFile functions **/
/**
* Get the underlying IDiscReader.
* @return IDiscReaderPtr
*/
inline IDiscReaderPtr getIDiscReader(void)
{
return m_partition;
}
protected:
IDiscReaderPtr m_partition;
off64_t m_offset; // File starting offset.
off64_t m_size; // File size.
off64_t m_pos; // Current position.
};
typedef std::shared_ptr<PartitionFile> PartitionFilePtr;

View File

@ -23,6 +23,10 @@ SparseDiscReaderPrivate::SparseDiscReaderPrivate(SparseDiscReader *q)
, disc_size(0)
, pos(-1)
, block_size(0)
, hasCdromInfo(false)
, cdromSectorMode(0)
, cdromSectorSize(0)
, cdromSubchannelSize(0)
{
// NOTE: Can't check q->m_file here.
@ -209,6 +213,50 @@ off64_t SparseDiscReader::size(void)
return d->disc_size;
}
/** SparseDiscReader-specific properties **/
// CD-ROM specific information
/**
* Is CD-ROM specific information set?
* @return True if set; false if not.
*/
bool SparseDiscReader::hasCdromInfo(void) const
{
RP_D(const SparseDiscReader);
return d->hasCdromInfo;
}
/**
* Get the CD-ROM sector mode.
* @return 0 for audio, 1 for MODE1, or 2 for MODE2.
*/
uint8_t SparseDiscReader::cdromSectorMode(void) const
{
RP_D(const SparseDiscReader);
return d->cdromSectorMode;
}
/**
* Get the CD-ROM sector size.
* @return CD-ROM sector size, or 0 if not applicable.
*/
unsigned int SparseDiscReader::cdromSectorSize(void) const
{
RP_D(const SparseDiscReader);
return d->cdromSectorSize;
}
/**
* Get the CD-ROM subchannel size.
* @return CD-ROM subchannel size, or 0 if not applicable.
*/
unsigned int SparseDiscReader::cdromSubchannelSize(void) const
{
RP_D(const SparseDiscReader);
return d->cdromSubchannelSize;
}
/** SparseDiscReader **/
/**

View File

@ -29,7 +29,7 @@ protected:
SparseDiscReaderPrivate *const d_ptr;
public:
/** IDiscReader functions. **/
/** IDiscReader functions **/
/**
* Read data from the disc image.
@ -59,8 +59,37 @@ public:
*/
off64_t size(void) final;
public:
/** SparseDiscReader-specific properties **/
// CD-ROM specific information
/**
* Is CD-ROM specific information set?
* @return True if set; false if not.
*/
bool hasCdromInfo(void) const;
/**
* Get the CD-ROM sector mode.
* @return 1 or 2 for MODE1 or MODE2, or 0 if not applicable. (Audio CDs are not supported.)
*/
uint8_t cdromSectorMode(void) const;
/**
* Get the CD-ROM sector size.
* @return CD-ROM sector size, or 0 if not applicable.
*/
unsigned int cdromSectorSize(void) const;
/**
* Get the CD-ROM subchannel size.
* @return CD-ROM subchannel size, or 0 if not applicable.
*/
unsigned int cdromSubchannelSize(void) const;
protected:
/** Virtual functions for SparseDiscReader subclasses. **/
/** Virtual functions for SparseDiscReader subclasses **/
/**
* Get the physical address of the specified logical block index.

View File

@ -33,6 +33,12 @@ public:
off64_t disc_size; // Virtual disc image size.
off64_t pos; // Read position.
unsigned int block_size; // Block size.
// CD-ROM specific information
bool hasCdromInfo;
uint8_t cdromSectorMode;
unsigned int cdromSectorSize;
unsigned int cdromSubchannelSize;
};
}