quartz: Add mediaseeking stub to audio renderer.
[wine] / dlls / mcicda / mcicda.c
1 /*
2  * MCI driver for audio CD (MCICDA)
3  *
4  * Copyright 1994    Martin Ayotte
5  * Copyright 1998-99 Eric Pouech
6  * Copyright 2000    Andreas Mohr
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21  */
22
23 #include "config.h"
24 #include <stdarg.h>
25 #include <stdio.h>
26 #include <string.h>
27
28 #define WIN32_NO_STATUS
29 #include "windef.h"
30 #include "winbase.h"
31 #include "wingdi.h"
32 #include "winuser.h"
33 #include "wownt32.h"
34 #include "mmddk.h"
35 #include "winioctl.h"
36 #include "ntddcdrm.h"
37 #include "winternl.h"
38 #include "wine/debug.h"
39 #include "wine/unicode.h"
40
41 WINE_DEFAULT_DEBUG_CHANNEL(mcicda);
42
43 #define CDFRAMES_PERSEC                 75
44 #define CDFRAMES_PERMIN                 (CDFRAMES_PERSEC * 60)
45 #define FRAME_OF_ADDR(a) ((a)[1] * CDFRAMES_PERMIN + (a)[2] * CDFRAMES_PERSEC + (a)[3])
46 #define FRAME_OF_TOC(toc, idx)  FRAME_OF_ADDR((toc).TrackData[idx - (toc).FirstTrack].Address)
47
48 typedef struct {
49     UINT                wDevID;
50     int                 nUseCount;          /* Incremented for each shared open */
51     BOOL                fShareable;         /* TRUE if first open was shareable */
52     WORD                wNotifyDeviceID;    /* MCI device ID with a pending notification */
53     HANDLE              hCallback;          /* Callback handle for pending notification */
54     DWORD               dwTimeFormat;
55     HANDLE              handle;
56 } WINE_MCICDAUDIO;
57
58 /*-----------------------------------------------------------------------*/
59
60 /**************************************************************************
61  *                              MCICDA_drvOpen                  [internal]
62  */
63 static  DWORD   MCICDA_drvOpen(LPCWSTR str, LPMCI_OPEN_DRIVER_PARMSW modp)
64 {
65     WINE_MCICDAUDIO*    wmcda;
66
67     if (!modp) return 0xFFFFFFFF;
68
69     wmcda = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCICDAUDIO));
70
71     if (!wmcda)
72         return 0;
73
74     wmcda->wDevID = modp->wDeviceID;
75     mciSetDriverData(wmcda->wDevID, (DWORD_PTR)wmcda);
76     modp->wCustomCommandTable = MCI_NO_COMMAND_TABLE;
77     modp->wType = MCI_DEVTYPE_CD_AUDIO;
78     return modp->wDeviceID;
79 }
80
81 /**************************************************************************
82  *                              MCICDA_drvClose                 [internal]
83  */
84 static  DWORD   MCICDA_drvClose(DWORD dwDevID)
85 {
86     WINE_MCICDAUDIO*  wmcda = (WINE_MCICDAUDIO*)mciGetDriverData(dwDevID);
87
88     if (wmcda) {
89         HeapFree(GetProcessHeap(), 0, wmcda);
90         mciSetDriverData(dwDevID, 0);
91     }
92     return (dwDevID == 0xFFFFFFFF) ? 1 : 0;
93 }
94
95 /**************************************************************************
96  *                              MCICDA_GetOpenDrv               [internal]
97  */
98 static WINE_MCICDAUDIO*  MCICDA_GetOpenDrv(UINT wDevID)
99 {
100     WINE_MCICDAUDIO*    wmcda = (WINE_MCICDAUDIO*)mciGetDriverData(wDevID);
101
102     if (wmcda == NULL || wmcda->nUseCount == 0) {
103         WARN("Invalid wDevID=%u\n", wDevID);
104         return 0;
105     }
106     return wmcda;
107 }
108
109 /**************************************************************************
110  *                              MCICDA_GetStatus                [internal]
111  */
112 static  DWORD    MCICDA_GetStatus(WINE_MCICDAUDIO* wmcda)
113 {
114     CDROM_SUB_Q_DATA_FORMAT     fmt;
115     SUB_Q_CHANNEL_DATA          data;
116     DWORD                       br;
117     DWORD                       mode = MCI_MODE_NOT_READY;
118
119     fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
120     if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
121                          &data, sizeof(data), &br, NULL)) {
122         if (GetLastError() == ERROR_NOT_READY) mode = MCI_MODE_OPEN;
123     } else {
124         switch (data.CurrentPosition.Header.AudioStatus)
125         {
126         case AUDIO_STATUS_IN_PROGRESS:          mode = MCI_MODE_PLAY;   break;
127         case AUDIO_STATUS_PAUSED:               mode = MCI_MODE_PAUSE;  break;
128         case AUDIO_STATUS_NO_STATUS:
129         case AUDIO_STATUS_PLAY_COMPLETE:        mode = MCI_MODE_STOP;   break;
130         case AUDIO_STATUS_PLAY_ERROR:
131         case AUDIO_STATUS_NOT_SUPPORTED:
132         default:
133             break;
134         }
135     }
136     return mode;
137 }
138
139 /**************************************************************************
140  *                              MCICDA_GetError                 [internal]
141  */
142 static  int     MCICDA_GetError(WINE_MCICDAUDIO* wmcda)
143 {
144     switch (GetLastError())
145     {
146     case ERROR_NOT_READY:     return MCIERR_DEVICE_NOT_READY;
147     case ERROR_IO_DEVICE:     return MCIERR_HARDWARE;
148     default:
149         FIXME("Unknown mode %u\n", GetLastError());
150     }
151     return MCIERR_DRIVER_INTERNAL;
152 }
153
154 /**************************************************************************
155  *                      MCICDA_CalcFrame                        [internal]
156  */
157 static DWORD MCICDA_CalcFrame(WINE_MCICDAUDIO* wmcda, DWORD dwTime)
158 {
159     DWORD       dwFrame = 0;
160     UINT        wTrack;
161     CDROM_TOC   toc;
162     DWORD       br;
163     BYTE*       addr;
164
165     TRACE("(%p, %08X, %u);\n", wmcda, wmcda->dwTimeFormat, dwTime);
166
167     switch (wmcda->dwTimeFormat) {
168     case MCI_FORMAT_MILLISECONDS:
169         dwFrame = ((dwTime - 1) * CDFRAMES_PERSEC + 500) / 1000;
170         TRACE("MILLISECONDS %u\n", dwFrame);
171         break;
172     case MCI_FORMAT_MSF:
173         TRACE("MSF %02u:%02u:%02u\n",
174               MCI_MSF_MINUTE(dwTime), MCI_MSF_SECOND(dwTime), MCI_MSF_FRAME(dwTime));
175         dwFrame += CDFRAMES_PERMIN * MCI_MSF_MINUTE(dwTime);
176         dwFrame += CDFRAMES_PERSEC * MCI_MSF_SECOND(dwTime);
177         dwFrame += MCI_MSF_FRAME(dwTime);
178         break;
179     case MCI_FORMAT_TMSF:
180     default: /* unknown format ! force TMSF ! ... */
181         wTrack = MCI_TMSF_TRACK(dwTime);
182         if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
183                              &toc, sizeof(toc), &br, NULL))
184             return 0;
185         if (wTrack < toc.FirstTrack || wTrack > toc.LastTrack)
186             return 0;
187         TRACE("MSF %02u-%02u:%02u:%02u\n",
188               MCI_TMSF_TRACK(dwTime), MCI_TMSF_MINUTE(dwTime),
189               MCI_TMSF_SECOND(dwTime), MCI_TMSF_FRAME(dwTime));
190         addr = toc.TrackData[wTrack - toc.FirstTrack].Address;
191         TRACE("TMSF trackpos[%u]=%d:%d:%d\n",
192               wTrack, addr[1], addr[2], addr[3]);
193         dwFrame = CDFRAMES_PERMIN * (addr[1] + MCI_TMSF_MINUTE(dwTime)) +
194             CDFRAMES_PERSEC * (addr[2] + MCI_TMSF_SECOND(dwTime)) +
195             addr[3] + MCI_TMSF_FRAME(dwTime);
196         break;
197     }
198     return dwFrame;
199 }
200
201 /**************************************************************************
202  *                      MCICDA_CalcTime                         [internal]
203  */
204 static DWORD MCICDA_CalcTime(WINE_MCICDAUDIO* wmcda, DWORD tf, DWORD dwFrame, LPDWORD lpRet)
205 {
206     DWORD       dwTime = 0;
207     UINT        wTrack;
208     UINT        wMinutes;
209     UINT        wSeconds;
210     UINT        wFrames;
211     CDROM_TOC   toc;
212     DWORD       br;
213
214     TRACE("(%p, %08X, %u);\n", wmcda, tf, dwFrame);
215
216     switch (tf) {
217     case MCI_FORMAT_MILLISECONDS:
218         dwTime = (dwFrame * 1000) / CDFRAMES_PERSEC + 1;
219         TRACE("MILLISECONDS %u\n", dwTime);
220         *lpRet = 0;
221         break;
222     case MCI_FORMAT_MSF:
223         wMinutes = dwFrame / CDFRAMES_PERMIN;
224         wSeconds = (dwFrame - CDFRAMES_PERMIN * wMinutes) / CDFRAMES_PERSEC;
225         wFrames = dwFrame - CDFRAMES_PERMIN * wMinutes - CDFRAMES_PERSEC * wSeconds;
226         dwTime = MCI_MAKE_MSF(wMinutes, wSeconds, wFrames);
227         TRACE("MSF %02u:%02u:%02u -> dwTime=%u\n",
228               wMinutes, wSeconds, wFrames, dwTime);
229         *lpRet = MCI_COLONIZED3_RETURN;
230         break;
231     case MCI_FORMAT_TMSF:
232     default:    /* unknown format ! force TMSF ! ... */
233         if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
234                              &toc, sizeof(toc), &br, NULL))
235             return 0;
236         if (dwFrame < FRAME_OF_TOC(toc, toc.FirstTrack) ||
237             dwFrame > FRAME_OF_TOC(toc, toc.LastTrack + 1)) {
238             ERR("Out of range value %u [%u,%u]\n",
239                 dwFrame, FRAME_OF_TOC(toc, toc.FirstTrack),
240                 FRAME_OF_TOC(toc, toc.LastTrack + 1));
241             *lpRet = 0;
242             return 0;
243         }
244         for (wTrack = toc.FirstTrack; wTrack <= toc.LastTrack; wTrack++) {
245             if (FRAME_OF_TOC(toc, wTrack) > dwFrame)
246                 break;
247         }
248         wTrack--;
249         dwFrame -= FRAME_OF_TOC(toc, wTrack);
250         wMinutes = dwFrame / CDFRAMES_PERMIN;
251         wSeconds = (dwFrame - CDFRAMES_PERMIN * wMinutes) / CDFRAMES_PERSEC;
252         wFrames = dwFrame - CDFRAMES_PERMIN * wMinutes - CDFRAMES_PERSEC * wSeconds;
253         dwTime = MCI_MAKE_TMSF(wTrack, wMinutes, wSeconds, wFrames);
254         TRACE("%02u-%02u:%02u:%02u\n", wTrack, wMinutes, wSeconds, wFrames);
255         *lpRet = MCI_COLONIZED4_RETURN;
256         break;
257     }
258     return dwTime;
259 }
260
261 static DWORD MCICDA_Seek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms);
262 static DWORD MCICDA_Stop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms);
263
264 /**************************************************************************
265  *                              MCICDA_Open                     [internal]
266  */
267 static DWORD MCICDA_Open(UINT wDevID, DWORD dwFlags, LPMCI_OPEN_PARMSW lpOpenParms)
268 {
269     DWORD               dwDeviceID;
270     DWORD               ret = MCIERR_HARDWARE;
271     WINE_MCICDAUDIO*    wmcda = (WINE_MCICDAUDIO*)mciGetDriverData(wDevID);
272     WCHAR               root[7], drive = 0;
273     int                 count;
274
275     TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpOpenParms);
276
277     if (lpOpenParms == NULL)            return MCIERR_NULL_PARAMETER_BLOCK;
278     if (wmcda == NULL)                  return MCIERR_INVALID_DEVICE_ID;
279
280     dwDeviceID = lpOpenParms->wDeviceID;
281
282     if (wmcda->nUseCount > 0) {
283         /* The driver is already open on this channel */
284         /* If the driver was opened shareable before and this open specifies */
285         /* shareable then increment the use count */
286         if (wmcda->fShareable && (dwFlags & MCI_OPEN_SHAREABLE))
287             ++wmcda->nUseCount;
288         else
289             return MCIERR_MUST_USE_SHAREABLE;
290     } else {
291         wmcda->nUseCount = 1;
292         wmcda->fShareable = dwFlags & MCI_OPEN_SHAREABLE;
293     }
294     if (dwFlags & MCI_OPEN_ELEMENT) {
295         if (dwFlags & MCI_OPEN_ELEMENT_ID) {
296             WARN("MCI_OPEN_ELEMENT_ID %p! Abort\n", lpOpenParms->lpstrElementName);
297             return MCIERR_NO_ELEMENT_ALLOWED;
298         }
299         TRACE("MCI_OPEN_ELEMENT element name: %s\n", debugstr_w(lpOpenParms->lpstrElementName));
300         if (!isalpha(lpOpenParms->lpstrElementName[0]) || lpOpenParms->lpstrElementName[1] != ':' ||
301             (lpOpenParms->lpstrElementName[2] && lpOpenParms->lpstrElementName[2] != '\\'))
302         {
303             WARN("MCI_OPEN_ELEMENT unsupported format: %s\n", 
304                  debugstr_w(lpOpenParms->lpstrElementName));
305             ret = MCIERR_NO_ELEMENT_ALLOWED;
306             goto the_error;
307         }
308         drive = toupper(lpOpenParms->lpstrElementName[0]);
309         root[0] = drive; root[1] = ':'; root[2] = '\\'; root[3] = '\0';
310         if (GetDriveTypeW(root) != DRIVE_CDROM)
311         {
312             ret = MCIERR_INVALID_DEVICE_NAME;
313             goto the_error;
314         }
315     }
316     else
317     {
318         /* drive letter isn't passed... get the dwDeviceID'th cdrom in the system */
319         root[0] = 'A'; root[1] = ':'; root[2] = '\\'; root[3] = '\0';
320         for (count = 0; root[0] <= 'Z'; root[0]++)
321         {
322             if (GetDriveTypeW(root) == DRIVE_CDROM && ++count >= dwDeviceID)
323             {
324                 drive = root[0];
325                 break;
326             }
327         }
328         if (!drive)
329         {
330             ret = MCIERR_INVALID_DEVICE_ID;
331             goto the_error;
332         }
333     }
334
335     wmcda->wNotifyDeviceID = dwDeviceID;
336     wmcda->dwTimeFormat = MCI_FORMAT_MSF;
337
338     /* now, open the handle */
339     root[0] = root[1] = '\\'; root[2] = '.'; root[3] = '\\'; root[4] = drive; root[5] = ':'; root[6] = '\0';
340     wmcda->handle = CreateFileW(root, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
341     if (wmcda->handle != INVALID_HANDLE_VALUE)
342         return 0;
343
344  the_error:
345     --wmcda->nUseCount;
346     return ret;
347 }
348
349 /**************************************************************************
350  *                              MCICDA_Close                    [internal]
351  */
352 static DWORD MCICDA_Close(UINT wDevID, DWORD dwParam, LPMCI_GENERIC_PARMS lpParms)
353 {
354     WINE_MCICDAUDIO*    wmcda = MCICDA_GetOpenDrv(wDevID);
355
356     TRACE("(%04X, %08X, %p);\n", wDevID, dwParam, lpParms);
357
358     if (wmcda == NULL)  return MCIERR_INVALID_DEVICE_ID;
359
360     if (--wmcda->nUseCount == 0) {
361         CloseHandle(wmcda->handle);
362     }
363     return 0;
364 }
365
366 /**************************************************************************
367  *                              MCICDA_GetDevCaps               [internal]
368  */
369 static DWORD MCICDA_GetDevCaps(UINT wDevID, DWORD dwFlags,
370                                    LPMCI_GETDEVCAPS_PARMS lpParms)
371 {
372     DWORD       ret = 0;
373
374     TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
375
376     if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
377
378     if (dwFlags & MCI_GETDEVCAPS_ITEM) {
379         TRACE("MCI_GETDEVCAPS_ITEM dwItem=%08X;\n", lpParms->dwItem);
380
381         switch (lpParms->dwItem) {
382         case MCI_GETDEVCAPS_CAN_RECORD:
383             lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
384             ret = MCI_RESOURCE_RETURNED;
385             break;
386         case MCI_GETDEVCAPS_HAS_AUDIO:
387             lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
388             ret = MCI_RESOURCE_RETURNED;
389             break;
390         case MCI_GETDEVCAPS_HAS_VIDEO:
391             lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
392             ret = MCI_RESOURCE_RETURNED;
393             break;
394         case MCI_GETDEVCAPS_DEVICE_TYPE:
395             lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_CD_AUDIO, MCI_DEVTYPE_CD_AUDIO);
396             ret = MCI_RESOURCE_RETURNED;
397             break;
398         case MCI_GETDEVCAPS_USES_FILES:
399             lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
400             ret = MCI_RESOURCE_RETURNED;
401             break;
402         case MCI_GETDEVCAPS_COMPOUND_DEVICE:
403             lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
404             ret = MCI_RESOURCE_RETURNED;
405             break;
406         case MCI_GETDEVCAPS_CAN_EJECT:
407             lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
408             ret = MCI_RESOURCE_RETURNED;
409             break;
410         case MCI_GETDEVCAPS_CAN_PLAY:
411             lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
412             ret = MCI_RESOURCE_RETURNED;
413             break;
414         case MCI_GETDEVCAPS_CAN_SAVE:
415             lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
416             ret = MCI_RESOURCE_RETURNED;
417             break;
418         default:
419             ERR("Unsupported %x devCaps item\n", lpParms->dwItem);
420             return MCIERR_UNRECOGNIZED_COMMAND;
421         }
422     } else {
423         TRACE("No GetDevCaps-Item !\n");
424         return MCIERR_UNRECOGNIZED_COMMAND;
425     }
426     TRACE("lpParms->dwReturn=%08X;\n", lpParms->dwReturn);
427     return ret;
428 }
429
430 static DWORD CDROM_Audio_GetSerial(CDROM_TOC* toc)
431 {
432     unsigned long serial = 0;
433     int i;
434     WORD wMagic;
435     DWORD dwStart, dwEnd;
436
437     /*
438      * wMagic collects the wFrames from track 1
439      * dwStart, dwEnd collect the beginning and end of the disc respectively, in
440      * frames.
441      * There it is collected for correcting the serial when there are less than
442      * 3 tracks.
443      */
444     wMagic = toc->TrackData[0].Address[3];
445     dwStart = FRAME_OF_TOC(*toc, toc->FirstTrack);
446
447     for (i = 0; i <= toc->LastTrack - toc->FirstTrack; i++) {
448         serial += (toc->TrackData[i].Address[1] << 16) |
449             (toc->TrackData[i].Address[2] << 8) | toc->TrackData[i].Address[3];
450     }
451     dwEnd = FRAME_OF_TOC(*toc, toc->LastTrack + 1);
452
453     if (toc->LastTrack - toc->FirstTrack + 1 < 3)
454         serial += wMagic + (dwEnd - dwStart);
455
456     return serial;
457 }
458
459
460 /**************************************************************************
461  *                              MCICDA_Info                     [internal]
462  */
463 static DWORD MCICDA_Info(UINT wDevID, DWORD dwFlags, LPMCI_INFO_PARMSW lpParms)
464 {
465     LPCWSTR             str = NULL;
466     WINE_MCICDAUDIO*    wmcda = MCICDA_GetOpenDrv(wDevID);
467     DWORD               ret = 0;
468     WCHAR               buffer[16];
469
470     TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
471
472     if (lpParms == NULL || lpParms->lpstrReturn == NULL)
473         return MCIERR_NULL_PARAMETER_BLOCK;
474     if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
475
476     TRACE("buf=%p, len=%u\n", lpParms->lpstrReturn, lpParms->dwRetSize);
477
478     if (dwFlags & MCI_INFO_PRODUCT) {
479         static const WCHAR wszAudioCd[] = {'W','i','n','e','\'','s',' ','a','u','d','i','o',' ','C','D',0};
480         str = wszAudioCd;
481     } else if (dwFlags & MCI_INFO_MEDIA_UPC) {
482         ret = MCIERR_NO_IDENTITY;
483     } else if (dwFlags & MCI_INFO_MEDIA_IDENTITY) {
484         DWORD       res = 0;
485         CDROM_TOC   toc;
486         DWORD       br;
487         static const WCHAR wszLu[] = {'%','l','u',0};
488
489         if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
490                              &toc, sizeof(toc), &br, NULL)) {
491             return MCICDA_GetError(wmcda);
492         }
493
494         res = CDROM_Audio_GetSerial(&toc);
495         sprintfW(buffer, wszLu, res);
496         str = buffer;
497     } else {
498         WARN("Don't know this info command (%u)\n", dwFlags);
499         ret = MCIERR_UNRECOGNIZED_COMMAND;
500     }
501     if (str) {
502         if (lpParms->dwRetSize <= strlenW(str)) {
503             lstrcpynW(lpParms->lpstrReturn, str, lpParms->dwRetSize - 1);
504             ret = MCIERR_PARAM_OVERFLOW;
505         } else {
506             strcpyW(lpParms->lpstrReturn, str);
507         }
508     } else {
509         *lpParms->lpstrReturn = 0;
510     }
511     TRACE("=> %s (%d)\n", debugstr_w(lpParms->lpstrReturn), ret);
512     return ret;
513 }
514
515 /**************************************************************************
516  *                              MCICDA_Status                   [internal]
517  */
518 static DWORD MCICDA_Status(UINT wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
519 {
520     WINE_MCICDAUDIO*            wmcda = MCICDA_GetOpenDrv(wDevID);
521     DWORD                       idx;
522     DWORD                       ret = 0;
523     CDROM_SUB_Q_DATA_FORMAT     fmt;
524     SUB_Q_CHANNEL_DATA          data;
525     CDROM_TOC                   toc;
526     DWORD                       br;
527
528     TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
529
530     if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
531     if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
532
533     if (dwFlags & MCI_NOTIFY) {
534         TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
535         mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
536                         wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
537     }
538     if (dwFlags & MCI_STATUS_ITEM) {
539         TRACE("dwItem = %x\n", lpParms->dwItem);
540         switch (lpParms->dwItem) {
541         case MCI_STATUS_CURRENT_TRACK:
542             fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
543             if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
544                                  &data, sizeof(data), &br, NULL))
545             {
546                 return MCICDA_GetError(wmcda);
547             }
548             lpParms->dwReturn = data.CurrentPosition.TrackNumber;
549             TRACE("CURRENT_TRACK=%u!\n", lpParms->dwReturn);
550             break;
551         case MCI_STATUS_LENGTH:
552             if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
553                                  &toc, sizeof(toc), &br, NULL)) {
554                 WARN("error reading TOC !\n");
555                 return MCICDA_GetError(wmcda);
556             }
557             if (dwFlags & MCI_TRACK) {
558                 TRACE("MCI_TRACK #%u LENGTH=??? !\n", lpParms->dwTrack);
559                 if (lpParms->dwTrack < toc.FirstTrack || lpParms->dwTrack > toc.LastTrack)
560                     return MCIERR_OUTOFRANGE;
561                 idx = lpParms->dwTrack - toc.FirstTrack;
562                 lpParms->dwReturn = FRAME_OF_TOC(toc, lpParms->dwTrack + 1) -
563                     FRAME_OF_TOC(toc, lpParms->dwTrack);
564                 /* Windows returns one frame less than the total track length for the
565                    last track on the CD.  See CDDB HOWTO.  Verified on Win95OSR2. */
566                 if (lpParms->dwTrack == toc.LastTrack)
567                     lpParms->dwReturn--;
568             } else {
569                 /* Sum of the lengths of all of the tracks.  Inherits the
570                    'off by one frame' behavior from the length of the last track.
571                    See above comment. */
572                 lpParms->dwReturn = FRAME_OF_TOC(toc, toc.LastTrack + 1) -
573                     FRAME_OF_TOC(toc, toc.FirstTrack) - 1;
574             }
575             lpParms->dwReturn = MCICDA_CalcTime(wmcda,
576                                                  (wmcda->dwTimeFormat == MCI_FORMAT_TMSF)
577                                                     ? MCI_FORMAT_MSF : wmcda->dwTimeFormat,
578                                                  lpParms->dwReturn,
579                                                  &ret);
580             TRACE("LENGTH=%u !\n", lpParms->dwReturn);
581             break;
582         case MCI_STATUS_MODE:
583             lpParms->dwReturn = MCICDA_GetStatus(wmcda);
584             TRACE("MCI_STATUS_MODE=%08X !\n", lpParms->dwReturn);
585             lpParms->dwReturn = MAKEMCIRESOURCE(lpParms->dwReturn, lpParms->dwReturn);
586             ret = MCI_RESOURCE_RETURNED;
587             break;
588         case MCI_STATUS_MEDIA_PRESENT:
589             lpParms->dwReturn = (MCICDA_GetStatus(wmcda) == MCI_MODE_OPEN) ?
590                 MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
591             TRACE("MCI_STATUS_MEDIA_PRESENT =%c!\n", LOWORD(lpParms->dwReturn) ? 'Y' : 'N');
592             ret = MCI_RESOURCE_RETURNED;
593             break;
594         case MCI_STATUS_NUMBER_OF_TRACKS:
595             if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
596                                  &toc, sizeof(toc), &br, NULL)) {
597                 WARN("error reading TOC !\n");
598                 return MCICDA_GetError(wmcda);
599             }
600             lpParms->dwReturn = toc.LastTrack - toc.FirstTrack + 1;
601             TRACE("MCI_STATUS_NUMBER_OF_TRACKS = %u !\n", lpParms->dwReturn);
602             if (lpParms->dwReturn == (WORD)-1)
603                 return MCICDA_GetError(wmcda);
604             break;
605         case MCI_STATUS_POSITION:
606             if (dwFlags & MCI_STATUS_START) {
607                 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
608                                      &toc, sizeof(toc), &br, NULL)) {
609                     WARN("error reading TOC !\n");
610                     return MCICDA_GetError(wmcda);
611                 }
612                 lpParms->dwReturn = FRAME_OF_TOC(toc, toc.FirstTrack);
613                 TRACE("get MCI_STATUS_START !\n");
614             } else if (dwFlags & MCI_TRACK) {
615                 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
616                                      &toc, sizeof(toc), &br, NULL)) {
617                     WARN("error reading TOC !\n");
618                     return MCICDA_GetError(wmcda);
619                 }
620                 if (lpParms->dwTrack < toc.FirstTrack || lpParms->dwTrack > toc.LastTrack)
621                     return MCIERR_OUTOFRANGE;
622                 lpParms->dwReturn = FRAME_OF_TOC(toc, lpParms->dwTrack);
623                 TRACE("get MCI_TRACK #%u !\n", lpParms->dwTrack);
624             } else {
625                 fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
626                 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
627                                      &data, sizeof(data), &br, NULL)) {
628                     return MCICDA_GetError(wmcda);
629                 }
630                 lpParms->dwReturn = FRAME_OF_ADDR(data.CurrentPosition.AbsoluteAddress);
631             }
632             lpParms->dwReturn = MCICDA_CalcTime(wmcda, wmcda->dwTimeFormat, lpParms->dwReturn, &ret);
633             TRACE("MCI_STATUS_POSITION=%08X !\n", lpParms->dwReturn);
634             break;
635         case MCI_STATUS_READY:
636             TRACE("MCI_STATUS_READY !\n");
637             switch (MCICDA_GetStatus(wmcda))
638             {
639             case MCI_MODE_NOT_READY:
640             case MCI_MODE_OPEN:
641                 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
642                 break;
643             default:
644                 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
645                 break;
646             }
647             TRACE("MCI_STATUS_READY=%u!\n", LOWORD(lpParms->dwReturn));
648             ret = MCI_RESOURCE_RETURNED;
649             break;
650         case MCI_STATUS_TIME_FORMAT:
651             lpParms->dwReturn = MAKEMCIRESOURCE(wmcda->dwTimeFormat, MCI_FORMAT_RETURN_BASE + wmcda->dwTimeFormat);
652             TRACE("MCI_STATUS_TIME_FORMAT=%08x!\n", LOWORD(lpParms->dwReturn));
653             ret = MCI_RESOURCE_RETURNED;
654             break;
655         case 4001: /* FIXME: for bogus FullCD */
656         case MCI_CDA_STATUS_TYPE_TRACK:
657             if (!(dwFlags & MCI_TRACK))
658                 ret = MCIERR_MISSING_PARAMETER;
659             else {
660                 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
661                                      &toc, sizeof(toc), &br, NULL)) {
662                     WARN("error reading TOC !\n");
663                     return MCICDA_GetError(wmcda);
664                 }
665                 if (lpParms->dwTrack < toc.FirstTrack || lpParms->dwTrack > toc.LastTrack)
666                     ret = MCIERR_OUTOFRANGE;
667                 else
668                     lpParms->dwReturn = (toc.TrackData[lpParms->dwTrack - toc.FirstTrack].Control & 0x04) ?
669                                          MCI_CDA_TRACK_OTHER : MCI_CDA_TRACK_AUDIO;
670             }
671             TRACE("MCI_CDA_STATUS_TYPE_TRACK[%d]=%d\n", lpParms->dwTrack, lpParms->dwReturn);
672             break;
673         default:
674             FIXME("unknown command %08X !\n", lpParms->dwItem);
675             return MCIERR_UNRECOGNIZED_COMMAND;
676         }
677     } else {
678         WARN("not MCI_STATUS_ITEM !\n");
679     }
680     return ret;
681 }
682
683 /**************************************************************************
684  *                              MCICDA_SkipDataTracks           [internal]
685  */
686 static DWORD MCICDA_SkipDataTracks(WINE_MCICDAUDIO* wmcda,DWORD *frame)
687 {
688   int i;
689   DWORD br;
690   CDROM_TOC toc;
691   if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
692                       &toc, sizeof(toc), &br, NULL)) {
693     WARN("error reading TOC !\n");
694     return MCICDA_GetError(wmcda);
695   }
696   /* Locate first track whose starting frame is bigger than frame */
697   for(i=toc.FirstTrack;i<=toc.LastTrack+1;i++) 
698     if ( FRAME_OF_TOC(toc, i) > *frame ) break;
699   if (i <= toc.FirstTrack && i>toc.LastTrack+1) {
700     i = 0; /* requested address is out of range: go back to start */
701     *frame = FRAME_OF_TOC(toc,toc.FirstTrack);
702   }
703   else
704     i--;
705   /* i points to last track whose start address is not greater than frame.
706    * Now skip non-audio tracks */
707   for(;i<=toc.LastTrack+1;i++)
708     if ( ! (toc.TrackData[i-toc.FirstTrack].Control & 4) )
709       break;
710   /* The frame will be an address in the next audio track or
711    * address of lead-out. */
712   if ( FRAME_OF_TOC(toc, i) > *frame )
713     *frame = FRAME_OF_TOC(toc, i);
714   return 0;
715 }
716
717 /**************************************************************************
718  *                              MCICDA_Play                     [internal]
719  */
720 static DWORD MCICDA_Play(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
721 {
722     WINE_MCICDAUDIO*            wmcda = MCICDA_GetOpenDrv(wDevID);
723     DWORD                       ret = 0, start, end;
724     DWORD                       br;
725     CDROM_PLAY_AUDIO_MSF        play;
726     CDROM_SUB_Q_DATA_FORMAT     fmt;
727     SUB_Q_CHANNEL_DATA          data;
728     CDROM_TOC                   toc;
729
730     TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
731
732     if (lpParms == NULL)
733         return MCIERR_NULL_PARAMETER_BLOCK;
734
735     if (wmcda == NULL)
736         return MCIERR_INVALID_DEVICE_ID;
737
738     if (dwFlags & MCI_FROM) {
739         start = MCICDA_CalcFrame(wmcda, lpParms->dwFrom);
740         if ( (ret=MCICDA_SkipDataTracks(wmcda, &start)) )
741           return ret;
742         TRACE("MCI_FROM=%08X -> %u\n", lpParms->dwFrom, start);
743     } else {
744         fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
745         if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
746                              &data, sizeof(data), &br, NULL)) {
747             return MCICDA_GetError(wmcda);
748         }
749         start = FRAME_OF_ADDR(data.CurrentPosition.AbsoluteAddress);
750         if ( (ret=MCICDA_SkipDataTracks(wmcda, &start)) )
751           return ret;
752     }
753     if (dwFlags & MCI_TO) {
754         end = MCICDA_CalcFrame(wmcda, lpParms->dwTo);
755         TRACE("MCI_TO=%08X -> %u\n", lpParms->dwTo, end);
756     } else {
757         if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
758                              &toc, sizeof(toc), &br, NULL)) {
759             WARN("error reading TOC !\n");
760             return MCICDA_GetError(wmcda);
761         }
762         end = FRAME_OF_TOC(toc, toc.LastTrack + 1) - 1;
763     }
764     TRACE("Playing from %u to %u\n", start, end);
765     play.StartingM = start / CDFRAMES_PERMIN;
766     play.StartingS = (start / CDFRAMES_PERSEC) % 60;
767     play.StartingF = start % CDFRAMES_PERSEC;
768     play.EndingM   = end / CDFRAMES_PERMIN;
769     play.EndingS   = (end / CDFRAMES_PERSEC) % 60;
770     play.EndingF   = end % CDFRAMES_PERSEC;
771     if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_PLAY_AUDIO_MSF, &play, sizeof(play),
772                          NULL, 0, &br, NULL)) {
773         ret = MCIERR_HARDWARE;
774     } else if (dwFlags & MCI_NOTIFY) {
775         TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
776         /*
777           mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
778           wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
779         */
780     }
781     return ret;
782 }
783
784 /**************************************************************************
785  *                              MCICDA_Stop                     [internal]
786  */
787 static DWORD MCICDA_Stop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
788 {
789     WINE_MCICDAUDIO*    wmcda = MCICDA_GetOpenDrv(wDevID);
790     DWORD               br;
791
792     TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
793
794     if (wmcda == NULL)  return MCIERR_INVALID_DEVICE_ID;
795
796     if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_STOP_AUDIO, NULL, 0, NULL, 0, &br, NULL))
797         return MCIERR_HARDWARE;
798
799     if (lpParms && (dwFlags & MCI_NOTIFY)) {
800         TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
801         mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
802                         wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
803     }
804     return 0;
805 }
806
807 /**************************************************************************
808  *                              MCICDA_Pause                    [internal]
809  */
810 static DWORD MCICDA_Pause(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
811 {
812     WINE_MCICDAUDIO*    wmcda = MCICDA_GetOpenDrv(wDevID);
813     DWORD               br;
814
815     TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
816
817     if (wmcda == NULL)  return MCIERR_INVALID_DEVICE_ID;
818
819     if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_PAUSE_AUDIO, NULL, 0, NULL, 0, &br, NULL))
820         return MCIERR_HARDWARE;
821
822     if (lpParms && (dwFlags & MCI_NOTIFY)) {
823         TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
824         mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
825                         wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
826     }
827     return 0;
828 }
829
830 /**************************************************************************
831  *                              MCICDA_Resume                   [internal]
832  */
833 static DWORD MCICDA_Resume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
834 {
835     WINE_MCICDAUDIO*    wmcda = MCICDA_GetOpenDrv(wDevID);
836     DWORD               br;
837
838     TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
839
840     if (wmcda == NULL)  return MCIERR_INVALID_DEVICE_ID;
841
842     if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_RESUME_AUDIO, NULL, 0, NULL, 0, &br, NULL))
843         return MCIERR_HARDWARE;
844
845     if (lpParms && (dwFlags & MCI_NOTIFY)) {
846         TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
847         mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
848                         wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
849     }
850     return 0;
851 }
852
853 /**************************************************************************
854  *                              MCICDA_Seek                     [internal]
855  */
856 static DWORD MCICDA_Seek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
857 {
858     DWORD                       at;
859     WINE_MCICDAUDIO*            wmcda = MCICDA_GetOpenDrv(wDevID);
860     CDROM_SEEK_AUDIO_MSF        seek;
861     DWORD                       br, ret;
862     CDROM_TOC                   toc;
863
864     TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
865
866     if (wmcda == NULL)  return MCIERR_INVALID_DEVICE_ID;
867     if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
868
869     if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
870                          &toc, sizeof(toc), &br, NULL)) {
871         WARN("error reading TOC !\n");
872         return MCICDA_GetError(wmcda);
873     }
874     switch (dwFlags & ~(MCI_NOTIFY|MCI_WAIT)) {
875     case MCI_SEEK_TO_START:
876         TRACE("Seeking to start\n");
877         at = FRAME_OF_TOC(toc,toc.FirstTrack);
878         if ( (ret=MCICDA_SkipDataTracks(wmcda, &at)) )
879           return ret;
880         break;
881     case MCI_SEEK_TO_END:
882         TRACE("Seeking to end\n");
883         at = FRAME_OF_TOC(toc, toc.LastTrack + 1) - 1;
884         if ( (ret=MCICDA_SkipDataTracks(wmcda, &at)) )
885           return ret;
886         break;
887     case MCI_TO:
888         TRACE("Seeking to %u\n", lpParms->dwTo);
889         at = MCICDA_CalcFrame(wmcda, lpParms->dwTo);
890         if ( (ret=MCICDA_SkipDataTracks(wmcda, &at)) )
891           return ret;
892         break;
893     default:
894         TRACE("Unknown seek action %08lX\n",
895               (dwFlags & ~(MCI_NOTIFY|MCI_WAIT)));
896         return MCIERR_UNSUPPORTED_FUNCTION;
897     }
898     seek.M = at / CDFRAMES_PERMIN;
899     seek.S = (at / CDFRAMES_PERSEC) % 60;
900     seek.F = at % CDFRAMES_PERSEC;
901     if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_SEEK_AUDIO_MSF, &seek, sizeof(seek),
902                          NULL, 0, &br, NULL))
903         return MCIERR_HARDWARE;
904
905     if (dwFlags & MCI_NOTIFY) {
906         TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
907         mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
908                           wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
909     }
910     return 0;
911 }
912
913 /**************************************************************************
914  *                              MCICDA_SetDoor                  [internal]
915  */
916 static DWORD    MCICDA_SetDoor(UINT wDevID, BOOL open)
917 {
918     WINE_MCICDAUDIO*    wmcda = MCICDA_GetOpenDrv(wDevID);
919     DWORD               br;
920
921     TRACE("(%04x, %s) !\n", wDevID, (open) ? "OPEN" : "CLOSE");
922
923     if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
924
925     if (!DeviceIoControl(wmcda->handle,
926                          (open) ? IOCTL_STORAGE_EJECT_MEDIA : IOCTL_STORAGE_LOAD_MEDIA,
927                          NULL, 0, NULL, 0, &br, NULL))
928         return MCIERR_HARDWARE;
929
930     return 0;
931 }
932
933 /**************************************************************************
934  *                              MCICDA_Set                      [internal]
935  */
936 static DWORD MCICDA_Set(UINT wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
937 {
938     WINE_MCICDAUDIO*    wmcda = MCICDA_GetOpenDrv(wDevID);
939
940     TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
941
942     if (wmcda == NULL)  return MCIERR_INVALID_DEVICE_ID;
943
944     if (dwFlags & MCI_SET_DOOR_OPEN) {
945         MCICDA_SetDoor(wDevID, TRUE);
946     }
947     if (dwFlags & MCI_SET_DOOR_CLOSED) {
948         MCICDA_SetDoor(wDevID, FALSE);
949     }
950
951     /* only functions which require valid lpParms below this line ! */
952     if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
953     /*
954       TRACE("dwTimeFormat=%08lX\n", lpParms->dwTimeFormat);
955       TRACE("dwAudio=%08lX\n", lpParms->dwAudio);
956     */
957     if (dwFlags & MCI_SET_TIME_FORMAT) {
958         switch (lpParms->dwTimeFormat) {
959         case MCI_FORMAT_MILLISECONDS:
960             TRACE("MCI_FORMAT_MILLISECONDS !\n");
961             break;
962         case MCI_FORMAT_MSF:
963             TRACE("MCI_FORMAT_MSF !\n");
964             break;
965         case MCI_FORMAT_TMSF:
966             TRACE("MCI_FORMAT_TMSF !\n");
967             break;
968         default:
969             WARN("bad time format !\n");
970             return MCIERR_BAD_TIME_FORMAT;
971         }
972         wmcda->dwTimeFormat = lpParms->dwTimeFormat;
973     }
974     if (dwFlags & MCI_SET_VIDEO) return MCIERR_UNSUPPORTED_FUNCTION;
975     if (dwFlags & MCI_SET_ON) return MCIERR_UNSUPPORTED_FUNCTION;
976     if (dwFlags & MCI_SET_OFF) return MCIERR_UNSUPPORTED_FUNCTION;
977     if (dwFlags & MCI_NOTIFY) {
978         TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n",
979               lpParms->dwCallback);
980         mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
981                         wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
982     }
983     return 0;
984 }
985
986 /**************************************************************************
987  *                      DriverProc (MCICDA.@)
988  */
989 LRESULT CALLBACK MCICDA_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
990                                    LPARAM dwParam1, LPARAM dwParam2)
991 {
992     switch(wMsg) {
993     case DRV_LOAD:              return 1;
994     case DRV_FREE:              return 1;
995     case DRV_OPEN:              return MCICDA_drvOpen((LPCWSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSW)dwParam2);
996     case DRV_CLOSE:             return MCICDA_drvClose(dwDevID);
997     case DRV_ENABLE:            return 1;
998     case DRV_DISABLE:           return 1;
999     case DRV_QUERYCONFIGURE:    return 1;
1000     case DRV_CONFIGURE:         MessageBoxA(0, "MCI audio CD driver !", "Wine Driver", MB_OK); return 1;
1001     case DRV_INSTALL:           return DRVCNF_RESTART;
1002     case DRV_REMOVE:            return DRVCNF_RESTART;
1003     }
1004
1005     if (dwDevID == 0xFFFFFFFF) return MCIERR_UNSUPPORTED_FUNCTION;
1006
1007     switch (wMsg) {
1008     case MCI_OPEN_DRIVER:       return MCICDA_Open(dwDevID, dwParam1, (LPMCI_OPEN_PARMSW)dwParam2);
1009     case MCI_CLOSE_DRIVER:      return MCICDA_Close(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
1010     case MCI_GETDEVCAPS:        return MCICDA_GetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS)dwParam2);
1011     case MCI_INFO:              return MCICDA_Info(dwDevID, dwParam1, (LPMCI_INFO_PARMSW)dwParam2);
1012     case MCI_STATUS:            return MCICDA_Status(dwDevID, dwParam1, (LPMCI_STATUS_PARMS)dwParam2);
1013     case MCI_SET:               return MCICDA_Set(dwDevID, dwParam1, (LPMCI_SET_PARMS)dwParam2);
1014     case MCI_PLAY:              return MCICDA_Play(dwDevID, dwParam1, (LPMCI_PLAY_PARMS)dwParam2);
1015     case MCI_STOP:              return MCICDA_Stop(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
1016     case MCI_PAUSE:             return MCICDA_Pause(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
1017     case MCI_RESUME:            return MCICDA_Resume(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
1018     case MCI_SEEK:              return MCICDA_Seek(dwDevID, dwParam1, (LPMCI_SEEK_PARMS)dwParam2);
1019     /* commands that should report an error as they are not supported in
1020      * the native version */
1021     case MCI_SET_DOOR_CLOSED:
1022     case MCI_SET_DOOR_OPEN:
1023     case MCI_LOAD:
1024     case MCI_SAVE:
1025     case MCI_FREEZE:
1026     case MCI_PUT:
1027     case MCI_REALIZE:
1028     case MCI_UNFREEZE:
1029     case MCI_UPDATE:
1030     case MCI_WHERE:
1031     case MCI_STEP:
1032     case MCI_SPIN:
1033     case MCI_ESCAPE:
1034     case MCI_COPY:
1035     case MCI_CUT:
1036     case MCI_DELETE:
1037     case MCI_PASTE:
1038     case MCI_WINDOW:
1039         TRACE("Unsupported command [0x%x]\n", wMsg);
1040         break;
1041     case MCI_OPEN:
1042     case MCI_CLOSE:
1043         ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1044         break;
1045     default:
1046         TRACE("Sending msg [0x%x] to default driver proc\n", wMsg);
1047         return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1048     }
1049     return MCIERR_UNRECOGNIZED_COMMAND;
1050 }
1051
1052 /*-----------------------------------------------------------------------*/