Merged msacm and msacm32 dlls.
[wine] / misc / cdrom.c
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
2 /*
3  * Main file for CD-ROM support
4  *
5  * Copyright 1994 Martin Ayotte
6  * Copyright 1999 Eric Pouech
7  * Copyright 2000 Andreas Mohr
8  */
9
10 #include "config.h"
11
12 #include <errno.h>
13 #include <string.h>
14 #include <fcntl.h>
15 #include <sys/ioctl.h>
16 #include "cdrom.h"
17 #include "drive.h"
18 #include "debugtools.h"
19
20 DEFAULT_DEBUG_CHANNEL(cdrom);
21
22 #define MAX_CDAUDIO_TRACKS      256
23
24 /**************************************************************************
25  *                              CDROM_Open                      [internal]
26  *
27  * drive = 0, 1, ...
28  *      or -1 (figure it out)
29  */
30 int     CDROM_Open(WINE_CDAUDIO* wcda, int drive)
31 {
32     int i;
33     BOOL avail = FALSE;
34     const char *dev;
35
36     if (drive == -1)
37     {
38         for (i=0; i < MAX_DOS_DRIVES; i++)
39             if (DRIVE_GetType(i) == TYPE_CDROM)
40             {
41                 drive = i;
42                 avail = TRUE;
43                 break;
44             }
45     }
46     else
47         avail = TRUE;
48     
49     if (avail == FALSE)
50     {
51         WARN("No CD-ROM #%d found !\n", drive);
52         return -1;
53     }
54     if ((dev = DRIVE_GetDevice(drive)) == NULL)
55 {
56         WARN("No device entry for CD-ROM #%d (drive %c:) found !\n",
57                 drive, 'A' + drive);
58         return -1;
59     }
60
61     wcda->unixdev = open(dev, O_RDONLY | O_NONBLOCK, 0);
62     if (wcda->unixdev == -1) {
63         WARN("can't open '%s'!. %s\n", dev, strerror(errno));
64         return -1;
65     }
66     wcda->cdaMode = WINE_CDA_OPEN;      /* to force reading tracks info */
67     wcda->nCurTrack = 0;
68     wcda->nTracks = 0;
69     wcda->dwFirstFrame = 0;
70     wcda->dwLastFrame = 0;
71     wcda->lpdwTrackLen = NULL;
72     wcda->lpdwTrackPos = NULL;
73     wcda->lpbTrackFlags = NULL;
74     return 0;
75 }
76
77 /**************************************************************************
78  *                              CDROM_GetMediaType              [internal]
79  */
80 int     CDROM_GetMediaType(WINE_CDAUDIO* wcda)
81 {
82 #ifdef linux
83     return ioctl(wcda->unixdev, CDROM_DISC_STATUS);
84 #else
85     return -1;
86 #endif
87 }
88
89 /**************************************************************************
90  *                              CDROM_Close                     [internal]
91  */
92 int     CDROM_Close(WINE_CDAUDIO* wcda)
93 {
94 #if defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__)
95     if (wcda->lpdwTrackLen != NULL) free(wcda->lpdwTrackLen);
96     if (wcda->lpdwTrackPos != NULL) free(wcda->lpdwTrackPos);
97     if (wcda->lpbTrackFlags != NULL) free(wcda->lpbTrackFlags);
98     close(wcda->unixdev);
99     return 0;
100 #else
101     return -1;
102 #endif
103 }
104
105 /**************************************************************************
106  *                              CDROM_Get_UPC                   [internal]
107  *
108  * upc has to be 14 bytes long
109  */
110 int CDROM_Get_UPC(WINE_CDAUDIO* wcda, LPSTR upc)
111 {
112 #ifdef linux
113     struct cdrom_mcn mcn;
114     int status = ioctl(wcda->unixdev, CDROM_GET_MCN, &mcn);
115     if (status)
116 {
117         ERR("ioctl() failed with code %d\n",status);
118         return -1;
119     }
120     strcpy(upc, mcn.medium_catalog_number);
121     return 0;
122 #else
123     return -1;
124 #endif
125 }
126
127 /**************************************************************************
128  *                              CDROM_Audio_GetNumberOfTracks   [internal]
129  */
130 UINT16 CDROM_Audio_GetNumberOfTracks(WINE_CDAUDIO* wcda)
131 {
132 #if defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__)
133 #ifdef linux
134     struct cdrom_tochdr         hdr;
135 #else
136     struct ioc_toc_header       hdr;
137 #endif
138     
139     if (wcda->nTracks == 0) {
140 #ifdef linux
141         if (ioctl(wcda->unixdev, CDROMREADTOCHDR, &hdr))
142 #else
143         if (ioctl(wcda->unixdev, CDIOREADTOCHEADER, &hdr))
144 #endif
145         {
146             WARN("(%p) -- Error occurred (%d)!\n", wcda, errno);
147             return (WORD)-1;
148         }
149 #ifdef linux
150         wcda->nFirstTrack = hdr.cdth_trk0;
151         wcda->nLastTrack  = hdr.cdth_trk1;
152 #else   
153         wcda->nFirstTrack = hdr.starting_track;
154         wcda->nLastTrack  = hdr.ending_track;
155 #endif
156         wcda->nTracks = wcda->nLastTrack - wcda->nFirstTrack + 1;
157     }
158     return wcda->nTracks;
159 #else
160     return (WORD)-1;
161 #endif
162 }
163
164 /**************************************************************************
165  *                      CDROM_Audio_GetTracksInfo               [internal]
166  */
167 BOOL CDROM_Audio_GetTracksInfo(WINE_CDAUDIO* wcda)
168 {
169 #if defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__)
170     int         i, length;
171     int         start, last_start = 0;
172     int         total_length = 0;
173 #ifdef linux
174     struct cdrom_tocentry       entry;
175 #else
176     struct ioc_read_toc_entry   entry;
177     struct cd_toc_entry         toc_buffer;
178 #endif
179     
180     if (wcda->nTracks == 0) {
181         if (CDROM_Audio_GetNumberOfTracks(wcda) == (WORD)-1) return FALSE;
182     }
183     TRACE("nTracks=%u\n", wcda->nTracks);
184     
185     if (wcda->lpdwTrackLen != NULL) 
186         free(wcda->lpdwTrackLen);
187     wcda->lpdwTrackLen = (LPDWORD)malloc((wcda->nTracks + 1) * sizeof(DWORD));
188     if (wcda->lpdwTrackPos != NULL) 
189         free(wcda->lpdwTrackPos);
190     wcda->lpdwTrackPos = (LPDWORD)malloc((wcda->nTracks + 1) * sizeof(DWORD));
191     if (wcda->lpbTrackFlags != NULL)
192         free(wcda->lpbTrackFlags);
193     wcda->lpbTrackFlags = (LPBYTE)malloc((wcda->nTracks + 1) * sizeof(BYTE));
194     if (wcda->lpdwTrackLen == NULL || wcda->lpdwTrackPos == NULL ||
195         wcda->lpbTrackFlags == NULL) {
196         WARN("error allocating track table !\n");
197         return FALSE;
198     }
199     memset(wcda->lpdwTrackLen, 0, (wcda->nTracks + 1) * sizeof(DWORD));
200     memset(wcda->lpdwTrackPos, 0, (wcda->nTracks + 1) * sizeof(DWORD));
201     memset(wcda->lpbTrackFlags, 0, (wcda->nTracks + 1) * sizeof(BYTE));
202     for (i = 0; i <= wcda->nTracks; i++) {
203         if (i == wcda->nTracks)
204 #ifdef linux
205             entry.cdte_track = CDROM_LEADOUT;
206 #else
207 #define LEADOUT 0xaa
208         entry.starting_track = LEADOUT; /* XXX */
209 #endif
210         else
211 #ifdef linux
212         entry.cdte_track = i + 1;
213 #else
214         entry.starting_track = i + 1;
215 #endif
216 #ifdef linux
217         entry.cdte_format = CDROM_MSF;
218 #else
219         bzero((char *)&toc_buffer, sizeof(toc_buffer));
220         entry.address_format = CD_MSF_FORMAT;
221         entry.data_len = sizeof(toc_buffer);
222         entry.data = &toc_buffer;
223 #endif
224 #ifdef linux
225         if (ioctl(wcda->unixdev, CDROMREADTOCENTRY, &entry))
226 #else
227         if (ioctl(wcda->unixdev, CDIOREADTOCENTRYS, &entry))
228 #endif
229         {
230             WARN("error read entry (%s)\n", strerror(errno));
231             /* update status according to new status */
232             CDROM_Audio_GetCDStatus(wcda);
233
234             return FALSE;
235         }
236 #ifdef linux
237         start = CDFRAMES_PERSEC * (SECONDS_PERMIN * 
238                                    entry.cdte_addr.msf.minute + entry.cdte_addr.msf.second) + 
239             entry.cdte_addr.msf.frame;
240 #else
241         start = CDFRAMES_PERSEC * (SECONDS_PERMIN *
242                                    toc_buffer.addr.msf.minute + toc_buffer.addr.msf.second) +
243             toc_buffer.addr.msf.frame;
244 #endif
245         if (i == 0) {
246             last_start = start;
247             wcda->dwFirstFrame = start;
248             TRACE("dwFirstOffset=%u\n", start);
249         } else {
250             length = start - last_start;
251             last_start = start;
252             start = last_start - length;
253             total_length += length;
254             wcda->lpdwTrackLen[i - 1] = length;
255             wcda->lpdwTrackPos[i - 1] = start;
256             TRACE("track #%u start=%u len=%u\n", i, start, length);
257         }
258 #ifdef linux
259         wcda->lpbTrackFlags[i] =
260             (entry.cdte_adr << 4) | (entry.cdte_ctrl & 0x0f);
261 #else
262         wcda->lpbTrackFlags[i] =
263             (toc_buffer.addr_type << 4) | (toc_buffer.control & 0x0f);
264 #endif 
265         TRACE("track #%u flags=%02x\n", i + 1, wcda->lpbTrackFlags[i]);
266     }
267     wcda->dwLastFrame = last_start;
268     TRACE("total_len=%u\n", total_length);
269     return TRUE;
270 #else
271     return FALSE;
272 #endif
273 }
274
275 /**************************************************************************
276  *                              CDROM_Audio_GetCDStatus         [internal]
277  */
278 BOOL CDROM_Audio_GetCDStatus(WINE_CDAUDIO* wcda)
279 {
280 #if defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__)
281     int         oldmode = wcda->cdaMode;
282 #ifdef linux
283     wcda->sc.cdsc_format = CDROM_MSF;
284 #else
285     struct ioc_read_subchannel  read_sc;
286     
287     read_sc.address_format = CD_MSF_FORMAT;
288     read_sc.data_format    = CD_CURRENT_POSITION;
289     read_sc.track          = 0;
290     read_sc.data_len       = sizeof(wcda->sc);
291     read_sc.data           = (struct cd_sub_channel_info *)&wcda->sc;
292 #endif
293 #ifdef linux
294     if (ioctl(wcda->unixdev, CDROMSUBCHNL, &wcda->sc))
295 #else
296     if (ioctl(wcda->unixdev, CDIOCREADSUBCHANNEL, &read_sc))
297 #endif
298     {
299         TRACE("opened or no_media (%s)!\n", strerror(errno));
300         wcda->cdaMode = WINE_CDA_OPEN; /* was NOT_READY */
301         return TRUE;
302     }
303     switch (
304 #ifdef linux
305             wcda->sc.cdsc_audiostatus
306 #else
307             wcda->sc.header.audio_status
308 #endif
309             ) {
310 #ifdef linux
311     case CDROM_AUDIO_INVALID:
312 #else
313     case CD_AS_AUDIO_INVALID:
314 #endif
315         WARN("device doesn't support status.\n");
316         wcda->cdaMode = WINE_CDA_DONTKNOW;
317         break;
318 #ifdef linux
319     case CDROM_AUDIO_NO_STATUS: 
320 #else
321     case CD_AS_NO_STATUS:
322 #endif
323         wcda->cdaMode = WINE_CDA_STOP;
324         TRACE("WINE_CDA_STOP !\n");
325         break;
326 #ifdef linux
327     case CDROM_AUDIO_PLAY: 
328 #else
329     case CD_AS_PLAY_IN_PROGRESS:
330 #endif
331         wcda->cdaMode = WINE_CDA_PLAY;
332         break;
333 #ifdef linux
334     case CDROM_AUDIO_PAUSED:
335 #else
336     case CD_AS_PLAY_PAUSED:
337 #endif
338         wcda->cdaMode = WINE_CDA_PAUSE;
339         TRACE("WINE_CDA_PAUSE !\n");
340         break;
341     default:
342 #ifdef linux
343         TRACE("status=%02X !\n",
344               wcda->sc.cdsc_audiostatus);
345 #else
346         TRACE("status=%02X !\n",
347               wcda->sc.header.audio_status);
348 #endif
349     }
350 #ifdef linux
351     wcda->nCurTrack = wcda->sc.cdsc_trk;
352     wcda->dwCurFrame = 
353         CDFRAMES_PERMIN * wcda->sc.cdsc_absaddr.msf.minute +
354         CDFRAMES_PERSEC * wcda->sc.cdsc_absaddr.msf.second +
355         wcda->sc.cdsc_absaddr.msf.frame;
356 #else
357     wcda->nCurTrack = wcda->sc.what.position.track_number;
358     wcda->dwCurFrame = 
359         CDFRAMES_PERMIN * wcda->sc.what.position.absaddr.msf.minute +
360         CDFRAMES_PERSEC * wcda->sc.what.position.absaddr.msf.second +
361         wcda->sc.what.position.absaddr.msf.frame;
362 #endif
363 #ifdef linux
364     TRACE("%02u-%02u:%02u:%02u \n",
365           wcda->sc.cdsc_trk,
366           wcda->sc.cdsc_absaddr.msf.minute,
367           wcda->sc.cdsc_absaddr.msf.second,
368           wcda->sc.cdsc_absaddr.msf.frame);
369 #else
370     TRACE("%02u-%02u:%02u:%02u \n",
371           wcda->sc.what.position.track_number,
372           wcda->sc.what.position.absaddr.msf.minute,
373           wcda->sc.what.position.absaddr.msf.second,
374           wcda->sc.what.position.absaddr.msf.frame);
375 #endif
376     
377     if (oldmode != wcda->cdaMode && oldmode == WINE_CDA_OPEN) {
378         if (!CDROM_Audio_GetTracksInfo(wcda)) {
379             WARN("error updating TracksInfo !\n");
380             return FALSE;
381         }
382     }
383     return TRUE;
384 #else
385     return FALSE;
386 #endif
387 }
388
389 /**************************************************************************
390  *                              CDROM_Audio_Play                [internal]
391  */
392 int     CDROM_Audio_Play(WINE_CDAUDIO* wcda, DWORD start, DWORD end)
393 {
394 #if defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__)
395 #ifdef linux
396     struct      cdrom_msf       msf;
397 #else
398     struct      ioc_play_msf    msf;
399 #endif
400
401 #ifdef linux
402     msf.cdmsf_min0 = start / CDFRAMES_PERMIN;
403     msf.cdmsf_sec0 = (start % CDFRAMES_PERMIN) / CDFRAMES_PERSEC;
404     msf.cdmsf_frame0 = start % CDFRAMES_PERSEC;
405     msf.cdmsf_min1 = end / CDFRAMES_PERMIN;
406     msf.cdmsf_sec1 = (end % CDFRAMES_PERMIN) / CDFRAMES_PERSEC;
407     msf.cdmsf_frame1 = end % CDFRAMES_PERSEC;
408 #else
409     msf.start_m     = start / CDFRAMES_PERMIN;
410     msf.start_s     = (start % CDFRAMES_PERMIN) / CDFRAMES_PERSEC;
411     msf.start_f     = start % CDFRAMES_PERSEC;
412     msf.end_m       = end / CDFRAMES_PERMIN;
413     msf.end_s       = (end % CDFRAMES_PERMIN) / CDFRAMES_PERSEC;
414     msf.end_f       = end % CDFRAMES_PERSEC;
415 #endif
416 #ifdef linux
417     if (ioctl(wcda->unixdev, CDROMSTART))
418 #else
419     if (ioctl(wcda->unixdev, CDIOCSTART, NULL))
420 #endif
421     {
422         WARN("motor doesn't start !\n");
423         return -1;
424     }
425 #ifdef linux
426     if (ioctl(wcda->unixdev, CDROMPLAYMSF, &msf))
427 #else
428     if (ioctl(wcda->unixdev, CDIOCPLAYMSF, &msf))
429 #endif
430     {
431         WARN("device doesn't play !\n");
432         return -1;
433     }
434 #ifdef linux
435     TRACE("msf = %d:%d:%d %d:%d:%d\n",
436           msf.cdmsf_min0, msf.cdmsf_sec0, msf.cdmsf_frame0,
437           msf.cdmsf_min1, msf.cdmsf_sec1, msf.cdmsf_frame1);
438 #else
439     TRACE("msf = %d:%d:%d %d:%d:%d\n",
440           msf.start_m, msf.start_s, msf.start_f,
441           msf.end_m,   msf.end_s,   msf.end_f);
442 #endif
443     return 0;
444 #else
445     return -1;
446 #endif
447 }
448
449 /**************************************************************************
450  *                              CDROM_Audio_Stop                [internal]
451  */
452 int     CDROM_Audio_Stop(WINE_CDAUDIO* wcda)
453 {
454 #if defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__)
455     int ret = 0;
456 #ifdef linux
457     ret = ioctl(wcda->unixdev, CDROMSTOP);
458 #else
459     ret = ioctl(wcda->unixdev, CDIOCSTOP, NULL);
460 #endif
461     return ret;
462 #else
463     return -1;
464 #endif
465 }
466
467 /**************************************************************************
468  *                              CDROM_Audio_Pause               [internal]
469  */
470 int     CDROM_Audio_Pause(WINE_CDAUDIO* wcda, int pauseOn)
471 {
472 #if defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__)
473     int ret = 0;    
474 #ifdef linux
475     ret = ioctl(wcda->unixdev, pauseOn ? CDROMPAUSE : CDROMRESUME);
476 #else
477     ret = ioctl(wcda->unixdev, pauseOn ? CDIOCPAUSE : CDIOCRESUME, NULL);
478 #endif
479     return ret;
480 #else
481     return -1;
482 #endif
483 }
484
485 /**************************************************************************
486  *                              CDROM_Audio_Seek                [internal]
487  */
488 int     CDROM_Audio_Seek(WINE_CDAUDIO* wcda, DWORD at)
489 {
490 #if defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__)
491     int                         ret = 0;    
492 #ifdef linux
493     struct cdrom_msf0           msf;
494     msf.minute = at / CDFRAMES_PERMIN;
495     msf.second = (at % CDFRAMES_PERMIN) / CDFRAMES_PERSEC;
496     msf.frame  = at % CDFRAMES_PERSEC;
497
498     ret = ioctl(wcda->unixdev, CDROMSEEK, &msf);
499 #else
500    /* FIXME: the current end for play is lost 
501     * use end of CD ROM instead
502     */
503    FIXME("Could a BSD expert implement the seek function ?\n");
504    CDROM_Audio_Play(wcda, at, wcda->lpdwTrackPos[wcda->nTracks] + wcda->lpdwTrackLen[wcda->nTracks]);
505    
506 #endif
507     return ret;
508 #else
509     return -1;
510 #endif
511 }
512
513 /**************************************************************************
514  *                              CDROM_SetDoor                   [internal]
515  */
516 int     CDROM_SetDoor(WINE_CDAUDIO* wcda, int open)
517 {
518 #if defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__)
519     int ret = 0;    
520 #ifdef linux
521     if (open) {
522         ret = ioctl(wcda->unixdev, CDROMEJECT);
523     } else {
524         ret = ioctl(wcda->unixdev, CDROMEJECT, 1);
525     }
526 #else
527     ret = (ioctl(wcda->unixdev, CDIOCALLOW, NULL)) || 
528         (ioctl(wcda->unixdev, open ? CDIOCEJECT : CDIOCCLOSE, NULL)) ||
529         (ioctl(wcda->unixdev, CDIOCPREVENT, NULL));
530 #endif
531     wcda->nTracks = 0;
532     return ret;
533 #else
534     return -1;
535 #endif
536 }
537
538 /**************************************************************************
539  *                              CDROM_Reset                     [internal]
540  */
541 int     CDROM_Reset(WINE_CDAUDIO* wcda) 
542 {
543 #if defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__)
544     int ret = 0;
545 #ifdef linux
546     ret = ioctl(wcda->unixdev, CDROMRESET);
547 #else
548     ret = ioctl(wcda->unixdev, CDIOCRESET, NULL);
549 #endif
550     return ret;
551 #else
552     return -1;
553 #endif
554 }
555
556 unsigned int get_offs_best_voldesc(int fd)
557 {
558     BYTE cur_vd_type, max_vd_type = 0;
559     unsigned int offs, best_offs = 0;
560
561     for (offs=0x8000; offs <= 0x9800; offs += 0x800)
562     {
563         lseek(fd, offs, SEEK_SET);
564         read(fd, &cur_vd_type, 1);
565         if (cur_vd_type == 0xff)
566             break;
567         if (cur_vd_type > max_vd_type)
568         {
569             max_vd_type = cur_vd_type;
570             best_offs = offs;
571         }
572     }
573     return best_offs;
574 }
575
576 /**************************************************************************
577  *                              CDROM_Audio_GetSerial           [internal]
578  */
579 DWORD CDROM_Audio_GetSerial(WINE_CDAUDIO* wcda)
580 {
581     unsigned long serial = 0;
582     int i;
583     DWORD dwFrame, msf;
584     WORD wMinutes, wSeconds, wFrames;
585
586     for (i = 0; i < wcda->nTracks; i++) {
587         dwFrame = wcda->lpdwTrackPos[i];
588         wMinutes = dwFrame / CDFRAMES_PERMIN;
589         wSeconds = (dwFrame - CDFRAMES_PERMIN * wMinutes) / CDFRAMES_PERSEC;
590         wFrames = dwFrame - CDFRAMES_PERMIN * wMinutes - CDFRAMES_PERSEC * wSeconds;
591         msf = CDROM_MAKE_MSF(wMinutes, wSeconds, wFrames);
592
593         serial += (CDROM_MSF_MINUTE(msf) << 16) +
594         (CDROM_MSF_SECOND(msf) << 8) +
595         (CDROM_MSF_FRAME(msf));
596     }
597     return serial;
598 }
599
600 /**************************************************************************
601  *                              CDROM_Data_GetSerial            [internal]
602  */
603 DWORD CDROM_Data_GetSerial(WINE_CDAUDIO* wcda)
604 {
605     unsigned int offs = get_offs_best_voldesc(wcda->unixdev);
606     union {
607         unsigned long val;
608         unsigned char p[4];
609     } serial;
610
611     serial.val = 0;
612     if (offs)
613     {
614         BYTE buf[2048];
615         int i;
616
617         lseek(wcda->unixdev,offs,SEEK_SET);
618         read(wcda->unixdev,buf,2048);
619         for(i=0; i<2048; i+=4)
620         {
621             /* DON'T optimize this into DWORD !! (breaks overflow) */
622             serial.p[0] += buf[i+0];
623             serial.p[1] += buf[i+1];
624             serial.p[2] += buf[i+2];
625             serial.p[3] += buf[i+3];
626         }
627     }
628     return serial.val;
629 }
630
631 /**************************************************************************
632  *                              CDROM_GetSerial                 [internal]
633  */
634 DWORD CDROM_GetSerial(int drive)
635 {
636     WINE_CDAUDIO wcda;
637     DWORD serial = 0;
638
639     /* EXPIRES 01.01.2001 */
640     FIXME("CD-ROM serial number calculation might fail.\n");
641     FIXME("Please test with as many exotic CDs as possible !\n");
642
643     if (!(CDROM_Open(&wcda, drive)))
644     {
645         int media = CDROM_GetMediaType(&wcda);
646         LPSTR p;
647
648         if (media == CDS_AUDIO)
649         {
650             if (!(CDROM_Audio_GetCDStatus(&wcda))) {
651                 ERR("couldn't get CD status !\n");
652                 CDROM_Close(&wcda);
653                 return 0;
654             }
655             serial = CDROM_Audio_GetSerial(&wcda);
656         }
657         else
658         if (media > CDS_AUDIO)
659             /* hopefully a data CD */
660             serial = CDROM_Data_GetSerial(&wcda);
661
662         p = (media == CDS_AUDIO) ? "Audio " :
663             (media > CDS_AUDIO) ? "Data " : "";
664         if (serial)
665             FIXME("%sCD serial number is %04x-%04x.\n",
666                 p, HIWORD(serial), LOWORD(serial));
667         else
668             ERR("couldn't get %sCD serial !\n", p);
669         CDROM_Close(&wcda);
670     }
671     return serial;
672 }
673