2019-09-20 10:14:00 +00:00
|
|
|
#pragma once
|
2019-10-04 09:05:19 +00:00
|
|
|
#include "bitfield.h"
|
2019-09-20 10:14:00 +00:00
|
|
|
#include "types.h"
|
2019-10-18 08:18:04 +00:00
|
|
|
#include <memory>
|
2019-10-17 13:54:51 +00:00
|
|
|
#include <tuple>
|
2019-10-18 08:18:04 +00:00
|
|
|
#include <vector>
|
2019-09-20 10:14:00 +00:00
|
|
|
|
|
|
|
class ByteStream;
|
|
|
|
|
|
|
|
class CDImage
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
CDImage();
|
2019-10-18 08:18:04 +00:00
|
|
|
virtual ~CDImage();
|
|
|
|
|
|
|
|
using LBA = u32;
|
2019-09-20 10:14:00 +00:00
|
|
|
|
2019-09-21 15:12:16 +00:00
|
|
|
enum : u32
|
|
|
|
{
|
|
|
|
RAW_SECTOR_SIZE = 2352,
|
|
|
|
DATA_SECTOR_SIZE = 2048,
|
|
|
|
SECTOR_SYNC_SIZE = 12,
|
2019-10-04 09:05:19 +00:00
|
|
|
SECTOR_HEADER_SIZE = 4,
|
2019-09-21 15:12:16 +00:00
|
|
|
FRAMES_PER_SECOND = 75, // "sectors"
|
|
|
|
SECONDS_PER_MINUTE = 60,
|
2019-10-15 07:24:11 +00:00
|
|
|
FRAMES_PER_MINUTE = FRAMES_PER_SECOND * SECONDS_PER_MINUTE
|
2019-09-21 15:12:16 +00:00
|
|
|
};
|
|
|
|
|
2019-09-20 10:14:00 +00:00
|
|
|
enum class ReadMode : u32
|
|
|
|
{
|
2019-10-04 09:05:19 +00:00
|
|
|
DataOnly, // 2048 bytes per sector.
|
|
|
|
RawSector, // 2352 bytes per sector.
|
|
|
|
RawNoSync, // 2340 bytes per sector.
|
|
|
|
};
|
|
|
|
|
|
|
|
struct SectorHeader
|
|
|
|
{
|
|
|
|
u8 minute;
|
|
|
|
u8 second;
|
|
|
|
u8 frame;
|
|
|
|
u8 sector_mode;
|
|
|
|
};
|
|
|
|
|
2019-10-18 08:18:04 +00:00
|
|
|
struct Position
|
2019-10-17 13:54:51 +00:00
|
|
|
{
|
2019-10-18 08:18:04 +00:00
|
|
|
u8 minute;
|
|
|
|
u8 second;
|
|
|
|
u8 frame;
|
|
|
|
|
|
|
|
static constexpr Position FromBCD(u8 minute, u8 second, u8 frame)
|
|
|
|
{
|
|
|
|
return Position{BCDToDecimal(minute), BCDToDecimal(second), BCDToDecimal(frame)};
|
|
|
|
}
|
|
|
|
|
|
|
|
static constexpr Position FromLBA(LBA lba)
|
|
|
|
{
|
|
|
|
const u8 frame = Truncate8(lba % FRAMES_PER_SECOND);
|
|
|
|
lba /= FRAMES_PER_SECOND;
|
|
|
|
|
|
|
|
const u8 second = Truncate8(lba % SECONDS_PER_MINUTE);
|
|
|
|
lba /= SECONDS_PER_MINUTE;
|
|
|
|
|
|
|
|
const u8 minute = Truncate8(lba);
|
|
|
|
|
|
|
|
return Position{minute, second, frame};
|
|
|
|
}
|
|
|
|
|
|
|
|
LBA ToLBA() const
|
|
|
|
{
|
|
|
|
return ZeroExtend32(minute) * FRAMES_PER_MINUTE + ZeroExtend32(second) * FRAMES_PER_SECOND + ZeroExtend32(frame);
|
|
|
|
}
|
|
|
|
|
|
|
|
Position operator+(const Position& rhs) { return FromLBA(ToLBA() + rhs.ToLBA()); }
|
|
|
|
Position& operator+=(const Position& pos)
|
|
|
|
{
|
|
|
|
*this = *this + pos;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// 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);
|
2019-09-20 10:14:00 +00:00
|
|
|
|
|
|
|
// Accessors.
|
2019-10-17 13:54:51 +00:00
|
|
|
const std::string& GetFileName() const { return m_filename; }
|
2019-10-18 08:18:04 +00:00
|
|
|
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; }
|
2019-10-18 12:44:28 +00:00
|
|
|
u32 GetIndexNumber() const { return m_current_index->index_number; }
|
2019-10-18 08:18:04 +00:00
|
|
|
u32 GetTrackNumber() const { return m_current_index->track_number; }
|
2019-10-18 12:44:28 +00:00
|
|
|
u32 GetTrackCount() const { return static_cast<u32>(m_tracks.size()); }
|
2019-09-20 10:14:00 +00:00
|
|
|
|
|
|
|
// Seek to data LBA.
|
2019-10-18 08:18:04 +00:00
|
|
|
bool Seek(LBA lba);
|
2019-09-20 10:14:00 +00:00
|
|
|
|
2019-10-18 08:18:04 +00:00
|
|
|
// Seek to disc position (MSF).
|
|
|
|
bool Seek(const Position& pos);
|
2019-09-20 10:14:00 +00:00
|
|
|
|
2019-10-18 08:18:04 +00:00
|
|
|
// Seek to track and position.
|
|
|
|
bool Seek(u32 track_number, const Position& pos_in_track);
|
2019-09-20 10:14:00 +00:00
|
|
|
|
|
|
|
// Read from the current LBA. Returns the number of sectors read.
|
|
|
|
u32 Read(ReadMode read_mode, u32 sector_count, void* buffer);
|
|
|
|
|
2019-10-18 08:18:04 +00:00
|
|
|
protected:
|
|
|
|
struct Track
|
|
|
|
{
|
|
|
|
u32 track_number;
|
|
|
|
LBA start_lba;
|
|
|
|
u32 first_index;
|
|
|
|
u32 length;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Index
|
|
|
|
{
|
|
|
|
u64 file_offset;
|
|
|
|
std::FILE* file;
|
|
|
|
u32 file_sector_size;
|
|
|
|
LBA start_lba_on_disc;
|
|
|
|
u32 track_number;
|
2019-10-18 12:44:28 +00:00
|
|
|
u32 index_number;
|
2019-10-18 08:18:04 +00:00
|
|
|
LBA start_lba_in_track;
|
|
|
|
u32 length;
|
|
|
|
bool is_pregap;
|
|
|
|
};
|
|
|
|
|
|
|
|
const Index* GetIndexForDiscPosition(LBA pos);
|
|
|
|
const Index* GetIndexForTrackPosition(u32 track_number, LBA track_pos);
|
|
|
|
|
2019-10-17 13:54:51 +00:00
|
|
|
std::string m_filename;
|
2019-10-18 08:18:04 +00:00
|
|
|
u32 m_lba_count = 0;
|
2019-10-17 13:54:51 +00:00
|
|
|
|
2019-10-18 08:18:04 +00:00
|
|
|
std::vector<Track> m_tracks;
|
|
|
|
std::vector<Index> m_indices;
|
2019-09-20 10:14:00 +00:00
|
|
|
|
2019-10-18 08:18:04 +00:00
|
|
|
// Position on disc.
|
|
|
|
LBA m_position_on_disc = 0;
|
2019-09-25 14:15:06 +00:00
|
|
|
|
2019-10-18 08:18:04 +00:00
|
|
|
// Position in track/index.
|
|
|
|
const Index* m_current_index = nullptr;
|
|
|
|
LBA m_position_in_index = 0;
|
|
|
|
LBA m_position_in_track = 0;
|
2019-09-20 10:14:00 +00:00
|
|
|
};
|