ISOReader: Add XA and raw extraction modes

This commit is contained in:
Stenzek 2024-12-17 14:19:41 +10:00
parent b68370dff7
commit f010d81652
No known key found for this signature in database
9 changed files with 215 additions and 134 deletions

View File

@ -941,7 +941,7 @@ std::string System::GetExecutableNameForImage(IsoReader& iso, bool strip_subdire
{
// Read SYSTEM.CNF
std::vector<u8> system_cnf_data;
if (!iso.ReadFile("SYSTEM.CNF", &system_cnf_data))
if (!iso.ReadFile("SYSTEM.CNF", &system_cnf_data, IsoReader::ReadMode::Data))
return FALLBACK_EXE_NAME;
// Parse lines
@ -1053,7 +1053,7 @@ bool System::ReadExecutableFromImage(IsoReader& iso, std::string* out_executable
DEV_LOG("Executable path: '{}'", executable_path);
if (!executable_path.empty() && out_executable_data)
{
if (!iso.ReadFile(executable_path, out_executable_data))
if (!iso.ReadFile(executable_path, out_executable_data, IsoReader::ReadMode::Data))
{
ERROR_LOG("Failed to read executable '{}' from disc", executable_path);
return false;
@ -1104,9 +1104,11 @@ DiscRegion System::GetRegionForSerial(const std::string_view serial)
DiscRegion System::GetRegionFromSystemArea(CDImage* cdi)
{
// The license code is on sector 4 of the disc.
u8 sector[CDImage::DATA_SECTOR_SIZE];
std::array<u8, CDImage::RAW_SECTOR_SIZE> sector;
std::span<const u8> sector_data;
if (cdi->GetTrackMode(1) == CDImage::TrackMode::Audio || !cdi->Seek(1, 4) ||
cdi->Read(CDImage::ReadMode::DataOnly, 1, sector) != 1)
!cdi->ReadRawSector(sector.data(), nullptr) ||
(sector_data = IsoReader::ExtractSectorData(sector, IsoReader::ReadMode::Data, nullptr)).empty())
{
return DiscRegion::Other;
}
@ -1116,11 +1118,11 @@ DiscRegion System::GetRegionFromSystemArea(CDImage* cdi)
static constexpr char pal_string[] = " Licensed by Sony Computer Entertainment Euro pe";
// subtract one for the terminating null
if (std::equal(ntsc_u_string, ntsc_u_string + countof(ntsc_u_string) - 1, sector))
if (std::memcmp(sector_data.data(), ntsc_u_string, std::size(ntsc_u_string) - 1) == 0)
return DiscRegion::NTSC_U;
else if (std::equal(ntsc_j_string, ntsc_j_string + countof(ntsc_j_string) - 1, sector))
else if (std::memcmp(sector_data.data(), ntsc_j_string, std::size(ntsc_j_string) - 1) == 0)
return DiscRegion::NTSC_J;
else if (std::equal(pal_string, pal_string + countof(pal_string) - 1, sector))
else if (std::memcmp(sector_data.data(), pal_string, std::size(pal_string) - 1) == 0)
return DiscRegion::PAL;
else
return DiscRegion::Other;

View File

@ -38,7 +38,7 @@ ConsoleSettingsWidget::ConsoleSettingsWidget(SettingsWindow* dialog, QWidget* pa
.arg(i)
.arg(static_cast<float>(i) * TIME_PER_SECTOR_DOUBLE_SPEED, 0, 'f', 0)
.arg(static_cast<float>(i * CDImage::DATA_SECTOR_SIZE) / 1024.0f));
.arg(static_cast<float>(i * CDImage::RAW_SECTOR_SIZE) / 1024.0f));
}
SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.region, "Console", "Region", &Settings::ParseConsoleRegionName,

View File

@ -29,7 +29,10 @@ ISOBrowserWindow::ISOBrowserWindow(QWidget* parent) : QWidget(parent)
enableUi(false);
connect(m_ui.openFile, &QAbstractButton::clicked, this, &ISOBrowserWindow::onOpenFileClicked);
connect(m_ui.extract, &QAbstractButton::clicked, this, &ISOBrowserWindow::onExtractClicked);
connect(m_ui.extract, &QAbstractButton::clicked, this, [this]() { onExtractClicked(IsoReader::ReadMode::Data); });
connect(m_ui.extractMode2, &QAbstractButton::clicked, this,
[this]() { onExtractClicked(IsoReader::ReadMode::Mode2); });
connect(m_ui.extractRaw, &QAbstractButton::clicked, this, [this]() { onExtractClicked(IsoReader::ReadMode::Raw); });
connect(m_ui.directoryView, &QTreeWidget::itemClicked, this, &ISOBrowserWindow::onDirectoryItemClicked);
connect(m_ui.fileView, &QTreeWidget::itemActivated, this, &ISOBrowserWindow::onFileItemActivated);
connect(m_ui.fileView, &QTreeWidget::itemSelectionChanged, this, &ISOBrowserWindow::onFileItemSelectionChanged);
@ -105,14 +108,14 @@ void ISOBrowserWindow::onOpenFileClicked()
}
}
void ISOBrowserWindow::onExtractClicked()
void ISOBrowserWindow::onExtractClicked(IsoReader::ReadMode mode)
{
const QList<QTreeWidgetItem*> items = m_ui.fileView->selectedItems();
if (items.isEmpty())
return;
const QString path = items.front()->data(0, Qt::UserRole).toString();
extractFile(path);
extractFile(path, mode);
}
void ISOBrowserWindow::onDirectoryItemClicked(QTreeWidgetItem* item, int column)
@ -141,21 +144,16 @@ void ISOBrowserWindow::onFileItemActivated(QTreeWidgetItem* item, int column)
}
// file, go to extract
extractFile(item->data(0, Qt::UserRole).toString());
extractFile(item->data(0, Qt::UserRole).toString(), IsoReader::ReadMode::Data);
}
void ISOBrowserWindow::onFileItemSelectionChanged()
{
const QList<QTreeWidgetItem*> items = m_ui.fileView->selectedItems();
if (items.isEmpty())
{
m_ui.extract->setEnabled(false);
return;
}
// directory?
const bool is_directory = items.front()->data(0, Qt::UserRole + 1).toBool();
m_ui.extract->setEnabled(!is_directory);
const bool enabled = (!items.isEmpty() && !items.front()->data(0, Qt::UserRole + 1).toBool());
enableExtractButtons(enabled);
}
void ISOBrowserWindow::onFileContextMenuRequested(const QPoint& pos)
@ -176,7 +174,11 @@ void ISOBrowserWindow::onFileContextMenuRequested(const QPoint& pos)
else
{
connect(menu.addAction(QIcon::fromTheme(QIcon::ThemeIcon::DocumentSaveAs), tr("&Extract")), &QAction::triggered,
this, [this, &path]() { extractFile(path); });
this, [this, &path]() { extractFile(path, IsoReader::ReadMode::Data); });
connect(menu.addAction(QIcon::fromTheme(QIcon::ThemeIcon::DocumentSaveAs), tr("Extract (&XA)")),
&QAction::triggered, this, [this, &path]() { extractFile(path, IsoReader::ReadMode::Mode2); });
connect(menu.addAction(QIcon::fromTheme(QIcon::ThemeIcon::DocumentSaveAs), tr("Extract (&Raw)")),
&QAction::triggered, this, [this, &path]() { extractFile(path, IsoReader::ReadMode::Raw); });
}
menu.exec(m_ui.fileView->mapToGlobal(pos));
@ -187,7 +189,7 @@ void ISOBrowserWindow::resizeFileListColumns()
QtUtils::ResizeColumnsForTreeView(m_ui.fileView, {-1, 200, 100});
}
void ISOBrowserWindow::extractFile(const QString& path)
void ISOBrowserWindow::extractFile(const QString& path, IsoReader::ReadMode mode)
{
const std::string spath = path.toStdString();
const QString filename = QtUtils::StringViewToQString(Path::GetFileName(spath));
@ -207,7 +209,7 @@ void ISOBrowserWindow::extractFile(const QString& path)
cb.SetCancellable(true);
cb.SetTitle("ISO Browser");
cb.SetStatusText(tr("Extracting %1...").arg(filename).toStdString());
if (m_iso.WriteFileToStream(de.value(), fp.get(), &error, &cb))
if (m_iso.WriteFileToStream(de.value(), fp.get(), mode, &error, &cb))
{
if (FileSystem::CommitAtomicRenamedFile(fp, &error))
return;
@ -256,13 +258,20 @@ void ISOBrowserWindow::enableUi(bool enabled)
m_ui.fileView->setEnabled(enabled);
if (!enabled)
m_ui.extract->setEnabled(enabled);
enableExtractButtons(enabled);
}
void ISOBrowserWindow::enableExtractButtons(bool enabled)
{
m_ui.extract->setEnabled(enabled);
m_ui.extractMode2->setEnabled(enabled);
m_ui.extractRaw->setEnabled(enabled);
}
void ISOBrowserWindow::populateDirectories()
{
m_ui.directoryView->clear();
m_ui.extract->setEnabled(false);
enableExtractButtons(false);
QTreeWidgetItem* root = new QTreeWidgetItem;
root->setIcon(0, QIcon::fromTheme("disc-line"));

View File

@ -25,7 +25,6 @@ protected:
private Q_SLOTS:
void onOpenFileClicked();
void onExtractClicked();
void onDirectoryItemClicked(QTreeWidgetItem* item, int column);
void onFileItemActivated(QTreeWidgetItem* item, int column);
void onFileItemSelectionChanged();
@ -34,10 +33,12 @@ private Q_SLOTS:
private:
void enableUi(bool enabled);
void enableExtractButtons(bool enabled);
void populateDirectories();
void populateSubdirectories(std::string_view dir, QTreeWidgetItem* parent);
void populateFiles(const QString& path);
void extractFile(const QString& path);
void onExtractClicked(IsoReader::ReadMode mode);
void extractFile(const QString& path, IsoReader::ReadMode mode);
QTreeWidgetItem* findDirectoryItemForPath(const QString& path, QTreeWidgetItem* parent = nullptr) const;

View File

@ -114,6 +114,20 @@
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="extractMode2">
<property name="text">
<string>Extract (XA)</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="extractRaw">
<property name="text">
<string>Extract (Raw)</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="close">
<property name="text">

View File

@ -249,60 +249,6 @@ bool CDImage::Seek(u32 track_number, LBA lba)
return Seek(track.start_lba + lba);
}
u32 CDImage::Read(ReadMode read_mode, u32 sector_count, void* buffer)
{
u8* buffer_ptr = static_cast<u8*>(buffer);
u32 sectors_read = 0;
for (; sectors_read < sector_count; sectors_read++)
{
// get raw sector
u8 raw_sector[RAW_SECTOR_SIZE];
if (!ReadRawSector(raw_sector, nullptr))
break;
switch (read_mode)
{
case ReadMode::DataOnly:
{
const SectorHeader* header = reinterpret_cast<const SectorHeader*>(raw_sector + SECTOR_SYNC_SIZE);
if (header->sector_mode == 1)
{
std::memcpy(buffer_ptr, raw_sector + SECTOR_SYNC_SIZE + MODE1_HEADER_SIZE, DATA_SECTOR_SIZE);
}
else if (header->sector_mode == 2)
{
std::memcpy(buffer_ptr, raw_sector + SECTOR_SYNC_SIZE + MODE2_HEADER_SIZE, DATA_SECTOR_SIZE);
}
else
{
ERROR_LOG("Invalid sector mode {} at LBA {}", header->sector_mode,
m_current_index->start_lba_on_disc + m_position_in_track);
break;
}
buffer_ptr += DATA_SECTOR_SIZE;
}
break;
case ReadMode::RawNoSync:
std::memcpy(buffer_ptr, raw_sector + SECTOR_SYNC_SIZE, RAW_SECTOR_SIZE - SECTOR_SYNC_SIZE);
buffer_ptr += RAW_SECTOR_SIZE - SECTOR_SYNC_SIZE;
break;
case ReadMode::RawSector:
std::memcpy(buffer_ptr, raw_sector, RAW_SECTOR_SIZE);
buffer_ptr += RAW_SECTOR_SIZE;
break;
default:
UnreachableCode();
break;
}
}
return sectors_read;
}
bool CDImage::ReadRawSector(void* buffer, SubChannelQ* subq)
{
if (m_position_in_index == m_current_index->length)

View File

@ -32,6 +32,7 @@ public:
SECTOR_HEADER_SIZE = 4,
MODE1_HEADER_SIZE = 4,
MODE2_HEADER_SIZE = 12,
MODE2_DATA_SECTOR_SIZE = 2336, // header + edc
FRAMES_PER_SECOND = 75, // "sectors", or "timecode frames" (not "channel frames")
SECONDS_PER_MINUTE = 60,
FRAMES_PER_MINUTE = FRAMES_PER_SECOND * SECONDS_PER_MINUTE,
@ -47,13 +48,6 @@ public:
LEAD_OUT_TRACK_NUMBER = 0xAA
};
enum class ReadMode : u8
{
DataOnly, // 2048 bytes per sector.
RawSector, // 2352 bytes per sector.
RawNoSync, // 2340 bytes per sector.
};
enum class TrackMode : u8
{
Audio, // 2352 bytes per sector
@ -296,9 +290,6 @@ public:
// Seek to track and LBA.
bool Seek(u32 track_number, LBA lba);
// Read from the current LBA. Returns the number of sectors read.
u32 Read(ReadMode read_mode, u32 sector_count, void* buffer);
// Read a single raw sector, and subchannel from the current LBA.
bool ReadRawSector(void* buffer, SubChannelQ* subq);

View File

@ -4,9 +4,10 @@
#include "iso_reader.h"
#include "cd_image.h"
#include "common/align.h"
#include "common/assert.h"
#include "common/error.h"
#include "common/file_system.h"
#include "common/log.h"
#include "common/progress_callback.h"
#include "common/string_util.h"
@ -41,20 +42,25 @@ bool IsoReader::Open(CDImage* image, u32 track_number, Error* error)
return true;
}
bool IsoReader::ReadSector(u8* buf, u32 lsn, Error* error)
bool IsoReader::ReadSector(std::span<u8, SECTOR_SIZE> buf, u32 lsn, Error* error)
{
if (!m_image->Seek(m_track_number, lsn))
{
Error::SetString(error, fmt::format("Failed to seek to LSN #{}", lsn));
Error::SetStringFmt(error, "Failed to seek to LSN #{}", lsn);
return false;
}
if (m_image->Read(CDImage::ReadMode::DataOnly, 1, buf) != 1)
std::array<u8, CDImage::RAW_SECTOR_SIZE> raw_sector;
std::span<const u8> sector_data;
if (!m_image->ReadRawSector(raw_sector.data(), nullptr) ||
(sector_data = ExtractSectorData(raw_sector, ReadMode::Data, error)).empty())
{
Error::SetString(error, fmt::format("Failed to read LSN #{}", lsn));
Error::SetStringFmt(error, "Failed to read LSN #{}: ", lsn);
return false;
}
Assert(buf.size() == SECTOR_SIZE);
std::memcpy(buf.data(), sector_data.data(), SECTOR_SIZE);
return true;
}
@ -64,13 +70,13 @@ bool IsoReader::ReadPVD(Error* error)
static constexpr u32 START_SECTOR = 16;
// try only a maximum of 256 volume descriptors
std::array<u8, SECTOR_SIZE> buffer;
for (u32 i = 0; i < 256; i++)
{
u8 buffer[SECTOR_SIZE];
if (!ReadSector(buffer, START_SECTOR + i, error))
return false;
const ISOVolumeDescriptorHeader* header = reinterpret_cast<ISOVolumeDescriptorHeader*>(buffer);
const ISOVolumeDescriptorHeader* header = reinterpret_cast<ISOVolumeDescriptorHeader*>(buffer.data());
if (std::memcmp(header->standard_identifier, "CD001", 5) != 0)
continue;
else if (header->type_code != 1)
@ -79,7 +85,7 @@ bool IsoReader::ReadPVD(Error* error)
break;
m_pvd_lba = START_SECTOR + i;
std::memcpy(&m_pvd, buffer, sizeof(ISOPrimaryVolumeDescriptor));
std::memcpy(&m_pvd, buffer.data(), sizeof(ISOPrimaryVolumeDescriptor));
return true;
}
@ -101,16 +107,16 @@ std::optional<IsoReader::ISODirectoryEntry> IsoReader::LocateFile(std::string_vi
return LocateFile(path, sector_buffer, root_de->location_le, root_de->length_le, error);
}
std::string_view IsoReader::GetDirectoryEntryFileName(const u8* sector, u32 de_sector_offset)
std::string_view IsoReader::GetDirectoryEntryFileName(std::span<const u8, SECTOR_SIZE> sector, u32 de_sector_offset)
{
const ISODirectoryEntry* de = reinterpret_cast<const ISODirectoryEntry*>(sector + de_sector_offset);
const ISODirectoryEntry* de = reinterpret_cast<const ISODirectoryEntry*>(sector.data() + de_sector_offset);
if ((sizeof(ISODirectoryEntry) + de->filename_length) > de->entry_length ||
(sizeof(ISODirectoryEntry) + de->filename_length + de_sector_offset) > SECTOR_SIZE)
{
return std::string_view();
}
const char* str = reinterpret_cast<const char*>(sector + de_sector_offset + sizeof(ISODirectoryEntry));
const char* str = reinterpret_cast<const char*>(sector.data() + de_sector_offset + sizeof(ISODirectoryEntry));
if (de->filename_length == 1)
{
if (str[0] == '\0')
@ -130,7 +136,71 @@ std::string_view IsoReader::GetDirectoryEntryFileName(const u8* sector, u32 de_s
return std::string_view(str, length_without_version);
}
std::optional<IsoReader::ISODirectoryEntry> IsoReader::LocateFile(std::string_view path, u8* sector_buffer,
u32 IsoReader::GetReadModeSectorSize(ReadMode mode)
{
switch (mode)
{
case ReadMode::Data:
return CDImage::DATA_SECTOR_SIZE;
case ReadMode::Mode2:
return CDImage::MODE2_DATA_SECTOR_SIZE;
case ReadMode::Raw:
return CDImage::RAW_SECTOR_SIZE;
DefaultCaseIsUnreachable();
}
}
std::span<const u8> IsoReader::ExtractSectorData(std::span<const u8> raw_sector, ReadMode mode, Error* error)
{
switch (mode)
{
case ReadMode::Data:
{
const CDImage::SectorHeader* header =
reinterpret_cast<const CDImage::SectorHeader*>(raw_sector.data() + CDImage::SECTOR_SYNC_SIZE);
if (header->sector_mode == 1)
{
return raw_sector.subspan(CDImage::SECTOR_SYNC_SIZE + CDImage::MODE1_HEADER_SIZE, CDImage::DATA_SECTOR_SIZE);
}
else if (header->sector_mode == 2)
{
return raw_sector.subspan(CDImage::SECTOR_SYNC_SIZE + CDImage::MODE2_HEADER_SIZE, CDImage::DATA_SECTOR_SIZE);
}
else
{
Error::SetStringFmt(error, "Invalid sector mode {}", header->sector_mode);
return {};
}
}
case ReadMode::Mode2:
{
const CDImage::SectorHeader* header =
reinterpret_cast<const CDImage::SectorHeader*>(raw_sector.data() + CDImage::SECTOR_SYNC_SIZE);
if (header->sector_mode != 2)
{
Error::SetStringView(error, "Non-mode 2 sector found");
return {};
}
return raw_sector.subspan(CDImage::SECTOR_SYNC_SIZE + CDImage::MODE1_HEADER_SIZE,
CDImage::MODE2_DATA_SECTOR_SIZE);
}
case ReadMode::Raw:
{
return raw_sector.subspan(0, CDImage::RAW_SECTOR_SIZE);
}
DefaultCaseIsUnreachable();
}
}
std::optional<IsoReader::ISODirectoryEntry> IsoReader::LocateFile(std::string_view path,
std::span<u8, SECTOR_SIZE> sector_buffer,
u32 directory_record_lba, u32 directory_record_size,
Error* error)
{
@ -352,16 +422,17 @@ bool IsoReader::DirectoryExists(std::string_view path, Error* error)
return (de->flags & ISODirectoryEntryFlag_Directory) == ISODirectoryEntryFlag_Directory;
}
bool IsoReader::ReadFile(std::string_view path, std::vector<u8>* data, Error* error)
bool IsoReader::ReadFile(std::string_view path, std::vector<u8>* data, ReadMode read_mode, Error* error)
{
auto de = LocateFile(path, error);
if (!de)
return false;
return ReadFile(de.value(), data, error);
return ReadFile(de.value(), data, read_mode, error);
}
bool IsoReader::ReadFile(const ISODirectoryEntry& de, std::vector<u8>* data, Error* error /*= nullptr*/)
bool IsoReader::ReadFile(const ISODirectoryEntry& de, std::vector<u8>* data, ReadMode read_mode,
Error* error /*= nullptr*/)
{
if (de.flags & ISODirectoryEntryFlag_Directory)
{
@ -375,31 +446,52 @@ bool IsoReader::ReadFile(const ISODirectoryEntry& de, std::vector<u8>* data, Err
return true;
}
const u32 num_sectors = (de.length_le + (SECTOR_SIZE - 1)) / SECTOR_SIZE;
data->resize(num_sectors * static_cast<size_t>(SECTOR_SIZE));
for (u32 i = 0, lsn = de.location_le; i < num_sectors; i++, lsn++)
if (!m_image->Seek(1, de.location_le))
{
if (!ReadSector(data->data() + (i * SECTOR_SIZE), lsn, error))
return false;
Error::SetStringFmt(error, "Failed to seek to LSN #{}", de.location_le);
return false;
}
// Might not be sector aligned, so reduce it back.
data->resize(de.length_le);
// NOTE: ISO uses 2048 byte "sectors" in the directory listing regardless of the file mode.
const u32 sector_size = GetReadModeSectorSize(read_mode);
const u32 num_sectors = de.GetSizeInSectors();
data->resize(num_sectors * sector_size);
std::array<u8, CDImage::RAW_SECTOR_SIZE> raw_sector;
size_t data_offset = 0;
for (u32 i = 0; i < num_sectors; i++)
{
std::span<const u8> sector_data;
if (!m_image->ReadRawSector(raw_sector.data(), nullptr) ||
(sector_data = ExtractSectorData(raw_sector, read_mode, error)).empty())
{
Error::AddPrefixFmt(error, "Failed to read LSN #{}", de.location_le + i);
return false;
}
std::memcpy(data->data() + data_offset, sector_data.data(), sector_data.size());
data_offset += sector_data.size();
}
// only shrink for data read mode
if (read_mode == ReadMode::Data)
data->resize(de.length_le);
return true;
}
bool IsoReader::WriteFileToStream(std::string_view path, std::FILE* fp, Error* error /* = nullptr */,
ProgressCallback* progress /* = nullptr */)
bool IsoReader::WriteFileToStream(std::string_view path, std::FILE* fp, ReadMode read_mode,
Error* error /* = nullptr */, ProgressCallback* progress /* = nullptr */)
{
auto de = LocateFile(path, error);
if (!de)
return false;
return WriteFileToStream(de.value(), fp, error, progress);
return WriteFileToStream(de.value(), fp, read_mode, error, progress);
}
bool IsoReader::WriteFileToStream(const ISODirectoryEntry& de, std::FILE* fp, Error* error /* = nullptr */,
ProgressCallback* progress /* = nullptr */)
bool IsoReader::WriteFileToStream(const ISODirectoryEntry& de, std::FILE* fp, ReadMode read_mode,
Error* error /* = nullptr */, ProgressCallback* progress /* = nullptr */)
{
if (de.flags & ISODirectoryEntryFlag_Directory)
{
@ -413,9 +505,11 @@ bool IsoReader::WriteFileToStream(const ISODirectoryEntry& de, std::FILE* fp, Er
if (de.length_le == 0)
return FileSystem::FTruncate64(fp, 0, error);
const u32 num_sectors = (de.length_le + (SECTOR_SIZE - 1)) / SECTOR_SIZE;
u32 file_pos = 0;
u8 sector_buffer[SECTOR_SIZE];
if (!m_image->Seek(1, de.location_le))
{
Error::SetStringFmt(error, "Failed to seek to LSN #{}", de.location_le);
return false;
}
if (progress)
{
@ -423,13 +517,26 @@ bool IsoReader::WriteFileToStream(const ISODirectoryEntry& de, std::FILE* fp, Er
progress->SetProgressValue(0);
}
for (u32 i = 0, lsn = de.location_le; i < num_sectors; i++, lsn++)
{
if (!ReadSector(sector_buffer, lsn, error))
return false;
const u32 num_sectors = de.GetSizeInSectors();
const u32 write_size = std::min<u32>(de.length_le - file_pos, SECTOR_SIZE);
if (std::fwrite(sector_buffer, write_size, 1, fp) != 1)
std::array<u8, CDImage::RAW_SECTOR_SIZE> raw_sector;
u32 file_pos = 0;
for (u32 i = 0; i < num_sectors; i++)
{
std::span<const u8> sector_data;
if (!m_image->ReadRawSector(raw_sector.data(), nullptr) ||
(sector_data = ExtractSectorData(raw_sector, read_mode, error)).empty())
{
Error::AddPrefixFmt(error, "Failed to read LSN #{}", de.location_le + i);
return false;
}
// only shrink for data mode
const u32 write_size = (read_mode == ReadMode::Data) ?
std::min<u32>(de.length_le - file_pos, static_cast<u32>(sector_data.size())) :
static_cast<u32>(sector_data.size());
if (std::fwrite(sector_data.data(), write_size, 1, fp) != 1)
{
Error::SetErrno(error, "fwrite() failed: ", errno);
return false;

View File

@ -8,6 +8,7 @@
#include <cstdio>
#include <memory>
#include <optional>
#include <span>
#include <string>
#include <vector>
@ -142,11 +143,21 @@ public:
#pragma pack(pop)
enum class ReadMode : u8
{
Data,
Mode2,
Raw,
};
IsoReader();
~IsoReader();
static std::string_view RemoveVersionIdentifierFromPath(std::string_view path);
static u32 GetReadModeSectorSize(ReadMode mode);
static std::span<const u8> ExtractSectorData(std::span<const u8> raw_sector, ReadMode mode, Error* error);
ALWAYS_INLINE const CDImage* GetImage() const { return m_image; }
ALWAYS_INLINE u32 GetTrackNumber() const { return m_track_number; }
ALWAYS_INLINE u32 GetPVDLBA() const { return m_pvd_lba; }
@ -162,22 +173,22 @@ public:
bool FileExists(std::string_view path, Error* error = nullptr);
bool DirectoryExists(std::string_view path, Error* error = nullptr);
bool ReadFile(std::string_view path, std::vector<u8>* data, Error* error = nullptr);
bool ReadFile(const ISODirectoryEntry& de, std::vector<u8>* data, Error* error = nullptr);
bool ReadFile(std::string_view path, std::vector<u8>* data, ReadMode read_mode, Error* error = nullptr);
bool ReadFile(const ISODirectoryEntry& de, std::vector<u8>* data, ReadMode read_mode, Error* error = nullptr);
bool WriteFileToStream(std::string_view path, std::FILE* fp, Error* error = nullptr,
bool WriteFileToStream(std::string_view path, std::FILE* fp, ReadMode read_mode, Error* error = nullptr,
ProgressCallback* progress = nullptr);
bool WriteFileToStream(const ISODirectoryEntry& de, std::FILE* fp, Error* error = nullptr,
bool WriteFileToStream(const ISODirectoryEntry& de, std::FILE* fp, ReadMode read_mode, Error* error = nullptr,
ProgressCallback* progress = nullptr);
private:
static std::string_view GetDirectoryEntryFileName(const u8* sector, u32 de_sector_offset);
static std::string_view GetDirectoryEntryFileName(std::span<const u8, SECTOR_SIZE> sector, u32 de_sector_offset);
bool ReadSector(u8* buf, u32 lsn, Error* error);
bool ReadSector(std::span<u8, SECTOR_SIZE> buf, u32 lsn, Error* error);
bool ReadPVD(Error* error);
std::optional<ISODirectoryEntry> LocateFile(std::string_view path, u8* sector_buffer, u32 directory_record_lba,
u32 directory_record_size, Error* error);
std::optional<ISODirectoryEntry> LocateFile(std::string_view path, std::span<u8, SECTOR_SIZE> sector_buffer,
u32 directory_record_lba, u32 directory_record_size, Error* error);
CDImage* m_image;
u32 m_track_number;