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