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