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