2 * MCI driver for audio CD (MCICDA)
4 * Copyright 1994 Martin Ayotte
5 * Copyright 1998-99 Eric Pouech
6 * Copyright 2000 Andreas Mohr
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.
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.
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
28 #define WIN32_NO_STATUS
38 #include "wine/debug.h"
39 #include "wine/unicode.h"
41 WINE_DEFAULT_DEBUG_CHANNEL(mcicda);
43 #define CDFRAMES_PERSEC 75
44 #define CDFRAMES_PERMIN (CDFRAMES_PERSEC * 60)
45 #define FRAME_OF_ADDR(a) ((a)[1] * CDFRAMES_PERMIN + (a)[2] * CDFRAMES_PERSEC + (a)[3])
46 #define FRAME_OF_TOC(toc, idx) FRAME_OF_ADDR((toc).TrackData[idx - (toc).FirstTrack].Address)
50 int nUseCount; /* Incremented for each shared open */
51 BOOL fShareable; /* TRUE if first open was shareable */
52 WORD wNotifyDeviceID; /* MCI device ID with a pending notification */
53 HANDLE hCallback; /* Callback handle for pending notification */
58 /*-----------------------------------------------------------------------*/
60 /**************************************************************************
61 * MCICDA_drvOpen [internal]
63 static DWORD MCICDA_drvOpen(LPCWSTR str, LPMCI_OPEN_DRIVER_PARMSW modp)
65 WINE_MCICDAUDIO* wmcda;
67 if (!modp) return 0xFFFFFFFF;
69 wmcda = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCICDAUDIO));
74 wmcda->wDevID = modp->wDeviceID;
75 mciSetDriverData(wmcda->wDevID, (DWORD_PTR)wmcda);
76 modp->wCustomCommandTable = MCI_NO_COMMAND_TABLE;
77 modp->wType = MCI_DEVTYPE_CD_AUDIO;
78 return modp->wDeviceID;
81 /**************************************************************************
82 * MCICDA_drvClose [internal]
84 static DWORD MCICDA_drvClose(DWORD dwDevID)
86 WINE_MCICDAUDIO* wmcda = (WINE_MCICDAUDIO*)mciGetDriverData(dwDevID);
89 HeapFree(GetProcessHeap(), 0, wmcda);
90 mciSetDriverData(dwDevID, 0);
92 return (dwDevID == 0xFFFFFFFF) ? 1 : 0;
95 /**************************************************************************
96 * MCICDA_GetOpenDrv [internal]
98 static WINE_MCICDAUDIO* MCICDA_GetOpenDrv(UINT wDevID)
100 WINE_MCICDAUDIO* wmcda = (WINE_MCICDAUDIO*)mciGetDriverData(wDevID);
102 if (wmcda == NULL || wmcda->nUseCount == 0) {
103 WARN("Invalid wDevID=%u\n", wDevID);
109 /**************************************************************************
110 * MCICDA_GetStatus [internal]
112 static DWORD MCICDA_GetStatus(WINE_MCICDAUDIO* wmcda)
114 CDROM_SUB_Q_DATA_FORMAT fmt;
115 SUB_Q_CHANNEL_DATA data;
117 DWORD mode = MCI_MODE_NOT_READY;
119 fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
120 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
121 &data, sizeof(data), &br, NULL)) {
122 if (GetLastError() == ERROR_NOT_READY) mode = MCI_MODE_OPEN;
124 switch (data.CurrentPosition.Header.AudioStatus)
126 case AUDIO_STATUS_IN_PROGRESS: mode = MCI_MODE_PLAY; break;
127 case AUDIO_STATUS_PAUSED: mode = MCI_MODE_PAUSE; break;
128 case AUDIO_STATUS_NO_STATUS:
129 case AUDIO_STATUS_PLAY_COMPLETE: mode = MCI_MODE_STOP; break;
130 case AUDIO_STATUS_PLAY_ERROR:
131 case AUDIO_STATUS_NOT_SUPPORTED:
139 /**************************************************************************
140 * MCICDA_GetError [internal]
142 static int MCICDA_GetError(WINE_MCICDAUDIO* wmcda)
144 switch (GetLastError())
146 case ERROR_NOT_READY: return MCIERR_DEVICE_NOT_READY;
147 case ERROR_IO_DEVICE: return MCIERR_HARDWARE;
149 FIXME("Unknown mode %u\n", GetLastError());
151 return MCIERR_DRIVER_INTERNAL;
154 /**************************************************************************
155 * MCICDA_CalcFrame [internal]
157 static DWORD MCICDA_CalcFrame(WINE_MCICDAUDIO* wmcda, DWORD dwTime)
165 TRACE("(%p, %08X, %u);\n", wmcda, wmcda->dwTimeFormat, dwTime);
167 switch (wmcda->dwTimeFormat) {
168 case MCI_FORMAT_MILLISECONDS:
169 dwFrame = ((dwTime - 1) * CDFRAMES_PERSEC + 500) / 1000;
170 TRACE("MILLISECONDS %u\n", dwFrame);
173 TRACE("MSF %02u:%02u:%02u\n",
174 MCI_MSF_MINUTE(dwTime), MCI_MSF_SECOND(dwTime), MCI_MSF_FRAME(dwTime));
175 dwFrame += CDFRAMES_PERMIN * MCI_MSF_MINUTE(dwTime);
176 dwFrame += CDFRAMES_PERSEC * MCI_MSF_SECOND(dwTime);
177 dwFrame += MCI_MSF_FRAME(dwTime);
179 case MCI_FORMAT_TMSF:
180 default: /* unknown format ! force TMSF ! ... */
181 wTrack = MCI_TMSF_TRACK(dwTime);
182 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
183 &toc, sizeof(toc), &br, NULL))
185 if (wTrack < toc.FirstTrack || wTrack > toc.LastTrack)
187 TRACE("MSF %02u-%02u:%02u:%02u\n",
188 MCI_TMSF_TRACK(dwTime), MCI_TMSF_MINUTE(dwTime),
189 MCI_TMSF_SECOND(dwTime), MCI_TMSF_FRAME(dwTime));
190 addr = toc.TrackData[wTrack - toc.FirstTrack].Address;
191 TRACE("TMSF trackpos[%u]=%d:%d:%d\n",
192 wTrack, addr[1], addr[2], addr[3]);
193 dwFrame = CDFRAMES_PERMIN * (addr[1] + MCI_TMSF_MINUTE(dwTime)) +
194 CDFRAMES_PERSEC * (addr[2] + MCI_TMSF_SECOND(dwTime)) +
195 addr[3] + MCI_TMSF_FRAME(dwTime);
201 /**************************************************************************
202 * MCICDA_CalcTime [internal]
204 static DWORD MCICDA_CalcTime(WINE_MCICDAUDIO* wmcda, DWORD tf, DWORD dwFrame, LPDWORD lpRet)
214 TRACE("(%p, %08X, %u);\n", wmcda, tf, dwFrame);
217 case MCI_FORMAT_MILLISECONDS:
218 dwTime = (dwFrame * 1000) / CDFRAMES_PERSEC + 1;
219 TRACE("MILLISECONDS %u\n", dwTime);
223 wMinutes = dwFrame / CDFRAMES_PERMIN;
224 wSeconds = (dwFrame - CDFRAMES_PERMIN * wMinutes) / CDFRAMES_PERSEC;
225 wFrames = dwFrame - CDFRAMES_PERMIN * wMinutes - CDFRAMES_PERSEC * wSeconds;
226 dwTime = MCI_MAKE_MSF(wMinutes, wSeconds, wFrames);
227 TRACE("MSF %02u:%02u:%02u -> dwTime=%u\n",
228 wMinutes, wSeconds, wFrames, dwTime);
229 *lpRet = MCI_COLONIZED3_RETURN;
231 case MCI_FORMAT_TMSF:
232 default: /* unknown format ! force TMSF ! ... */
233 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
234 &toc, sizeof(toc), &br, NULL))
236 if (dwFrame < FRAME_OF_TOC(toc, toc.FirstTrack) ||
237 dwFrame > FRAME_OF_TOC(toc, toc.LastTrack + 1)) {
238 ERR("Out of range value %u [%u,%u]\n",
239 dwFrame, FRAME_OF_TOC(toc, toc.FirstTrack),
240 FRAME_OF_TOC(toc, toc.LastTrack + 1));
244 for (wTrack = toc.FirstTrack; wTrack <= toc.LastTrack; wTrack++) {
245 if (FRAME_OF_TOC(toc, wTrack) > dwFrame)
249 dwFrame -= FRAME_OF_TOC(toc, wTrack);
250 wMinutes = dwFrame / CDFRAMES_PERMIN;
251 wSeconds = (dwFrame - CDFRAMES_PERMIN * wMinutes) / CDFRAMES_PERSEC;
252 wFrames = dwFrame - CDFRAMES_PERMIN * wMinutes - CDFRAMES_PERSEC * wSeconds;
253 dwTime = MCI_MAKE_TMSF(wTrack, wMinutes, wSeconds, wFrames);
254 TRACE("%02u-%02u:%02u:%02u\n", wTrack, wMinutes, wSeconds, wFrames);
255 *lpRet = MCI_COLONIZED4_RETURN;
261 static DWORD MCICDA_Seek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms);
262 static DWORD MCICDA_Stop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms);
264 /**************************************************************************
265 * MCICDA_Open [internal]
267 static DWORD MCICDA_Open(UINT wDevID, DWORD dwFlags, LPMCI_OPEN_PARMSW lpOpenParms)
270 DWORD ret = MCIERR_HARDWARE;
271 WINE_MCICDAUDIO* wmcda = (WINE_MCICDAUDIO*)mciGetDriverData(wDevID);
272 WCHAR root[7], drive = 0;
275 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpOpenParms);
277 if (lpOpenParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
278 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
280 dwDeviceID = lpOpenParms->wDeviceID;
282 if (wmcda->nUseCount > 0) {
283 /* The driver is already open on this channel */
284 /* If the driver was opened shareable before and this open specifies */
285 /* shareable then increment the use count */
286 if (wmcda->fShareable && (dwFlags & MCI_OPEN_SHAREABLE))
289 return MCIERR_MUST_USE_SHAREABLE;
291 wmcda->nUseCount = 1;
292 wmcda->fShareable = dwFlags & MCI_OPEN_SHAREABLE;
294 if (dwFlags & MCI_OPEN_ELEMENT) {
295 if (dwFlags & MCI_OPEN_ELEMENT_ID) {
296 WARN("MCI_OPEN_ELEMENT_ID %p! Abort\n", lpOpenParms->lpstrElementName);
297 return MCIERR_NO_ELEMENT_ALLOWED;
299 TRACE("MCI_OPEN_ELEMENT element name: %s\n", debugstr_w(lpOpenParms->lpstrElementName));
300 if (!isalpha(lpOpenParms->lpstrElementName[0]) || lpOpenParms->lpstrElementName[1] != ':' ||
301 (lpOpenParms->lpstrElementName[2] && lpOpenParms->lpstrElementName[2] != '\\'))
303 WARN("MCI_OPEN_ELEMENT unsupported format: %s\n",
304 debugstr_w(lpOpenParms->lpstrElementName));
305 ret = MCIERR_NO_ELEMENT_ALLOWED;
308 drive = toupper(lpOpenParms->lpstrElementName[0]);
309 root[0] = drive; root[1] = ':'; root[2] = '\\'; root[3] = '\0';
310 if (GetDriveTypeW(root) != DRIVE_CDROM)
312 ret = MCIERR_INVALID_DEVICE_NAME;
318 /* drive letter isn't passed... get the dwDeviceID'th cdrom in the system */
319 root[0] = 'A'; root[1] = ':'; root[2] = '\\'; root[3] = '\0';
320 for (count = 0; root[0] <= 'Z'; root[0]++)
322 if (GetDriveTypeW(root) == DRIVE_CDROM && ++count >= dwDeviceID)
330 ret = MCIERR_INVALID_DEVICE_ID;
335 wmcda->wNotifyDeviceID = dwDeviceID;
336 wmcda->dwTimeFormat = MCI_FORMAT_MSF;
338 /* now, open the handle */
339 root[0] = root[1] = '\\'; root[2] = '.'; root[3] = '\\'; root[4] = drive; root[5] = ':'; root[6] = '\0';
340 wmcda->handle = CreateFileW(root, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
341 if (wmcda->handle != INVALID_HANDLE_VALUE)
349 /**************************************************************************
350 * MCICDA_Close [internal]
352 static DWORD MCICDA_Close(UINT wDevID, DWORD dwParam, LPMCI_GENERIC_PARMS lpParms)
354 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
356 TRACE("(%04X, %08X, %p);\n", wDevID, dwParam, lpParms);
358 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
360 if (--wmcda->nUseCount == 0) {
361 CloseHandle(wmcda->handle);
366 /**************************************************************************
367 * MCICDA_GetDevCaps [internal]
369 static DWORD MCICDA_GetDevCaps(UINT wDevID, DWORD dwFlags,
370 LPMCI_GETDEVCAPS_PARMS lpParms)
374 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
376 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
378 if (dwFlags & MCI_GETDEVCAPS_ITEM) {
379 TRACE("MCI_GETDEVCAPS_ITEM dwItem=%08X;\n", lpParms->dwItem);
381 switch (lpParms->dwItem) {
382 case MCI_GETDEVCAPS_CAN_RECORD:
383 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
384 ret = MCI_RESOURCE_RETURNED;
386 case MCI_GETDEVCAPS_HAS_AUDIO:
387 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
388 ret = MCI_RESOURCE_RETURNED;
390 case MCI_GETDEVCAPS_HAS_VIDEO:
391 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
392 ret = MCI_RESOURCE_RETURNED;
394 case MCI_GETDEVCAPS_DEVICE_TYPE:
395 lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_CD_AUDIO, MCI_DEVTYPE_CD_AUDIO);
396 ret = MCI_RESOURCE_RETURNED;
398 case MCI_GETDEVCAPS_USES_FILES:
399 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
400 ret = MCI_RESOURCE_RETURNED;
402 case MCI_GETDEVCAPS_COMPOUND_DEVICE:
403 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
404 ret = MCI_RESOURCE_RETURNED;
406 case MCI_GETDEVCAPS_CAN_EJECT:
407 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
408 ret = MCI_RESOURCE_RETURNED;
410 case MCI_GETDEVCAPS_CAN_PLAY:
411 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
412 ret = MCI_RESOURCE_RETURNED;
414 case MCI_GETDEVCAPS_CAN_SAVE:
415 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
416 ret = MCI_RESOURCE_RETURNED;
419 ERR("Unsupported %x devCaps item\n", lpParms->dwItem);
420 return MCIERR_UNRECOGNIZED_COMMAND;
423 TRACE("No GetDevCaps-Item !\n");
424 return MCIERR_UNRECOGNIZED_COMMAND;
426 TRACE("lpParms->dwReturn=%08X;\n", lpParms->dwReturn);
430 static DWORD CDROM_Audio_GetSerial(CDROM_TOC* toc)
432 unsigned long serial = 0;
435 DWORD dwStart, dwEnd;
438 * wMagic collects the wFrames from track 1
439 * dwStart, dwEnd collect the beginning and end of the disc respectively, in
441 * There it is collected for correcting the serial when there are less than
444 wMagic = toc->TrackData[0].Address[3];
445 dwStart = FRAME_OF_TOC(*toc, toc->FirstTrack);
447 for (i = 0; i <= toc->LastTrack - toc->FirstTrack; i++) {
448 serial += (toc->TrackData[i].Address[1] << 16) |
449 (toc->TrackData[i].Address[2] << 8) | toc->TrackData[i].Address[3];
451 dwEnd = FRAME_OF_TOC(*toc, toc->LastTrack + 1);
453 if (toc->LastTrack - toc->FirstTrack + 1 < 3)
454 serial += wMagic + (dwEnd - dwStart);
460 /**************************************************************************
461 * MCICDA_Info [internal]
463 static DWORD MCICDA_Info(UINT wDevID, DWORD dwFlags, LPMCI_INFO_PARMSW lpParms)
466 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
470 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
472 if (lpParms == NULL || lpParms->lpstrReturn == NULL)
473 return MCIERR_NULL_PARAMETER_BLOCK;
474 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
476 TRACE("buf=%p, len=%u\n", lpParms->lpstrReturn, lpParms->dwRetSize);
478 if (dwFlags & MCI_INFO_PRODUCT) {
479 static const WCHAR wszAudioCd[] = {'W','i','n','e','\'','s',' ','a','u','d','i','o',' ','C','D',0};
481 } else if (dwFlags & MCI_INFO_MEDIA_UPC) {
482 ret = MCIERR_NO_IDENTITY;
483 } else if (dwFlags & MCI_INFO_MEDIA_IDENTITY) {
487 static const WCHAR wszLu[] = {'%','l','u',0};
489 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
490 &toc, sizeof(toc), &br, NULL)) {
491 return MCICDA_GetError(wmcda);
494 res = CDROM_Audio_GetSerial(&toc);
495 sprintfW(buffer, wszLu, res);
498 WARN("Don't know this info command (%u)\n", dwFlags);
499 ret = MCIERR_UNRECOGNIZED_COMMAND;
502 if (lpParms->dwRetSize <= strlenW(str)) {
503 lstrcpynW(lpParms->lpstrReturn, str, lpParms->dwRetSize - 1);
504 ret = MCIERR_PARAM_OVERFLOW;
506 strcpyW(lpParms->lpstrReturn, str);
509 *lpParms->lpstrReturn = 0;
511 TRACE("=> %s (%d)\n", debugstr_w(lpParms->lpstrReturn), ret);
515 /**************************************************************************
516 * MCICDA_Status [internal]
518 static DWORD MCICDA_Status(UINT wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
520 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
523 CDROM_SUB_Q_DATA_FORMAT fmt;
524 SUB_Q_CHANNEL_DATA data;
528 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
530 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
531 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
533 if (dwFlags & MCI_NOTIFY) {
534 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
535 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
536 wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
538 if (dwFlags & MCI_STATUS_ITEM) {
539 TRACE("dwItem = %x\n", lpParms->dwItem);
540 switch (lpParms->dwItem) {
541 case MCI_STATUS_CURRENT_TRACK:
542 fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
543 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
544 &data, sizeof(data), &br, NULL))
546 return MCICDA_GetError(wmcda);
548 lpParms->dwReturn = data.CurrentPosition.TrackNumber;
549 TRACE("CURRENT_TRACK=%u!\n", lpParms->dwReturn);
551 case MCI_STATUS_LENGTH:
552 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
553 &toc, sizeof(toc), &br, NULL)) {
554 WARN("error reading TOC !\n");
555 return MCICDA_GetError(wmcda);
557 if (dwFlags & MCI_TRACK) {
558 TRACE("MCI_TRACK #%u LENGTH=??? !\n", lpParms->dwTrack);
559 if (lpParms->dwTrack < toc.FirstTrack || lpParms->dwTrack > toc.LastTrack)
560 return MCIERR_OUTOFRANGE;
561 idx = lpParms->dwTrack - toc.FirstTrack;
562 lpParms->dwReturn = FRAME_OF_TOC(toc, lpParms->dwTrack + 1) -
563 FRAME_OF_TOC(toc, lpParms->dwTrack);
564 /* Windows returns one frame less than the total track length for the
565 last track on the CD. See CDDB HOWTO. Verified on Win95OSR2. */
566 if (lpParms->dwTrack == toc.LastTrack)
569 /* Sum of the lengths of all of the tracks. Inherits the
570 'off by one frame' behavior from the length of the last track.
571 See above comment. */
572 lpParms->dwReturn = FRAME_OF_TOC(toc, toc.LastTrack + 1) -
573 FRAME_OF_TOC(toc, toc.FirstTrack) - 1;
575 lpParms->dwReturn = MCICDA_CalcTime(wmcda,
576 (wmcda->dwTimeFormat == MCI_FORMAT_TMSF)
577 ? MCI_FORMAT_MSF : wmcda->dwTimeFormat,
580 TRACE("LENGTH=%u !\n", lpParms->dwReturn);
582 case MCI_STATUS_MODE:
583 lpParms->dwReturn = MCICDA_GetStatus(wmcda);
584 TRACE("MCI_STATUS_MODE=%08X !\n", lpParms->dwReturn);
585 lpParms->dwReturn = MAKEMCIRESOURCE(lpParms->dwReturn, lpParms->dwReturn);
586 ret = MCI_RESOURCE_RETURNED;
588 case MCI_STATUS_MEDIA_PRESENT:
589 lpParms->dwReturn = (MCICDA_GetStatus(wmcda) == MCI_MODE_OPEN) ?
590 MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
591 TRACE("MCI_STATUS_MEDIA_PRESENT =%c!\n", LOWORD(lpParms->dwReturn) ? 'Y' : 'N');
592 ret = MCI_RESOURCE_RETURNED;
594 case MCI_STATUS_NUMBER_OF_TRACKS:
595 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
596 &toc, sizeof(toc), &br, NULL)) {
597 WARN("error reading TOC !\n");
598 return MCICDA_GetError(wmcda);
600 lpParms->dwReturn = toc.LastTrack - toc.FirstTrack + 1;
601 TRACE("MCI_STATUS_NUMBER_OF_TRACKS = %u !\n", lpParms->dwReturn);
602 if (lpParms->dwReturn == (WORD)-1)
603 return MCICDA_GetError(wmcda);
605 case MCI_STATUS_POSITION:
606 if (dwFlags & MCI_STATUS_START) {
607 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
608 &toc, sizeof(toc), &br, NULL)) {
609 WARN("error reading TOC !\n");
610 return MCICDA_GetError(wmcda);
612 lpParms->dwReturn = FRAME_OF_TOC(toc, toc.FirstTrack);
613 TRACE("get MCI_STATUS_START !\n");
614 } else if (dwFlags & MCI_TRACK) {
615 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
616 &toc, sizeof(toc), &br, NULL)) {
617 WARN("error reading TOC !\n");
618 return MCICDA_GetError(wmcda);
620 if (lpParms->dwTrack < toc.FirstTrack || lpParms->dwTrack > toc.LastTrack)
621 return MCIERR_OUTOFRANGE;
622 lpParms->dwReturn = FRAME_OF_TOC(toc, lpParms->dwTrack);
623 TRACE("get MCI_TRACK #%u !\n", lpParms->dwTrack);
625 fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
626 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
627 &data, sizeof(data), &br, NULL)) {
628 return MCICDA_GetError(wmcda);
630 lpParms->dwReturn = FRAME_OF_ADDR(data.CurrentPosition.AbsoluteAddress);
632 lpParms->dwReturn = MCICDA_CalcTime(wmcda, wmcda->dwTimeFormat, lpParms->dwReturn, &ret);
633 TRACE("MCI_STATUS_POSITION=%08X !\n", lpParms->dwReturn);
635 case MCI_STATUS_READY:
636 TRACE("MCI_STATUS_READY !\n");
637 switch (MCICDA_GetStatus(wmcda))
639 case MCI_MODE_NOT_READY:
641 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
644 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
647 TRACE("MCI_STATUS_READY=%u!\n", LOWORD(lpParms->dwReturn));
648 ret = MCI_RESOURCE_RETURNED;
650 case MCI_STATUS_TIME_FORMAT:
651 lpParms->dwReturn = MAKEMCIRESOURCE(wmcda->dwTimeFormat, MCI_FORMAT_RETURN_BASE + wmcda->dwTimeFormat);
652 TRACE("MCI_STATUS_TIME_FORMAT=%08x!\n", LOWORD(lpParms->dwReturn));
653 ret = MCI_RESOURCE_RETURNED;
655 case 4001: /* FIXME: for bogus FullCD */
656 case MCI_CDA_STATUS_TYPE_TRACK:
657 if (!(dwFlags & MCI_TRACK))
658 ret = MCIERR_MISSING_PARAMETER;
660 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
661 &toc, sizeof(toc), &br, NULL)) {
662 WARN("error reading TOC !\n");
663 return MCICDA_GetError(wmcda);
665 if (lpParms->dwTrack < toc.FirstTrack || lpParms->dwTrack > toc.LastTrack)
666 ret = MCIERR_OUTOFRANGE;
668 lpParms->dwReturn = (toc.TrackData[lpParms->dwTrack - toc.FirstTrack].Control & 0x04) ?
669 MCI_CDA_TRACK_OTHER : MCI_CDA_TRACK_AUDIO;
671 TRACE("MCI_CDA_STATUS_TYPE_TRACK[%d]=%d\n", lpParms->dwTrack, lpParms->dwReturn);
674 FIXME("unknown command %08X !\n", lpParms->dwItem);
675 return MCIERR_UNRECOGNIZED_COMMAND;
678 WARN("not MCI_STATUS_ITEM !\n");
683 /**************************************************************************
684 * MCICDA_SkipDataTracks [internal]
686 static DWORD MCICDA_SkipDataTracks(WINE_MCICDAUDIO* wmcda,DWORD *frame)
691 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
692 &toc, sizeof(toc), &br, NULL)) {
693 WARN("error reading TOC !\n");
694 return MCICDA_GetError(wmcda);
696 /* Locate first track whose starting frame is bigger than frame */
697 for(i=toc.FirstTrack;i<=toc.LastTrack+1;i++)
698 if ( FRAME_OF_TOC(toc, i) > *frame ) break;
699 if (i <= toc.FirstTrack && i>toc.LastTrack+1) {
700 i = 0; /* requested address is out of range: go back to start */
701 *frame = FRAME_OF_TOC(toc,toc.FirstTrack);
705 /* i points to last track whose start address is not greater than frame.
706 * Now skip non-audio tracks */
707 for(;i<=toc.LastTrack+1;i++)
708 if ( ! (toc.TrackData[i-toc.FirstTrack].Control & 4) )
710 /* The frame will be an address in the next audio track or
711 * address of lead-out. */
712 if ( FRAME_OF_TOC(toc, i) > *frame )
713 *frame = FRAME_OF_TOC(toc, i);
717 /**************************************************************************
718 * MCICDA_Play [internal]
720 static DWORD MCICDA_Play(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
722 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
723 DWORD ret = 0, start, end;
725 CDROM_PLAY_AUDIO_MSF play;
726 CDROM_SUB_Q_DATA_FORMAT fmt;
727 SUB_Q_CHANNEL_DATA data;
730 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
733 return MCIERR_NULL_PARAMETER_BLOCK;
736 return MCIERR_INVALID_DEVICE_ID;
738 if (dwFlags & MCI_FROM) {
739 start = MCICDA_CalcFrame(wmcda, lpParms->dwFrom);
740 if ( (ret=MCICDA_SkipDataTracks(wmcda, &start)) )
742 TRACE("MCI_FROM=%08X -> %u\n", lpParms->dwFrom, start);
744 fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
745 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
746 &data, sizeof(data), &br, NULL)) {
747 return MCICDA_GetError(wmcda);
749 start = FRAME_OF_ADDR(data.CurrentPosition.AbsoluteAddress);
750 if ( (ret=MCICDA_SkipDataTracks(wmcda, &start)) )
753 if (dwFlags & MCI_TO) {
754 end = MCICDA_CalcFrame(wmcda, lpParms->dwTo);
755 TRACE("MCI_TO=%08X -> %u\n", lpParms->dwTo, end);
757 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
758 &toc, sizeof(toc), &br, NULL)) {
759 WARN("error reading TOC !\n");
760 return MCICDA_GetError(wmcda);
762 end = FRAME_OF_TOC(toc, toc.LastTrack + 1) - 1;
764 TRACE("Playing from %u to %u\n", start, end);
765 play.StartingM = start / CDFRAMES_PERMIN;
766 play.StartingS = (start / CDFRAMES_PERSEC) % 60;
767 play.StartingF = start % CDFRAMES_PERSEC;
768 play.EndingM = end / CDFRAMES_PERMIN;
769 play.EndingS = (end / CDFRAMES_PERSEC) % 60;
770 play.EndingF = end % CDFRAMES_PERSEC;
771 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_PLAY_AUDIO_MSF, &play, sizeof(play),
772 NULL, 0, &br, NULL)) {
773 ret = MCIERR_HARDWARE;
774 } else if (dwFlags & MCI_NOTIFY) {
775 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
777 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
778 wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
784 /**************************************************************************
785 * MCICDA_Stop [internal]
787 static DWORD MCICDA_Stop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
789 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
792 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
794 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
796 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_STOP_AUDIO, NULL, 0, NULL, 0, &br, NULL))
797 return MCIERR_HARDWARE;
799 if (lpParms && (dwFlags & MCI_NOTIFY)) {
800 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
801 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
802 wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
807 /**************************************************************************
808 * MCICDA_Pause [internal]
810 static DWORD MCICDA_Pause(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
812 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
815 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
817 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
819 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_PAUSE_AUDIO, NULL, 0, NULL, 0, &br, NULL))
820 return MCIERR_HARDWARE;
822 if (lpParms && (dwFlags & MCI_NOTIFY)) {
823 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
824 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
825 wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
830 /**************************************************************************
831 * MCICDA_Resume [internal]
833 static DWORD MCICDA_Resume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
835 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
838 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
840 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
842 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_RESUME_AUDIO, NULL, 0, NULL, 0, &br, NULL))
843 return MCIERR_HARDWARE;
845 if (lpParms && (dwFlags & MCI_NOTIFY)) {
846 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
847 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
848 wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
853 /**************************************************************************
854 * MCICDA_Seek [internal]
856 static DWORD MCICDA_Seek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
859 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
860 CDROM_SEEK_AUDIO_MSF seek;
864 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
866 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
867 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
869 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
870 &toc, sizeof(toc), &br, NULL)) {
871 WARN("error reading TOC !\n");
872 return MCICDA_GetError(wmcda);
874 switch (dwFlags & ~(MCI_NOTIFY|MCI_WAIT)) {
875 case MCI_SEEK_TO_START:
876 TRACE("Seeking to start\n");
877 at = FRAME_OF_TOC(toc,toc.FirstTrack);
878 if ( (ret=MCICDA_SkipDataTracks(wmcda, &at)) )
881 case MCI_SEEK_TO_END:
882 TRACE("Seeking to end\n");
883 at = FRAME_OF_TOC(toc, toc.LastTrack + 1) - 1;
884 if ( (ret=MCICDA_SkipDataTracks(wmcda, &at)) )
888 TRACE("Seeking to %u\n", lpParms->dwTo);
889 at = MCICDA_CalcFrame(wmcda, lpParms->dwTo);
890 if ( (ret=MCICDA_SkipDataTracks(wmcda, &at)) )
894 TRACE("Unknown seek action %08lX\n",
895 (dwFlags & ~(MCI_NOTIFY|MCI_WAIT)));
896 return MCIERR_UNSUPPORTED_FUNCTION;
898 seek.M = at / CDFRAMES_PERMIN;
899 seek.S = (at / CDFRAMES_PERSEC) % 60;
900 seek.F = at % CDFRAMES_PERSEC;
901 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_SEEK_AUDIO_MSF, &seek, sizeof(seek),
903 return MCIERR_HARDWARE;
905 if (dwFlags & MCI_NOTIFY) {
906 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
907 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
908 wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
913 /**************************************************************************
914 * MCICDA_SetDoor [internal]
916 static DWORD MCICDA_SetDoor(UINT wDevID, BOOL open)
918 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
921 TRACE("(%04x, %s) !\n", wDevID, (open) ? "OPEN" : "CLOSE");
923 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
925 if (!DeviceIoControl(wmcda->handle,
926 (open) ? IOCTL_STORAGE_EJECT_MEDIA : IOCTL_STORAGE_LOAD_MEDIA,
927 NULL, 0, NULL, 0, &br, NULL))
928 return MCIERR_HARDWARE;
933 /**************************************************************************
934 * MCICDA_Set [internal]
936 static DWORD MCICDA_Set(UINT wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
938 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
940 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
942 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
944 if (dwFlags & MCI_SET_DOOR_OPEN) {
945 MCICDA_SetDoor(wDevID, TRUE);
947 if (dwFlags & MCI_SET_DOOR_CLOSED) {
948 MCICDA_SetDoor(wDevID, FALSE);
951 /* only functions which require valid lpParms below this line ! */
952 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
954 TRACE("dwTimeFormat=%08lX\n", lpParms->dwTimeFormat);
955 TRACE("dwAudio=%08lX\n", lpParms->dwAudio);
957 if (dwFlags & MCI_SET_TIME_FORMAT) {
958 switch (lpParms->dwTimeFormat) {
959 case MCI_FORMAT_MILLISECONDS:
960 TRACE("MCI_FORMAT_MILLISECONDS !\n");
963 TRACE("MCI_FORMAT_MSF !\n");
965 case MCI_FORMAT_TMSF:
966 TRACE("MCI_FORMAT_TMSF !\n");
969 WARN("bad time format !\n");
970 return MCIERR_BAD_TIME_FORMAT;
972 wmcda->dwTimeFormat = lpParms->dwTimeFormat;
974 if (dwFlags & MCI_SET_VIDEO) return MCIERR_UNSUPPORTED_FUNCTION;
975 if (dwFlags & MCI_SET_ON) return MCIERR_UNSUPPORTED_FUNCTION;
976 if (dwFlags & MCI_SET_OFF) return MCIERR_UNSUPPORTED_FUNCTION;
977 if (dwFlags & MCI_NOTIFY) {
978 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n",
979 lpParms->dwCallback);
980 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
981 wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
986 /**************************************************************************
987 * DriverProc (MCICDA.@)
989 LRESULT CALLBACK MCICDA_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
990 LPARAM dwParam1, LPARAM dwParam2)
993 case DRV_LOAD: return 1;
994 case DRV_FREE: return 1;
995 case DRV_OPEN: return MCICDA_drvOpen((LPCWSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSW)dwParam2);
996 case DRV_CLOSE: return MCICDA_drvClose(dwDevID);
997 case DRV_ENABLE: return 1;
998 case DRV_DISABLE: return 1;
999 case DRV_QUERYCONFIGURE: return 1;
1000 case DRV_CONFIGURE: MessageBoxA(0, "MCI audio CD driver !", "Wine Driver", MB_OK); return 1;
1001 case DRV_INSTALL: return DRVCNF_RESTART;
1002 case DRV_REMOVE: return DRVCNF_RESTART;
1005 if (dwDevID == 0xFFFFFFFF) return MCIERR_UNSUPPORTED_FUNCTION;
1008 case MCI_OPEN_DRIVER: return MCICDA_Open(dwDevID, dwParam1, (LPMCI_OPEN_PARMSW)dwParam2);
1009 case MCI_CLOSE_DRIVER: return MCICDA_Close(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
1010 case MCI_GETDEVCAPS: return MCICDA_GetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS)dwParam2);
1011 case MCI_INFO: return MCICDA_Info(dwDevID, dwParam1, (LPMCI_INFO_PARMSW)dwParam2);
1012 case MCI_STATUS: return MCICDA_Status(dwDevID, dwParam1, (LPMCI_STATUS_PARMS)dwParam2);
1013 case MCI_SET: return MCICDA_Set(dwDevID, dwParam1, (LPMCI_SET_PARMS)dwParam2);
1014 case MCI_PLAY: return MCICDA_Play(dwDevID, dwParam1, (LPMCI_PLAY_PARMS)dwParam2);
1015 case MCI_STOP: return MCICDA_Stop(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
1016 case MCI_PAUSE: return MCICDA_Pause(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
1017 case MCI_RESUME: return MCICDA_Resume(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
1018 case MCI_SEEK: return MCICDA_Seek(dwDevID, dwParam1, (LPMCI_SEEK_PARMS)dwParam2);
1019 /* commands that should report an error as they are not supported in
1020 * the native version */
1021 case MCI_SET_DOOR_CLOSED:
1022 case MCI_SET_DOOR_OPEN:
1039 TRACE("Unsupported command [0x%x]\n", wMsg);
1043 ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1046 TRACE("Sending msg [0x%x] to default driver proc\n", wMsg);
1047 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1049 return MCIERR_UNRECOGNIZED_COMMAND;
1052 /*-----------------------------------------------------------------------*/