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