1 //==================================================================//
2 /*
3 AtomicParsley - CDtoc.cpp
4
5 AtomicParsley is GPL software; you can freely distribute,
6 redistribute, modify & use under the terms of the GNU General
7 Public License; either version 2 or its successor.
8
9 AtomicParsley is distributed under the GPL "AS IS", without
10 any warranty; without the implied warranty of merchantability
11 or fitness for either an expressed or implied particular purpose.
12
13 Please see the included GNU General Public License (GPL) for
14 your rights and further details; see the file COPYING. If you
15 cannot, write to the Free Software Foundation, 59 Temple Place
16 Suite 330, Boston, MA 02111-1307, USA. Or www.fsf.org
17
18 Copyright (C)2006-2007 puck_lock
19 with contributions from others; see the CREDITS file
20 */
21 //==================================================================//
22
23 // gathering of a CD's Table of Contents is going to be hardware specific
24 // currently only Mac OS X is implemented - using IOKit framework.
25 // another avenue (applicable to other *nix platforms): ioctl
26
27 #include "AtomicParsley.h"
28 #include "CDtoc.h"
29
30 #if defined(__APPLE__)
31 #include <CoreFoundation/CoreFoundation.h>
32 #include <IOKit/IOBSD.h>
33 #include <IOKit/IOKitLib.h>
34 #include <IOKit/storage/IOCDMedia.h>
35 #include <IOKit/storage/IOCDTypes.h>
36
37 const uint8_t MACOSX_LEADOUT_TRACK = 0xA2;
38 #endif
39
40 const uint8_t CDOBJECT_DATACD = 0;
41 const uint8_t CDOBJECT_AUDIOCD = 1;
42
43 struct CD_TDesc {
44 uint8_t session;
45 uint8_t controladdress;
46 uint8_t unused1; // refered to as 'tno' which equates to "track number" -
47 // but... its 'point' that actually is the tracknumber in
48 // mode1 TOC. set to zero for all mode1 TOC
49 uint8_t tracknumber; // refered to as 'point', but this is actually the
50 // tracknumber in mode1 audio toc entries.
51 uint8_t rel_minutes;
52 uint8_t rel_seconds;
53 uint8_t rel_frames;
54 uint8_t zero_space;
55 uint8_t abs_minutes;
56 uint8_t abs_seconds;
57 uint8_t abs_frames;
58 void *next_description;
59 };
60 typedef struct CD_TDesc CD_TDesc;
61
62 struct CD_TOC_ {
63 uint16_t toc_length;
64 uint8_t first_session;
65 uint8_t last_session;
66 CD_TDesc *track_description; // entry to the first track in the linked list
67 };
68 typedef struct CD_TOC_ CD_TOC_;
69
70 CD_TOC_ *cdTOC = NULL;
71
72 #if defined(__APPLE__)
73 uint8_t LEADOUT_TRACK_NUMBER = MACOSX_LEADOUT_TRACK;
74 #elif defined(__linux__)
75 uint8_t LEADOUT_TRACK_NUMBER = CDROM_LEADOUT;
76 #else
77 uint8_t LEADOUT_TRACK_NUMBER =
78 0xAA; // NOTE: for WinXP IOCTL_CDROM_READ_TOC_EX code, its 0xA2
79 #endif
80
81 /*
82 MCDI describes the CD TOC - actually talks about "a binary dump of the TOC".
83 So, a TOC is made up of: a 4 byte TOC header (2 bytes length of the entire TOC,
84 1 byte start session, 1 byte end session) an array of track entries, and
85 depending on the mode, of varying lengths. For audio CDs, TOC track entries are
86 mode1 (or for CD-R/RW mode5, but lets stick to mode1) a mode1 track entry is 11
87 bytes: 1byte session, 1 byte (packed control/address), 1byte NULL (unused TNO),
88 1 byte for tracknumber (expressed as the word POINT in mmc nomenclature),
89 3bytes relative start frametime, 1 byte NULL, 3 bytes duration timeframe
90
91 while "binary dump of the TOC" is there, its also modified so that the
92 timeframe listing in mm:ss::frames (3bytes) is converted to a 4byte LBA
93 timecode. Combining the first 4 bytes of the "binary dump of the TOC" with the
94 modifications of the 3byte(frame)->4byte(block), we arrive at MCDI as:
95
96 struct mcdi_track_entry {
97 uint8_t cd_toc_session;
98 uint8_t cd_toc_controladdress; //bitpacked uint4_t of control & address
99 uint8_t cd_toc_TNO = 0; //hardcoded to 0 for mode1 audio tracks in the TOC
100 uint8_t cd_toc_tracknumber; //this is the 1-99 tracknumber (listed in mmc-2
101 as POINT) uint32_t cd_frame_address; //converted from the 3byte mm:ss:frame
102 absolute duration
103 };
104 struct toc_header {
105 uint16_t toc_length;
106 uin8_t first_track;
107 uint8_t last_track;
108 };
109 struct mcdi_frame {
110 struct toc_header;
111 struct mcdi_track_entry[total_audio_tracks];
112 struct mcdi_track_entry lead_out;
113 };
114
115 The problem with including the TOC header is that it can't be used directly
116 because on the CD toc entries are 3byte msf address, but here they are 4byte
117 LBA. In any event this header should not have ever been included because the
118 length can be deduced from the frame length & tracks by dividing by 8. So, the
119 header length that MCDI refers to: is it for MSF or LBA addressing? Well, since
120 the rest of MCDI is LBA-based, lets say LBA - which means it needs to be
121 calculated. As it just so happens, then its the length of the frame. All that
122 needs to be added are the first & last tracks.
123
124 Unfortunately, this frame can't be used as a CD Identifier *AS IS* across
125 platforms. Because the leadout track is platform specific (a Linux leadout is
126 0xAA, MacOSX leadout is 0xA2), consideration of the leadout track would have to
127 be given by anything else using this frame.
128
129 */
130
131 ///////////////////////////////////////////////////////////////////////////
132 // Generating MCDI data from a CD TOC //
133 ///////////////////////////////////////////////////////////////////////////
134
DataControlField(uint8_t controlfield)135 uint8_t DataControlField(uint8_t controlfield) {
136 #if defined(__ppc__) || defined(__ppc64__)
137 if (controlfield & 0x04) { // data uninterrupted or increment OR reserved;
138 // this field is already bitpacked as controlfield
139 return 1;
140 }
141 #else
142 if (controlfield &
143 0x40) { // data uninterrupted or increment OR reserved; bitpacked already
144 return 1;
145 }
146 #endif
147 return 0;
148 }
149
DetermineCDType(CD_TOC_ * cdTOCdata)150 uint8_t DetermineCDType(CD_TOC_ *cdTOCdata) {
151 CD_TDesc *track_TOC_desc = cdTOCdata->track_description;
152 while (track_TOC_desc != NULL) {
153 if (track_TOC_desc->tracknumber >= 1 && track_TOC_desc->tracknumber <= 99 &&
154 !DataControlField(track_TOC_desc->controladdress)) {
155 return CDOBJECT_AUDIOCD;
156 }
157 track_TOC_desc = (CD_TDesc *)track_TOC_desc->next_description;
158 }
159 return CDOBJECT_DATACD;
160 }
161
LeadOutTrack(CD_TOC_ * cdTOCdata)162 CD_TDesc *LeadOutTrack(CD_TOC_ *cdTOCdata) {
163 CD_TDesc *track_TOC_desc = cdTOCdata->track_description;
164 while (track_TOC_desc != NULL) {
165 if (track_TOC_desc->tracknumber == LEADOUT_TRACK_NUMBER) {
166 return track_TOC_desc;
167 }
168 track_TOC_desc = (CD_TDesc *)track_TOC_desc->next_description;
169 }
170 return NULL;
171 }
172
FillSingleMCDIentry(CD_TDesc * atrack,char * mcdi_data_entry)173 uint8_t FillSingleMCDIentry(CD_TDesc *atrack, char *mcdi_data_entry) {
174 mcdi_data_entry[0] = atrack->session;
175 mcdi_data_entry[1] = atrack->controladdress;
176 mcdi_data_entry[2] = 0;
177 mcdi_data_entry[3] = atrack->tracknumber;
178 // LBA=(M*60+S)*75+F - 150 (table 374)
179 uint32_t frameduration =
180 ((((atrack->abs_minutes * 60) + atrack->abs_seconds) * 75) +
181 atrack->abs_frames) -
182 150;
183 UInt32_TO_String4(frameduration, mcdi_data_entry + 4);
184 return 8;
185 }
186
FormMCDIdata(char * mcdi_data)187 uint16_t FormMCDIdata(char *mcdi_data) {
188 uint16_t mcdi_len = 0;
189 uint8_t first_track = 0;
190 uint8_t last_track = 0;
191
192 CD_TDesc *track_TOC_desc = cdTOC->track_description;
193
194 if (cdTOC->track_description != NULL) {
195 mcdi_len += 4;
196
197 while (track_TOC_desc != NULL) {
198 if (track_TOC_desc->tracknumber >= 1 &&
199 track_TOC_desc->tracknumber <= 99 &&
200 !DataControlField(track_TOC_desc->controladdress)) {
201 mcdi_len += FillSingleMCDIentry(track_TOC_desc, mcdi_data + mcdi_len);
202 if (first_track == 0) {
203 first_track = track_TOC_desc->tracknumber;
204 }
205 last_track = track_TOC_desc->tracknumber;
206 }
207 track_TOC_desc = (CD_TDesc *)track_TOC_desc->next_description;
208 }
209 if (mcdi_len > 0) {
210 CD_TDesc *leadout = LeadOutTrack(cdTOC);
211 if (leadout != NULL) {
212 mcdi_len += FillSingleMCDIentry(leadout, mcdi_data + mcdi_len);
213 }
214 }
215 // backtrack & fill in the header
216 UInt16_TO_String2(mcdi_len, mcdi_data);
217 mcdi_data[2] = first_track;
218 mcdi_data[3] = last_track;
219 }
220 return mcdi_len;
221 }
222
223 /////////////////////////////////////////////////////////////////////////////
224 // Platform Specifics //
225 /////////////////////////////////////////////////////////////////////////////
226
227 #if defined(__linux__)
Linux_ReadCDTOC(int cd_fd)228 void Linux_ReadCDTOC(int cd_fd) {
229 cdrom_tochdr toc_header;
230 cdrom_tocentry toc_entry;
231 CD_TDesc *a_TOC_desc = NULL;
232 CD_TDesc *prev_desc = NULL;
233
234 if (ioctl(cd_fd, CDROMREADTOCHDR, &toc_header) == -1) {
235 fprintf(stderr,
236 "AtomicParsley error: there was an error reading the CD "
237 "Table of Contents header.\n");
238 return;
239 }
240 cdTOC = (CD_TOC_ *)calloc(1, sizeof(CD_TOC_));
241 cdTOC->track_description = NULL;
242
243 for (uint8_t i = toc_header.cdth_trk0; i <= toc_header.cdth_trk1 + 1; i++) {
244 memset(&toc_entry, 0, sizeof(toc_entry));
245 if (i == toc_header.cdth_trk1 + 1) {
246 toc_entry.cdte_track = CDROM_LEADOUT;
247 } else {
248 toc_entry.cdte_track = i;
249 }
250 toc_entry.cdte_format =
251 CDROM_MSF; // although it could just be easier to use CDROM_LBA
252
253 if (ioctl(cd_fd, CDROMREADTOCENTRY, &toc_entry) == -1) {
254 fprintf(stderr,
255 "AtomicParsley error: there was an error reading a "
256 "CD Table of Contents entry (linux cdrom).\n");
257 return;
258 }
259
260 if (cdTOC->track_description == NULL) {
261 cdTOC->track_description = (CD_TDesc *)calloc(1, (sizeof(CD_TDesc)));
262 a_TOC_desc = cdTOC->track_description;
263 prev_desc = a_TOC_desc;
264 } else {
265 prev_desc->next_description = (CD_TDesc *)calloc(1, (sizeof(CD_TDesc)));
266 a_TOC_desc = (CD_TDesc *)prev_desc->next_description;
267 prev_desc = a_TOC_desc;
268 }
269
270 a_TOC_desc->session = 1; // and for vanilla audio CDs it is session 1, but
271 // for multi-session...
272 #if defined(__ppc__) || defined(__ppc64__)
273 a_TOC_desc->controladdress =
274 (toc_entry.cdte_ctrl << 4) | toc_entry.cdte_adr;
275 #else
276 a_TOC_desc->controladdress =
277 (toc_entry.cdte_adr << 4) | toc_entry.cdte_ctrl;
278 #endif
279 a_TOC_desc->unused1 = 0;
280 a_TOC_desc->tracknumber = toc_entry.cdte_track;
281 a_TOC_desc->rel_minutes =
282 0; // is there anyway to even find this out on
283 // linux without playing the track? //cdmsf_min0
284 a_TOC_desc->rel_seconds = 0;
285 a_TOC_desc->rel_frames = 0;
286 a_TOC_desc->zero_space = 0;
287 a_TOC_desc->abs_minutes = toc_entry.cdte_addr.msf.minute;
288 a_TOC_desc->abs_seconds = toc_entry.cdte_addr.msf.second;
289 a_TOC_desc->abs_frames = toc_entry.cdte_addr.msf.frame;
290 }
291 return;
292 }
293
Linux_ioctlProbeTargetDrive(const char * id3args_drive,char * mcdi_data)294 uint16_t Linux_ioctlProbeTargetDrive(const char *id3args_drive,
295 char *mcdi_data) {
296 uint16_t mcdi_data_len = 0;
297 int cd_fd = 0;
298
299 cd_fd = open(id3args_drive, O_RDONLY | O_NONBLOCK);
300
301 if (cd_fd != -1) {
302 int cd_mode = ioctl(cd_fd, CDROM_DISC_STATUS);
303 if (cd_mode != CDS_AUDIO || cd_mode != CDS_MIXED) {
304 Linux_ReadCDTOC(cd_fd);
305 mcdi_data_len = FormMCDIdata(mcdi_data);
306 } else {
307 // scan for available devices
308 }
309 } else {
310 // scan for available devices
311 }
312
313 return mcdi_data_len;
314 }
315 #endif
316
317 #if defined(__APPLE__)
Extract_cdTOCrawdata(CFDataRef cdTOCdata,char * cdTOCrawdata)318 uint16_t Extract_cdTOCrawdata(CFDataRef cdTOCdata, char *cdTOCrawdata) {
319 CFRange cdrange;
320 CFIndex cdTOClen = CFDataGetLength(cdTOCdata);
321 cdTOCrawdata = (char *)calloc(1, sizeof(char) * cdTOClen + 1);
322 cdrange = CFRangeMake(0, cdTOClen + 1);
323 CFDataGetBytes(cdTOCdata, cdrange, (unsigned char *)cdTOCrawdata);
324
325 cdTOC = (CD_TOC_ *)calloc(1, sizeof(CD_TOC_));
326 cdTOC->toc_length = UInt16FromBigEndian(cdTOCrawdata);
327 cdTOC->first_session = cdTOCrawdata[2];
328 cdTOC->first_session = cdTOCrawdata[3];
329 cdTOC->track_description = NULL;
330
331 CD_TDesc *a_TOC_desc = NULL;
332 CD_TDesc *prev_desc = NULL;
333
334 uint16_t toc_offset = 0;
335 for (toc_offset = 4; toc_offset <= cdTOClen; toc_offset += 11) {
336 if (cdTOC->track_description == NULL) {
337 cdTOC->track_description = (CD_TDesc *)calloc(1, (sizeof(CD_TDesc)));
338 a_TOC_desc = cdTOC->track_description;
339 prev_desc = a_TOC_desc;
340 } else {
341 prev_desc->next_description = (CD_TDesc *)calloc(1, (sizeof(CD_TDesc)));
342 a_TOC_desc = (CD_TDesc *)prev_desc->next_description;
343 prev_desc = a_TOC_desc;
344 }
345 a_TOC_desc->session = cdTOCrawdata[toc_offset];
346 a_TOC_desc->controladdress = cdTOCrawdata[toc_offset + 1];
347 a_TOC_desc->unused1 = cdTOCrawdata[toc_offset + 2];
348 a_TOC_desc->tracknumber = cdTOCrawdata[toc_offset + 3];
349 a_TOC_desc->rel_minutes = cdTOCrawdata[toc_offset + 4];
350 a_TOC_desc->rel_seconds = cdTOCrawdata[toc_offset + 5];
351 a_TOC_desc->rel_frames = cdTOCrawdata[toc_offset + 6];
352 a_TOC_desc->zero_space = 0;
353 a_TOC_desc->abs_minutes = cdTOCrawdata[toc_offset + 8];
354 a_TOC_desc->abs_seconds = cdTOCrawdata[toc_offset + 9];
355 a_TOC_desc->abs_frames = cdTOCrawdata[toc_offset + 10];
356 }
357
358 return (uint16_t)cdTOClen;
359 }
360
OSX_ReadCDTOC(io_object_t cdobject)361 void OSX_ReadCDTOC(io_object_t cdobject) {
362 CFMutableDictionaryRef cd_props = 0;
363 CFDataRef cdTOCdata = NULL;
364 char *cdTOCrawdata = NULL;
365
366 if (IORegistryEntryCreateCFProperties(
367 cdobject, &cd_props, kCFAllocatorDefault, kNilOptions) !=
368 kIOReturnSuccess)
369 return;
370
371 cdTOCdata =
372 (CFDataRef)CFDictionaryGetValue(cd_props, CFSTR(kIOCDMediaTOCKey));
373 if (cdTOCdata != NULL) {
374 Extract_cdTOCrawdata(cdTOCdata, cdTOCrawdata);
375 }
376 CFRelease(cd_props);
377 cd_props = NULL;
378 return;
379 }
380
OSX_ScanForCDDrive()381 void OSX_ScanForCDDrive() {
382 io_iterator_t drive_iter = MACH_PORT_NULL;
383 io_object_t driveobject = MACH_PORT_NULL;
384 CFTypeRef drive_path = NULL;
385 char drive_path_str[20];
386 if (IOServiceGetMatchingServices(kIOMasterPortDefault,
387 IOServiceMatching(kIOCDMediaClass),
388 &drive_iter) !=
389 kIOReturnSuccess) { // create the iterator
390 fprintf(stdout, "No device capable of reading cd media present\n");
391 }
392
393 driveobject = IOIteratorNext(drive_iter);
394 while (driveobject != MACH_PORT_NULL) {
395 drive_path = IORegistryEntryCreateCFProperty(
396 driveobject, CFSTR(kIOBSDNameKey), kCFAllocatorDefault, 0);
397 if (drive_path != NULL) {
398 CFStringGetCString((CFStringRef)drive_path,
399 (char *)&drive_path_str,
400 20,
401 kCFStringEncodingASCII);
402 fprintf(stdout, "Device '%s' contains cd media\n", drive_path_str);
403 OSX_ReadCDTOC(driveobject);
404 if (cdTOC != NULL) {
405 uint8_t cdType = DetermineCDType(cdTOC);
406 if (cdType == CDOBJECT_AUDIOCD) {
407 fprintf(stdout,
408 "Good news, device '%s' is an Audio CD and can be used for "
409 "'MCDI' setting\n",
410 drive_path_str);
411 } else {
412 fprintf(stdout, "Tragically, it was a data CD.\n");
413 }
414 free(cdTOC); // the other malloced members should be freed also
415 }
416 }
417 IOObjectRelease(driveobject);
418 driveobject = IOIteratorNext(drive_iter);
419 }
420
421 if (drive_path_str[0] == (uint8_t)0x00) {
422 fprintf(stdout, "No CD media was found in any device\n");
423 }
424 IOObjectRelease(drive_iter);
425 drive_iter = MACH_PORT_NULL;
426 exit(0);
427 }
428
OSX_ProbeTargetDrive(const char * id3args_drive,char * mcdi_data)429 uint16_t OSX_ProbeTargetDrive(const char *id3args_drive, char *mcdi_data) {
430 uint16_t mcdi_data_len = 0;
431 io_object_t cdobject = MACH_PORT_NULL;
432
433 if (strncmp(id3args_drive, "disk", 4) != 0) {
434 OSX_ScanForCDDrive();
435 exit(0);
436 }
437 cdobject = IOServiceGetMatchingService(
438 kIOMasterPortDefault,
439 IOBSDNameMatching(kIOMasterPortDefault, 0, id3args_drive));
440
441 if (cdobject == MACH_PORT_NULL) {
442 fprintf(stdout,
443 "No device found at %s; searching for possible drives...\n",
444 id3args_drive);
445 OSX_ScanForCDDrive();
446
447 } else if (IOObjectConformsTo(cdobject, kIOCDMediaClass) == false) {
448 fprintf(stdout, "No cd present in drive at %s\n", id3args_drive);
449 IOObjectRelease(cdobject);
450 cdobject = MACH_PORT_NULL;
451 OSX_ScanForCDDrive();
452 } else {
453 // we now have a cd object
454 OSX_ReadCDTOC(cdobject);
455 if (cdTOC != NULL) {
456 uint8_t cdType = DetermineCDType(cdTOC);
457 if (cdType == CDOBJECT_AUDIOCD) {
458 mcdi_data_len = FormMCDIdata(mcdi_data);
459 }
460 }
461 }
462
463 IOObjectRelease(cdobject);
464 cdobject = MACH_PORT_NULL;
465 return mcdi_data_len;
466 }
467
468 #endif
469
470 #if defined(_WIN32)
Windows_ioctlReadCDTOC(HANDLE cdrom_device)471 void Windows_ioctlReadCDTOC(HANDLE cdrom_device) {
472 DWORD bytes_returned;
473 CDROM_TOC win_cdrom_toc;
474
475 // WARNING: "This IOCTL is obsolete beginning with the Microsoft Windows
476 // Vista. Do not use this IOCTL to develop drivers in Microsoft Windows
477 // Vista."
478 if (DeviceIoControl(cdrom_device,
479 IOCTL_CDROM_READ_TOC,
480 NULL,
481 0,
482 &win_cdrom_toc,
483 sizeof(CDROM_TOC),
484 &bytes_returned,
485 NULL) == 0) {
486 fprintf(stderr,
487 "AtomicParsley error: there was an error reading the CD "
488 "Table of Contents header (win32).\n");
489 return;
490 }
491
492 cdTOC = (CD_TOC_ *)calloc(1, sizeof(CD_TOC_));
493 cdTOC->toc_length = ((win_cdrom_toc.Length[0] & 0xFF) << 8) |
494 (win_cdrom_toc.Length[1] & 0xFF);
495 // cdTOC->first_session = 0; //seems windows doesn't store session info with
496 // IOCTL_CDROM_READ_TOC, all tracks from all sessions are available
497 // cdTOC->last_session = 0; //not used anyway; IOCTL_CDROM_READ_TOC_EX could
498 // be used to get session information, but its only available on XP
499 //...............and interestingly enough in XP, IOCTL_CDROM_TOC_EX returns
500 // the leadout track as 0xA2, which makes a Win2k MCDI & a WinXP MCDI
501 // different (by 1 byte)
502 cdTOC->track_description = NULL;
503
504 CD_TDesc *a_TOC_desc = NULL;
505 CD_TDesc *prev_desc = NULL;
506
507 for (uint8_t i = win_cdrom_toc.FirstTrack; i <= win_cdrom_toc.LastTrack + 1;
508 i++) {
509 TRACK_DATA *thisTrackData = &(win_cdrom_toc.TrackData[i - 1]);
510
511 if (cdTOC->track_description == NULL) {
512 cdTOC->track_description = (CD_TDesc *)calloc(1, (sizeof(CD_TDesc)));
513 a_TOC_desc = cdTOC->track_description;
514 prev_desc = a_TOC_desc;
515 } else {
516 prev_desc->next_description = (CD_TDesc *)calloc(1, (sizeof(CD_TDesc)));
517 a_TOC_desc = (CD_TDesc *)prev_desc->next_description;
518 prev_desc = a_TOC_desc;
519 }
520
521 a_TOC_desc->session = 1; // and for vanilla audio CDs it is session 1, but
522 // for multi-session...
523 #if defined(__ppc__) || defined(__ppc64__)
524 a_TOC_desc->controladdress =
525 (thisTrackData->Control << 4) | thisTrackData->Adr;
526 #else
527 a_TOC_desc->controladdress =
528 (thisTrackData->Adr << 4) | thisTrackData->Control;
529 #endif
530 a_TOC_desc->unused1 = 0;
531 a_TOC_desc->tracknumber = thisTrackData->TrackNumber;
532 a_TOC_desc->rel_minutes =
533 0; // didn't look too much into finding this since it is unused
534 a_TOC_desc->rel_seconds = 0;
535 a_TOC_desc->rel_frames = 0;
536 a_TOC_desc->zero_space = 0;
537 a_TOC_desc->abs_minutes = thisTrackData->Address[1];
538 a_TOC_desc->abs_seconds = thisTrackData->Address[2];
539 a_TOC_desc->abs_frames = thisTrackData->Address[3];
540 }
541
542 return;
543 }
544
Windows_ioctlProbeTargetDrive(const char * id3args_drive,char * mcdi_data)545 uint16_t Windows_ioctlProbeTargetDrive(const char *id3args_drive,
546 char *mcdi_data) {
547 uint16_t mcdi_data_len = 0;
548 char cd_device_path[16];
549
550 memset(cd_device_path, 0, 16);
551 sprintf(cd_device_path, "\\\\.\\%s:", id3args_drive);
552
553 HANDLE cdrom_device = APar_OpenFileWin32(cd_device_path,
554 GENERIC_READ,
555 FILE_SHARE_READ,
556 NULL,
557 OPEN_EXISTING,
558 FILE_ATTRIBUTE_NORMAL,
559 NULL);
560 if (cdrom_device != INVALID_HANDLE_VALUE) {
561 Windows_ioctlReadCDTOC(cdrom_device);
562 if (cdTOC != NULL) {
563 uint8_t cdType = DetermineCDType(cdTOC);
564 if (cdType == CDOBJECT_AUDIOCD) {
565 mcdi_data_len = FormMCDIdata(mcdi_data);
566 }
567 }
568 CloseHandle(cdrom_device);
569 }
570
571 return mcdi_data_len;
572 }
573 #endif
574
575 ////////////////////////////////////////////////////////////////////////////
576 // CD TOC Entry Area //
577 ////////////////////////////////////////////////////////////////////////////
578
GenerateMCDIfromCD(const char * drive,char * dest_buffer)579 uint16_t GenerateMCDIfromCD(const char *drive, char *dest_buffer) {
580 uint16_t mcdi_bytes = 0;
581 #if defined(__APPLE__)
582 mcdi_bytes = OSX_ProbeTargetDrive(drive, dest_buffer);
583 #elif defined(__linux__)
584 mcdi_bytes = Linux_ioctlProbeTargetDrive(drive, dest_buffer);
585 #elif defined(_WIN32)
586 mcdi_bytes = Windows_ioctlProbeTargetDrive(drive, dest_buffer);
587 #endif
588 return mcdi_bytes;
589 }
590