server: Move socket async activation to sock_poll_event.
[wine] / dlls / mcicda / mcicda.c
1 /*
2  * MCI driver for audio CD (MCICDA)
3  *
4  * Copyright 1994    Martin Ayotte
5  * Copyright 1998-99 Eric Pouech
6  * Copyright 2000    Andreas Mohr
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21  */
22
23 #include "config.h"
24 #include <stdarg.h>
25 #include <stdio.h>
26 #include <string.h>
27
28 #define WIN32_NO_STATUS
29 #include "windef.h"
30 #include "winbase.h"
31 #include "wingdi.h"
32 #include "winuser.h"
33 #include "wownt32.h"
34 #include "mmddk.h"
35 #include "winioctl.h"
36 #include "ntddcdrm.h"
37 #include "winternl.h"
38 #include "wine/debug.h"
39 #include "wine/unicode.h"
40 #include "dsound.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 /* Defined by red-book standard; do not change! */
50 #define RAW_SECTOR_SIZE  (2352)
51
52 /* Must be >= RAW_SECTOR_SIZE */
53 #define CDDA_FRAG_SIZE   (32768)
54 /* Must be >= 2 */
55 #define CDDA_FRAG_COUNT  (3)
56
57 typedef struct {
58     UINT                wDevID;
59     int                 nUseCount;          /* Incremented for each shared open */
60     BOOL                fShareable;         /* TRUE if first open was shareable */
61     WORD                wNotifyDeviceID;    /* MCI device ID with a pending notification */
62     HANDLE              hCallback;          /* Callback handle for pending notification */
63     DWORD               dwTimeFormat;
64     HANDLE              handle;
65
66     /* The following are used for digital playback only */
67     HANDLE hThread;
68     HANDLE stopEvent;
69     DWORD start, end;
70
71     IDirectSound *dsObj;
72     IDirectSoundBuffer *dsBuf;
73
74     CRITICAL_SECTION cs;
75 } WINE_MCICDAUDIO;
76
77 /*-----------------------------------------------------------------------*/
78
79 typedef HRESULT(WINAPI*LPDIRECTSOUNDCREATE)(LPCGUID,LPDIRECTSOUND*,LPUNKNOWN);
80 static LPDIRECTSOUNDCREATE pDirectSoundCreate;
81
82 static DWORD CALLBACK MCICDA_playLoop(void *ptr)
83 {
84     WINE_MCICDAUDIO *wmcda = (WINE_MCICDAUDIO*)ptr;
85     DWORD lastPos, curPos, endPos, br;
86     void *cdData;
87     DWORD lockLen, fragLen;
88     DSBCAPS caps;
89     RAW_READ_INFO rdInfo;
90     HRESULT hr = DS_OK;
91
92     memset(&caps, 0, sizeof(caps));
93     caps.dwSize = sizeof(caps);
94     hr = IDirectSoundBuffer_GetCaps(wmcda->dsBuf, &caps);
95
96     fragLen = caps.dwBufferBytes/CDDA_FRAG_COUNT;
97     curPos = lastPos = 0;
98     endPos = ~0u;
99     while (SUCCEEDED(hr) && endPos != lastPos &&
100            WaitForSingleObject(wmcda->stopEvent, 0) != WAIT_OBJECT_0) {
101         hr = IDirectSoundBuffer_GetCurrentPosition(wmcda->dsBuf, &curPos, NULL);
102         if ((curPos-lastPos+caps.dwBufferBytes)%caps.dwBufferBytes < fragLen) {
103             Sleep(1);
104             continue;
105         }
106
107         EnterCriticalSection(&wmcda->cs);
108         rdInfo.DiskOffset.QuadPart = wmcda->start<<11;
109         rdInfo.SectorCount = min(fragLen/RAW_SECTOR_SIZE, wmcda->end-wmcda->start);
110         rdInfo.TrackMode = CDDA;
111
112         hr = IDirectSoundBuffer_Lock(wmcda->dsBuf, lastPos, fragLen, &cdData, &lockLen, NULL, NULL, 0);
113         if (hr == DSERR_BUFFERLOST) {
114             if(FAILED(IDirectSoundBuffer_Restore(wmcda->dsBuf)) ||
115                FAILED(IDirectSoundBuffer_Play(wmcda->dsBuf, 0, 0, DSBPLAY_LOOPING))) {
116                 LeaveCriticalSection(&wmcda->cs);
117                 break;
118             }
119             hr = IDirectSoundBuffer_Lock(wmcda->dsBuf, lastPos, fragLen, &cdData, &lockLen, NULL, NULL, 0);
120         }
121
122         if (SUCCEEDED(hr)) {
123             if (rdInfo.SectorCount > 0) {
124                 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_RAW_READ, &rdInfo, sizeof(rdInfo), cdData, lockLen, &br, NULL))
125                     WARN("CD read failed at sector %d: 0x%x\n", wmcda->start, GetLastError());
126             }
127             if (rdInfo.SectorCount*RAW_SECTOR_SIZE < lockLen) {
128                 if(endPos == ~0u) endPos = lastPos;
129                 memset((BYTE*)cdData + rdInfo.SectorCount*RAW_SECTOR_SIZE, 0,
130                        lockLen - rdInfo.SectorCount*RAW_SECTOR_SIZE);
131             }
132             hr = IDirectSoundBuffer_Unlock(wmcda->dsBuf, cdData, lockLen, NULL, 0);
133         }
134
135         lastPos += fragLen;
136         lastPos %= caps.dwBufferBytes;
137         wmcda->start += rdInfo.SectorCount;
138
139         LeaveCriticalSection(&wmcda->cs);
140     }
141     IDirectSoundBuffer_Stop(wmcda->dsBuf);
142     SetEvent(wmcda->stopEvent);
143
144     EnterCriticalSection(&wmcda->cs);
145     if (wmcda->hCallback) {
146         mciDriverNotify(wmcda->hCallback, wmcda->wNotifyDeviceID,
147                         FAILED(hr) ? MCI_NOTIFY_FAILURE :
148                         ((endPos!=lastPos) ? MCI_NOTIFY_ABORTED :
149                          MCI_NOTIFY_SUCCESSFUL));
150         wmcda->hCallback = NULL;
151     }
152     LeaveCriticalSection(&wmcda->cs);
153
154     ExitThread(0);
155     return 0;
156 }
157
158
159
160 /**************************************************************************
161  *                              MCICDA_drvOpen                  [internal]
162  */
163 static  DWORD   MCICDA_drvOpen(LPCWSTR str, LPMCI_OPEN_DRIVER_PARMSW modp)
164 {
165     static HMODULE dsHandle;
166     WINE_MCICDAUDIO*    wmcda;
167
168     if (!modp) return 0xFFFFFFFF;
169
170     wmcda = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCICDAUDIO));
171
172     if (!wmcda)
173         return 0;
174
175     if (!dsHandle) {
176         dsHandle = LoadLibraryA("dsound.dll");
177         if(dsHandle)
178             pDirectSoundCreate = (LPDIRECTSOUNDCREATE)GetProcAddress(dsHandle, "DirectSoundCreate");
179     }
180
181     wmcda->wDevID = modp->wDeviceID;
182     mciSetDriverData(wmcda->wDevID, (DWORD_PTR)wmcda);
183     modp->wCustomCommandTable = MCI_NO_COMMAND_TABLE;
184     modp->wType = MCI_DEVTYPE_CD_AUDIO;
185     InitializeCriticalSection(&wmcda->cs);
186     return modp->wDeviceID;
187 }
188
189 /**************************************************************************
190  *                              MCICDA_drvClose                 [internal]
191  */
192 static  DWORD   MCICDA_drvClose(DWORD dwDevID)
193 {
194     WINE_MCICDAUDIO*  wmcda = (WINE_MCICDAUDIO*)mciGetDriverData(dwDevID);
195
196     if (wmcda) {
197         DeleteCriticalSection(&wmcda->cs);
198         HeapFree(GetProcessHeap(), 0, wmcda);
199         mciSetDriverData(dwDevID, 0);
200     }
201     return (dwDevID == 0xFFFFFFFF) ? 1 : 0;
202 }
203
204 /**************************************************************************
205  *                              MCICDA_GetOpenDrv               [internal]
206  */
207 static WINE_MCICDAUDIO*  MCICDA_GetOpenDrv(UINT wDevID)
208 {
209     WINE_MCICDAUDIO*    wmcda = (WINE_MCICDAUDIO*)mciGetDriverData(wDevID);
210
211     if (wmcda == NULL || wmcda->nUseCount == 0) {
212         WARN("Invalid wDevID=%u\n", wDevID);
213         return 0;
214     }
215     return wmcda;
216 }
217
218 /**************************************************************************
219  *                              MCICDA_GetStatus                [internal]
220  */
221 static  DWORD    MCICDA_GetStatus(WINE_MCICDAUDIO* wmcda)
222 {
223     CDROM_SUB_Q_DATA_FORMAT     fmt;
224     SUB_Q_CHANNEL_DATA          data;
225     DWORD                       br;
226     DWORD                       mode = MCI_MODE_NOT_READY;
227
228     fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
229     if(wmcda->hThread != 0) {
230         DWORD status;
231         HRESULT hr;
232
233         hr = IDirectSoundBuffer_GetStatus(wmcda->dsBuf, &status);
234         if(SUCCEEDED(hr)) {
235             if(!(status&DSBSTATUS_PLAYING)) {
236                 if(WaitForSingleObject(wmcda->stopEvent, 0) == WAIT_OBJECT_0)
237                     mode = MCI_MODE_STOP;
238                 else
239                     mode = MCI_MODE_PAUSE;
240             }
241             else
242                 mode = MCI_MODE_PLAY;
243         }
244     }
245     else if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
246                               &data, sizeof(data), &br, NULL)) {
247         if (GetLastError() == ERROR_NOT_READY) mode = MCI_MODE_OPEN;
248     } else {
249         switch (data.CurrentPosition.Header.AudioStatus)
250         {
251         case AUDIO_STATUS_IN_PROGRESS:          mode = MCI_MODE_PLAY;   break;
252         case AUDIO_STATUS_PAUSED:               mode = MCI_MODE_PAUSE;  break;
253         case AUDIO_STATUS_NO_STATUS:
254         case AUDIO_STATUS_PLAY_COMPLETE:        mode = MCI_MODE_STOP;   break;
255         case AUDIO_STATUS_PLAY_ERROR:
256         case AUDIO_STATUS_NOT_SUPPORTED:
257         default:
258             break;
259         }
260     }
261     return mode;
262 }
263
264 /**************************************************************************
265  *                              MCICDA_GetError                 [internal]
266  */
267 static  int     MCICDA_GetError(WINE_MCICDAUDIO* wmcda)
268 {
269     switch (GetLastError())
270     {
271     case ERROR_NOT_READY:     return MCIERR_DEVICE_NOT_READY;
272     case ERROR_IO_DEVICE:     return MCIERR_HARDWARE;
273     default:
274         FIXME("Unknown mode %u\n", GetLastError());
275     }
276     return MCIERR_DRIVER_INTERNAL;
277 }
278
279 /**************************************************************************
280  *                      MCICDA_CalcFrame                        [internal]
281  */
282 static DWORD MCICDA_CalcFrame(WINE_MCICDAUDIO* wmcda, DWORD dwTime)
283 {
284     DWORD       dwFrame = 0;
285     UINT        wTrack;
286     CDROM_TOC   toc;
287     DWORD       br;
288     BYTE*       addr;
289
290     TRACE("(%p, %08X, %u);\n", wmcda, wmcda->dwTimeFormat, dwTime);
291
292     switch (wmcda->dwTimeFormat) {
293     case MCI_FORMAT_MILLISECONDS:
294         dwFrame = ((dwTime - 1) * CDFRAMES_PERSEC + 500) / 1000;
295         TRACE("MILLISECONDS %u\n", dwFrame);
296         break;
297     case MCI_FORMAT_MSF:
298         TRACE("MSF %02u:%02u:%02u\n",
299               MCI_MSF_MINUTE(dwTime), MCI_MSF_SECOND(dwTime), MCI_MSF_FRAME(dwTime));
300         dwFrame += CDFRAMES_PERMIN * MCI_MSF_MINUTE(dwTime);
301         dwFrame += CDFRAMES_PERSEC * MCI_MSF_SECOND(dwTime);
302         dwFrame += MCI_MSF_FRAME(dwTime);
303         break;
304     case MCI_FORMAT_TMSF:
305     default: /* unknown format ! force TMSF ! ... */
306         wTrack = MCI_TMSF_TRACK(dwTime);
307         if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
308                              &toc, sizeof(toc), &br, NULL))
309             return 0;
310         if (wTrack < toc.FirstTrack || wTrack > toc.LastTrack)
311             return 0;
312         TRACE("MSF %02u-%02u:%02u:%02u\n",
313               MCI_TMSF_TRACK(dwTime), MCI_TMSF_MINUTE(dwTime),
314               MCI_TMSF_SECOND(dwTime), MCI_TMSF_FRAME(dwTime));
315         addr = toc.TrackData[wTrack - toc.FirstTrack].Address;
316         TRACE("TMSF trackpos[%u]=%d:%d:%d\n",
317               wTrack, addr[1], addr[2], addr[3]);
318         dwFrame = CDFRAMES_PERMIN * (addr[1] + MCI_TMSF_MINUTE(dwTime)) +
319             CDFRAMES_PERSEC * (addr[2] + MCI_TMSF_SECOND(dwTime)) +
320             addr[3] + MCI_TMSF_FRAME(dwTime);
321         break;
322     }
323     return dwFrame;
324 }
325
326 /**************************************************************************
327  *                      MCICDA_CalcTime                         [internal]
328  */
329 static DWORD MCICDA_CalcTime(WINE_MCICDAUDIO* wmcda, DWORD tf, DWORD dwFrame, LPDWORD lpRet)
330 {
331     DWORD       dwTime = 0;
332     UINT        wTrack;
333     UINT        wMinutes;
334     UINT        wSeconds;
335     UINT        wFrames;
336     CDROM_TOC   toc;
337     DWORD       br;
338
339     TRACE("(%p, %08X, %u);\n", wmcda, tf, dwFrame);
340
341     switch (tf) {
342     case MCI_FORMAT_MILLISECONDS:
343         dwTime = (dwFrame * 1000) / CDFRAMES_PERSEC + 1;
344         TRACE("MILLISECONDS %u\n", dwTime);
345         *lpRet = 0;
346         break;
347     case MCI_FORMAT_MSF:
348         wMinutes = dwFrame / CDFRAMES_PERMIN;
349         wSeconds = (dwFrame - CDFRAMES_PERMIN * wMinutes) / CDFRAMES_PERSEC;
350         wFrames = dwFrame - CDFRAMES_PERMIN * wMinutes - CDFRAMES_PERSEC * wSeconds;
351         dwTime = MCI_MAKE_MSF(wMinutes, wSeconds, wFrames);
352         TRACE("MSF %02u:%02u:%02u -> dwTime=%u\n",
353               wMinutes, wSeconds, wFrames, dwTime);
354         *lpRet = MCI_COLONIZED3_RETURN;
355         break;
356     case MCI_FORMAT_TMSF:
357     default:    /* unknown format ! force TMSF ! ... */
358         if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
359                              &toc, sizeof(toc), &br, NULL))
360             return 0;
361         if (dwFrame < FRAME_OF_TOC(toc, toc.FirstTrack) ||
362             dwFrame > FRAME_OF_TOC(toc, toc.LastTrack + 1)) {
363             ERR("Out of range value %u [%u,%u]\n",
364                 dwFrame, FRAME_OF_TOC(toc, toc.FirstTrack),
365                 FRAME_OF_TOC(toc, toc.LastTrack + 1));
366             *lpRet = 0;
367             return 0;
368         }
369         for (wTrack = toc.FirstTrack; wTrack <= toc.LastTrack; wTrack++) {
370             if (FRAME_OF_TOC(toc, wTrack) > dwFrame)
371                 break;
372         }
373         wTrack--;
374         dwFrame -= FRAME_OF_TOC(toc, wTrack);
375         wMinutes = dwFrame / CDFRAMES_PERMIN;
376         wSeconds = (dwFrame - CDFRAMES_PERMIN * wMinutes) / CDFRAMES_PERSEC;
377         wFrames = dwFrame - CDFRAMES_PERMIN * wMinutes - CDFRAMES_PERSEC * wSeconds;
378         dwTime = MCI_MAKE_TMSF(wTrack, wMinutes, wSeconds, wFrames);
379         TRACE("%02u-%02u:%02u:%02u\n", wTrack, wMinutes, wSeconds, wFrames);
380         *lpRet = MCI_COLONIZED4_RETURN;
381         break;
382     }
383     return dwTime;
384 }
385
386 static DWORD MCICDA_Seek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms);
387 static DWORD MCICDA_Stop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms);
388
389 /**************************************************************************
390  *                              MCICDA_Open                     [internal]
391  */
392 static DWORD MCICDA_Open(UINT wDevID, DWORD dwFlags, LPMCI_OPEN_PARMSW lpOpenParms)
393 {
394     DWORD               dwDeviceID;
395     DWORD               ret = MCIERR_HARDWARE;
396     WINE_MCICDAUDIO*    wmcda = (WINE_MCICDAUDIO*)mciGetDriverData(wDevID);
397     WCHAR               root[7], drive = 0;
398     unsigned int        count;
399
400     TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpOpenParms);
401
402     if (lpOpenParms == NULL)            return MCIERR_NULL_PARAMETER_BLOCK;
403     if (wmcda == NULL)                  return MCIERR_INVALID_DEVICE_ID;
404
405     dwDeviceID = lpOpenParms->wDeviceID;
406
407     if (wmcda->nUseCount > 0) {
408         /* The driver is already open on this channel */
409         /* If the driver was opened shareable before and this open specifies */
410         /* shareable then increment the use count */
411         if (wmcda->fShareable && (dwFlags & MCI_OPEN_SHAREABLE))
412             ++wmcda->nUseCount;
413         else
414             return MCIERR_MUST_USE_SHAREABLE;
415     } else {
416         wmcda->nUseCount = 1;
417         wmcda->fShareable = dwFlags & MCI_OPEN_SHAREABLE;
418     }
419     if (dwFlags & MCI_OPEN_ELEMENT) {
420         if (dwFlags & MCI_OPEN_ELEMENT_ID) {
421             WARN("MCI_OPEN_ELEMENT_ID %p! Abort\n", lpOpenParms->lpstrElementName);
422             ret = MCIERR_NO_ELEMENT_ALLOWED;
423             goto the_error;
424         }
425         TRACE("MCI_OPEN_ELEMENT element name: %s\n", debugstr_w(lpOpenParms->lpstrElementName));
426         if (!isalpha(lpOpenParms->lpstrElementName[0]) || lpOpenParms->lpstrElementName[1] != ':' ||
427             (lpOpenParms->lpstrElementName[2] && lpOpenParms->lpstrElementName[2] != '\\'))
428         {
429             WARN("MCI_OPEN_ELEMENT unsupported format: %s\n", 
430                  debugstr_w(lpOpenParms->lpstrElementName));
431             ret = MCIERR_NO_ELEMENT_ALLOWED;
432             goto the_error;
433         }
434         drive = toupper(lpOpenParms->lpstrElementName[0]);
435         root[0] = drive; root[1] = ':'; root[2] = '\\'; root[3] = '\0';
436         if (GetDriveTypeW(root) != DRIVE_CDROM)
437         {
438             ret = MCIERR_INVALID_DEVICE_NAME;
439             goto the_error;
440         }
441     }
442     else
443     {
444         /* drive letter isn't passed... get the dwDeviceID'th cdrom in the system */
445         root[0] = 'A'; root[1] = ':'; root[2] = '\\'; root[3] = '\0';
446         for (count = 0; root[0] <= 'Z'; root[0]++)
447         {
448             if (GetDriveTypeW(root) == DRIVE_CDROM && ++count >= dwDeviceID)
449             {
450                 drive = root[0];
451                 break;
452             }
453         }
454         if (!drive)
455         {
456             ret = MCIERR_INVALID_DEVICE_ID;
457             goto the_error;
458         }
459     }
460
461     wmcda->wNotifyDeviceID = dwDeviceID;
462     wmcda->dwTimeFormat = MCI_FORMAT_MSF;
463
464     /* now, open the handle */
465     root[0] = root[1] = '\\'; root[2] = '.'; root[3] = '\\'; root[4] = drive; root[5] = ':'; root[6] = '\0';
466     wmcda->handle = CreateFileW(root, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
467     if (wmcda->handle != INVALID_HANDLE_VALUE)
468         return 0;
469
470  the_error:
471     --wmcda->nUseCount;
472     return ret;
473 }
474
475 /**************************************************************************
476  *                              MCICDA_Close                    [internal]
477  */
478 static DWORD MCICDA_Close(UINT wDevID, DWORD dwParam, LPMCI_GENERIC_PARMS lpParms)
479 {
480     WINE_MCICDAUDIO*    wmcda = MCICDA_GetOpenDrv(wDevID);
481
482     TRACE("(%04X, %08X, %p);\n", wDevID, dwParam, lpParms);
483
484     if (wmcda == NULL)  return MCIERR_INVALID_DEVICE_ID;
485
486     MCICDA_Stop(wDevID, MCI_WAIT, NULL);
487
488     if (--wmcda->nUseCount == 0) {
489         CloseHandle(wmcda->handle);
490     }
491     return 0;
492 }
493
494 /**************************************************************************
495  *                              MCICDA_GetDevCaps               [internal]
496  */
497 static DWORD MCICDA_GetDevCaps(UINT wDevID, DWORD dwFlags,
498                                    LPMCI_GETDEVCAPS_PARMS lpParms)
499 {
500     DWORD       ret = 0;
501
502     TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
503
504     if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
505
506     if (dwFlags & MCI_GETDEVCAPS_ITEM) {
507         TRACE("MCI_GETDEVCAPS_ITEM dwItem=%08X;\n", lpParms->dwItem);
508
509         switch (lpParms->dwItem) {
510         case MCI_GETDEVCAPS_CAN_RECORD:
511             lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
512             ret = MCI_RESOURCE_RETURNED;
513             break;
514         case MCI_GETDEVCAPS_HAS_AUDIO:
515             lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
516             ret = MCI_RESOURCE_RETURNED;
517             break;
518         case MCI_GETDEVCAPS_HAS_VIDEO:
519             lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
520             ret = MCI_RESOURCE_RETURNED;
521             break;
522         case MCI_GETDEVCAPS_DEVICE_TYPE:
523             lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_CD_AUDIO, MCI_DEVTYPE_CD_AUDIO);
524             ret = MCI_RESOURCE_RETURNED;
525             break;
526         case MCI_GETDEVCAPS_USES_FILES:
527             lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
528             ret = MCI_RESOURCE_RETURNED;
529             break;
530         case MCI_GETDEVCAPS_COMPOUND_DEVICE:
531             lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
532             ret = MCI_RESOURCE_RETURNED;
533             break;
534         case MCI_GETDEVCAPS_CAN_EJECT:
535             lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
536             ret = MCI_RESOURCE_RETURNED;
537             break;
538         case MCI_GETDEVCAPS_CAN_PLAY:
539             lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
540             ret = MCI_RESOURCE_RETURNED;
541             break;
542         case MCI_GETDEVCAPS_CAN_SAVE:
543             lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
544             ret = MCI_RESOURCE_RETURNED;
545             break;
546         default:
547             ERR("Unsupported %x devCaps item\n", lpParms->dwItem);
548             return MCIERR_UNRECOGNIZED_COMMAND;
549         }
550     } else {
551         TRACE("No GetDevCaps-Item !\n");
552         return MCIERR_UNRECOGNIZED_COMMAND;
553     }
554     TRACE("lpParms->dwReturn=%08X;\n", lpParms->dwReturn);
555     return ret;
556 }
557
558 static DWORD CDROM_Audio_GetSerial(CDROM_TOC* toc)
559 {
560     unsigned long serial = 0;
561     int i;
562     WORD wMagic;
563     DWORD dwStart, dwEnd;
564
565     /*
566      * wMagic collects the wFrames from track 1
567      * dwStart, dwEnd collect the beginning and end of the disc respectively, in
568      * frames.
569      * There it is collected for correcting the serial when there are less than
570      * 3 tracks.
571      */
572     wMagic = toc->TrackData[0].Address[3];
573     dwStart = FRAME_OF_TOC(*toc, toc->FirstTrack);
574
575     for (i = 0; i <= toc->LastTrack - toc->FirstTrack; i++) {
576         serial += (toc->TrackData[i].Address[1] << 16) |
577             (toc->TrackData[i].Address[2] << 8) | toc->TrackData[i].Address[3];
578     }
579     dwEnd = FRAME_OF_TOC(*toc, toc->LastTrack + 1);
580
581     if (toc->LastTrack - toc->FirstTrack + 1 < 3)
582         serial += wMagic + (dwEnd - dwStart);
583
584     return serial;
585 }
586
587
588 /**************************************************************************
589  *                              MCICDA_Info                     [internal]
590  */
591 static DWORD MCICDA_Info(UINT wDevID, DWORD dwFlags, LPMCI_INFO_PARMSW lpParms)
592 {
593     LPCWSTR             str = NULL;
594     WINE_MCICDAUDIO*    wmcda = MCICDA_GetOpenDrv(wDevID);
595     DWORD               ret = 0;
596     WCHAR               buffer[16];
597
598     TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
599
600     if (lpParms == NULL || lpParms->lpstrReturn == NULL)
601         return MCIERR_NULL_PARAMETER_BLOCK;
602     if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
603
604     TRACE("buf=%p, len=%u\n", lpParms->lpstrReturn, lpParms->dwRetSize);
605
606     if (dwFlags & MCI_INFO_PRODUCT) {
607         static const WCHAR wszAudioCd[] = {'W','i','n','e','\'','s',' ','a','u','d','i','o',' ','C','D',0};
608         str = wszAudioCd;
609     } else if (dwFlags & MCI_INFO_MEDIA_UPC) {
610         ret = MCIERR_NO_IDENTITY;
611     } else if (dwFlags & MCI_INFO_MEDIA_IDENTITY) {
612         DWORD       res = 0;
613         CDROM_TOC   toc;
614         DWORD       br;
615         static const WCHAR wszLu[] = {'%','l','u',0};
616
617         if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
618                              &toc, sizeof(toc), &br, NULL)) {
619             return MCICDA_GetError(wmcda);
620         }
621
622         res = CDROM_Audio_GetSerial(&toc);
623         sprintfW(buffer, wszLu, res);
624         str = buffer;
625     } else {
626         WARN("Don't know this info command (%u)\n", dwFlags);
627         ret = MCIERR_UNRECOGNIZED_COMMAND;
628     }
629     if (str) {
630         if (lpParms->dwRetSize <= strlenW(str)) {
631             lstrcpynW(lpParms->lpstrReturn, str, lpParms->dwRetSize - 1);
632             ret = MCIERR_PARAM_OVERFLOW;
633         } else {
634             strcpyW(lpParms->lpstrReturn, str);
635         }
636     } else {
637         *lpParms->lpstrReturn = 0;
638     }
639     TRACE("=> %s (%d)\n", debugstr_w(lpParms->lpstrReturn), ret);
640     return ret;
641 }
642
643 /**************************************************************************
644  *                              MCICDA_Status                   [internal]
645  */
646 static DWORD MCICDA_Status(UINT wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
647 {
648     WINE_MCICDAUDIO*            wmcda = MCICDA_GetOpenDrv(wDevID);
649     DWORD                       ret = 0;
650     CDROM_SUB_Q_DATA_FORMAT     fmt;
651     SUB_Q_CHANNEL_DATA          data;
652     CDROM_TOC                   toc;
653     DWORD                       br;
654
655     TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
656
657     if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
658     if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
659
660     if (dwFlags & MCI_NOTIFY) {
661         TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
662         mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
663                         wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
664     }
665     if (dwFlags & MCI_STATUS_ITEM) {
666         TRACE("dwItem = %x\n", lpParms->dwItem);
667         switch (lpParms->dwItem) {
668         case MCI_STATUS_CURRENT_TRACK:
669             fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
670             if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
671                                  &data, sizeof(data), &br, NULL))
672             {
673                 return MCICDA_GetError(wmcda);
674             }
675             lpParms->dwReturn = data.CurrentPosition.TrackNumber;
676             TRACE("CURRENT_TRACK=%lu\n", lpParms->dwReturn);
677             break;
678         case MCI_STATUS_LENGTH:
679             if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
680                                  &toc, sizeof(toc), &br, NULL)) {
681                 WARN("error reading TOC !\n");
682                 return MCICDA_GetError(wmcda);
683             }
684             if (dwFlags & MCI_TRACK) {
685                 TRACE("MCI_TRACK #%u LENGTH=??? !\n", lpParms->dwTrack);
686                 if (lpParms->dwTrack < toc.FirstTrack || lpParms->dwTrack > toc.LastTrack)
687                     return MCIERR_OUTOFRANGE;
688                 lpParms->dwReturn = FRAME_OF_TOC(toc, lpParms->dwTrack + 1) -
689                     FRAME_OF_TOC(toc, lpParms->dwTrack);
690                 /* Windows returns one frame less than the total track length for the
691                    last track on the CD.  See CDDB HOWTO.  Verified on Win95OSR2. */
692                 if (lpParms->dwTrack == toc.LastTrack)
693                     lpParms->dwReturn--;
694             } else {
695                 /* Sum of the lengths of all of the tracks.  Inherits the
696                    'off by one frame' behavior from the length of the last track.
697                    See above comment. */
698                 lpParms->dwReturn = FRAME_OF_TOC(toc, toc.LastTrack + 1) -
699                     FRAME_OF_TOC(toc, toc.FirstTrack) - 1;
700             }
701             lpParms->dwReturn = MCICDA_CalcTime(wmcda,
702                                                  (wmcda->dwTimeFormat == MCI_FORMAT_TMSF)
703                                                     ? MCI_FORMAT_MSF : wmcda->dwTimeFormat,
704                                                  lpParms->dwReturn,
705                                                  &ret);
706             TRACE("LENGTH=%lu\n", lpParms->dwReturn);
707             break;
708         case MCI_STATUS_MODE:
709             lpParms->dwReturn = MCICDA_GetStatus(wmcda);
710             TRACE("MCI_STATUS_MODE=%08lX\n", lpParms->dwReturn);
711             lpParms->dwReturn = MAKEMCIRESOURCE(lpParms->dwReturn, lpParms->dwReturn);
712             ret = MCI_RESOURCE_RETURNED;
713             break;
714         case MCI_STATUS_MEDIA_PRESENT:
715             lpParms->dwReturn = (MCICDA_GetStatus(wmcda) == MCI_MODE_OPEN) ?
716                 MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
717             TRACE("MCI_STATUS_MEDIA_PRESENT =%c!\n", LOWORD(lpParms->dwReturn) ? 'Y' : 'N');
718             ret = MCI_RESOURCE_RETURNED;
719             break;
720         case MCI_STATUS_NUMBER_OF_TRACKS:
721             if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
722                                  &toc, sizeof(toc), &br, NULL)) {
723                 WARN("error reading TOC !\n");
724                 return MCICDA_GetError(wmcda);
725             }
726             lpParms->dwReturn = toc.LastTrack - toc.FirstTrack + 1;
727             TRACE("MCI_STATUS_NUMBER_OF_TRACKS = %lu\n", lpParms->dwReturn);
728             if (lpParms->dwReturn == (WORD)-1)
729                 return MCICDA_GetError(wmcda);
730             break;
731         case MCI_STATUS_POSITION:
732             if (dwFlags & MCI_STATUS_START) {
733                 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
734                                      &toc, sizeof(toc), &br, NULL)) {
735                     WARN("error reading TOC !\n");
736                     return MCICDA_GetError(wmcda);
737                 }
738                 lpParms->dwReturn = FRAME_OF_TOC(toc, toc.FirstTrack);
739                 TRACE("get MCI_STATUS_START !\n");
740             } else if (dwFlags & MCI_TRACK) {
741                 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
742                                      &toc, sizeof(toc), &br, NULL)) {
743                     WARN("error reading TOC !\n");
744                     return MCICDA_GetError(wmcda);
745                 }
746                 if (lpParms->dwTrack < toc.FirstTrack || lpParms->dwTrack > toc.LastTrack)
747                     return MCIERR_OUTOFRANGE;
748                 lpParms->dwReturn = FRAME_OF_TOC(toc, lpParms->dwTrack);
749                 TRACE("get MCI_TRACK #%u !\n", lpParms->dwTrack);
750             } else {
751                 fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
752                 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
753                                      &data, sizeof(data), &br, NULL)) {
754                     return MCICDA_GetError(wmcda);
755                 }
756                 lpParms->dwReturn = FRAME_OF_ADDR(data.CurrentPosition.AbsoluteAddress);
757             }
758             lpParms->dwReturn = MCICDA_CalcTime(wmcda, wmcda->dwTimeFormat, lpParms->dwReturn, &ret);
759             TRACE("MCI_STATUS_POSITION=%08lX\n", lpParms->dwReturn);
760             break;
761         case MCI_STATUS_READY:
762             TRACE("MCI_STATUS_READY !\n");
763             switch (MCICDA_GetStatus(wmcda))
764             {
765             case MCI_MODE_NOT_READY:
766             case MCI_MODE_OPEN:
767                 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
768                 break;
769             default:
770                 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
771                 break;
772             }
773             TRACE("MCI_STATUS_READY=%u!\n", LOWORD(lpParms->dwReturn));
774             ret = MCI_RESOURCE_RETURNED;
775             break;
776         case MCI_STATUS_TIME_FORMAT:
777             lpParms->dwReturn = MAKEMCIRESOURCE(wmcda->dwTimeFormat, MCI_FORMAT_RETURN_BASE + wmcda->dwTimeFormat);
778             TRACE("MCI_STATUS_TIME_FORMAT=%08x!\n", LOWORD(lpParms->dwReturn));
779             ret = MCI_RESOURCE_RETURNED;
780             break;
781         case 4001: /* FIXME: for bogus FullCD */
782         case MCI_CDA_STATUS_TYPE_TRACK:
783             if (!(dwFlags & MCI_TRACK))
784                 ret = MCIERR_MISSING_PARAMETER;
785             else {
786                 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
787                                      &toc, sizeof(toc), &br, NULL)) {
788                     WARN("error reading TOC !\n");
789                     return MCICDA_GetError(wmcda);
790                 }
791                 if (lpParms->dwTrack < toc.FirstTrack || lpParms->dwTrack > toc.LastTrack)
792                     ret = MCIERR_OUTOFRANGE;
793                 else
794                     lpParms->dwReturn = (toc.TrackData[lpParms->dwTrack - toc.FirstTrack].Control & 0x04) ?
795                                          MCI_CDA_TRACK_OTHER : MCI_CDA_TRACK_AUDIO;
796             }
797             TRACE("MCI_CDA_STATUS_TYPE_TRACK[%d]=%ld\n", lpParms->dwTrack, lpParms->dwReturn);
798             break;
799         default:
800             FIXME("unknown command %08X !\n", lpParms->dwItem);
801             return MCIERR_UNRECOGNIZED_COMMAND;
802         }
803     } else {
804         WARN("not MCI_STATUS_ITEM !\n");
805     }
806     return ret;
807 }
808
809 /**************************************************************************
810  *                              MCICDA_SkipDataTracks           [internal]
811  */
812 static DWORD MCICDA_SkipDataTracks(WINE_MCICDAUDIO* wmcda,DWORD *frame)
813 {
814   int i;
815   DWORD br;
816   CDROM_TOC toc;
817   if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
818                       &toc, sizeof(toc), &br, NULL)) {
819     WARN("error reading TOC !\n");
820     return MCICDA_GetError(wmcda);
821   }
822   /* Locate first track whose starting frame is bigger than frame */
823   for(i=toc.FirstTrack;i<=toc.LastTrack+1;i++) 
824     if ( FRAME_OF_TOC(toc, i) > *frame ) break;
825   if (i <= toc.FirstTrack && i>toc.LastTrack+1) {
826     i = 0; /* requested address is out of range: go back to start */
827     *frame = FRAME_OF_TOC(toc,toc.FirstTrack);
828   }
829   else
830     i--;
831   /* i points to last track whose start address is not greater than frame.
832    * Now skip non-audio tracks */
833   for(;i<=toc.LastTrack+1;i++)
834     if ( ! (toc.TrackData[i-toc.FirstTrack].Control & 4) )
835       break;
836   /* The frame will be an address in the next audio track or
837    * address of lead-out. */
838   if ( FRAME_OF_TOC(toc, i) > *frame )
839     *frame = FRAME_OF_TOC(toc, i);
840   return 0;
841 }
842
843 /**************************************************************************
844  *                              MCICDA_Play                     [internal]
845  */
846 static DWORD MCICDA_Play(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
847 {
848     WINE_MCICDAUDIO*            wmcda = MCICDA_GetOpenDrv(wDevID);
849     DWORD                       ret = 0, start, end;
850     DWORD                       br;
851     CDROM_PLAY_AUDIO_MSF        play;
852     CDROM_SUB_Q_DATA_FORMAT     fmt;
853     SUB_Q_CHANNEL_DATA          data;
854     CDROM_TOC                   toc;
855
856     TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
857
858     if (lpParms == NULL)
859         return MCIERR_NULL_PARAMETER_BLOCK;
860
861     if (wmcda == NULL)
862         return MCIERR_INVALID_DEVICE_ID;
863
864     if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
865                          &toc, sizeof(toc), &br, NULL)) {
866         WARN("error reading TOC !\n");
867         return MCICDA_GetError(wmcda);
868     }
869
870     if (dwFlags & MCI_FROM) {
871         start = MCICDA_CalcFrame(wmcda, lpParms->dwFrom);
872         if ( (ret=MCICDA_SkipDataTracks(wmcda, &start)) )
873           return ret;
874         TRACE("MCI_FROM=%08X -> %u\n", lpParms->dwFrom, start);
875     } else {
876         fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
877         if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
878                              &data, sizeof(data), &br, NULL)) {
879             return MCICDA_GetError(wmcda);
880         }
881         start = FRAME_OF_ADDR(data.CurrentPosition.AbsoluteAddress);
882         if ( (ret=MCICDA_SkipDataTracks(wmcda, &start)) )
883           return ret;
884     }
885     if (dwFlags & MCI_TO) {
886         end = MCICDA_CalcFrame(wmcda, lpParms->dwTo);
887         TRACE("MCI_TO=%08X -> %u\n", lpParms->dwTo, end);
888     } else {
889         end = FRAME_OF_TOC(toc, toc.LastTrack + 1) - 1;
890     }
891     TRACE("Playing from %u to %u\n", start, end);
892
893     if (wmcda->hThread != 0) {
894         SetEvent(wmcda->stopEvent);
895         WaitForSingleObject(wmcda->hThread, INFINITE);
896
897         CloseHandle(wmcda->hThread);
898         wmcda->hThread = 0;
899         CloseHandle(wmcda->stopEvent);
900         wmcda->stopEvent = 0;
901
902         IDirectSoundBuffer_Stop(wmcda->dsBuf);
903         IDirectSoundBuffer_Release(wmcda->dsBuf);
904         wmcda->dsBuf = NULL;
905         IDirectSound_Release(wmcda->dsObj);
906         wmcda->dsObj = NULL;
907     }
908     else if(wmcda->hCallback) {
909         mciDriverNotify(wmcda->hCallback, wmcda->wNotifyDeviceID,
910                         MCI_NOTIFY_ABORTED);
911         wmcda->hCallback = NULL;
912     }
913
914     if ((dwFlags&MCI_NOTIFY))
915         wmcda->hCallback = HWND_32(LOWORD(lpParms->dwCallback));
916
917     if (pDirectSoundCreate) {
918         WAVEFORMATEX format;
919         DSBUFFERDESC desc;
920         DWORD lockLen;
921         void *cdData;
922         HRESULT hr;
923
924         hr = pDirectSoundCreate(NULL, &wmcda->dsObj, NULL);
925         if (SUCCEEDED(hr)) {
926             IDirectSound_SetCooperativeLevel(wmcda->dsObj, GetDesktopWindow(), DSSCL_PRIORITY);
927
928             /* The "raw" frame is relative to the start of the first track */
929             wmcda->start = start - FRAME_OF_TOC(toc, toc.FirstTrack);
930             wmcda->end = end - FRAME_OF_TOC(toc, toc.FirstTrack);
931
932             memset(&format, 0, sizeof(format));
933             format.wFormatTag = WAVE_FORMAT_PCM;
934             format.nChannels = 2;
935             format.nSamplesPerSec = 44100;
936             format.wBitsPerSample = 16;
937             format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8;
938             format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;
939             format.cbSize = 0;
940
941             memset(&desc, 0, sizeof(desc));
942             desc.dwSize = sizeof(desc);
943             desc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS;
944             desc.dwBufferBytes = (CDDA_FRAG_SIZE - (CDDA_FRAG_SIZE%RAW_SECTOR_SIZE)) * CDDA_FRAG_COUNT;
945             desc.lpwfxFormat = &format;
946
947             hr = IDirectSound_CreateSoundBuffer(wmcda->dsObj, &desc, &wmcda->dsBuf, NULL);
948         }
949         if (SUCCEEDED(hr)) {
950             hr = IDirectSoundBuffer_Lock(wmcda->dsBuf, 0, 0, &cdData, &lockLen,
951                                          NULL, NULL, DSBLOCK_ENTIREBUFFER);
952         }
953         if (SUCCEEDED(hr)) {
954             RAW_READ_INFO rdInfo;
955             int readok;
956
957             rdInfo.DiskOffset.QuadPart = wmcda->start<<11;
958             rdInfo.SectorCount = min(desc.dwBufferBytes/RAW_SECTOR_SIZE,
959                                      wmcda->end-wmcda->start);
960             rdInfo.TrackMode = CDDA;
961
962             readok = DeviceIoControl(wmcda->handle, IOCTL_CDROM_RAW_READ,
963                                      &rdInfo, sizeof(rdInfo), cdData, lockLen,
964                                      &br, NULL);
965             IDirectSoundBuffer_Unlock(wmcda->dsBuf, cdData, lockLen, NULL, 0);
966
967             if (readok) {
968                 wmcda->start += rdInfo.SectorCount;
969                 wmcda->stopEvent = CreateEventA(NULL, TRUE, FALSE, NULL);
970             }
971             if (wmcda->stopEvent != 0)
972                 wmcda->hThread = CreateThread(NULL, 0, MCICDA_playLoop, wmcda, 0, &br);
973             if (wmcda->hThread != 0) {
974                 hr = IDirectSoundBuffer_Play(wmcda->dsBuf, 0, 0, DSBPLAY_LOOPING);
975                 if (SUCCEEDED(hr))
976                     return ret;
977
978                 SetEvent(wmcda->stopEvent);
979                 WaitForSingleObject(wmcda->hThread, INFINITE);
980                 CloseHandle(wmcda->hThread);
981                 wmcda->hThread = 0;
982             }
983         }
984
985         if (wmcda->stopEvent != 0) {
986             CloseHandle(wmcda->stopEvent);
987             wmcda->stopEvent = 0;
988         }
989         if (wmcda->dsBuf) {
990             IDirectSoundBuffer_Release(wmcda->dsBuf);
991             wmcda->dsBuf = NULL;
992         }
993         if (wmcda->dsObj) {
994             IDirectSound_Release(wmcda->dsObj);
995             wmcda->dsObj = NULL;
996         }
997     }
998
999     play.StartingM = start / CDFRAMES_PERMIN;
1000     play.StartingS = (start / CDFRAMES_PERSEC) % 60;
1001     play.StartingF = start % CDFRAMES_PERSEC;
1002     play.EndingM   = end / CDFRAMES_PERMIN;
1003     play.EndingS   = (end / CDFRAMES_PERSEC) % 60;
1004     play.EndingF   = end % CDFRAMES_PERSEC;
1005     if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_PLAY_AUDIO_MSF, &play, sizeof(play),
1006                          NULL, 0, &br, NULL)) {
1007         wmcda->hCallback = NULL;
1008         ret = MCIERR_HARDWARE;
1009     } else if (dwFlags & MCI_NOTIFY) {
1010         TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
1011         /*
1012           mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1013           wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
1014         */
1015     }
1016     return ret;
1017 }
1018
1019 /**************************************************************************
1020  *                              MCICDA_Stop                     [internal]
1021  */
1022 static DWORD MCICDA_Stop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1023 {
1024     WINE_MCICDAUDIO*    wmcda = MCICDA_GetOpenDrv(wDevID);
1025     DWORD               br;
1026
1027     TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
1028
1029     if (wmcda == NULL)  return MCIERR_INVALID_DEVICE_ID;
1030
1031     if (wmcda->hThread != 0) {
1032         SetEvent(wmcda->stopEvent);
1033         WaitForSingleObject(wmcda->hThread, INFINITE);
1034
1035         CloseHandle(wmcda->hThread);
1036         wmcda->hThread = 0;
1037         CloseHandle(wmcda->stopEvent);
1038         wmcda->stopEvent = 0;
1039
1040         IDirectSoundBuffer_Release(wmcda->dsBuf);
1041         wmcda->dsBuf = NULL;
1042         IDirectSound_Release(wmcda->dsObj);
1043         wmcda->dsObj = NULL;
1044     }
1045     else if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_STOP_AUDIO, NULL, 0, NULL, 0, &br, NULL))
1046         return MCIERR_HARDWARE;
1047
1048     if (wmcda->hCallback) {
1049         mciDriverNotify(wmcda->hCallback, wmcda->wNotifyDeviceID, MCI_NOTIFY_ABORTED);
1050         wmcda->hCallback = NULL;
1051     }
1052
1053     if (lpParms && (dwFlags & MCI_NOTIFY)) {
1054         TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
1055         mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1056                         wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
1057     }
1058     return 0;
1059 }
1060
1061 /**************************************************************************
1062  *                              MCICDA_Pause                    [internal]
1063  */
1064 static DWORD MCICDA_Pause(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1065 {
1066     WINE_MCICDAUDIO*    wmcda = MCICDA_GetOpenDrv(wDevID);
1067     DWORD               br;
1068
1069     TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
1070
1071     if (wmcda == NULL)  return MCIERR_INVALID_DEVICE_ID;
1072
1073     if (wmcda->hThread != 0) {
1074         /* Don't bother calling stop if the playLoop thread has already stopped */
1075         if(WaitForSingleObject(wmcda->stopEvent, 0) != WAIT_OBJECT_0 &&
1076            FAILED(IDirectSoundBuffer_Stop(wmcda->dsBuf)))
1077             return MCIERR_HARDWARE;
1078     }
1079     else if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_PAUSE_AUDIO, NULL, 0, NULL, 0, &br, NULL))
1080         return MCIERR_HARDWARE;
1081
1082     EnterCriticalSection(&wmcda->cs);
1083     if (wmcda->hCallback) {
1084         mciDriverNotify(wmcda->hCallback, wmcda->wNotifyDeviceID, MCI_NOTIFY_SUPERSEDED);
1085         wmcda->hCallback = NULL;
1086     }
1087     LeaveCriticalSection(&wmcda->cs);
1088
1089     if (lpParms && (dwFlags & MCI_NOTIFY)) {
1090         TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
1091         mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1092                         wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
1093     }
1094     return 0;
1095 }
1096
1097 /**************************************************************************
1098  *                              MCICDA_Resume                   [internal]
1099  */
1100 static DWORD MCICDA_Resume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1101 {
1102     WINE_MCICDAUDIO*    wmcda = MCICDA_GetOpenDrv(wDevID);
1103     DWORD               br;
1104
1105     TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
1106
1107     if (wmcda == NULL)  return MCIERR_INVALID_DEVICE_ID;
1108
1109     if (wmcda->hThread != 0) {
1110         /* Don't restart if the playLoop thread has already stopped */
1111         if(WaitForSingleObject(wmcda->stopEvent, 0) != WAIT_OBJECT_0 &&
1112            FAILED(IDirectSoundBuffer_Play(wmcda->dsBuf, 0, 0, DSBPLAY_LOOPING)))
1113             return MCIERR_HARDWARE;
1114     }
1115     else if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_RESUME_AUDIO, NULL, 0, NULL, 0, &br, NULL))
1116         return MCIERR_HARDWARE;
1117
1118     if (lpParms && (dwFlags & MCI_NOTIFY)) {
1119         TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
1120         mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1121                         wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
1122     }
1123     return 0;
1124 }
1125
1126 /**************************************************************************
1127  *                              MCICDA_Seek                     [internal]
1128  */
1129 static DWORD MCICDA_Seek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
1130 {
1131     DWORD                       at;
1132     WINE_MCICDAUDIO*            wmcda = MCICDA_GetOpenDrv(wDevID);
1133     CDROM_SEEK_AUDIO_MSF        seek;
1134     DWORD                       br, ret;
1135     CDROM_TOC                   toc;
1136
1137     TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
1138
1139     if (wmcda == NULL)  return MCIERR_INVALID_DEVICE_ID;
1140     if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1141
1142     if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
1143                          &toc, sizeof(toc), &br, NULL)) {
1144         WARN("error reading TOC !\n");
1145         return MCICDA_GetError(wmcda);
1146     }
1147     switch (dwFlags & ~(MCI_NOTIFY|MCI_WAIT)) {
1148     case MCI_SEEK_TO_START:
1149         TRACE("Seeking to start\n");
1150         at = FRAME_OF_TOC(toc,toc.FirstTrack);
1151         if ( (ret=MCICDA_SkipDataTracks(wmcda, &at)) )
1152           return ret;
1153         break;
1154     case MCI_SEEK_TO_END:
1155         TRACE("Seeking to end\n");
1156         at = FRAME_OF_TOC(toc, toc.LastTrack + 1) - 1;
1157         if ( (ret=MCICDA_SkipDataTracks(wmcda, &at)) )
1158           return ret;
1159         break;
1160     case MCI_TO:
1161         TRACE("Seeking to %u\n", lpParms->dwTo);
1162         at = MCICDA_CalcFrame(wmcda, lpParms->dwTo);
1163         if ( (ret=MCICDA_SkipDataTracks(wmcda, &at)) )
1164           return ret;
1165         break;
1166     default:
1167         TRACE("Unknown seek action %08lX\n",
1168               (dwFlags & ~(MCI_NOTIFY|MCI_WAIT)));
1169         return MCIERR_UNSUPPORTED_FUNCTION;
1170     }
1171
1172     if (wmcda->hThread != 0) {
1173         EnterCriticalSection(&wmcda->cs);
1174         wmcda->start = at - FRAME_OF_TOC(toc, toc.FirstTrack);
1175         /* Flush remaining data, or just let it play into the new data? */
1176         LeaveCriticalSection(&wmcda->cs);
1177     }
1178     else {
1179         seek.M = at / CDFRAMES_PERMIN;
1180         seek.S = (at / CDFRAMES_PERSEC) % 60;
1181         seek.F = at % CDFRAMES_PERSEC;
1182         if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_SEEK_AUDIO_MSF, &seek, sizeof(seek),
1183                              NULL, 0, &br, NULL))
1184             return MCIERR_HARDWARE;
1185     }
1186
1187     if (dwFlags & MCI_NOTIFY) {
1188         TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
1189         mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1190                           wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
1191     }
1192     return 0;
1193 }
1194
1195 /**************************************************************************
1196  *                              MCICDA_SetDoor                  [internal]
1197  */
1198 static DWORD    MCICDA_SetDoor(UINT wDevID, BOOL open)
1199 {
1200     WINE_MCICDAUDIO*    wmcda = MCICDA_GetOpenDrv(wDevID);
1201     DWORD               br;
1202
1203     TRACE("(%04x, %s) !\n", wDevID, (open) ? "OPEN" : "CLOSE");
1204
1205     if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
1206
1207     if (!DeviceIoControl(wmcda->handle,
1208                          (open) ? IOCTL_STORAGE_EJECT_MEDIA : IOCTL_STORAGE_LOAD_MEDIA,
1209                          NULL, 0, NULL, 0, &br, NULL))
1210         return MCIERR_HARDWARE;
1211
1212     return 0;
1213 }
1214
1215 /**************************************************************************
1216  *                              MCICDA_Set                      [internal]
1217  */
1218 static DWORD MCICDA_Set(UINT wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
1219 {
1220     WINE_MCICDAUDIO*    wmcda = MCICDA_GetOpenDrv(wDevID);
1221
1222     TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
1223
1224     if (wmcda == NULL)  return MCIERR_INVALID_DEVICE_ID;
1225
1226     if (dwFlags & MCI_SET_DOOR_OPEN) {
1227         MCICDA_SetDoor(wDevID, TRUE);
1228     }
1229     if (dwFlags & MCI_SET_DOOR_CLOSED) {
1230         MCICDA_SetDoor(wDevID, FALSE);
1231     }
1232
1233     /* only functions which require valid lpParms below this line ! */
1234     if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1235     /*
1236       TRACE("dwTimeFormat=%08lX\n", lpParms->dwTimeFormat);
1237       TRACE("dwAudio=%08lX\n", lpParms->dwAudio);
1238     */
1239     if (dwFlags & MCI_SET_TIME_FORMAT) {
1240         switch (lpParms->dwTimeFormat) {
1241         case MCI_FORMAT_MILLISECONDS:
1242             TRACE("MCI_FORMAT_MILLISECONDS !\n");
1243             break;
1244         case MCI_FORMAT_MSF:
1245             TRACE("MCI_FORMAT_MSF !\n");
1246             break;
1247         case MCI_FORMAT_TMSF:
1248             TRACE("MCI_FORMAT_TMSF !\n");
1249             break;
1250         default:
1251             WARN("bad time format !\n");
1252             return MCIERR_BAD_TIME_FORMAT;
1253         }
1254         wmcda->dwTimeFormat = lpParms->dwTimeFormat;
1255     }
1256     if (dwFlags & MCI_SET_VIDEO) return MCIERR_UNSUPPORTED_FUNCTION;
1257     if (dwFlags & MCI_SET_ON) return MCIERR_UNSUPPORTED_FUNCTION;
1258     if (dwFlags & MCI_SET_OFF) return MCIERR_UNSUPPORTED_FUNCTION;
1259     if (dwFlags & MCI_NOTIFY) {
1260         TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n",
1261               lpParms->dwCallback);
1262         mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1263                         wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
1264     }
1265     return 0;
1266 }
1267
1268 /**************************************************************************
1269  *                      DriverProc (MCICDA.@)
1270  */
1271 LRESULT CALLBACK MCICDA_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
1272                                    LPARAM dwParam1, LPARAM dwParam2)
1273 {
1274     switch(wMsg) {
1275     case DRV_LOAD:              return 1;
1276     case DRV_FREE:              return 1;
1277     case DRV_OPEN:              return MCICDA_drvOpen((LPCWSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSW)dwParam2);
1278     case DRV_CLOSE:             return MCICDA_drvClose(dwDevID);
1279     case DRV_ENABLE:            return 1;
1280     case DRV_DISABLE:           return 1;
1281     case DRV_QUERYCONFIGURE:    return 1;
1282     case DRV_CONFIGURE:         MessageBoxA(0, "MCI audio CD driver !", "Wine Driver", MB_OK); return 1;
1283     case DRV_INSTALL:           return DRVCNF_RESTART;
1284     case DRV_REMOVE:            return DRVCNF_RESTART;
1285     }
1286
1287     if (dwDevID == 0xFFFFFFFF) return MCIERR_UNSUPPORTED_FUNCTION;
1288
1289     switch (wMsg) {
1290     case MCI_OPEN_DRIVER:       return MCICDA_Open(dwDevID, dwParam1, (LPMCI_OPEN_PARMSW)dwParam2);
1291     case MCI_CLOSE_DRIVER:      return MCICDA_Close(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
1292     case MCI_GETDEVCAPS:        return MCICDA_GetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS)dwParam2);
1293     case MCI_INFO:              return MCICDA_Info(dwDevID, dwParam1, (LPMCI_INFO_PARMSW)dwParam2);
1294     case MCI_STATUS:            return MCICDA_Status(dwDevID, dwParam1, (LPMCI_STATUS_PARMS)dwParam2);
1295     case MCI_SET:               return MCICDA_Set(dwDevID, dwParam1, (LPMCI_SET_PARMS)dwParam2);
1296     case MCI_PLAY:              return MCICDA_Play(dwDevID, dwParam1, (LPMCI_PLAY_PARMS)dwParam2);
1297     case MCI_STOP:              return MCICDA_Stop(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
1298     case MCI_PAUSE:             return MCICDA_Pause(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
1299     case MCI_RESUME:            return MCICDA_Resume(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
1300     case MCI_SEEK:              return MCICDA_Seek(dwDevID, dwParam1, (LPMCI_SEEK_PARMS)dwParam2);
1301     /* commands that should report an error as they are not supported in
1302      * the native version */
1303     case MCI_SET_DOOR_CLOSED:
1304     case MCI_SET_DOOR_OPEN:
1305     case MCI_LOAD:
1306     case MCI_SAVE:
1307     case MCI_FREEZE:
1308     case MCI_PUT:
1309     case MCI_REALIZE:
1310     case MCI_UNFREEZE:
1311     case MCI_UPDATE:
1312     case MCI_WHERE:
1313     case MCI_STEP:
1314     case MCI_SPIN:
1315     case MCI_ESCAPE:
1316     case MCI_COPY:
1317     case MCI_CUT:
1318     case MCI_DELETE:
1319     case MCI_PASTE:
1320     case MCI_WINDOW:
1321         TRACE("Unsupported command [0x%x]\n", wMsg);
1322         break;
1323     case MCI_OPEN:
1324     case MCI_CLOSE:
1325         ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1326         break;
1327     default:
1328         TRACE("Sending msg [0x%x] to default driver proc\n", wMsg);
1329         return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1330     }
1331     return MCIERR_UNRECOGNIZED_COMMAND;
1332 }
1333
1334 /*-----------------------------------------------------------------------*/