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