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