ISOReader: Add XA and raw extraction modes
This commit is contained in:
parent
b68370dff7
commit
f010d81652
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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"));
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue