1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
3 * Main file for CD-ROM support
5 * Copyright 1994 Martin Ayotte
6 * Copyright 1999 Eric Pouech
7 * Copyright 2000 Andreas Mohr
15 #include <sys/ioctl.h>
18 #include "debugtools.h"
21 DEFAULT_DEBUG_CHANNEL(cdrom
);
23 #define MAX_CDAUDIO_TRACKS 256
25 /**************************************************************************
26 * CDROM_Open [internal]
29 * or -1 (figure it out)
31 int CDROM_Open(WINE_CDAUDIO
* wcda
, int drive
)
39 for (i
=0; i
< MAX_DOS_DRIVES
; i
++)
40 if (DRIVE_GetType(i
) == TYPE_CDROM
)
52 WARN("No CD-ROM #%d found !\n", drive
);
55 if ((dev
= DRIVE_GetDevice(drive
)) == NULL
)
57 WARN("No device entry for CD-ROM #%d (drive %c:) found !\n",
62 wcda
->unixdev
= open(dev
, O_RDONLY
| O_NONBLOCK
, 0);
63 if (wcda
->unixdev
== -1) {
64 WARN("can't open '%s'!. %s\n", dev
, strerror(errno
));
67 wcda
->cdaMode
= WINE_CDA_OPEN
; /* to force reading tracks info */
70 wcda
->dwFirstFrame
= 0;
71 wcda
->dwLastFrame
= 0;
72 wcda
->lpdwTrackLen
= NULL
;
73 wcda
->lpdwTrackPos
= NULL
;
74 wcda
->lpbTrackFlags
= NULL
;
78 /**************************************************************************
79 * CDROM_GetMediaType [internal]
81 int CDROM_GetMediaType(WINE_CDAUDIO
* wcda
)
84 return ioctl(wcda
->unixdev
, CDROM_DISC_STATUS
);
90 /**************************************************************************
91 * CDROM_Close [internal]
93 int CDROM_Close(WINE_CDAUDIO
* wcda
)
95 #if defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__)
96 if (wcda
->lpdwTrackLen
!= NULL
) free(wcda
->lpdwTrackLen
);
97 if (wcda
->lpdwTrackPos
!= NULL
) free(wcda
->lpdwTrackPos
);
98 if (wcda
->lpbTrackFlags
!= NULL
) free(wcda
->lpbTrackFlags
);
106 /**************************************************************************
107 * CDROM_Get_UPC [internal]
109 * upc has to be 14 bytes long
111 int CDROM_Get_UPC(WINE_CDAUDIO
* wcda
, LPSTR upc
)
114 struct cdrom_mcn mcn
;
115 int status
= ioctl(wcda
->unixdev
, CDROM_GET_MCN
, &mcn
);
118 ERR("ioctl() failed with code %d\n",status
);
121 strcpy(upc
, mcn
.medium_catalog_number
);
128 /**************************************************************************
129 * CDROM_Audio_GetNumberOfTracks [internal]
131 UINT16
CDROM_Audio_GetNumberOfTracks(WINE_CDAUDIO
* wcda
)
133 #if defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__)
135 struct cdrom_tochdr hdr
;
137 struct ioc_toc_header hdr
;
140 if (wcda
->nTracks
== 0) {
142 if (ioctl(wcda
->unixdev
, CDROMREADTOCHDR
, &hdr
))
144 if (ioctl(wcda
->unixdev
, CDIOREADTOCHEADER
, &hdr
))
147 WARN("(%p) -- Error occurred (%d)!\n", wcda
, errno
);
151 wcda
->nFirstTrack
= hdr
.cdth_trk0
;
152 wcda
->nLastTrack
= hdr
.cdth_trk1
;
154 wcda
->nFirstTrack
= hdr
.starting_track
;
155 wcda
->nLastTrack
= hdr
.ending_track
;
157 wcda
->nTracks
= wcda
->nLastTrack
- wcda
->nFirstTrack
+ 1;
159 return wcda
->nTracks
;
165 /**************************************************************************
166 * CDROM_Audio_GetTracksInfo [internal]
168 BOOL
CDROM_Audio_GetTracksInfo(WINE_CDAUDIO
* wcda
)
170 #if defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__)
172 int start
, last_start
= 0;
173 int total_length
= 0;
175 struct cdrom_tocentry entry
;
177 struct ioc_read_toc_entry entry
;
178 struct cd_toc_entry toc_buffer
;
181 if (wcda
->nTracks
== 0) {
182 if (CDROM_Audio_GetNumberOfTracks(wcda
) == (WORD
)-1) return FALSE
;
184 TRACE("nTracks=%u\n", wcda
->nTracks
);
186 if (wcda
->lpdwTrackLen
!= NULL
)
187 free(wcda
->lpdwTrackLen
);
188 wcda
->lpdwTrackLen
= (LPDWORD
)malloc((wcda
->nTracks
+ 1) * sizeof(DWORD
));
189 if (wcda
->lpdwTrackPos
!= NULL
)
190 free(wcda
->lpdwTrackPos
);
191 wcda
->lpdwTrackPos
= (LPDWORD
)malloc((wcda
->nTracks
+ 1) * sizeof(DWORD
));
192 if (wcda
->lpbTrackFlags
!= NULL
)
193 free(wcda
->lpbTrackFlags
);
194 wcda
->lpbTrackFlags
= (LPBYTE
)malloc((wcda
->nTracks
+ 1) * sizeof(BYTE
));
195 if (wcda
->lpdwTrackLen
== NULL
|| wcda
->lpdwTrackPos
== NULL
||
196 wcda
->lpbTrackFlags
== NULL
) {
197 WARN("error allocating track table !\n");
200 memset(wcda
->lpdwTrackLen
, 0, (wcda
->nTracks
+ 1) * sizeof(DWORD
));
201 memset(wcda
->lpdwTrackPos
, 0, (wcda
->nTracks
+ 1) * sizeof(DWORD
));
202 memset(wcda
->lpbTrackFlags
, 0, (wcda
->nTracks
+ 1) * sizeof(BYTE
));
203 for (i
= 0; i
<= wcda
->nTracks
; i
++) {
204 if (i
== wcda
->nTracks
)
206 entry
.cdte_track
= CDROM_LEADOUT
;
209 entry
.starting_track
= LEADOUT
; /* FIXME */
213 entry
.cdte_track
= i
+ 1;
215 entry
.starting_track
= i
+ 1;
218 entry
.cdte_format
= CDROM_MSF
;
220 bzero((char *)&toc_buffer
, sizeof(toc_buffer
));
221 entry
.address_format
= CD_MSF_FORMAT
;
222 entry
.data_len
= sizeof(toc_buffer
);
223 entry
.data
= &toc_buffer
;
226 if (ioctl(wcda
->unixdev
, CDROMREADTOCENTRY
, &entry
))
228 if (ioctl(wcda
->unixdev
, CDIOREADTOCENTRYS
, &entry
))
231 WARN("error read entry (%s)\n", strerror(errno
));
232 /* update status according to new status */
233 CDROM_Audio_GetCDStatus(wcda
);
238 start
= CDFRAMES_PERSEC
* (SECONDS_PERMIN
*
239 entry
.cdte_addr
.msf
.minute
+ entry
.cdte_addr
.msf
.second
) +
240 entry
.cdte_addr
.msf
.frame
;
242 start
= CDFRAMES_PERSEC
* (SECONDS_PERMIN
*
243 toc_buffer
.addr
.msf
.minute
+ toc_buffer
.addr
.msf
.second
) +
244 toc_buffer
.addr
.msf
.frame
;
248 wcda
->dwFirstFrame
= start
;
249 TRACE("dwFirstOffset=%u\n", start
);
251 length
= start
- last_start
;
253 start
= last_start
- length
;
254 total_length
+= length
;
255 wcda
->lpdwTrackLen
[i
- 1] = length
;
256 wcda
->lpdwTrackPos
[i
- 1] = start
;
257 TRACE("track #%u start=%u len=%u\n", i
, start
, length
);
260 wcda
->lpbTrackFlags
[i
] =
261 (entry
.cdte_adr
<< 4) | (entry
.cdte_ctrl
& 0x0f);
263 wcda
->lpbTrackFlags
[i
] =
264 (toc_buffer
.addr_type
<< 4) | (toc_buffer
.control
& 0x0f);
266 TRACE("track #%u flags=%02x\n", i
+ 1, wcda
->lpbTrackFlags
[i
]);
268 wcda
->dwLastFrame
= last_start
;
269 TRACE("total_len=%u\n", total_length
);
276 /**************************************************************************
277 * CDROM_Audio_GetCDStatus [internal]
279 BOOL
CDROM_Audio_GetCDStatus(WINE_CDAUDIO
* wcda
)
281 #if defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__)
282 int oldmode
= wcda
->cdaMode
;
284 wcda
->sc
.cdsc_format
= CDROM_MSF
;
286 struct ioc_read_subchannel read_sc
;
288 read_sc
.address_format
= CD_MSF_FORMAT
;
289 read_sc
.data_format
= CD_CURRENT_POSITION
;
291 read_sc
.data_len
= sizeof(wcda
->sc
);
292 read_sc
.data
= (struct cd_sub_channel_info
*)&wcda
->sc
;
295 if (ioctl(wcda
->unixdev
, CDROMSUBCHNL
, &wcda
->sc
))
297 if (ioctl(wcda
->unixdev
, CDIOCREADSUBCHANNEL
, &read_sc
))
300 TRACE("opened or no_media (%s)!\n", strerror(errno
));
301 wcda
->cdaMode
= WINE_CDA_OPEN
; /* was NOT_READY */
306 wcda
->sc
.cdsc_audiostatus
308 wcda
->sc
.header
.audio_status
312 case CDROM_AUDIO_INVALID
:
314 case CD_AS_AUDIO_INVALID
:
316 WARN("device doesn't support status.\n");
317 wcda
->cdaMode
= WINE_CDA_DONTKNOW
;
320 case CDROM_AUDIO_NO_STATUS
:
322 case CD_AS_NO_STATUS
:
324 wcda
->cdaMode
= WINE_CDA_STOP
;
325 TRACE("WINE_CDA_STOP !\n");
328 case CDROM_AUDIO_PLAY
:
330 case CD_AS_PLAY_IN_PROGRESS
:
332 wcda
->cdaMode
= WINE_CDA_PLAY
;
335 case CDROM_AUDIO_PAUSED
:
337 case CD_AS_PLAY_PAUSED
:
339 wcda
->cdaMode
= WINE_CDA_PAUSE
;
340 TRACE("WINE_CDA_PAUSE !\n");
344 TRACE("status=%02X !\n",
345 wcda
->sc
.cdsc_audiostatus
);
347 TRACE("status=%02X !\n",
348 wcda
->sc
.header
.audio_status
);
352 wcda
->nCurTrack
= wcda
->sc
.cdsc_trk
;
354 CDFRAMES_PERMIN
* wcda
->sc
.cdsc_absaddr
.msf
.minute
+
355 CDFRAMES_PERSEC
* wcda
->sc
.cdsc_absaddr
.msf
.second
+
356 wcda
->sc
.cdsc_absaddr
.msf
.frame
;
358 wcda
->nCurTrack
= wcda
->sc
.what
.position
.track_number
;
360 CDFRAMES_PERMIN
* wcda
->sc
.what
.position
.absaddr
.msf
.minute
+
361 CDFRAMES_PERSEC
* wcda
->sc
.what
.position
.absaddr
.msf
.second
+
362 wcda
->sc
.what
.position
.absaddr
.msf
.frame
;
365 TRACE("%02u-%02u:%02u:%02u \n",
367 wcda
->sc
.cdsc_absaddr
.msf
.minute
,
368 wcda
->sc
.cdsc_absaddr
.msf
.second
,
369 wcda
->sc
.cdsc_absaddr
.msf
.frame
);
371 TRACE("%02u-%02u:%02u:%02u \n",
372 wcda
->sc
.what
.position
.track_number
,
373 wcda
->sc
.what
.position
.absaddr
.msf
.minute
,
374 wcda
->sc
.what
.position
.absaddr
.msf
.second
,
375 wcda
->sc
.what
.position
.absaddr
.msf
.frame
);
378 if (oldmode
!= wcda
->cdaMode
&& oldmode
== WINE_CDA_OPEN
) {
379 if (!CDROM_Audio_GetTracksInfo(wcda
)) {
380 WARN("error updating TracksInfo !\n");
390 /**************************************************************************
391 * CDROM_Audio_Play [internal]
393 int CDROM_Audio_Play(WINE_CDAUDIO
* wcda
, DWORD start
, DWORD end
)
395 #if defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__)
397 struct cdrom_msf msf
;
399 struct ioc_play_msf msf
;
403 msf
.cdmsf_min0
= start
/ CDFRAMES_PERMIN
;
404 msf
.cdmsf_sec0
= (start
% CDFRAMES_PERMIN
) / CDFRAMES_PERSEC
;
405 msf
.cdmsf_frame0
= start
% CDFRAMES_PERSEC
;
406 msf
.cdmsf_min1
= end
/ CDFRAMES_PERMIN
;
407 msf
.cdmsf_sec1
= (end
% CDFRAMES_PERMIN
) / CDFRAMES_PERSEC
;
408 msf
.cdmsf_frame1
= end
% CDFRAMES_PERSEC
;
410 msf
.start_m
= start
/ CDFRAMES_PERMIN
;
411 msf
.start_s
= (start
% CDFRAMES_PERMIN
) / CDFRAMES_PERSEC
;
412 msf
.start_f
= start
% CDFRAMES_PERSEC
;
413 msf
.end_m
= end
/ CDFRAMES_PERMIN
;
414 msf
.end_s
= (end
% CDFRAMES_PERMIN
) / CDFRAMES_PERSEC
;
415 msf
.end_f
= end
% CDFRAMES_PERSEC
;
418 if (ioctl(wcda
->unixdev
, CDROMSTART
))
420 if (ioctl(wcda
->unixdev
, CDIOCSTART
, NULL
))
423 WARN("motor doesn't start !\n");
427 if (ioctl(wcda
->unixdev
, CDROMPLAYMSF
, &msf
))
429 if (ioctl(wcda
->unixdev
, CDIOCPLAYMSF
, &msf
))
432 WARN("device doesn't play !\n");
436 TRACE("msf = %d:%d:%d %d:%d:%d\n",
437 msf
.cdmsf_min0
, msf
.cdmsf_sec0
, msf
.cdmsf_frame0
,
438 msf
.cdmsf_min1
, msf
.cdmsf_sec1
, msf
.cdmsf_frame1
);
440 TRACE("msf = %d:%d:%d %d:%d:%d\n",
441 msf
.start_m
, msf
.start_s
, msf
.start_f
,
442 msf
.end_m
, msf
.end_s
, msf
.end_f
);
450 /**************************************************************************
451 * CDROM_Audio_Stop [internal]
453 int CDROM_Audio_Stop(WINE_CDAUDIO
* wcda
)
455 #if defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__)
458 ret
= ioctl(wcda
->unixdev
, CDROMSTOP
);
460 ret
= ioctl(wcda
->unixdev
, CDIOCSTOP
, NULL
);
468 /**************************************************************************
469 * CDROM_Audio_Pause [internal]
471 int CDROM_Audio_Pause(WINE_CDAUDIO
* wcda
, int pauseOn
)
473 #if defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__)
476 ret
= ioctl(wcda
->unixdev
, pauseOn
? CDROMPAUSE
: CDROMRESUME
);
478 ret
= ioctl(wcda
->unixdev
, pauseOn
? CDIOCPAUSE
: CDIOCRESUME
, NULL
);
486 /**************************************************************************
487 * CDROM_Audio_Seek [internal]
489 int CDROM_Audio_Seek(WINE_CDAUDIO
* wcda
, DWORD at
)
491 #if defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__)
494 struct cdrom_msf0 msf
;
495 msf
.minute
= at
/ CDFRAMES_PERMIN
;
496 msf
.second
= (at
% CDFRAMES_PERMIN
) / CDFRAMES_PERSEC
;
497 msf
.frame
= at
% CDFRAMES_PERSEC
;
499 ret
= ioctl(wcda
->unixdev
, CDROMSEEK
, &msf
);
501 /* FIXME: the current end for play is lost
502 * use end of CD ROM instead
504 FIXME("Could a BSD expert implement the seek function ?\n");
505 CDROM_Audio_Play(wcda
, at
, wcda
->lpdwTrackPos
[wcda
->nTracks
] + wcda
->lpdwTrackLen
[wcda
->nTracks
]);
514 /**************************************************************************
515 * CDROM_SetDoor [internal]
517 int CDROM_SetDoor(WINE_CDAUDIO
* wcda
, int open
)
519 #if defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__)
523 ret
= ioctl(wcda
->unixdev
, CDROMEJECT
);
525 ret
= ioctl(wcda
->unixdev
, CDROMEJECT
, 1);
528 ret
= (ioctl(wcda
->unixdev
, CDIOCALLOW
, NULL
)) ||
529 (ioctl(wcda
->unixdev
, open
? CDIOCEJECT
: CDIOCCLOSE
, NULL
)) ||
530 (ioctl(wcda
->unixdev
, CDIOCPREVENT
, NULL
));
539 /**************************************************************************
540 * CDROM_Reset [internal]
542 int CDROM_Reset(WINE_CDAUDIO
* wcda
)
544 #if defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__)
547 ret
= ioctl(wcda
->unixdev
, CDROMRESET
);
549 ret
= ioctl(wcda
->unixdev
, CDIOCRESET
, NULL
);
557 WORD
CDROM_Data_FindBestVoldesc(int fd
)
559 BYTE cur_vd_type
, max_vd_type
= 0;
560 unsigned int offs
, best_offs
= 0;
562 for (offs
=0x8000; offs
<= 0x9800; offs
+= 0x800)
564 lseek(fd
, offs
, SEEK_SET
);
565 read(fd
, &cur_vd_type
, 1);
566 if (cur_vd_type
== 0xff) /* voldesc set terminator */
568 if (cur_vd_type
> max_vd_type
)
570 max_vd_type
= cur_vd_type
;
577 /**************************************************************************
578 * CDROM_Audio_GetSerial [internal]
580 DWORD
CDROM_Audio_GetSerial(WINE_CDAUDIO
* wcda
)
582 unsigned long serial
= 0;
585 WORD wMinutes
, wSeconds
, wFrames
;
587 for (i
= 0; i
< wcda
->nTracks
; i
++) {
588 dwFrame
= wcda
->lpdwTrackPos
[i
];
589 wMinutes
= dwFrame
/ CDFRAMES_PERMIN
;
590 wSeconds
= (dwFrame
- CDFRAMES_PERMIN
* wMinutes
) / CDFRAMES_PERSEC
;
591 wFrames
= dwFrame
- CDFRAMES_PERMIN
* wMinutes
- CDFRAMES_PERSEC
* wSeconds
;
592 msf
= CDROM_MAKE_MSF(wMinutes
, wSeconds
, wFrames
);
594 serial
+= (CDROM_MSF_MINUTE(msf
) << 16) +
595 (CDROM_MSF_SECOND(msf
) << 8) +
596 (CDROM_MSF_FRAME(msf
));
601 /**************************************************************************
602 * CDROM_Data_GetSerial [internal]
604 DWORD
CDROM_Data_GetSerial(WINE_CDAUDIO
* wcda
)
606 WORD offs
= CDROM_Data_FindBestVoldesc(wcda
->unixdev
);
611 BYTE b0
= 0, b1
= 1, b2
= 2, b3
= 3;
620 lseek(wcda
->unixdev
,offs
,SEEK_SET
);
621 read(wcda
->unixdev
,buf
,2048);
623 * OK, another braindead one... argh. Just believe it.
624 * Me$$ysoft chose to reverse the serial number in NT4/W2K.
625 * It's true and nobody will ever be able to change it.
627 ovi
.dwOSVersionInfoSize
= sizeof(OSVERSIONINFOA
);
629 if ((ovi
.dwPlatformId
== VER_PLATFORM_WIN32_NT
)
630 && (ovi
.dwMajorVersion
>= 4))
632 b0
= 3; b1
= 2; b2
= 1; b3
= 0;
634 for(i
=0; i
<2048; i
+=4)
636 /* DON'T optimize this into DWORD !! (breaks overflow) */
637 serial
.p
[b0
] += buf
[i
+b0
];
638 serial
.p
[b1
] += buf
[i
+b1
];
639 serial
.p
[b2
] += buf
[i
+b2
];
640 serial
.p
[b3
] += buf
[i
+b3
];
646 /**************************************************************************
647 * CDROM_GetSerial [internal]
649 DWORD
CDROM_GetSerial(int drive
)
654 /* EXPIRES 01.01.2001 */
655 FIXME("CD-ROM serial number calculation might fail.\n");
656 FIXME("Please test with as many exotic CDs as possible !\n");
658 if (!(CDROM_Open(&wcda
, drive
)))
660 int media
= CDROM_GetMediaType(&wcda
);
663 if (media
== CDS_AUDIO
)
665 if (!(CDROM_Audio_GetCDStatus(&wcda
))) {
666 ERR("couldn't get CD status !\n");
670 serial
= CDROM_Audio_GetSerial(&wcda
);
673 if ((media
> CDS_AUDIO
)
674 || (media
== -1) /* ioctl() error: ISO9660 image file given ? */
676 /* hopefully a data CD */
677 serial
= CDROM_Data_GetSerial(&wcda
);
679 WARN("Strange CD type (%d) or empty ?\n", media
);
681 p
= (media
== CDS_AUDIO
) ? "Audio " :
682 (media
> CDS_AUDIO
) ? "Data " : "";
684 FIXME("%sCD serial number is %04x-%04x.\n",
685 p
, HIWORD(serial
), LOWORD(serial
));
687 ERR("couldn't get %sCD serial !\n", p
);
693 static const char empty_label
[] = " ";
695 /**************************************************************************
696 * CDROM_Data_GetLabel [internal]
698 DWORD
CDROM_Data_GetLabel(WINE_CDAUDIO
* wcda
, char *label
)
700 #define LABEL_LEN 32+1
701 WORD offs
= CDROM_Data_FindBestVoldesc(wcda
->unixdev
);
702 WCHAR label_read
[LABEL_LEN
]; /* Unicode possible, too */
703 DWORD unicode_id
= 0;
707 if ((lseek(wcda
->unixdev
, offs
+0x58, SEEK_SET
) == offs
+0x58)
708 && (read(wcda
->unixdev
, &unicode_id
, 3) == 3))
710 int ver
= (unicode_id
& 0xff0000) >> 16;
712 if ((lseek(wcda
->unixdev
, offs
+0x28, SEEK_SET
) != offs
+0x28)
713 || (read(wcda
->unixdev
, &label_read
, LABEL_LEN
) != LABEL_LEN
))
716 if ((LOWORD(unicode_id
) == 0x2f25) /* Unicode ID */
717 && ((ver
== 0x40) || (ver
== 0x43) || (ver
== 0x45)))
718 { /* yippee, unicode */
721 for (i
=0; i
<LABEL_LEN
;i
++)
722 { /* Motorola -> Intel Unicode conversion :-\ */
724 label_read
[i
] = (ch
<< 8) | (ch
>> 8);
726 lstrcpynWtoA(label
, label_read
, 11);
730 strncpy(label
, (LPSTR
)label_read
, 11);
737 ERR("error reading label !\n");
738 strcpy(label
, empty_label
);
742 /**************************************************************************
743 * CDROM_GetLabel [internal]
745 DWORD
CDROM_GetLabel(int drive
, char *label
)
750 if (!(CDROM_Open(&wcda
, drive
)))
752 int media
= CDROM_GetMediaType(&wcda
);
755 if (media
== CDS_AUDIO
)
757 strcpy(label
, "Audio CD ");
760 if (media
== CDS_NO_INFO
)
762 strcpy(label
, empty_label
);
765 if ((media
> CDS_AUDIO
)
766 || (media
== -1) /* ioctl() error: ISO9660 image file given ? */
768 /* hopefully a data CD */
769 CDROM_Data_GetLabel(&wcda
, label
);
772 WARN("Strange CD type (%d) or empty ?\n", media
);
773 strcpy(label
, empty_label
);
777 p
= (media
== CDS_AUDIO
) ? "Audio " :
778 (media
> CDS_AUDIO
) ? "Data " : "";
779 TRACE("%sCD label is '%s'.\n",