Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/src/util/cd_image.h
4214 views
1
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <[email protected]>
2
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
3
4
#pragma once
5
6
#include "common/bitfield.h"
7
#include "common/bitutils.h"
8
#include "common/progress_callback.h"
9
#include "common/types.h"
10
11
#include <array>
12
#include <memory>
13
#include <string>
14
#include <tuple>
15
#include <vector>
16
17
class Error;
18
19
class CDImage
20
{
21
public:
22
CDImage();
23
virtual ~CDImage();
24
25
using LBA = u32;
26
27
enum : u32
28
{
29
RAW_SECTOR_SIZE = 2352,
30
DATA_SECTOR_SIZE = 2048,
31
SECTOR_SYNC_SIZE = 12,
32
SECTOR_HEADER_SIZE = 4,
33
MODE1_HEADER_SIZE = 4,
34
MODE2_HEADER_SIZE = 12,
35
MODE2_DATA_SECTOR_SIZE = 2336, // header + edc
36
FRAMES_PER_SECOND = 75, // "sectors", or "timecode frames" (not "channel frames")
37
SECONDS_PER_MINUTE = 60,
38
FRAMES_PER_MINUTE = FRAMES_PER_SECOND * SECONDS_PER_MINUTE,
39
SUBCHANNEL_BYTES_PER_FRAME = 12,
40
LEAD_OUT_SECTOR_COUNT = 6750,
41
ALL_SUBCODE_SIZE = 96,
42
AUDIO_SAMPLE_RATE = 44100,
43
AUDIO_CHANNELS = 2,
44
};
45
46
enum : u8
47
{
48
LEAD_OUT_TRACK_NUMBER = 0xAA
49
};
50
51
enum class TrackMode : u8
52
{
53
Audio, // 2352 bytes per sector
54
Mode1, // 2048 bytes per sector
55
Mode1Raw, // 2352 bytes per sector
56
Mode2, // 2336 bytes per sector
57
Mode2Form1, // 2048 bytes per sector
58
Mode2Form2, // 2324 bytes per sector
59
Mode2FormMix, // 2332 bytes per sector
60
Mode2Raw // 2352 bytes per sector
61
};
62
63
enum class SubchannelMode : u8
64
{
65
None, // no subcode data stored
66
RawInterleaved, // raw interleaved 96 bytes per sector
67
Raw, // raw uninterleaved 96 bytes per sector
68
};
69
70
enum class PrecacheResult : u8
71
{
72
Unsupported,
73
ReadError,
74
Success,
75
};
76
77
struct SectorHeader
78
{
79
u8 minute;
80
u8 second;
81
u8 frame;
82
u8 sector_mode;
83
};
84
85
struct Position
86
{
87
u8 minute;
88
u8 second;
89
u8 frame;
90
91
static constexpr Position FromBCD(u8 minute, u8 second, u8 frame)
92
{
93
return Position{PackedBCDToBinary(minute), PackedBCDToBinary(second), PackedBCDToBinary(frame)};
94
}
95
96
static constexpr Position FromLBA(LBA lba)
97
{
98
const u8 frame = Truncate8(lba % FRAMES_PER_SECOND);
99
lba /= FRAMES_PER_SECOND;
100
101
const u8 second = Truncate8(lba % SECONDS_PER_MINUTE);
102
lba /= SECONDS_PER_MINUTE;
103
104
const u8 minute = Truncate8(lba);
105
106
return Position{minute, second, frame};
107
}
108
109
LBA ToLBA() const
110
{
111
return ZeroExtend32(minute) * FRAMES_PER_MINUTE + ZeroExtend32(second) * FRAMES_PER_SECOND + ZeroExtend32(frame);
112
}
113
114
constexpr std::tuple<u8, u8, u8> ToBCD() const
115
{
116
return std::make_tuple<u8, u8, u8>(BinaryToBCD(minute), BinaryToBCD(second), BinaryToBCD(frame));
117
}
118
119
Position operator+(const Position& rhs) { return FromLBA(ToLBA() + rhs.ToLBA()); }
120
Position& operator+=(const Position& pos)
121
{
122
*this = *this + pos;
123
return *this;
124
}
125
126
#define RELATIONAL_OPERATOR(op) \
127
bool operator op(const Position& rhs) const \
128
{ \
129
return std::tie(minute, second, frame) op std::tie(rhs.minute, rhs.second, rhs.frame); \
130
}
131
132
RELATIONAL_OPERATOR(==);
133
RELATIONAL_OPERATOR(!=);
134
RELATIONAL_OPERATOR(<);
135
RELATIONAL_OPERATOR(<=);
136
RELATIONAL_OPERATOR(>);
137
RELATIONAL_OPERATOR(>=);
138
139
#undef RELATIONAL_OPERATOR
140
};
141
142
union SubChannelQ
143
{
144
using Data = std::array<u8, SUBCHANNEL_BYTES_PER_FRAME>;
145
146
union Control
147
{
148
u8 bits;
149
150
BitField<u8, u8, 0, 4> adr;
151
BitField<u8, bool, 4, 1> audio_preemphasis;
152
BitField<u8, bool, 5, 1> digital_copy_permitted;
153
BitField<u8, bool, 6, 1> data;
154
BitField<u8, bool, 7, 1> four_channel_audio;
155
156
Control() = default;
157
158
Control(u8 bits_) : bits(bits_) {}
159
};
160
161
struct
162
{
163
u8 control_bits;
164
u8 track_number_bcd;
165
u8 index_number_bcd;
166
u8 relative_minute_bcd;
167
u8 relative_second_bcd;
168
u8 relative_frame_bcd;
169
u8 reserved;
170
u8 absolute_minute_bcd;
171
u8 absolute_second_bcd;
172
u8 absolute_frame_bcd;
173
u16 crc;
174
};
175
176
Data data;
177
178
static u16 ComputeCRC(const Data& data);
179
180
Control GetControl() const { return Control(control_bits); }
181
bool IsData() const { return GetControl().data; }
182
183
bool IsCRCValid() const;
184
};
185
static_assert(sizeof(SubChannelQ) == SUBCHANNEL_BYTES_PER_FRAME, "SubChannelQ is correct size");
186
187
struct Track
188
{
189
u32 track_number;
190
LBA start_lba;
191
u32 first_index;
192
u32 length;
193
TrackMode mode;
194
SubchannelMode submode;
195
SubChannelQ::Control control;
196
};
197
198
struct Index
199
{
200
u64 file_offset;
201
u32 file_index;
202
u32 file_sector_size;
203
LBA start_lba_on_disc;
204
u32 track_number;
205
u32 index_number;
206
LBA start_lba_in_track;
207
u32 length;
208
TrackMode mode;
209
SubchannelMode submode;
210
SubChannelQ::Control control;
211
bool is_pregap;
212
};
213
214
// Helper functions.
215
static u32 GetBytesPerSector(TrackMode mode);
216
static void DeinterleaveSubcode(const u8* subcode_in, u8* subcode_out);
217
218
/// Returns a list of physical CD-ROM devices, .first being the device path, .second being the device name.
219
static std::vector<std::pair<std::string, std::string>> GetDeviceList();
220
221
/// Returns true if the specified filename is a CD-ROM device name.
222
static bool IsDeviceName(const char* filename);
223
224
// Opening disc image.
225
static std::unique_ptr<CDImage> Open(const char* path, bool allow_patches, Error* error);
226
static std::unique_ptr<CDImage> OpenBinImage(const char* path, Error* error);
227
static std::unique_ptr<CDImage> OpenCueSheetImage(const char* path, Error* error);
228
static std::unique_ptr<CDImage> OpenCHDImage(const char* path, Error* error);
229
static std::unique_ptr<CDImage> OpenMdsImage(const char* path, Error* error);
230
static std::unique_ptr<CDImage> OpenPBPImage(const char* path, Error* error);
231
static std::unique_ptr<CDImage> OpenM3uImage(const char* path, bool apply_patches, Error* error);
232
static std::unique_ptr<CDImage> OpenDeviceImage(const char* path, Error* error);
233
static std::unique_ptr<CDImage>
234
CreateMemoryImage(CDImage* image, ProgressCallback* progress = ProgressCallback::NullProgressCallback);
235
static std::unique_ptr<CDImage> OverlayPPFPatch(const char* path, std::unique_ptr<CDImage> parent_image,
236
ProgressCallback* progress = ProgressCallback::NullProgressCallback);
237
238
// Accessors.
239
const std::string& GetPath() const { return m_filename; }
240
LBA GetPositionOnDisc() const { return m_position_on_disc; }
241
Position GetMSFPositionOnDisc() const { return Position::FromLBA(m_position_on_disc); }
242
LBA GetPositionInTrack() const { return m_position_in_track; }
243
Position GetMSFPositionInTrack() const { return Position::FromLBA(m_position_in_track); }
244
LBA GetLBACount() const { return m_lba_count; }
245
u32 GetIndexNumber() const { return m_current_index->index_number; }
246
u32 GetTrackNumber() const { return m_current_index->track_number; }
247
u32 GetTrackCount() const { return static_cast<u32>(m_tracks.size()); }
248
LBA GetTrackStartPosition(u8 track) const;
249
Position GetTrackStartMSFPosition(u8 track) const;
250
LBA GetTrackLength(u8 track) const;
251
Position GetTrackMSFLength(u8 track) const;
252
TrackMode GetTrackMode(u8 track) const;
253
LBA GetTrackIndexPosition(u8 track, u8 index) const;
254
LBA GetTrackIndexLength(u8 track, u8 index) const;
255
u32 GetFirstTrackNumber() const { return m_tracks.front().track_number; }
256
u32 GetLastTrackNumber() const { return m_tracks.back().track_number; }
257
u32 GetIndexCount() const { return static_cast<u32>(m_indices.size()); }
258
const std::vector<Track>& GetTracks() const { return m_tracks; }
259
const std::vector<Index>& GetIndices() const { return m_indices; }
260
const Track& GetTrack(u32 track) const;
261
const Index& GetIndex(u32 i) const;
262
263
// Seek to data LBA.
264
bool Seek(LBA lba);
265
266
// Seek to disc position (MSF).
267
bool Seek(const Position& pos);
268
269
// Seek to track and position.
270
bool Seek(u32 track_number, const Position& pos_in_track);
271
272
// Seek to track and LBA.
273
bool Seek(u32 track_number, LBA lba);
274
275
// Read a single raw sector, and subchannel from the current LBA.
276
bool ReadRawSector(void* buffer, SubChannelQ* subq);
277
278
/// Generates sub-channel Q given the specified position.
279
bool GenerateSubChannelQ(SubChannelQ* subq, LBA lba) const;
280
281
/// Generates sub-channel Q from the given index and index-offset.
282
void GenerateSubChannelQ(SubChannelQ* subq, const Index& index, u32 index_offset) const;
283
284
// Reads sub-channel Q for the specified index+LBA.
285
virtual bool ReadSubChannelQ(SubChannelQ* subq, const Index& index, LBA lba_in_index);
286
287
// Returns true if the image has replacement subchannel data.
288
virtual bool HasSubchannelData() const;
289
290
// Reads a single sector from an index.
291
virtual bool ReadSectorFromIndex(void* buffer, const Index& index, LBA lba_in_index) = 0;
292
293
// Retrieve image metadata.
294
virtual std::string GetMetadata(std::string_view type) const;
295
296
// Returns true if this image type has sub-images (e.g. m3u).
297
virtual bool HasSubImages() const;
298
299
// Returns the number of sub-images in this image, if the format supports multiple.
300
virtual u32 GetSubImageCount() const;
301
302
// Returns the current sub-image index, if any.
303
virtual u32 GetCurrentSubImage() const;
304
305
// Changes the current sub-image. If this fails, the image state is unchanged.
306
virtual bool SwitchSubImage(u32 index, Error* error);
307
308
// Retrieve sub-image metadata.
309
virtual std::string GetSubImageMetadata(u32 index, std::string_view type) const;
310
311
// Returns true if the source supports precaching, which may be more optimal than an in-memory copy.
312
virtual PrecacheResult Precache(ProgressCallback* progress = ProgressCallback::NullProgressCallback);
313
virtual bool IsPrecached() const;
314
315
// Returns the size on disk of the image. This could be multiple files.
316
// If this function returns -1, it means the size could not be computed.
317
virtual s64 GetSizeOnDisk() const;
318
319
protected:
320
void ClearTOC();
321
void CopyTOC(const CDImage* image);
322
323
const Index* GetIndexForDiscPosition(LBA pos) const;
324
const Index* GetIndexForTrackPosition(u32 track_number, LBA track_pos) const;
325
326
/// Synthesis of lead-out data.
327
void AddLeadOutIndex();
328
329
std::string m_filename;
330
u32 m_lba_count = 0;
331
332
std::vector<Track> m_tracks;
333
std::vector<Index> m_indices;
334
335
private:
336
// Position on disc.
337
LBA m_position_on_disc = 0;
338
339
// Position in track/index.
340
const Index* m_current_index = nullptr;
341
LBA m_position_in_index = 0;
342
LBA m_position_in_track = 0;
343
};
344
345