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