CDImage: Add memory CD image class
This commit is contained in:
parent
8b91bf5e57
commit
3187d07d03
|
@ -15,6 +15,7 @@ add_library(common
|
||||||
cd_image_chd.cpp
|
cd_image_chd.cpp
|
||||||
cd_image_hasher.cpp
|
cd_image_hasher.cpp
|
||||||
cd_image_hasher.h
|
cd_image_hasher.h
|
||||||
|
cd_image_memory.cpp
|
||||||
cd_subchannel_replacement.cpp
|
cd_subchannel_replacement.cpp
|
||||||
cd_subchannel_replacement.h
|
cd_subchannel_replacement.h
|
||||||
cd_xa.cpp
|
cd_xa.cpp
|
||||||
|
|
|
@ -94,6 +94,17 @@ CDImage::LBA CDImage::GetTrackIndexLength(u8 track, u8 index) const
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const CDImage::CDImage::Track& CDImage::GetTrack(u32 track) const
|
||||||
|
{
|
||||||
|
Assert(track > 0 && track <= m_tracks.size());
|
||||||
|
return m_tracks[track - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
const CDImage::CDImage::Index& CDImage::GetIndex(u32 i) const
|
||||||
|
{
|
||||||
|
return m_indices[i];
|
||||||
|
}
|
||||||
|
|
||||||
bool CDImage::Seek(LBA lba)
|
bool CDImage::Seek(LBA lba)
|
||||||
{
|
{
|
||||||
const Index* new_index;
|
const Index* new_index;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "bitfield.h"
|
#include "bitfield.h"
|
||||||
|
#include "progress_callback.h"
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
@ -161,57 +162,6 @@ public:
|
||||||
};
|
};
|
||||||
static_assert(sizeof(SubChannelQ) == SUBCHANNEL_BYTES_PER_FRAME, "SubChannelQ is correct size");
|
static_assert(sizeof(SubChannelQ) == SUBCHANNEL_BYTES_PER_FRAME, "SubChannelQ is correct size");
|
||||||
|
|
||||||
// Helper functions.
|
|
||||||
static u32 GetBytesPerSector(TrackMode mode);
|
|
||||||
|
|
||||||
// Opening disc image.
|
|
||||||
static std::unique_ptr<CDImage> Open(const char* filename);
|
|
||||||
static std::unique_ptr<CDImage> OpenBinImage(const char* filename);
|
|
||||||
static std::unique_ptr<CDImage> OpenCueSheetImage(const char* filename);
|
|
||||||
static std::unique_ptr<CDImage> OpenCHDImage(const char* filename);
|
|
||||||
|
|
||||||
// Accessors.
|
|
||||||
const std::string& GetFileName() const { return m_filename; }
|
|
||||||
LBA GetPositionOnDisc() const { return m_position_on_disc; }
|
|
||||||
Position GetMSFPositionOnDisc() const { return Position::FromLBA(m_position_on_disc); }
|
|
||||||
LBA GetPositionInTrack() const { return m_position_in_track; }
|
|
||||||
Position GetMSFPositionInTrack() const { return Position::FromLBA(m_position_in_track); }
|
|
||||||
LBA GetLBACount() const { return m_lba_count; }
|
|
||||||
u32 GetIndexNumber() const { return m_current_index->index_number; }
|
|
||||||
u32 GetTrackNumber() const { return m_current_index->track_number; }
|
|
||||||
u32 GetTrackCount() const { return static_cast<u32>(m_tracks.size()); }
|
|
||||||
LBA GetTrackStartPosition(u8 track) const;
|
|
||||||
Position GetTrackStartMSFPosition(u8 track) const;
|
|
||||||
LBA GetTrackLength(u8 track) const;
|
|
||||||
Position GetTrackMSFLength(u8 track) const;
|
|
||||||
TrackMode GetTrackMode(u8 track) const;
|
|
||||||
LBA GetTrackIndexPosition(u8 track, u8 index) const;
|
|
||||||
LBA GetTrackIndexLength(u8 track, u8 index) const;
|
|
||||||
u32 GetFirstTrackNumber() const { return m_tracks.front().track_number; }
|
|
||||||
u32 GetLastTrackNumber() const { return m_tracks.back().track_number; }
|
|
||||||
|
|
||||||
// Seek to data LBA.
|
|
||||||
bool Seek(LBA lba);
|
|
||||||
|
|
||||||
// Seek to disc position (MSF).
|
|
||||||
bool Seek(const Position& pos);
|
|
||||||
|
|
||||||
// Seek to track and position.
|
|
||||||
bool Seek(u32 track_number, const Position& pos_in_track);
|
|
||||||
|
|
||||||
// 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 from the current LBA.
|
|
||||||
bool ReadRawSector(void* buffer);
|
|
||||||
|
|
||||||
// Reads sub-channel Q for the current LBA.
|
|
||||||
virtual bool ReadSubChannelQ(SubChannelQ* subq);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
struct Track
|
struct Track
|
||||||
{
|
{
|
||||||
u32 track_number;
|
u32 track_number;
|
||||||
|
@ -237,9 +187,65 @@ protected:
|
||||||
bool is_pregap;
|
bool is_pregap;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Helper functions.
|
||||||
|
static u32 GetBytesPerSector(TrackMode mode);
|
||||||
|
|
||||||
|
// Opening disc image.
|
||||||
|
static std::unique_ptr<CDImage> Open(const char* filename);
|
||||||
|
static std::unique_ptr<CDImage> OpenBinImage(const char* filename);
|
||||||
|
static std::unique_ptr<CDImage> OpenCueSheetImage(const char* filename);
|
||||||
|
static std::unique_ptr<CDImage> OpenCHDImage(const char* filename);
|
||||||
|
static std::unique_ptr<CDImage>
|
||||||
|
CreateMemoryImage(CDImage* image, ProgressCallback* progress = ProgressCallback::NullProgressCallback);
|
||||||
|
|
||||||
|
// Accessors.
|
||||||
|
const std::string& GetFileName() const { return m_filename; }
|
||||||
|
LBA GetPositionOnDisc() const { return m_position_on_disc; }
|
||||||
|
Position GetMSFPositionOnDisc() const { return Position::FromLBA(m_position_on_disc); }
|
||||||
|
LBA GetPositionInTrack() const { return m_position_in_track; }
|
||||||
|
Position GetMSFPositionInTrack() const { return Position::FromLBA(m_position_in_track); }
|
||||||
|
LBA GetLBACount() const { return m_lba_count; }
|
||||||
|
u32 GetIndexNumber() const { return m_current_index->index_number; }
|
||||||
|
u32 GetTrackNumber() const { return m_current_index->track_number; }
|
||||||
|
u32 GetTrackCount() const { return static_cast<u32>(m_tracks.size()); }
|
||||||
|
LBA GetTrackStartPosition(u8 track) const;
|
||||||
|
Position GetTrackStartMSFPosition(u8 track) const;
|
||||||
|
LBA GetTrackLength(u8 track) const;
|
||||||
|
Position GetTrackMSFLength(u8 track) const;
|
||||||
|
TrackMode GetTrackMode(u8 track) const;
|
||||||
|
LBA GetTrackIndexPosition(u8 track, u8 index) const;
|
||||||
|
LBA GetTrackIndexLength(u8 track, u8 index) const;
|
||||||
|
u32 GetFirstTrackNumber() const { return m_tracks.front().track_number; }
|
||||||
|
u32 GetLastTrackNumber() const { return m_tracks.back().track_number; }
|
||||||
|
u32 GetIndexCount() const { return static_cast<u32>(m_indices.size()); }
|
||||||
|
const Track& GetTrack(u32 track) const;
|
||||||
|
const Index& GetIndex(u32 i) const;
|
||||||
|
|
||||||
|
// Seek to data LBA.
|
||||||
|
bool Seek(LBA lba);
|
||||||
|
|
||||||
|
// Seek to disc position (MSF).
|
||||||
|
bool Seek(const Position& pos);
|
||||||
|
|
||||||
|
// Seek to track and position.
|
||||||
|
bool Seek(u32 track_number, const Position& pos_in_track);
|
||||||
|
|
||||||
|
// 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 from the current LBA.
|
||||||
|
bool ReadRawSector(void* buffer);
|
||||||
|
|
||||||
|
// Reads sub-channel Q for the current LBA.
|
||||||
|
virtual bool ReadSubChannelQ(SubChannelQ* subq);
|
||||||
|
|
||||||
// Reads a single sector from an index.
|
// Reads a single sector from an index.
|
||||||
virtual bool ReadSectorFromIndex(void* buffer, const Index& index, LBA lba_in_index) = 0;
|
virtual bool ReadSectorFromIndex(void* buffer, const Index& index, LBA lba_in_index) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
const Index* GetIndexForDiscPosition(LBA pos);
|
const Index* GetIndexForDiscPosition(LBA pos);
|
||||||
const Index* GetIndexForTrackPosition(u32 track_number, LBA track_pos);
|
const Index* GetIndexForTrackPosition(u32 track_number, LBA track_pos);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,160 @@
|
||||||
|
#include "assert.h"
|
||||||
|
#include "cd_image.h"
|
||||||
|
#include "cd_subchannel_replacement.h"
|
||||||
|
#include "file_system.h"
|
||||||
|
#include "log.h"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cerrno>
|
||||||
|
#include <libcue/libcue.h>
|
||||||
|
#include <map>
|
||||||
|
Log_SetChannel(CDImageMemory);
|
||||||
|
|
||||||
|
class CDImageMemory : public CDImage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CDImageMemory();
|
||||||
|
~CDImageMemory() override;
|
||||||
|
|
||||||
|
bool CopyImage(CDImage* image, ProgressCallback* progress);
|
||||||
|
|
||||||
|
bool ReadSubChannelQ(SubChannelQ* subq) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool ReadSectorFromIndex(void* buffer, const Index& index, LBA lba_in_index) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
u8* m_memory = nullptr;
|
||||||
|
u32 m_memory_sectors = 0;
|
||||||
|
CDSubChannelReplacement m_sbi;
|
||||||
|
};
|
||||||
|
|
||||||
|
CDImageMemory::CDImageMemory() = default;
|
||||||
|
|
||||||
|
CDImageMemory::~CDImageMemory()
|
||||||
|
{
|
||||||
|
if (m_memory)
|
||||||
|
std::free(m_memory);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CDImageMemory::CopyImage(CDImage* image, ProgressCallback* progress)
|
||||||
|
{
|
||||||
|
// figure out the total number of sectors (not including blank pregaps)
|
||||||
|
m_memory_sectors = 0;
|
||||||
|
for (u32 i = 0; i < image->GetIndexCount(); i++)
|
||||||
|
{
|
||||||
|
const Index& index = image->GetIndex(i);
|
||||||
|
if (index.file_sector_size > 0)
|
||||||
|
m_memory_sectors += image->GetIndex(i).length;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((static_cast<u64>(RAW_SECTOR_SIZE) * static_cast<u64>(m_memory_sectors)) >=
|
||||||
|
static_cast<u64>(std::numeric_limits<size_t>::max()))
|
||||||
|
{
|
||||||
|
progress->DisplayFormattedModalError("Insufficient address space");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
progress->SetFormattedStatusText("Allocating memory for %u sectors...", m_memory_sectors);
|
||||||
|
|
||||||
|
m_memory =
|
||||||
|
static_cast<u8*>(std::malloc(static_cast<size_t>(RAW_SECTOR_SIZE) * static_cast<size_t>(m_memory_sectors)));
|
||||||
|
if (!m_memory)
|
||||||
|
{
|
||||||
|
progress->DisplayFormattedModalError("Failed to allocate memory for %llu sectors", m_memory_sectors);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
progress->SetStatusText("Preloading CD image to RAM...");
|
||||||
|
progress->SetProgressRange(m_memory_sectors);
|
||||||
|
progress->SetProgressValue(0);
|
||||||
|
|
||||||
|
u8* memory_ptr = m_memory;
|
||||||
|
u32 sectors_read = 0;
|
||||||
|
for (u32 i = 0; i < image->GetIndexCount(); i++)
|
||||||
|
{
|
||||||
|
const Index& index = image->GetIndex(i);
|
||||||
|
if (index.file_sector_size == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (u32 lba = 0; lba < index.length; lba++)
|
||||||
|
{
|
||||||
|
if (!image->ReadSectorFromIndex(memory_ptr, index, lba))
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("Failed to read LBA %u in index %u", lba, index);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
progress->SetProgressValue(sectors_read);
|
||||||
|
memory_ptr += RAW_SECTOR_SIZE;
|
||||||
|
sectors_read++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (u32 i = 1; i <= image->GetTrackCount(); i++)
|
||||||
|
m_tracks.push_back(image->GetTrack(i));
|
||||||
|
|
||||||
|
u32 current_offset = 0;
|
||||||
|
for (u32 i = 0; i < image->GetIndexCount(); i++)
|
||||||
|
{
|
||||||
|
Index new_index = image->GetIndex(i);
|
||||||
|
new_index.file_index = 0;
|
||||||
|
if (new_index.file_sector_size > 0)
|
||||||
|
{
|
||||||
|
new_index.file_offset = current_offset;
|
||||||
|
current_offset += new_index.length;
|
||||||
|
}
|
||||||
|
m_indices.push_back(new_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert(current_offset == m_memory_sectors);
|
||||||
|
m_filename = image->GetFileName();
|
||||||
|
m_lba_count = image->GetLBACount();
|
||||||
|
|
||||||
|
if (!image->Seek(0))
|
||||||
|
{
|
||||||
|
progress->ModalError("Failed to seek to start of image for subq read");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
progress->SetStatusText("Looking for invalid subchannel data...");
|
||||||
|
|
||||||
|
CDImage::SubChannelQ subq;
|
||||||
|
for (LBA lba = 0; lba < m_lba_count; lba++)
|
||||||
|
{
|
||||||
|
if (ReadSubChannelQ(&subq) && !subq.IsCRCValid())
|
||||||
|
m_sbi.AddReplacementSubChannelQ(lba, subq);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Seek(1, Position{0, 0, 0});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CDImageMemory::ReadSubChannelQ(SubChannelQ* subq)
|
||||||
|
{
|
||||||
|
if (m_sbi.GetReplacementSubChannelQ(m_position_on_disc, subq))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return CDImage::ReadSubChannelQ(subq);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CDImageMemory::ReadSectorFromIndex(void* buffer, const Index& index, LBA lba_in_index)
|
||||||
|
{
|
||||||
|
DebugAssert(index.file_index == 0);
|
||||||
|
|
||||||
|
const u64 sector_number = index.file_offset + lba_in_index;
|
||||||
|
if (sector_number >= m_memory_sectors)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const size_t file_offset = static_cast<size_t>(sector_number) * static_cast<size_t>(RAW_SECTOR_SIZE);
|
||||||
|
std::memcpy(buffer, &m_memory[file_offset], RAW_SECTOR_SIZE);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<CDImage>
|
||||||
|
CDImage::CreateMemoryImage(CDImage* image, ProgressCallback* progress /* = ProgressCallback::NullProgressCallback */)
|
||||||
|
{
|
||||||
|
std::unique_ptr<CDImageMemory> memory_image = std::make_unique<CDImageMemory>();
|
||||||
|
if (!memory_image->CopyImage(image, progress))
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return memory_image;
|
||||||
|
}
|
|
@ -101,6 +101,7 @@
|
||||||
<ClCompile Include="cd_image_chd.cpp" />
|
<ClCompile Include="cd_image_chd.cpp" />
|
||||||
<ClCompile Include="cd_image_cue.cpp" />
|
<ClCompile Include="cd_image_cue.cpp" />
|
||||||
<ClCompile Include="cd_image_hasher.cpp" />
|
<ClCompile Include="cd_image_hasher.cpp" />
|
||||||
|
<ClCompile Include="cd_image_memory.cpp" />
|
||||||
<ClCompile Include="cubeb_audio_stream.cpp" />
|
<ClCompile Include="cubeb_audio_stream.cpp" />
|
||||||
<ClCompile Include="d3d11\shader_cache.cpp" />
|
<ClCompile Include="d3d11\shader_cache.cpp" />
|
||||||
<ClCompile Include="d3d11\shader_compiler.cpp" />
|
<ClCompile Include="d3d11\shader_compiler.cpp" />
|
||||||
|
|
|
@ -190,6 +190,7 @@
|
||||||
<Filter>vulkan</Filter>
|
<Filter>vulkan</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="image.cpp" />
|
<ClCompile Include="image.cpp" />
|
||||||
|
<ClCompile Include="cd_image_memory.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Natvis Include="bitfield.natvis" />
|
<Natvis Include="bitfield.natvis" />
|
||||||
|
|
Loading…
Reference in New Issue