summaryrefslogtreecommitdiffstatshomepage
path: root/src/lib/util/cdrom.h
blob: b11ecb5b3ba956e849f8327e09b40ae6c4e00866 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
// license:BSD-3-Clause
// copyright-holders:Aaron Giles,R. Belmont
/***************************************************************************

    cdrom.h

    Generic MAME cd-rom implementation

***************************************************************************/
#ifndef MAME_LIB_UTIL_CDROM_H
#define MAME_LIB_UTIL_CDROM_H

#pragma once

#include "chd.h"
#include "ioprocs.h"
#include "osdcore.h"

#include <algorithm>
#include <string>
#include <string_view>
#include <system_error>


class cdrom_file {
public:
	// tracks are padded to a multiple of this many frames
	static constexpr uint32_t TRACK_PADDING    = 4;

	static constexpr uint32_t MAX_TRACKS       = 99;        /* AFAIK the theoretical limit */
	static constexpr uint32_t MAX_SECTOR_DATA  = 2352;
	static constexpr uint32_t MAX_SUBCODE_DATA = 96;
	static constexpr uint32_t MAX_INDEX        = 99;

	static constexpr uint32_t FRAME_SIZE       = MAX_SECTOR_DATA + MAX_SUBCODE_DATA;
	static constexpr uint32_t FRAMES_PER_HUNK  = 8;

	static constexpr uint32_t METADATA_WORDS   = 1 + MAX_TRACKS * 6;

	enum
	{
		CD_TRACK_MODE1 = 0,         /* mode 1 2048 bytes/sector */
		CD_TRACK_MODE1_RAW,         /* mode 1 2352 bytes/sector */
		CD_TRACK_MODE2,             /* mode 2 2336 bytes/sector */
		CD_TRACK_MODE2_FORM1,       /* mode 2 2048 bytes/sector */
		CD_TRACK_MODE2_FORM2,       /* mode 2 2324 bytes/sector */
		CD_TRACK_MODE2_FORM_MIX,    /* mode 2 2336 bytes/sector */
		CD_TRACK_MODE2_RAW,         /* mode 2 2352 bytes/sector */
		CD_TRACK_AUDIO,             /* redbook audio track 2352 bytes/sector (588 samples) */

		CD_TRACK_RAW_DONTCARE       /* special flag for cdrom_read_data: just return me whatever is there */
	};

	enum
	{
		CD_SUB_NORMAL = 0,          /* "cooked" 96 bytes per sector */
		CD_SUB_RAW,                 /* raw uninterleaved 96 bytes per sector */
		CD_SUB_NONE                 /* no subcode data stored */
	};

	enum
	{
		CD_FLAG_GDROM        = 0x00000001,  // disc is a GD-ROM, all tracks should be stored with GD-ROM metadata
		CD_FLAG_GDROMLE      = 0x00000002,  // legacy GD-ROM, with little-endian CDDA data
		CD_FLAG_MULTISESSION = 0x00000004,  // multisession CD-ROM
	};

	enum
	{
		CD_FLAG_CONTROL_PREEMPHASIS = 1,
		CD_FLAG_CONTROL_DIGITAL_COPY_PERMITTED = 2,
		CD_FLAG_CONTROL_DATA_TRACK = 4,
		CD_FLAG_CONTROL_4CH = 8,
	};

	enum
	{
		CD_FLAG_ADR_START_TIME = 1,
		CD_FLAG_ADR_CATALOG_CODE,
		CD_FLAG_ADR_ISRC_CODE,
	};

	struct track_info
	{
		/* fields used by CHDMAN and in MAME */
		uint32_t trktype;       /* track type */
		uint32_t subtype;       /* subcode data type */
		uint32_t datasize;      /* size of data in each sector of this track */
		uint32_t subsize;       /* size of subchannel data in each sector of this track */
		uint32_t frames;        /* number of frames in this track */
		uint32_t extraframes;   /* number of "spillage" frames in this track */
		uint32_t pregap;        /* number of pregap frames */
		uint32_t postgap;       /* number of postgap frames */
		uint32_t pgtype;        /* type of sectors in pregap */
		uint32_t pgsub;         /* type of subchannel data in pregap */
		uint32_t pgdatasize;    /* size of data in each sector of the pregap */
		uint32_t pgsubsize;     /* size of subchannel data in each sector of the pregap */
		uint32_t control_flags; /* metadata flags associated with each track */
		uint32_t session;       /* session number */

		/* fields used in CHDMAN only */
		uint32_t padframes;   /* number of frames of padding to add to the end of the track; needed for GDI */
		uint32_t splitframes; /* number of frames from the next file to add to the end of the current track after padding; needed for Redump split-bin GDI */

		/* fields used in MAME/MESS only */
		uint32_t logframeofs; /* logical frame of actual track data - offset by pregap size if pregap not physically present */
		uint32_t physframeofs; /* physical frame of actual track data in CHD data */
		uint32_t chdframeofs; /* frame number this track starts at on the CHD */
		uint32_t logframes; /* number of frames from logframeofs until end of track data */

		/* fields used in multi-cue GDI */
		uint32_t multicuearea;
	};


	struct toc
	{
		uint32_t numtrks;     /* number of tracks */
		uint32_t numsessions; /* number of sessions */
		uint32_t flags;       /* see FLAG_ above */
		track_info tracks[MAX_TRACKS + 1];
	};

	struct track_input_entry
	{
		track_input_entry() { reset(); }
		void reset() { fname.clear(); offset = 0; leadin = leadout = -1; swap = false; std::fill(std::begin(idx), std::end(idx), -1); }

		std::string fname;      // filename for each track
		uint32_t offset;      // offset in the data file for each track
		bool swap;          // data needs to be byte swapped
		int32_t idx[MAX_INDEX + 1];
		int32_t leadin, leadout; // TODO: these should probably be their own tracks entirely
	};

	struct track_input_info
	{
		void reset() { for (auto & elem : track) elem.reset(); }

		track_input_entry track[MAX_TRACKS];
	};


	cdrom_file(chd_file *chd);
	cdrom_file(std::string_view inputfile);
	~cdrom_file();


	/* core read access */
	bool read_data(uint32_t lbasector, void *buffer, uint32_t datatype, bool phys=false);
	bool read_subcode(uint32_t lbasector, void *buffer, bool phys=false);

	/* handy utilities */
	uint32_t get_track(uint32_t frame) const;
	uint32_t get_track_start(uint32_t track) const {return cdtoc.tracks[track == 0xaa ? cdtoc.numtrks : track].logframeofs; }
	uint32_t get_track_start_phys(uint32_t track) const { return cdtoc.tracks[track == 0xaa ? cdtoc.numtrks : track].physframeofs; }
	uint32_t get_track_index(uint32_t frame) const;

	/* TOC utilities */
	static std::error_condition parse_nero(std::string_view tocfname, toc &outtoc, track_input_info &outinfo);
	static std::error_condition parse_iso(std::string_view tocfname, toc &outtoc, track_input_info &outinfo);
	static std::error_condition parse_gdi(std::string_view tocfname, toc &outtoc, track_input_info &outinfo);
	static std::error_condition parse_cue(std::string_view tocfname, toc &outtoc, track_input_info &outinfo);
	static bool is_gdicue(std::string_view tocfname);
	static std::error_condition parse_toc(std::string_view tocfname, toc &outtoc, track_input_info &outinfo);
	int get_last_session() const { return cdtoc.numsessions ? cdtoc.numsessions : 1; }
	int get_last_track() const { return cdtoc.numtrks; }
	int get_adr_control(int track) const
	{
		if (track == 0xaa)
			track = get_last_track() - 1; // use last track's flags
		int adrctl = (CD_FLAG_ADR_START_TIME << 4) | (cdtoc.tracks[track].control_flags & 0x0f);
		if (cdtoc.tracks[track].trktype != CD_TRACK_AUDIO)
			adrctl |= CD_FLAG_CONTROL_DATA_TRACK;
		return adrctl;
	}
	int get_track_type(int track) const { return cdtoc.tracks[track].trktype; }
	const toc &get_toc() const { return cdtoc; }

	/* extra utilities */
	static void convert_type_string_to_track_info(const char *typestring, track_info *info);
	static void convert_type_string_to_pregap_info(const char *typestring, track_info *info);
	static void convert_subtype_string_to_track_info(const char *typestring, track_info *info);
	static void convert_subtype_string_to_pregap_info(const char *typestring, track_info *info);
	static const char *get_type_string(uint32_t trktype);
	static const char *get_subtype_string(uint32_t subtype);
	static std::error_condition parse_metadata(chd_file *chd, toc &toc);
	static std::error_condition write_metadata(chd_file *chd, const toc &toc);
	bool is_gdrom() const { return cdtoc.flags & (CD_FLAG_GDROM|CD_FLAG_GDROMLE); }

	// ECC utilities
	static bool ecc_verify(const uint8_t *sector);
	static void ecc_generate(uint8_t *sector);
	static void ecc_clear(uint8_t *sector);



	static inline uint32_t msf_to_lba(uint32_t msf)
	{
		return ( ((msf&0x00ff0000)>>16) * 60 * 75) + (((msf&0x0000ff00)>>8) * 75) + ((msf&0x000000ff)>>0);
	}

	static inline uint32_t lba_to_msf(uint32_t lba)
	{
		uint8_t m, s, f;

		m = lba / (60 * 75);
		lba -= m * (60 * 75);
		s = lba / 75;
		f = lba % 75;

		return ((m / 10) << 20) | ((m % 10) << 16) |
			((s / 10) << 12) | ((s % 10) <<  8) |
			((f / 10) <<  4) | ((f % 10) <<  0);
	}

	// segacd needs it like this.. investigate
	// Angelo also says PCE tracks often start playing at the
	// wrong address.. related?
	static inline uint32_t lba_to_msf_alt(int lba)
	{
		uint32_t ret = 0;

		ret |= ((lba / (60 * 75))&0xff)<<16;
		ret |= (((lba / 75) % 60)&0xff)<<8;
		ret |= ((lba % 75)&0xff)<<0;

		return ret;
	}

private:
	enum gdi_area {
		SINGLE_DENSITY,
		HIGH_DENSITY
	};

	enum gdi_pattern {
		TYPE_UNKNOWN = 0,
		TYPE_I,
		TYPE_II,
		TYPE_III,
		TYPE_III_SPLIT
	};

	/** @brief  offset within sector. */
	static constexpr int SYNC_OFFSET = 0x000;
	/** @brief  12 bytes. */
	static constexpr int SYNC_NUM_BYTES = 12;

	/** @brief  offset within sector. */
	static constexpr int MODE_OFFSET = 0x00f;

	/** @brief  offset within sector. */
	static constexpr int ECC_P_OFFSET = 0x81c;
	/** @brief  2 lots of 86. */
	static constexpr int ECC_P_NUM_BYTES = 86;
	/** @brief  24 bytes each. */
	static constexpr int ECC_P_COMP = 24;

	/** @brief  The ECC q offset. */
	static constexpr int ECC_Q_OFFSET = ECC_P_OFFSET + 2 * ECC_P_NUM_BYTES;
	/** @brief  2 lots of 52. */
	static constexpr int ECC_Q_NUM_BYTES = 52;
	/** @brief  43 bytes each. */
	static constexpr int ECC_Q_COMP = 43;

	// ECC tables
	static const uint8_t ecclow[256];
	static const uint8_t ecchigh[256];
	static const uint16_t poffsets[ECC_P_NUM_BYTES][ECC_P_COMP];
	static const uint16_t qoffsets[ECC_Q_NUM_BYTES][ECC_Q_COMP];

	/** @brief  The chd. */
	chd_file *           chd;                /* CHD file */
	/** @brief  The cdtoc. */
	toc                  cdtoc;              /* TOC for the CD */
	/** @brief  Information describing the track. */
	track_input_info     cdtrack_info;       /* track info */
	/** @brief  The fhandle[ CD maximum tracks]. */
	util::random_read::ptr fhandle[MAX_TRACKS];/* file handle */

	inline uint32_t physical_to_chd_lba(uint32_t physlba, uint32_t &tracknum) const;
	inline uint32_t logical_to_chd_lba(uint32_t physlba, uint32_t &tracknum) const;

	static void get_info_from_type_string(const char *typestring, uint32_t *trktype, uint32_t *datasize);
	static uint8_t ecc_source_byte(const uint8_t *sector, uint32_t offset);
	static void ecc_compute_bytes(const uint8_t *sector, const uint16_t *row, int rowlen, uint8_t &val1, uint8_t &val2);
	std::error_condition read_partial_sector(void *dest, uint32_t lbasector, uint32_t chdsector, uint32_t tracknum, uint32_t startoffs, uint32_t length, bool phys);

	static std::string get_file_path(std::string &path);
	static uint64_t get_file_size(std::string_view filename);
	static int tokenize(const char *linebuffer, int i, int linebuffersize, char *token, int tokensize);
	static int msf_to_frames(const char *token);
	static uint32_t parse_wav_sample(std::string_view filename, uint32_t *dataoffs);
	static uint16_t read_uint16(FILE *infile);
	static uint32_t read_uint32(FILE *infile);
	static uint64_t read_uint64(FILE *infile);
};

#endif // MAME_LIB_UTIL_CDROM_H