Add <string.h> to files that needed it.
[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 "cdrom.h"
20 #include "debugtools.h"
21
22 DEFAULT_DEBUG_CHANNEL(mcicda);
23
24 typedef struct {
25     UINT                wDevID;
26     int                 nUseCount;          /* Incremented for each shared open */
27     BOOL                fShareable;         /* TRUE if first open was shareable */
28     WORD                wNotifyDeviceID;    /* MCI device ID with a pending notification */
29     HANDLE              hCallback;          /* Callback handle for pending notification */
30     DWORD               dwTimeFormat;
31     WINE_CDAUDIO        wcda;
32     int                 mciMode;
33 } WINE_MCICDAUDIO;
34
35 /*-----------------------------------------------------------------------*/
36
37 /**************************************************************************
38  *                              MCICDA_drvOpen                  [internal]      
39  */
40 static  DWORD   MCICDA_drvOpen(LPSTR str, LPMCI_OPEN_DRIVER_PARMSA modp)
41 {
42     WINE_MCICDAUDIO*    wmcda = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,  sizeof(WINE_MCICDAUDIO));
43
44     if (!wmcda)
45         return 0;
46
47     wmcda->wDevID = modp->wDeviceID;
48     mciSetDriverData(wmcda->wDevID, (DWORD)wmcda);
49     modp->wCustomCommandTable = MCI_NO_COMMAND_TABLE;
50     modp->wType = MCI_DEVTYPE_CD_AUDIO;
51     return modp->wDeviceID;
52 }
53
54 /**************************************************************************
55  *                              MCICDA_drvClose                 [internal]      
56  */
57 static  DWORD   MCICDA_drvClose(DWORD dwDevID)
58 {
59     WINE_MCICDAUDIO*  wmcda = (WINE_MCICDAUDIO*)mciGetDriverData(dwDevID);
60
61     if (wmcda) {
62         HeapFree(GetProcessHeap(), 0, wmcda);
63         mciSetDriverData(dwDevID, 0);
64     }
65     return 0;
66 }
67
68 /**************************************************************************
69  *                              MCICDA_GetOpenDrv               [internal]      
70  */
71 static WINE_MCICDAUDIO*  MCICDA_GetOpenDrv(UINT wDevID)
72 {
73     WINE_MCICDAUDIO*    wmcda = (WINE_MCICDAUDIO*)mciGetDriverData(wDevID);
74     
75     if (wmcda == NULL || wmcda->nUseCount == 0) {
76         WARN("Invalid wDevID=%u\n", wDevID);
77         return 0;
78     }
79     return wmcda;
80 }
81
82 /**************************************************************************
83  *                              MCICDA_Mode                     [internal]
84  */
85 static  int     MCICDA_Mode(int wcdaMode)
86 {
87     switch (wcdaMode) {
88     case WINE_CDA_DONTKNOW:     return MCI_MODE_STOP;
89     case WINE_CDA_NOTREADY:     return MCI_MODE_STOP;
90     case WINE_CDA_OPEN:         return MCI_MODE_OPEN;
91     case WINE_CDA_PLAY:         return MCI_MODE_PLAY;
92     case WINE_CDA_STOP:         return MCI_MODE_STOP;
93     case WINE_CDA_PAUSE:        return MCI_MODE_PAUSE;
94     default:
95         FIXME("Unknown mode %04x\n", wcdaMode);
96     }
97     return MCI_MODE_STOP;
98 }
99
100 /**************************************************************************
101  *                              MCICDA_GetError                 [internal]
102  */
103 static  int     MCICDA_GetError(WINE_MCICDAUDIO* wmcda)
104 {
105     switch (wmcda->wcda.cdaMode) {
106     case WINE_CDA_DONTKNOW:
107     case WINE_CDA_NOTREADY:     return MCIERR_DEVICE_NOT_READY;
108     case WINE_CDA_OPEN:         return MCIERR_HARDWARE;
109     case WINE_CDA_PLAY:         
110     case WINE_CDA_STOP:         
111     case WINE_CDA_PAUSE:        break;
112     default:
113         FIXME("Unknown mode %04x\n", wmcda->wcda.cdaMode);
114     }
115     return MCIERR_DRIVER_INTERNAL;
116 }
117
118 /**************************************************************************
119  *                      MCICDA_CalcFrame                        [internal]
120  */
121 static DWORD MCICDA_CalcFrame(WINE_MCICDAUDIO* wmcda, DWORD dwTime)
122 {
123     DWORD       dwFrame = 0;
124     UINT        wTrack;
125     
126     TRACE("(%p, %08lX, %lu);\n", wmcda, wmcda->dwTimeFormat, dwTime);
127     
128     switch (wmcda->dwTimeFormat) {
129     case MCI_FORMAT_MILLISECONDS:
130         dwFrame = ((dwTime - 1) * CDFRAMES_PERSEC + 500) / 1000;
131         TRACE("MILLISECONDS %lu\n", dwFrame);
132         break;
133     case MCI_FORMAT_MSF:
134         TRACE("MSF %02u:%02u:%02u\n",
135               MCI_MSF_MINUTE(dwTime), MCI_MSF_SECOND(dwTime), MCI_MSF_FRAME(dwTime));
136         dwFrame += CDFRAMES_PERMIN * MCI_MSF_MINUTE(dwTime);
137         dwFrame += CDFRAMES_PERSEC * MCI_MSF_SECOND(dwTime);
138         dwFrame += MCI_MSF_FRAME(dwTime);
139         break;
140     case MCI_FORMAT_TMSF:
141     default: /* unknown format ! force TMSF ! ... */
142         wTrack = MCI_TMSF_TRACK(dwTime);
143         TRACE("MSF %02u-%02u:%02u:%02u\n",
144               MCI_TMSF_TRACK(dwTime), MCI_TMSF_MINUTE(dwTime), 
145               MCI_TMSF_SECOND(dwTime), MCI_TMSF_FRAME(dwTime));
146         TRACE("TMSF trackpos[%u]=%lu\n",
147               wTrack, wmcda->wcda.lpdwTrackPos[wTrack - 1]);
148         dwFrame = wmcda->wcda.lpdwTrackPos[wTrack - 1];
149         dwFrame += CDFRAMES_PERMIN * MCI_TMSF_MINUTE(dwTime);
150         dwFrame += CDFRAMES_PERSEC * MCI_TMSF_SECOND(dwTime);
151         dwFrame += MCI_TMSF_FRAME(dwTime);
152         break;
153     }
154     return dwFrame;
155 }
156
157 /**************************************************************************
158  *                      MCICDA_CalcTime                         [internal]
159  */
160 static DWORD MCICDA_CalcTime(WINE_MCICDAUDIO* wmcda, DWORD tf, DWORD dwFrame, 
161                               LPDWORD lpRet)
162 {
163     DWORD       dwTime = 0;
164     UINT        wTrack;
165     UINT        wMinutes;
166     UINT        wSeconds;
167     UINT        wFrames;
168     
169     TRACE("(%p, %08lX, %lu);\n", wmcda, tf, dwFrame);
170     
171     switch (tf) {
172     case MCI_FORMAT_MILLISECONDS:
173         dwTime = (dwFrame * 1000) / CDFRAMES_PERSEC + 1;
174         TRACE("MILLISECONDS %lu\n", dwTime);
175         *lpRet = 0;
176         break;
177     case MCI_FORMAT_MSF:
178         wMinutes = dwFrame / CDFRAMES_PERMIN;
179         wSeconds = (dwFrame - CDFRAMES_PERMIN * wMinutes) / CDFRAMES_PERSEC;
180         wFrames = dwFrame - CDFRAMES_PERMIN * wMinutes - CDFRAMES_PERSEC * wSeconds;
181         dwTime = MCI_MAKE_MSF(wMinutes, wSeconds, wFrames);
182         TRACE("MSF %02u:%02u:%02u -> dwTime=%lu\n",
183               wMinutes, wSeconds, wFrames, dwTime);
184         *lpRet = MCI_COLONIZED3_RETURN;
185         break;
186     case MCI_FORMAT_TMSF:
187     default:    /* unknown format ! force TMSF ! ... */
188         if (dwFrame < wmcda->wcda.dwFirstFrame || dwFrame > wmcda->wcda.dwLastFrame) {
189             ERR("Out of range value %lu [%lu,%lu]\n", 
190                 dwFrame, wmcda->wcda.dwFirstFrame, wmcda->wcda.dwLastFrame);
191             *lpRet = 0;
192             return 0;
193         }
194         for (wTrack = 1; wTrack < wmcda->wcda.nTracks; wTrack++) {
195             if (wmcda->wcda.lpdwTrackPos[wTrack] > dwFrame)
196                 break;
197         }
198         dwFrame -= wmcda->wcda.lpdwTrackPos[wTrack - 1];
199         wMinutes = dwFrame / CDFRAMES_PERMIN;
200         wSeconds = (dwFrame - CDFRAMES_PERMIN * wMinutes) / CDFRAMES_PERSEC;
201         wFrames = dwFrame - CDFRAMES_PERMIN * wMinutes - CDFRAMES_PERSEC * wSeconds;
202         dwTime = MCI_MAKE_TMSF(wTrack, wMinutes, wSeconds, wFrames);
203         TRACE("%02u-%02u:%02u:%02u\n", wTrack, wMinutes, wSeconds, wFrames);
204         *lpRet = MCI_COLONIZED4_RETURN;
205         break;
206     }
207     return dwTime;
208 }
209
210 static DWORD MCICDA_Seek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms);
211 static DWORD MCICDA_Stop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms);
212
213 /**************************************************************************
214  *                              MCICDA_Open                     [internal]
215  */
216 static DWORD MCICDA_Open(UINT wDevID, DWORD dwFlags, LPMCI_OPEN_PARMSA lpOpenParms)
217 {
218     DWORD               dwDeviceID;
219     WINE_MCICDAUDIO*    wmcda = (WINE_MCICDAUDIO*)mciGetDriverData(wDevID);
220     MCI_SEEK_PARMS      seekParms;
221     int dev;
222
223     TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpOpenParms);
224     
225     if (lpOpenParms == NULL)            return MCIERR_NULL_PARAMETER_BLOCK;
226     if (wmcda == NULL)                  return MCIERR_INVALID_DEVICE_ID;
227
228     dwDeviceID = lpOpenParms->wDeviceID;
229
230     if (wmcda->nUseCount > 0) {
231         /* The driver is already open on this channel */
232         /* If the driver was opened shareable before and this open specifies */
233         /* shareable then increment the use count */
234         if (wmcda->fShareable && (dwFlags & MCI_OPEN_SHAREABLE))
235             ++wmcda->nUseCount;
236         else
237             return MCIERR_MUST_USE_SHAREABLE;
238     } else {
239         wmcda->nUseCount = 1;
240         wmcda->fShareable = dwFlags & MCI_OPEN_SHAREABLE;
241     }
242     if (dwFlags & MCI_OPEN_ELEMENT) {
243         if (dwFlags & MCI_OPEN_ELEMENT_ID) {
244             WARN("MCI_OPEN_ELEMENT_ID %8lx ! Abort", (DWORD)lpOpenParms->lpstrElementName);
245             return MCIERR_NO_ELEMENT_ALLOWED;
246         }
247         WARN("MCI_OPEN_ELEMENT %s ignored\n",lpOpenParms->lpstrElementName);
248         /*return MCIERR_NO_ELEMENT_ALLOWED; 
249           bon 19991106 allows cdplayer.exe to run*/
250     }
251
252     wmcda->wNotifyDeviceID = dwDeviceID;
253     if (CDROM_Open(&wmcda->wcda, -1) == -1) {
254         --wmcda->nUseCount;
255         return MCIERR_HARDWARE;
256     }
257     wmcda->mciMode = MCI_MODE_STOP;
258     wmcda->dwTimeFormat = MCI_FORMAT_MSF;
259     
260     dev = CDROM_OpenDev(&wmcda->wcda);
261     if (!CDROM_Audio_GetTracksInfo(&wmcda->wcda, dev)) {
262         wmcda->mciMode = MCI_MODE_OPEN;
263     } else {
264         MCICDA_Seek(wDevID, MCI_SEEK_TO_START, &seekParms);
265     }
266     CDROM_CloseDev(dev);
267
268     return 0;
269 }
270
271 /**************************************************************************
272  *                              MCICDA_Close                    [internal]
273  */
274 static DWORD MCICDA_Close(UINT wDevID, DWORD dwParam, LPMCI_GENERIC_PARMS lpParms)
275 {
276     WINE_MCICDAUDIO*    wmcda = MCICDA_GetOpenDrv(wDevID);
277
278     TRACE("(%04X, %08lX, %p);\n", wDevID, dwParam, lpParms);
279
280     if (wmcda == NULL)  return MCIERR_INVALID_DEVICE_ID;
281     
282     if (wmcda->nUseCount == 1) {
283         CDROM_Close(&wmcda->wcda);
284     }
285     wmcda->nUseCount--;
286     return 0;
287 }
288
289 /**************************************************************************
290  *                              MCICDA_GetDevCaps               [internal]
291  */
292 static DWORD MCICDA_GetDevCaps(UINT wDevID, DWORD dwFlags, 
293                                    LPMCI_GETDEVCAPS_PARMS lpParms)
294 {
295     DWORD       ret = 0;
296
297     TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
298
299     if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
300
301     if (dwFlags & MCI_GETDEVCAPS_ITEM) {
302         TRACE("MCI_GETDEVCAPS_ITEM dwItem=%08lX;\n", lpParms->dwItem);
303
304         switch (lpParms->dwItem) {
305         case MCI_GETDEVCAPS_CAN_RECORD:
306             lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
307             ret = MCI_RESOURCE_RETURNED;
308             break;
309         case MCI_GETDEVCAPS_HAS_AUDIO:
310             lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
311             ret = MCI_RESOURCE_RETURNED;
312             break;
313         case MCI_GETDEVCAPS_HAS_VIDEO:
314             lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
315             ret = MCI_RESOURCE_RETURNED;
316             break;
317         case MCI_GETDEVCAPS_DEVICE_TYPE:
318             lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_CD_AUDIO, MCI_DEVTYPE_CD_AUDIO);
319             ret = MCI_RESOURCE_RETURNED;
320             break;
321         case MCI_GETDEVCAPS_USES_FILES:
322             lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
323             ret = MCI_RESOURCE_RETURNED;
324             break;
325         case MCI_GETDEVCAPS_COMPOUND_DEVICE:
326             lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
327             ret = MCI_RESOURCE_RETURNED;
328             break;
329         case MCI_GETDEVCAPS_CAN_EJECT:
330             lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
331             ret = MCI_RESOURCE_RETURNED;
332             break;
333         case MCI_GETDEVCAPS_CAN_PLAY:
334             lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
335             ret = MCI_RESOURCE_RETURNED;
336             break;
337         case MCI_GETDEVCAPS_CAN_SAVE:
338             lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
339             ret = MCI_RESOURCE_RETURNED;
340             break;
341         default:
342             ERR("Unsupported %lx devCaps item\n", lpParms->dwItem);
343             return MCIERR_UNRECOGNIZED_COMMAND;
344         }
345     } else {
346         TRACE("No GetDevCaps-Item !\n");
347         return MCIERR_UNRECOGNIZED_COMMAND;
348     }
349     TRACE("lpParms->dwReturn=%08lX;\n", lpParms->dwReturn);
350     return ret;
351 }
352
353 /**************************************************************************
354  *                              MCICDA_Info                     [internal]
355  */
356 static DWORD MCICDA_Info(UINT wDevID, DWORD dwFlags, LPMCI_INFO_PARMSA lpParms)
357 {
358     LPSTR               str = NULL;
359     WINE_MCICDAUDIO*    wmcda = MCICDA_GetOpenDrv(wDevID);
360     DWORD               ret = 0;
361     char                buffer[16];
362
363     TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
364     
365     if (lpParms == NULL || lpParms->lpstrReturn == NULL)
366         return MCIERR_NULL_PARAMETER_BLOCK;
367     if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
368
369     TRACE("buf=%p, len=%lu\n", lpParms->lpstrReturn, lpParms->dwRetSize);
370     
371     if (dwFlags & MCI_INFO_PRODUCT) {
372         str = "Wine's audio CD";
373     } else if (dwFlags & MCI_INFO_MEDIA_UPC) {
374         ret = MCIERR_NO_IDENTITY;
375     } else if (dwFlags & MCI_INFO_MEDIA_IDENTITY) {
376         DWORD   res = 0;
377         int dev = CDROM_OpenDev(&wmcda->wcda);
378
379         if (!CDROM_Audio_GetCDStatus(&wmcda->wcda, dev)) {
380             CDROM_CloseDev(dev);
381             return MCICDA_GetError(wmcda);
382         }
383         CDROM_CloseDev(dev);
384
385         res = CDROM_Audio_GetSerial(&wmcda->wcda);
386         if (wmcda->wcda.nTracks <= 2) {
387             /* there are some other values added when # of tracks < 3
388              * for most Audio CD it will do without
389              */
390             FIXME("Value is not correct !! "
391                   "Please report with full audio CD information (-debugmsg +cdrom,mcicda)\n");
392         }
393         sprintf(buffer, "%lu", res);
394         str = buffer;
395     } else {
396         WARN("Don't know this info command (%lu)\n", dwFlags);
397         ret = MCIERR_UNRECOGNIZED_COMMAND;
398     }
399     if (str) {
400         if (lpParms->dwRetSize <= strlen(str)) {
401             lstrcpynA(lpParms->lpstrReturn, str, lpParms->dwRetSize - 1);
402             ret = MCIERR_PARAM_OVERFLOW;
403         } else {
404             strcpy(lpParms->lpstrReturn, str);
405         }       
406     } else {
407         *lpParms->lpstrReturn = 0;
408     }
409     TRACE("=> %s (%ld)\n", lpParms->lpstrReturn, ret);
410     return ret;
411 }
412
413 /**************************************************************************
414  *                              MCICDA_Status                   [internal]
415  */
416 static DWORD MCICDA_Status(UINT wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
417 {
418     WINE_MCICDAUDIO*    wmcda = MCICDA_GetOpenDrv(wDevID);
419     DWORD               ret = 0;
420
421     TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
422     
423     if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
424     if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
425
426     if (dwFlags & MCI_NOTIFY) {
427         TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
428         mciDriverNotify((HWND)LOWORD(lpParms->dwCallback), 
429                         wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
430     }
431     if (dwFlags & MCI_STATUS_ITEM) {
432         TRACE("dwItem = %lx\n", lpParms->dwItem);
433         switch (lpParms->dwItem) {
434         case MCI_STATUS_CURRENT_TRACK:
435             if (!CDROM_Audio_GetCDStatus(&wmcda->wcda, -1)) {
436                 return MCICDA_GetError(wmcda);
437             }
438             lpParms->dwReturn = wmcda->wcda.nCurTrack;
439             TRACE("CURRENT_TRACK=%lu!\n", lpParms->dwReturn);
440             break;
441         case MCI_STATUS_LENGTH:
442             if (wmcda->wcda.nTracks == 0) {
443                 if (!CDROM_Audio_GetTracksInfo(&wmcda->wcda, -1)) {
444                     WARN("error reading TracksInfo !\n");
445                     return MCICDA_GetError(wmcda);
446                 }
447             }
448             if (dwFlags & MCI_TRACK) {
449                 TRACE("MCI_TRACK #%lu LENGTH=??? !\n", lpParms->dwTrack);
450                 if (lpParms->dwTrack > wmcda->wcda.nTracks || lpParms->dwTrack == 0)
451                     return MCIERR_OUTOFRANGE;
452                 lpParms->dwReturn = wmcda->wcda.lpdwTrackLen[lpParms->dwTrack - 1];
453                 /* Windows returns one frame less than the total track length for the
454                    last track on the CD.  See CDDB HOWTO.  Verified on Win95OSR2. */
455                 if (lpParms->dwTrack == wmcda->wcda.nTracks)
456                     lpParms->dwReturn--;
457             } else {
458                 /* Sum of the lengths of all of the tracks.  Inherits the
459                    'off by one frame' behavior from the length of the last track.
460                    See above comment. */
461                 lpParms->dwReturn = wmcda->wcda.dwLastFrame - wmcda->wcda.dwFirstFrame - 1;
462             }
463             lpParms->dwReturn = MCICDA_CalcTime(wmcda, 
464                                                  (wmcda->dwTimeFormat == MCI_FORMAT_TMSF) 
465                                                     ? MCI_FORMAT_MSF : wmcda->dwTimeFormat,
466                                                  lpParms->dwReturn,
467                                                  &ret);
468             TRACE("LENGTH=%lu !\n", lpParms->dwReturn);
469             break;
470         case MCI_STATUS_MODE:
471             CDROM_Audio_GetCDStatus(&wmcda->wcda, -1);
472             lpParms->dwReturn = MCICDA_Mode(wmcda->wcda.cdaMode);
473             if (!lpParms->dwReturn) lpParms->dwReturn = wmcda->mciMode;
474             TRACE("MCI_STATUS_MODE=%08lX !\n", lpParms->dwReturn);
475             lpParms->dwReturn = MAKEMCIRESOURCE(lpParms->dwReturn, lpParms->dwReturn);
476             ret = MCI_RESOURCE_RETURNED;
477             break;
478         case MCI_STATUS_MEDIA_PRESENT:
479             CDROM_Audio_GetCDStatus(&wmcda->wcda, -1);
480             lpParms->dwReturn = (wmcda->wcda.nTracks == 0 || 
481                                  wmcda->wcda.cdaMode == WINE_CDA_OPEN) ?
482                 MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
483             TRACE("MCI_STATUS_MEDIA_PRESENT =%c!\n", LOWORD(lpParms->dwReturn) ? 'Y' : 'N');
484             ret = MCI_RESOURCE_RETURNED;
485             break;
486         case MCI_STATUS_NUMBER_OF_TRACKS:
487             lpParms->dwReturn = CDROM_Audio_GetNumberOfTracks(&wmcda->wcda, -1);
488             TRACE("MCI_STATUS_NUMBER_OF_TRACKS = %lu !\n", lpParms->dwReturn);
489             if (lpParms->dwReturn == (WORD)-1) 
490                 return MCICDA_GetError(wmcda);
491             break;
492         case MCI_STATUS_POSITION:
493             if (!CDROM_Audio_GetCDStatus(&wmcda->wcda, -1)) 
494                 return MCICDA_GetError(wmcda);
495             if(wmcda->wcda.cdaMode == WINE_CDA_OPEN)
496                 return MCIERR_HARDWARE;
497             lpParms->dwReturn = wmcda->wcda.dwCurFrame;
498             if (dwFlags & MCI_STATUS_START) {
499                 lpParms->dwReturn = wmcda->wcda.dwFirstFrame;
500                 TRACE("get MCI_STATUS_START !\n");
501             }
502             if (dwFlags & MCI_TRACK) {
503                 if (lpParms->dwTrack > wmcda->wcda.nTracks || lpParms->dwTrack == 0)
504                     return MCIERR_OUTOFRANGE;
505                 lpParms->dwReturn = wmcda->wcda.lpdwTrackPos[lpParms->dwTrack - 1];
506                 TRACE("get MCI_TRACK #%lu !\n", lpParms->dwTrack);
507             }
508             lpParms->dwReturn = MCICDA_CalcTime(wmcda, wmcda->dwTimeFormat, lpParms->dwReturn, &ret);
509             TRACE("MCI_STATUS_POSITION=%08lX !\n", lpParms->dwReturn);
510             break;
511         case MCI_STATUS_READY:
512             TRACE("MCI_STATUS_READY !\n");
513             lpParms->dwReturn = (wmcda->wcda.cdaMode == WINE_CDA_DONTKNOW ||
514                                  wmcda->wcda.cdaMode == WINE_CDA_NOTREADY) ?
515                 MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
516             TRACE("MCI_STATUS_READY=%u!\n", LOWORD(lpParms->dwReturn));
517             ret = MCI_RESOURCE_RETURNED;
518             break;
519         case MCI_STATUS_TIME_FORMAT:
520             lpParms->dwReturn = MAKEMCIRESOURCE(wmcda->dwTimeFormat, wmcda->dwTimeFormat);
521             TRACE("MCI_STATUS_TIME_FORMAT=%08x!\n", LOWORD(lpParms->dwReturn));
522             ret = MCI_RESOURCE_RETURNED;
523             break;
524         case 4001: /* FIXME: for bogus FullCD */
525         case MCI_CDA_STATUS_TYPE_TRACK:
526             if (!(dwFlags & MCI_TRACK)) 
527                 ret = MCIERR_MISSING_PARAMETER;
528             else {
529                 if(!CDROM_Audio_GetTracksInfo(&wmcda->wcda, -1)) {
530                     WARN("Error reading tracks info\n");
531                     return MCICDA_GetError(wmcda);
532                 }
533                 if (lpParms->dwTrack > wmcda->wcda.nTracks || lpParms->dwTrack == 0)
534                     ret = MCIERR_OUTOFRANGE;
535                 else
536                     lpParms->dwReturn = (wmcda->wcda.lpbTrackFlags[lpParms->dwTrack - 1] &
537                                      CDROM_DATA_TRACK) ? MCI_CDA_TRACK_OTHER : MCI_CDA_TRACK_AUDIO;
538             }
539             TRACE("MCI_CDA_STATUS_TYPE_TRACK[%ld]=%08lx\n", lpParms->dwTrack, lpParms->dwReturn);
540             break;
541         default:
542             FIXME("unknown command %08lX !\n", lpParms->dwItem);
543             return MCIERR_UNRECOGNIZED_COMMAND;
544         }
545     } else {
546         WARN("not MCI_STATUS_ITEM !\n");
547     }
548     return ret;
549 }
550
551 /**************************************************************************
552  *                              MCICDA_Play                     [internal]
553  */
554 static DWORD MCICDA_Play(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
555 {
556     int                 start, end;
557     WINE_MCICDAUDIO*    wmcda = MCICDA_GetOpenDrv(wDevID);
558     DWORD               ret = 0;
559     int dev = -1;
560     
561     TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
562     
563     if (lpParms == NULL)
564         return MCIERR_NULL_PARAMETER_BLOCK;
565
566     if (wmcda == NULL)
567         return MCIERR_INVALID_DEVICE_ID;
568
569     dev = CDROM_OpenDev(&wmcda->wcda);
570     if (wmcda->wcda.nTracks == 0) {
571         if (!CDROM_Audio_GetTracksInfo(&wmcda->wcda, dev)) {
572             WARN("error reading TracksInfo !\n");
573             ret = MCIERR_DRIVER_INTERNAL;
574             goto end;
575         }
576     }
577     wmcda->wcda.nCurTrack = 1;
578     if (dwFlags & MCI_FROM) {
579         start = MCICDA_CalcFrame(wmcda, lpParms->dwFrom);
580         TRACE("MCI_FROM=%08lX -> %u \n", lpParms->dwFrom, start);
581     } else {
582         if (!CDROM_Audio_GetCDStatus(&wmcda->wcda, dev))
583         {
584             ret = MCIERR_DRIVER_INTERNAL;
585             goto end;
586         }
587         start = wmcda->wcda.dwCurFrame;
588     }
589     if (dwFlags & MCI_TO) {
590         end = MCICDA_CalcFrame(wmcda, lpParms->dwTo);
591         TRACE("MCI_TO=%08lX -> %u \n", lpParms->dwTo, end);
592     } else
593         end = wmcda->wcda.dwLastFrame;
594
595     if (CDROM_Audio_Play(&wmcda->wcda, start, end, dev) == -1)
596     {
597         ret = MCIERR_HARDWARE;
598         goto end;
599     }
600     wmcda->mciMode = MCI_MODE_PLAY;
601     if (dwFlags & MCI_NOTIFY) {
602         TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
603         /*
604           mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
605           wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
606         */
607     }
608 end:
609     if (dev != -1)
610         CDROM_CloseDev(dev);
611     return ret;
612 }
613
614 /**************************************************************************
615  *                              MCICDA_Stop                     [internal]
616  */
617 static DWORD MCICDA_Stop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
618 {
619     WINE_MCICDAUDIO*    wmcda = MCICDA_GetOpenDrv(wDevID);
620     
621     TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
622     
623     if (wmcda == NULL)  return MCIERR_INVALID_DEVICE_ID;
624     
625     if (CDROM_Audio_Stop(&wmcda->wcda, -1) == -1)
626         return MCIERR_HARDWARE;
627
628     wmcda->mciMode = MCI_MODE_STOP;
629     if (lpParms && (dwFlags & MCI_NOTIFY)) {
630         TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
631         mciDriverNotify((HWND)LOWORD(lpParms->dwCallback), 
632                         wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
633     }
634     return 0;
635 }
636
637 /**************************************************************************
638  *                              MCICDA_Pause                    [internal]
639  */
640 static DWORD MCICDA_Pause(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
641 {
642     WINE_MCICDAUDIO*    wmcda = MCICDA_GetOpenDrv(wDevID);
643     
644     TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
645     
646     if (wmcda == NULL)  return MCIERR_INVALID_DEVICE_ID;
647     
648     if (CDROM_Audio_Pause(&wmcda->wcda, 1, -1) == -1)
649         return MCIERR_HARDWARE;
650     wmcda->mciMode = MCI_MODE_PAUSE;
651     if (lpParms && (dwFlags & MCI_NOTIFY)) {
652         TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
653         mciDriverNotify((HWND)LOWORD(lpParms->dwCallback), 
654                         wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
655     }
656     return 0;
657 }
658
659 /**************************************************************************
660  *                              MCICDA_Resume                   [internal]
661  */
662 static DWORD MCICDA_Resume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
663 {
664     WINE_MCICDAUDIO*    wmcda = MCICDA_GetOpenDrv(wDevID);
665     
666     TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
667     
668     if (wmcda == NULL)  return MCIERR_INVALID_DEVICE_ID;
669     
670     if (CDROM_Audio_Pause(&wmcda->wcda, 0, -1) == -1)
671         return MCIERR_HARDWARE;
672     wmcda->mciMode = MCI_MODE_STOP;
673     if (lpParms && (dwFlags & MCI_NOTIFY)) {
674         TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
675         mciDriverNotify((HWND)LOWORD(lpParms->dwCallback), 
676                         wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
677     }
678     return 0;
679 }
680
681 /**************************************************************************
682  *                              MCICDA_Seek                     [internal]
683  */
684 static DWORD MCICDA_Seek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
685 {
686     DWORD               at;
687     WINE_MCICDAUDIO*    wmcda = MCICDA_GetOpenDrv(wDevID);
688     
689     TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
690     
691     if (wmcda == NULL)  return MCIERR_INVALID_DEVICE_ID;
692     if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
693     
694     wmcda->mciMode = MCI_MODE_SEEK;
695     switch (dwFlags & ~(MCI_NOTIFY|MCI_WAIT)) {
696     case MCI_SEEK_TO_START:
697         TRACE("Seeking to start\n");
698         at = wmcda->wcda.dwFirstFrame;
699         break;
700     case MCI_SEEK_TO_END:
701         TRACE("Seeking to end\n");
702         at = wmcda->wcda.dwLastFrame;
703         break;
704     case MCI_TO:
705         TRACE("Seeking to %lu\n", lpParms->dwTo);
706         at = lpParms->dwTo;
707         break;
708     default:
709         TRACE("Seeking to ??=%lu\n", dwFlags);
710         return MCIERR_UNSUPPORTED_FUNCTION;
711     }
712     if (CDROM_Audio_Seek(&wmcda->wcda, at, -1) == -1) {
713         return MCIERR_HARDWARE;
714     }
715     if (dwFlags & MCI_NOTIFY) {
716         TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
717         mciDriverNotify((HWND)LOWORD(lpParms->dwCallback), 
718                           wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
719     }
720     return 0;
721 }
722
723 /**************************************************************************
724  *                              MCICDA_SetDoor                  [internal]
725  */
726 static DWORD    MCICDA_SetDoor(UINT wDevID, int open)
727 {
728     WINE_MCICDAUDIO*    wmcda = MCICDA_GetOpenDrv(wDevID);
729     
730     TRACE("(%04x, %s) !\n", wDevID, (open) ? "OPEN" : "CLOSE");
731     
732     if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
733     
734     if (CDROM_SetDoor(&wmcda->wcda, open, -1) == -1)
735         return MCIERR_HARDWARE;
736     wmcda->mciMode = (open) ? MCI_MODE_OPEN : MCI_MODE_STOP;
737     return 0;
738 }
739
740 /**************************************************************************
741  *                              MCICDA_Set                      [internal]
742  */
743 static DWORD MCICDA_Set(UINT wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
744 {
745     WINE_MCICDAUDIO*    wmcda = MCICDA_GetOpenDrv(wDevID);
746     
747     TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
748     
749     if (wmcda == NULL)  return MCIERR_INVALID_DEVICE_ID;
750
751     if (dwFlags & MCI_SET_DOOR_OPEN) {
752         MCICDA_SetDoor(wDevID, TRUE);
753     }
754     if (dwFlags & MCI_SET_DOOR_CLOSED) {
755         MCICDA_SetDoor(wDevID, FALSE);
756     }
757
758     /* only functions which require valid lpParms below this line ! */
759     if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
760     /*
761       TRACE("dwTimeFormat=%08lX\n", lpParms->dwTimeFormat);
762       TRACE("dwAudio=%08lX\n", lpParms->dwAudio);
763     */
764     if (dwFlags & MCI_SET_TIME_FORMAT) {
765         switch (lpParms->dwTimeFormat) {
766         case MCI_FORMAT_MILLISECONDS:
767             TRACE("MCI_FORMAT_MILLISECONDS !\n");
768             break;
769         case MCI_FORMAT_MSF:
770             TRACE("MCI_FORMAT_MSF !\n");
771             break;
772         case MCI_FORMAT_TMSF:
773             TRACE("MCI_FORMAT_TMSF !\n");
774             break;
775         default:
776             WARN("bad time format !\n");
777             return MCIERR_BAD_TIME_FORMAT;
778         }
779         wmcda->dwTimeFormat = lpParms->dwTimeFormat;
780     }
781     if (dwFlags & MCI_SET_VIDEO) return MCIERR_UNSUPPORTED_FUNCTION;
782     if (dwFlags & MCI_SET_ON) return MCIERR_UNSUPPORTED_FUNCTION;
783     if (dwFlags & MCI_SET_OFF) return MCIERR_UNSUPPORTED_FUNCTION;
784     if (dwFlags & MCI_NOTIFY) {
785         TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", 
786               lpParms->dwCallback);
787         mciDriverNotify((HWND)LOWORD(lpParms->dwCallback), 
788                         wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
789     }
790     return 0;
791 }
792
793 /**************************************************************************
794  *                      MCICDA_DriverProc                       [exported]
795  */
796 LONG CALLBACK   MCICDA_DriverProc(DWORD dwDevID, HDRVR hDriv, DWORD wMsg, 
797                                       DWORD dwParam1, DWORD dwParam2)
798 {
799     switch(wMsg) {
800     case DRV_LOAD:              return 1;
801     case DRV_FREE:              return 1;
802     case DRV_OPEN:              return MCICDA_drvOpen((LPSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSA)dwParam2);
803     case DRV_CLOSE:             return MCICDA_drvClose(dwDevID);
804     case DRV_ENABLE:            return 1;       
805     case DRV_DISABLE:           return 1;
806     case DRV_QUERYCONFIGURE:    return 1;
807     case DRV_CONFIGURE:         MessageBoxA(0, "MCI audio CD driver !", "Wine Driver", MB_OK); return 1;
808     case DRV_INSTALL:           return DRVCNF_RESTART;
809     case DRV_REMOVE:            return DRVCNF_RESTART;
810         
811     case MCI_OPEN_DRIVER:       return MCICDA_Open(dwDevID, dwParam1, (LPMCI_OPEN_PARMSA)dwParam2);
812     case MCI_CLOSE_DRIVER:      return MCICDA_Close(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
813     case MCI_GETDEVCAPS:        return MCICDA_GetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS)dwParam2);
814     case MCI_INFO:              return MCICDA_Info(dwDevID, dwParam1, (LPMCI_INFO_PARMSA)dwParam2);
815     case MCI_STATUS:            return MCICDA_Status(dwDevID, dwParam1, (LPMCI_STATUS_PARMS)dwParam2);
816     case MCI_SET:               return MCICDA_Set(dwDevID, dwParam1, (LPMCI_SET_PARMS)dwParam2);
817     case MCI_PLAY:              return MCICDA_Play(dwDevID, dwParam1, (LPMCI_PLAY_PARMS)dwParam2);
818     case MCI_STOP:              return MCICDA_Stop(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
819     case MCI_PAUSE:             return MCICDA_Pause(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
820     case MCI_RESUME:            return MCICDA_Resume(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
821     case MCI_SEEK:              return MCICDA_Seek(dwDevID, dwParam1, (LPMCI_SEEK_PARMS)dwParam2);
822     /* FIXME: I wonder if those two next items are really called ? */
823     case MCI_SET_DOOR_OPEN:     FIXME("MCI_SET_DOOR_OPEN called. Please report this.\n");
824                                 return MCICDA_SetDoor(dwDevID, TRUE);
825     case MCI_SET_DOOR_CLOSED:   FIXME("MCI_SET_DOOR_CLOSED called. Please report this.\n");
826                                 return MCICDA_SetDoor(dwDevID, FALSE);
827     /* commands that should be supported */
828     case MCI_LOAD:              
829     case MCI_SAVE:              
830     case MCI_FREEZE:            
831     case MCI_PUT:               
832     case MCI_REALIZE:           
833     case MCI_UNFREEZE:          
834     case MCI_UPDATE:            
835     case MCI_WHERE:             
836     case MCI_STEP:              
837     case MCI_SPIN:              
838     case MCI_ESCAPE:            
839     case MCI_COPY:              
840     case MCI_CUT:               
841     case MCI_DELETE:            
842     case MCI_PASTE:             
843         FIXME("Unsupported yet command [%lu]\n", wMsg);
844         break;
845     /* commands that should report an error */
846     case MCI_WINDOW:            
847         TRACE("Unsupported command [%lu]\n", wMsg);
848         break;
849     case MCI_OPEN:
850     case MCI_CLOSE:
851         ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
852         break;
853     default:
854         TRACE("Sending msg [%lu] to default driver proc\n", wMsg);
855         return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
856     }
857     return MCIERR_UNRECOGNIZED_COMMAND;
858 }
859
860 /*-----------------------------------------------------------------------*/