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
29 #define WIN32_NO_STATUS
40 #include "wine/debug.h"
41 #include "wine/unicode.h"
43 WINE_DEFAULT_DEBUG_CHANNEL(mcicda);
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)
52 int nUseCount; /* Incremented for each shared open */
53 BOOL fShareable; /* TRUE if first open was shareable */
54 WORD wNotifyDeviceID; /* MCI device ID with a pending notification */
55 HANDLE hCallback; /* Callback handle for pending notification */
60 /*-----------------------------------------------------------------------*/
62 /**************************************************************************
63 * MCICDA_drvOpen [internal]
65 static DWORD MCICDA_drvOpen(LPCWSTR str, LPMCI_OPEN_DRIVER_PARMSW modp)
67 WINE_MCICDAUDIO* wmcda;
69 if (!modp) return 0xFFFFFFFF;
71 wmcda = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCICDAUDIO));
76 wmcda->wDevID = modp->wDeviceID;
77 mciSetDriverData(wmcda->wDevID, (DWORD)wmcda);
78 modp->wCustomCommandTable = MCI_NO_COMMAND_TABLE;
79 modp->wType = MCI_DEVTYPE_CD_AUDIO;
80 return modp->wDeviceID;
83 /**************************************************************************
84 * MCICDA_drvClose [internal]
86 static DWORD MCICDA_drvClose(DWORD dwDevID)
88 WINE_MCICDAUDIO* wmcda = (WINE_MCICDAUDIO*)mciGetDriverData(dwDevID);
91 HeapFree(GetProcessHeap(), 0, wmcda);
92 mciSetDriverData(dwDevID, 0);
94 return (dwDevID == 0xFFFFFFFF) ? 1 : 0;
97 /**************************************************************************
98 * MCICDA_GetOpenDrv [internal]
100 static WINE_MCICDAUDIO* MCICDA_GetOpenDrv(UINT wDevID)
102 WINE_MCICDAUDIO* wmcda = (WINE_MCICDAUDIO*)mciGetDriverData(wDevID);
104 if (wmcda == NULL || wmcda->nUseCount == 0) {
105 WARN("Invalid wDevID=%u\n", wDevID);
111 /**************************************************************************
112 * MCICDA_GetStatus [internal]
114 static DWORD MCICDA_GetStatus(WINE_MCICDAUDIO* wmcda)
116 CDROM_SUB_Q_DATA_FORMAT fmt;
117 SUB_Q_CHANNEL_DATA data;
119 DWORD mode = MCI_MODE_NOT_READY;
121 fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
122 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
123 &data, sizeof(data), &br, NULL)) {
124 if (GetLastError() == ERROR_NOT_READY) mode = MCI_MODE_OPEN;
126 switch (data.CurrentPosition.Header.AudioStatus)
128 case AUDIO_STATUS_IN_PROGRESS: mode = MCI_MODE_PLAY; break;
129 case AUDIO_STATUS_PAUSED: mode = MCI_MODE_PAUSE; break;
130 case AUDIO_STATUS_NO_STATUS:
131 case AUDIO_STATUS_PLAY_COMPLETE: mode = MCI_MODE_STOP; break;
132 case AUDIO_STATUS_PLAY_ERROR:
133 case AUDIO_STATUS_NOT_SUPPORTED:
141 /**************************************************************************
142 * MCICDA_GetError [internal]
144 static int MCICDA_GetError(WINE_MCICDAUDIO* wmcda)
146 switch (GetLastError())
148 case ERROR_NOT_READY: return MCIERR_DEVICE_NOT_READY;
149 case ERROR_IO_DEVICE: return MCIERR_HARDWARE;
151 FIXME("Unknown mode %u\n", GetLastError());
153 return MCIERR_DRIVER_INTERNAL;
156 /**************************************************************************
157 * MCICDA_CalcFrame [internal]
159 static DWORD MCICDA_CalcFrame(WINE_MCICDAUDIO* wmcda, DWORD dwTime)
167 TRACE("(%p, %08X, %u);\n", wmcda, wmcda->dwTimeFormat, dwTime);
169 switch (wmcda->dwTimeFormat) {
170 case MCI_FORMAT_MILLISECONDS:
171 dwFrame = ((dwTime - 1) * CDFRAMES_PERSEC + 500) / 1000;
172 TRACE("MILLISECONDS %u\n", dwFrame);
175 TRACE("MSF %02u:%02u:%02u\n",
176 MCI_MSF_MINUTE(dwTime), MCI_MSF_SECOND(dwTime), MCI_MSF_FRAME(dwTime));
177 dwFrame += CDFRAMES_PERMIN * MCI_MSF_MINUTE(dwTime);
178 dwFrame += CDFRAMES_PERSEC * MCI_MSF_SECOND(dwTime);
179 dwFrame += MCI_MSF_FRAME(dwTime);
181 case MCI_FORMAT_TMSF:
182 default: /* unknown format ! force TMSF ! ... */
183 wTrack = MCI_TMSF_TRACK(dwTime);
184 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
185 &toc, sizeof(toc), &br, NULL))
187 if (wTrack < toc.FirstTrack || wTrack > toc.LastTrack)
189 TRACE("MSF %02u-%02u:%02u:%02u\n",
190 MCI_TMSF_TRACK(dwTime), MCI_TMSF_MINUTE(dwTime),
191 MCI_TMSF_SECOND(dwTime), MCI_TMSF_FRAME(dwTime));
192 addr = toc.TrackData[wTrack - toc.FirstTrack].Address;
193 TRACE("TMSF trackpos[%u]=%d:%d:%d\n",
194 wTrack, addr[1], addr[2], addr[3]);
195 dwFrame = CDFRAMES_PERMIN * (addr[1] + MCI_TMSF_MINUTE(dwTime)) +
196 CDFRAMES_PERSEC * (addr[2] + MCI_TMSF_SECOND(dwTime)) +
197 addr[3] + MCI_TMSF_FRAME(dwTime);
203 /**************************************************************************
204 * MCICDA_CalcTime [internal]
206 static DWORD MCICDA_CalcTime(WINE_MCICDAUDIO* wmcda, DWORD tf, DWORD dwFrame, LPDWORD lpRet)
216 TRACE("(%p, %08X, %u);\n", wmcda, tf, dwFrame);
219 case MCI_FORMAT_MILLISECONDS:
220 dwTime = (dwFrame * 1000) / CDFRAMES_PERSEC + 1;
221 TRACE("MILLISECONDS %u\n", dwTime);
225 wMinutes = dwFrame / CDFRAMES_PERMIN;
226 wSeconds = (dwFrame - CDFRAMES_PERMIN * wMinutes) / CDFRAMES_PERSEC;
227 wFrames = dwFrame - CDFRAMES_PERMIN * wMinutes - CDFRAMES_PERSEC * wSeconds;
228 dwTime = MCI_MAKE_MSF(wMinutes, wSeconds, wFrames);
229 TRACE("MSF %02u:%02u:%02u -> dwTime=%u\n",
230 wMinutes, wSeconds, wFrames, dwTime);
231 *lpRet = MCI_COLONIZED3_RETURN;
233 case MCI_FORMAT_TMSF:
234 default: /* unknown format ! force TMSF ! ... */
235 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
236 &toc, sizeof(toc), &br, NULL))
238 if (dwFrame < FRAME_OF_TOC(toc, toc.FirstTrack) ||
239 dwFrame > FRAME_OF_TOC(toc, toc.LastTrack + 1)) {
240 ERR("Out of range value %u [%u,%u]\n",
241 dwFrame, FRAME_OF_TOC(toc, toc.FirstTrack),
242 FRAME_OF_TOC(toc, toc.LastTrack + 1));
246 for (wTrack = toc.FirstTrack; wTrack <= toc.LastTrack; wTrack++) {
247 if (FRAME_OF_TOC(toc, wTrack) > dwFrame)
251 dwFrame -= FRAME_OF_TOC(toc, wTrack);
252 wMinutes = dwFrame / CDFRAMES_PERMIN;
253 wSeconds = (dwFrame - CDFRAMES_PERMIN * wMinutes) / CDFRAMES_PERSEC;
254 wFrames = dwFrame - CDFRAMES_PERMIN * wMinutes - CDFRAMES_PERSEC * wSeconds;
255 dwTime = MCI_MAKE_TMSF(wTrack, wMinutes, wSeconds, wFrames);
256 TRACE("%02u-%02u:%02u:%02u\n", wTrack, wMinutes, wSeconds, wFrames);
257 *lpRet = MCI_COLONIZED4_RETURN;
263 static DWORD MCICDA_Seek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms);
264 static DWORD MCICDA_Stop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms);
266 /**************************************************************************
267 * MCICDA_Open [internal]
269 static DWORD MCICDA_Open(UINT wDevID, DWORD dwFlags, LPMCI_OPEN_PARMSW lpOpenParms)
272 DWORD ret = MCIERR_HARDWARE;
273 WINE_MCICDAUDIO* wmcda = (WINE_MCICDAUDIO*)mciGetDriverData(wDevID);
274 WCHAR root[7], drive = 0;
277 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpOpenParms);
279 if (lpOpenParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
280 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
282 dwDeviceID = lpOpenParms->wDeviceID;
284 if (wmcda->nUseCount > 0) {
285 /* The driver is already open on this channel */
286 /* If the driver was opened shareable before and this open specifies */
287 /* shareable then increment the use count */
288 if (wmcda->fShareable && (dwFlags & MCI_OPEN_SHAREABLE))
291 return MCIERR_MUST_USE_SHAREABLE;
293 wmcda->nUseCount = 1;
294 wmcda->fShareable = dwFlags & MCI_OPEN_SHAREABLE;
296 if (dwFlags & MCI_OPEN_ELEMENT) {
297 if (dwFlags & MCI_OPEN_ELEMENT_ID) {
298 WARN("MCI_OPEN_ELEMENT_ID %8x ! Abort\n", (DWORD)lpOpenParms->lpstrElementName);
299 return MCIERR_NO_ELEMENT_ALLOWED;
301 TRACE("MCI_OPEN_ELEMENT element name: %s\n", debugstr_w(lpOpenParms->lpstrElementName));
302 if (!isalpha(lpOpenParms->lpstrElementName[0]) || lpOpenParms->lpstrElementName[1] != ':' ||
303 (lpOpenParms->lpstrElementName[2] && lpOpenParms->lpstrElementName[2] != '\\'))
305 WARN("MCI_OPEN_ELEMENT unsupported format: %s\n",
306 debugstr_w(lpOpenParms->lpstrElementName));
307 ret = MCIERR_NO_ELEMENT_ALLOWED;
310 drive = toupper(lpOpenParms->lpstrElementName[0]);
311 root[0] = drive; root[1] = ':'; root[2] = '\\'; root[3] = '\0';
312 if (GetDriveTypeW(root) != DRIVE_CDROM)
314 ret = MCIERR_INVALID_DEVICE_NAME;
320 /* drive letter isn't passed... get the dwDeviceID'th cdrom in the system */
321 root[0] = 'A'; root[1] = ':'; root[2] = '\\'; root[3] = '\0';
322 for (count = 0; root[0] <= 'Z'; root[0]++)
324 if (GetDriveTypeW(root) == DRIVE_CDROM && ++count >= dwDeviceID)
332 ret = MCIERR_INVALID_DEVICE_ID;
337 wmcda->wNotifyDeviceID = dwDeviceID;
338 wmcda->dwTimeFormat = MCI_FORMAT_MSF;
340 /* now, open the handle */
341 root[0] = root[1] = '\\'; root[2] = '.'; root[3] = '\\'; root[4] = drive; root[5] = ':'; root[6] = '\0';
342 wmcda->handle = CreateFileW(root, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
343 if (wmcda->handle != INVALID_HANDLE_VALUE)
351 /**************************************************************************
352 * MCICDA_Close [internal]
354 static DWORD MCICDA_Close(UINT wDevID, DWORD dwParam, LPMCI_GENERIC_PARMS lpParms)
356 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
358 TRACE("(%04X, %08X, %p);\n", wDevID, dwParam, lpParms);
360 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
362 if (--wmcda->nUseCount == 0) {
363 CloseHandle(wmcda->handle);
368 /**************************************************************************
369 * MCICDA_GetDevCaps [internal]
371 static DWORD MCICDA_GetDevCaps(UINT wDevID, DWORD dwFlags,
372 LPMCI_GETDEVCAPS_PARMS lpParms)
376 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
378 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
380 if (dwFlags & MCI_GETDEVCAPS_ITEM) {
381 TRACE("MCI_GETDEVCAPS_ITEM dwItem=%08X;\n", lpParms->dwItem);
383 switch (lpParms->dwItem) {
384 case MCI_GETDEVCAPS_CAN_RECORD:
385 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
386 ret = MCI_RESOURCE_RETURNED;
388 case MCI_GETDEVCAPS_HAS_AUDIO:
389 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
390 ret = MCI_RESOURCE_RETURNED;
392 case MCI_GETDEVCAPS_HAS_VIDEO:
393 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
394 ret = MCI_RESOURCE_RETURNED;
396 case MCI_GETDEVCAPS_DEVICE_TYPE:
397 lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_CD_AUDIO, MCI_DEVTYPE_CD_AUDIO);
398 ret = MCI_RESOURCE_RETURNED;
400 case MCI_GETDEVCAPS_USES_FILES:
401 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
402 ret = MCI_RESOURCE_RETURNED;
404 case MCI_GETDEVCAPS_COMPOUND_DEVICE:
405 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
406 ret = MCI_RESOURCE_RETURNED;
408 case MCI_GETDEVCAPS_CAN_EJECT:
409 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
410 ret = MCI_RESOURCE_RETURNED;
412 case MCI_GETDEVCAPS_CAN_PLAY:
413 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
414 ret = MCI_RESOURCE_RETURNED;
416 case MCI_GETDEVCAPS_CAN_SAVE:
417 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
418 ret = MCI_RESOURCE_RETURNED;
421 ERR("Unsupported %x devCaps item\n", lpParms->dwItem);
422 return MCIERR_UNRECOGNIZED_COMMAND;
425 TRACE("No GetDevCaps-Item !\n");
426 return MCIERR_UNRECOGNIZED_COMMAND;
428 TRACE("lpParms->dwReturn=%08X;\n", lpParms->dwReturn);
432 static DWORD CDROM_Audio_GetSerial(CDROM_TOC* toc)
434 unsigned long serial = 0;
437 DWORD dwStart, dwEnd;
440 * wMagic collects the wFrames from track 1
441 * dwStart, dwEnd collect the beginning and end of the disc respectively, in
443 * There it is collected for correcting the serial when there are less than
446 wMagic = toc->TrackData[0].Address[3];
447 dwStart = FRAME_OF_TOC(*toc, toc->FirstTrack);
449 for (i = 0; i <= toc->LastTrack - toc->FirstTrack; i++) {
450 serial += (toc->TrackData[i].Address[1] << 16) |
451 (toc->TrackData[i].Address[2] << 8) | toc->TrackData[i].Address[3];
453 dwEnd = FRAME_OF_TOC(*toc, toc->LastTrack + 1);
455 if (toc->LastTrack - toc->FirstTrack + 1 < 3)
456 serial += wMagic + (dwEnd - dwStart);
462 /**************************************************************************
463 * MCICDA_Info [internal]
465 static DWORD MCICDA_Info(UINT wDevID, DWORD dwFlags, LPMCI_INFO_PARMSW lpParms)
468 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
472 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
474 if (lpParms == NULL || lpParms->lpstrReturn == NULL)
475 return MCIERR_NULL_PARAMETER_BLOCK;
476 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
478 TRACE("buf=%p, len=%u\n", lpParms->lpstrReturn, lpParms->dwRetSize);
480 if (dwFlags & MCI_INFO_PRODUCT) {
481 static const WCHAR wszAudioCd[] = {'W','i','n','e','\'','s',' ','a','u','d','i','o',' ','C','D',0};
483 } else if (dwFlags & MCI_INFO_MEDIA_UPC) {
484 ret = MCIERR_NO_IDENTITY;
485 } else if (dwFlags & MCI_INFO_MEDIA_IDENTITY) {
489 static const WCHAR wszLu[] = {'%','l','u',0};
491 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
492 &toc, sizeof(toc), &br, NULL)) {
493 return MCICDA_GetError(wmcda);
496 res = CDROM_Audio_GetSerial(&toc);
497 sprintfW(buffer, wszLu, res);
500 WARN("Don't know this info command (%u)\n", dwFlags);
501 ret = MCIERR_UNRECOGNIZED_COMMAND;
504 if (lpParms->dwRetSize <= strlenW(str)) {
505 lstrcpynW(lpParms->lpstrReturn, str, lpParms->dwRetSize - 1);
506 ret = MCIERR_PARAM_OVERFLOW;
508 strcpyW(lpParms->lpstrReturn, str);
511 *lpParms->lpstrReturn = 0;
513 TRACE("=> %s (%d)\n", debugstr_w(lpParms->lpstrReturn), ret);
517 /**************************************************************************
518 * MCICDA_Status [internal]
520 static DWORD MCICDA_Status(UINT wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
522 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
525 CDROM_SUB_Q_DATA_FORMAT fmt;
526 SUB_Q_CHANNEL_DATA data;
530 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
532 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
533 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
535 if (dwFlags & MCI_NOTIFY) {
536 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
537 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
538 wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
540 if (dwFlags & MCI_STATUS_ITEM) {
541 TRACE("dwItem = %x\n", lpParms->dwItem);
542 switch (lpParms->dwItem) {
543 case MCI_STATUS_CURRENT_TRACK:
544 fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
545 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
546 &data, sizeof(data), &br, NULL))
548 return MCICDA_GetError(wmcda);
550 lpParms->dwReturn = data.CurrentPosition.TrackNumber;
551 TRACE("CURRENT_TRACK=%u!\n", lpParms->dwReturn);
553 case MCI_STATUS_LENGTH:
554 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
555 &toc, sizeof(toc), &br, NULL)) {
556 WARN("error reading TOC !\n");
557 return MCICDA_GetError(wmcda);
559 if (dwFlags & MCI_TRACK) {
560 TRACE("MCI_TRACK #%u LENGTH=??? !\n", lpParms->dwTrack);
561 if (lpParms->dwTrack < toc.FirstTrack || lpParms->dwTrack > toc.LastTrack)
562 return MCIERR_OUTOFRANGE;
563 idx = lpParms->dwTrack - toc.FirstTrack;
564 lpParms->dwReturn = FRAME_OF_TOC(toc, lpParms->dwTrack + 1) -
565 FRAME_OF_TOC(toc, lpParms->dwTrack);
566 /* Windows returns one frame less than the total track length for the
567 last track on the CD. See CDDB HOWTO. Verified on Win95OSR2. */
568 if (lpParms->dwTrack == toc.LastTrack)
571 /* Sum of the lengths of all of the tracks. Inherits the
572 'off by one frame' behavior from the length of the last track.
573 See above comment. */
574 lpParms->dwReturn = FRAME_OF_TOC(toc, toc.LastTrack + 1) -
575 FRAME_OF_TOC(toc, toc.FirstTrack) - 1;
577 lpParms->dwReturn = MCICDA_CalcTime(wmcda,
578 (wmcda->dwTimeFormat == MCI_FORMAT_TMSF)
579 ? MCI_FORMAT_MSF : wmcda->dwTimeFormat,
582 TRACE("LENGTH=%u !\n", lpParms->dwReturn);
584 case MCI_STATUS_MODE:
585 lpParms->dwReturn = MCICDA_GetStatus(wmcda);
586 TRACE("MCI_STATUS_MODE=%08X !\n", lpParms->dwReturn);
587 lpParms->dwReturn = MAKEMCIRESOURCE(lpParms->dwReturn, lpParms->dwReturn);
588 ret = MCI_RESOURCE_RETURNED;
590 case MCI_STATUS_MEDIA_PRESENT:
591 lpParms->dwReturn = (MCICDA_GetStatus(wmcda) == MCI_MODE_OPEN) ?
592 MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
593 TRACE("MCI_STATUS_MEDIA_PRESENT =%c!\n", LOWORD(lpParms->dwReturn) ? 'Y' : 'N');
594 ret = MCI_RESOURCE_RETURNED;
596 case MCI_STATUS_NUMBER_OF_TRACKS:
597 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
598 &toc, sizeof(toc), &br, NULL)) {
599 WARN("error reading TOC !\n");
600 return MCICDA_GetError(wmcda);
602 lpParms->dwReturn = toc.LastTrack - toc.FirstTrack + 1;
603 TRACE("MCI_STATUS_NUMBER_OF_TRACKS = %u !\n", lpParms->dwReturn);
604 if (lpParms->dwReturn == (WORD)-1)
605 return MCICDA_GetError(wmcda);
607 case MCI_STATUS_POSITION:
608 if (dwFlags & MCI_STATUS_START) {
609 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
610 &toc, sizeof(toc), &br, NULL)) {
611 WARN("error reading TOC !\n");
612 return MCICDA_GetError(wmcda);
614 lpParms->dwReturn = FRAME_OF_TOC(toc, toc.FirstTrack);
615 TRACE("get MCI_STATUS_START !\n");
616 } else if (dwFlags & MCI_TRACK) {
617 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
618 &toc, sizeof(toc), &br, NULL)) {
619 WARN("error reading TOC !\n");
620 return MCICDA_GetError(wmcda);
622 if (lpParms->dwTrack < toc.FirstTrack || lpParms->dwTrack > toc.LastTrack)
623 return MCIERR_OUTOFRANGE;
624 lpParms->dwReturn = FRAME_OF_TOC(toc, lpParms->dwTrack);
625 TRACE("get MCI_TRACK #%u !\n", lpParms->dwTrack);
627 fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
628 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
629 &data, sizeof(data), &br, NULL)) {
630 return MCICDA_GetError(wmcda);
632 lpParms->dwReturn = FRAME_OF_ADDR(data.CurrentPosition.AbsoluteAddress);
634 lpParms->dwReturn = MCICDA_CalcTime(wmcda, wmcda->dwTimeFormat, lpParms->dwReturn, &ret);
635 TRACE("MCI_STATUS_POSITION=%08X !\n", lpParms->dwReturn);
637 case MCI_STATUS_READY:
638 TRACE("MCI_STATUS_READY !\n");
639 switch (MCICDA_GetStatus(wmcda))
641 case MCI_MODE_NOT_READY:
643 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
646 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
649 TRACE("MCI_STATUS_READY=%u!\n", LOWORD(lpParms->dwReturn));
650 ret = MCI_RESOURCE_RETURNED;
652 case MCI_STATUS_TIME_FORMAT:
653 lpParms->dwReturn = MAKEMCIRESOURCE(wmcda->dwTimeFormat, MCI_FORMAT_RETURN_BASE + wmcda->dwTimeFormat);
654 TRACE("MCI_STATUS_TIME_FORMAT=%08x!\n", LOWORD(lpParms->dwReturn));
655 ret = MCI_RESOURCE_RETURNED;
657 case 4001: /* FIXME: for bogus FullCD */
658 case MCI_CDA_STATUS_TYPE_TRACK:
659 if (!(dwFlags & MCI_TRACK))
660 ret = MCIERR_MISSING_PARAMETER;
662 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
663 &toc, sizeof(toc), &br, NULL)) {
664 WARN("error reading TOC !\n");
665 return MCICDA_GetError(wmcda);
667 if (lpParms->dwTrack < toc.FirstTrack || lpParms->dwTrack > toc.LastTrack)
668 ret = MCIERR_OUTOFRANGE;
670 lpParms->dwReturn = (toc.TrackData[lpParms->dwTrack - toc.FirstTrack].Control & 0x04) ?
671 MCI_CDA_TRACK_OTHER : MCI_CDA_TRACK_AUDIO;
673 TRACE("MCI_CDA_STATUS_TYPE_TRACK[%d]=%d\n", lpParms->dwTrack, lpParms->dwReturn);
676 FIXME("unknown command %08X !\n", lpParms->dwItem);
677 return MCIERR_UNRECOGNIZED_COMMAND;
680 WARN("not MCI_STATUS_ITEM !\n");
685 /**************************************************************************
686 * MCICDA_SkipDataTracks [internal]
688 static DWORD MCICDA_SkipDataTracks(WINE_MCICDAUDIO* wmcda,DWORD *frame)
693 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
694 &toc, sizeof(toc), &br, NULL)) {
695 WARN("error reading TOC !\n");
696 return MCICDA_GetError(wmcda);
698 /* Locate first track whose starting frame is bigger than frame */
699 for(i=toc.FirstTrack;i<=toc.LastTrack+1;i++)
700 if ( FRAME_OF_TOC(toc, i) > *frame ) break;
701 if (i <= toc.FirstTrack && i>toc.LastTrack+1) {
702 i = 0; /* requested address is out of range: go back to start */
703 *frame = FRAME_OF_TOC(toc,toc.FirstTrack);
707 /* i points to last track whose start address is not greater than frame.
708 * Now skip non-audio tracks */
709 for(;i<=toc.LastTrack+1;i++)
710 if ( ! (toc.TrackData[i-toc.FirstTrack].Control & 4) )
712 /* The frame will be an address in the next audio track or
713 * address of lead-out. */
714 if ( FRAME_OF_TOC(toc, i) > *frame )
715 *frame = FRAME_OF_TOC(toc, i);
719 /**************************************************************************
720 * MCICDA_Play [internal]
722 static DWORD MCICDA_Play(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
724 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
725 DWORD ret = 0, start, end;
727 CDROM_PLAY_AUDIO_MSF play;
728 CDROM_SUB_Q_DATA_FORMAT fmt;
729 SUB_Q_CHANNEL_DATA data;
732 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
735 return MCIERR_NULL_PARAMETER_BLOCK;
738 return MCIERR_INVALID_DEVICE_ID;
740 if (dwFlags & MCI_FROM) {
741 start = MCICDA_CalcFrame(wmcda, lpParms->dwFrom);
742 if ( (ret=MCICDA_SkipDataTracks(wmcda, &start)) )
744 TRACE("MCI_FROM=%08X -> %u\n", lpParms->dwFrom, start);
746 fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
747 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
748 &data, sizeof(data), &br, NULL)) {
749 return MCICDA_GetError(wmcda);
751 start = FRAME_OF_ADDR(data.CurrentPosition.AbsoluteAddress);
752 if ( (ret=MCICDA_SkipDataTracks(wmcda, &start)) )
755 if (dwFlags & MCI_TO) {
756 end = MCICDA_CalcFrame(wmcda, lpParms->dwTo);
757 TRACE("MCI_TO=%08X -> %u\n", lpParms->dwTo, end);
759 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
760 &toc, sizeof(toc), &br, NULL)) {
761 WARN("error reading TOC !\n");
762 return MCICDA_GetError(wmcda);
764 end = FRAME_OF_TOC(toc, toc.LastTrack + 1) - 1;
766 TRACE("Playing from %u to %u\n", start, end);
767 play.StartingM = start / CDFRAMES_PERMIN;
768 play.StartingS = (start / CDFRAMES_PERSEC) % 60;
769 play.StartingF = start % CDFRAMES_PERSEC;
770 play.EndingM = end / CDFRAMES_PERMIN;
771 play.EndingS = (end / CDFRAMES_PERSEC) % 60;
772 play.EndingF = end % CDFRAMES_PERSEC;
773 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_PLAY_AUDIO_MSF, &play, sizeof(play),
774 NULL, 0, &br, NULL)) {
775 ret = MCIERR_HARDWARE;
776 } else if (dwFlags & MCI_NOTIFY) {
777 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
779 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
780 wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
786 /**************************************************************************
787 * MCICDA_Stop [internal]
789 static DWORD MCICDA_Stop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
791 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
794 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
796 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
798 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_STOP_AUDIO, NULL, 0, NULL, 0, &br, NULL))
799 return MCIERR_HARDWARE;
801 if (lpParms && (dwFlags & MCI_NOTIFY)) {
802 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
803 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
804 wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
809 /**************************************************************************
810 * MCICDA_Pause [internal]
812 static DWORD MCICDA_Pause(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
814 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
817 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
819 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
821 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_PAUSE_AUDIO, NULL, 0, NULL, 0, &br, NULL))
822 return MCIERR_HARDWARE;
824 if (lpParms && (dwFlags & MCI_NOTIFY)) {
825 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
826 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
827 wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
832 /**************************************************************************
833 * MCICDA_Resume [internal]
835 static DWORD MCICDA_Resume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
837 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
840 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
842 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
844 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_RESUME_AUDIO, NULL, 0, NULL, 0, &br, NULL))
845 return MCIERR_HARDWARE;
847 if (lpParms && (dwFlags & MCI_NOTIFY)) {
848 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
849 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
850 wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
855 /**************************************************************************
856 * MCICDA_Seek [internal]
858 static DWORD MCICDA_Seek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
861 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
862 CDROM_SEEK_AUDIO_MSF seek;
866 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
868 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
869 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
871 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
872 &toc, sizeof(toc), &br, NULL)) {
873 WARN("error reading TOC !\n");
874 return MCICDA_GetError(wmcda);
876 switch (dwFlags & ~(MCI_NOTIFY|MCI_WAIT)) {
877 case MCI_SEEK_TO_START:
878 TRACE("Seeking to start\n");
879 at = FRAME_OF_TOC(toc,toc.FirstTrack);
880 if ( (ret=MCICDA_SkipDataTracks(wmcda, &at)) )
883 case MCI_SEEK_TO_END:
884 TRACE("Seeking to end\n");
885 at = FRAME_OF_TOC(toc, toc.LastTrack + 1) - 1;
886 if ( (ret=MCICDA_SkipDataTracks(wmcda, &at)) )
890 TRACE("Seeking to %u\n", lpParms->dwTo);
891 at = MCICDA_CalcFrame(wmcda, lpParms->dwTo);
892 if ( (ret=MCICDA_SkipDataTracks(wmcda, &at)) )
896 TRACE("Unknown seek action %08lX\n",
897 (dwFlags & ~(MCI_NOTIFY|MCI_WAIT)));
898 return MCIERR_UNSUPPORTED_FUNCTION;
900 seek.M = at / CDFRAMES_PERMIN;
901 seek.S = (at / CDFRAMES_PERSEC) % 60;
902 seek.F = at % CDFRAMES_PERSEC;
903 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_SEEK_AUDIO_MSF, &seek, sizeof(seek),
905 return MCIERR_HARDWARE;
907 if (dwFlags & MCI_NOTIFY) {
908 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
909 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
910 wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
915 /**************************************************************************
916 * MCICDA_SetDoor [internal]
918 static DWORD MCICDA_SetDoor(UINT wDevID, BOOL open)
920 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
923 TRACE("(%04x, %s) !\n", wDevID, (open) ? "OPEN" : "CLOSE");
925 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
927 if (!DeviceIoControl(wmcda->handle,
928 (open) ? IOCTL_STORAGE_EJECT_MEDIA : IOCTL_STORAGE_LOAD_MEDIA,
929 NULL, 0, NULL, 0, &br, NULL))
930 return MCIERR_HARDWARE;
935 /**************************************************************************
936 * MCICDA_Set [internal]
938 static DWORD MCICDA_Set(UINT wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
940 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
942 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
944 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
946 if (dwFlags & MCI_SET_DOOR_OPEN) {
947 MCICDA_SetDoor(wDevID, TRUE);
949 if (dwFlags & MCI_SET_DOOR_CLOSED) {
950 MCICDA_SetDoor(wDevID, FALSE);
953 /* only functions which require valid lpParms below this line ! */
954 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
956 TRACE("dwTimeFormat=%08lX\n", lpParms->dwTimeFormat);
957 TRACE("dwAudio=%08lX\n", lpParms->dwAudio);
959 if (dwFlags & MCI_SET_TIME_FORMAT) {
960 switch (lpParms->dwTimeFormat) {
961 case MCI_FORMAT_MILLISECONDS:
962 TRACE("MCI_FORMAT_MILLISECONDS !\n");
965 TRACE("MCI_FORMAT_MSF !\n");
967 case MCI_FORMAT_TMSF:
968 TRACE("MCI_FORMAT_TMSF !\n");
971 WARN("bad time format !\n");
972 return MCIERR_BAD_TIME_FORMAT;
974 wmcda->dwTimeFormat = lpParms->dwTimeFormat;
976 if (dwFlags & MCI_SET_VIDEO) return MCIERR_UNSUPPORTED_FUNCTION;
977 if (dwFlags & MCI_SET_ON) return MCIERR_UNSUPPORTED_FUNCTION;
978 if (dwFlags & MCI_SET_OFF) return MCIERR_UNSUPPORTED_FUNCTION;
979 if (dwFlags & MCI_NOTIFY) {
980 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n",
981 lpParms->dwCallback);
982 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
983 wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
988 /**************************************************************************
989 * DriverProc (MCICDA.@)
991 LRESULT CALLBACK MCICDA_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
992 LPARAM dwParam1, LPARAM dwParam2)
995 case DRV_LOAD: return 1;
996 case DRV_FREE: return 1;
997 case DRV_OPEN: return MCICDA_drvOpen((LPCWSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSW)dwParam2);
998 case DRV_CLOSE: return MCICDA_drvClose(dwDevID);
999 case DRV_ENABLE: return 1;
1000 case DRV_DISABLE: return 1;
1001 case DRV_QUERYCONFIGURE: return 1;
1002 case DRV_CONFIGURE: MessageBoxA(0, "MCI audio CD driver !", "Wine Driver", MB_OK); return 1;
1003 case DRV_INSTALL: return DRVCNF_RESTART;
1004 case DRV_REMOVE: return DRVCNF_RESTART;
1007 if (dwDevID == 0xFFFFFFFF) return MCIERR_UNSUPPORTED_FUNCTION;
1010 case MCI_OPEN_DRIVER: return MCICDA_Open(dwDevID, dwParam1, (LPMCI_OPEN_PARMSW)dwParam2);
1011 case MCI_CLOSE_DRIVER: return MCICDA_Close(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
1012 case MCI_GETDEVCAPS: return MCICDA_GetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS)dwParam2);
1013 case MCI_INFO: return MCICDA_Info(dwDevID, dwParam1, (LPMCI_INFO_PARMSW)dwParam2);
1014 case MCI_STATUS: return MCICDA_Status(dwDevID, dwParam1, (LPMCI_STATUS_PARMS)dwParam2);
1015 case MCI_SET: return MCICDA_Set(dwDevID, dwParam1, (LPMCI_SET_PARMS)dwParam2);
1016 case MCI_PLAY: return MCICDA_Play(dwDevID, dwParam1, (LPMCI_PLAY_PARMS)dwParam2);
1017 case MCI_STOP: return MCICDA_Stop(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
1018 case MCI_PAUSE: return MCICDA_Pause(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
1019 case MCI_RESUME: return MCICDA_Resume(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
1020 case MCI_SEEK: return MCICDA_Seek(dwDevID, dwParam1, (LPMCI_SEEK_PARMS)dwParam2);
1021 /* commands that should report an error as they are not supported in
1022 * the native version */
1023 case MCI_SET_DOOR_CLOSED:
1024 case MCI_SET_DOOR_OPEN:
1041 TRACE("Unsupported command [0x%x]\n", wMsg);
1045 ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1048 TRACE("Sending msg [0x%x] to default driver proc\n", wMsg);
1049 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1051 return MCIERR_UNRECOGNIZED_COMMAND;
1054 /*-----------------------------------------------------------------------*/