1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
3 * MCI driver for audio CD (MCICDA)
5 * Copyright 1994 Martin Ayotte
6 * Copyright 1998-99 Eric Pouech
7 * Copyright 2000 Andreas Mohr
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
39 #include "wine/debug.h"
40 #include "wine/unicode.h"
42 WINE_DEFAULT_DEBUG_CHANNEL(mcicda);
44 #define CDFRAMES_PERSEC 75
45 #define CDFRAMES_PERMIN (CDFRAMES_PERSEC * 60)
46 #define FRAME_OF_ADDR(a) ((a)[1] * CDFRAMES_PERMIN + (a)[2] * CDFRAMES_PERSEC + (a)[3])
47 #define FRAME_OF_TOC(toc, idx) FRAME_OF_ADDR((toc).TrackData[idx - (toc).FirstTrack].Address)
51 int nUseCount; /* Incremented for each shared open */
52 BOOL fShareable; /* TRUE if first open was shareable */
53 WORD wNotifyDeviceID; /* MCI device ID with a pending notification */
54 HANDLE hCallback; /* Callback handle for pending notification */
59 /*-----------------------------------------------------------------------*/
61 /**************************************************************************
62 * MCICDA_drvOpen [internal]
64 static DWORD MCICDA_drvOpen(LPCWSTR str, LPMCI_OPEN_DRIVER_PARMSW modp)
66 WINE_MCICDAUDIO* wmcda;
68 if (!modp) return 0xFFFFFFFF;
70 wmcda = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCICDAUDIO));
75 wmcda->wDevID = modp->wDeviceID;
76 mciSetDriverData(wmcda->wDevID, (DWORD)wmcda);
77 modp->wCustomCommandTable = MCI_NO_COMMAND_TABLE;
78 modp->wType = MCI_DEVTYPE_CD_AUDIO;
79 return modp->wDeviceID;
82 /**************************************************************************
83 * MCICDA_drvClose [internal]
85 static DWORD MCICDA_drvClose(DWORD dwDevID)
87 WINE_MCICDAUDIO* wmcda = (WINE_MCICDAUDIO*)mciGetDriverData(dwDevID);
90 HeapFree(GetProcessHeap(), 0, wmcda);
91 mciSetDriverData(dwDevID, 0);
93 return (dwDevID == 0xFFFFFFFF) ? 1 : 0;
96 /**************************************************************************
97 * MCICDA_GetOpenDrv [internal]
99 static WINE_MCICDAUDIO* MCICDA_GetOpenDrv(UINT wDevID)
101 WINE_MCICDAUDIO* wmcda = (WINE_MCICDAUDIO*)mciGetDriverData(wDevID);
103 if (wmcda == NULL || wmcda->nUseCount == 0) {
104 WARN("Invalid wDevID=%u\n", wDevID);
110 /**************************************************************************
111 * MCICDA_GetStatus [internal]
113 static DWORD MCICDA_GetStatus(WINE_MCICDAUDIO* wmcda)
115 CDROM_SUB_Q_DATA_FORMAT fmt;
116 SUB_Q_CHANNEL_DATA data;
118 DWORD mode = MCI_MODE_NOT_READY;
120 fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
121 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
122 &data, sizeof(data), &br, NULL)) {
123 if (GetLastError() == STATUS_NO_MEDIA_IN_DEVICE) mode = MCI_MODE_OPEN;
125 switch (data.CurrentPosition.Header.AudioStatus)
127 case AUDIO_STATUS_IN_PROGRESS: mode = MCI_MODE_PLAY; break;
128 case AUDIO_STATUS_PAUSED: mode = MCI_MODE_PAUSE; break;
129 case AUDIO_STATUS_NO_STATUS:
130 case AUDIO_STATUS_PLAY_COMPLETE: mode = MCI_MODE_STOP; break;
131 case AUDIO_STATUS_PLAY_ERROR:
132 case AUDIO_STATUS_NOT_SUPPORTED:
140 /**************************************************************************
141 * MCICDA_GetError [internal]
143 static int MCICDA_GetError(WINE_MCICDAUDIO* wmcda)
145 switch (GetLastError())
147 case STATUS_NO_MEDIA_IN_DEVICE: return MCIERR_DEVICE_NOT_READY;
148 case STATUS_IO_DEVICE_ERROR: return MCIERR_HARDWARE;
150 FIXME("Unknown mode %lx\n", GetLastError());
152 return MCIERR_DRIVER_INTERNAL;
155 /**************************************************************************
156 * MCICDA_CalcFrame [internal]
158 static DWORD MCICDA_CalcFrame(WINE_MCICDAUDIO* wmcda, DWORD dwTime)
166 TRACE("(%p, %08lX, %lu);\n", wmcda, wmcda->dwTimeFormat, dwTime);
168 switch (wmcda->dwTimeFormat) {
169 case MCI_FORMAT_MILLISECONDS:
170 dwFrame = ((dwTime - 1) * CDFRAMES_PERSEC + 500) / 1000;
171 TRACE("MILLISECONDS %lu\n", dwFrame);
174 TRACE("MSF %02u:%02u:%02u\n",
175 MCI_MSF_MINUTE(dwTime), MCI_MSF_SECOND(dwTime), MCI_MSF_FRAME(dwTime));
176 dwFrame += CDFRAMES_PERMIN * MCI_MSF_MINUTE(dwTime);
177 dwFrame += CDFRAMES_PERSEC * MCI_MSF_SECOND(dwTime);
178 dwFrame += MCI_MSF_FRAME(dwTime);
180 case MCI_FORMAT_TMSF:
181 default: /* unknown format ! force TMSF ! ... */
182 wTrack = MCI_TMSF_TRACK(dwTime);
183 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
184 &toc, sizeof(toc), &br, NULL))
186 if (wTrack < toc.FirstTrack || wTrack > toc.LastTrack)
188 TRACE("MSF %02u-%02u:%02u:%02u\n",
189 MCI_TMSF_TRACK(dwTime), MCI_TMSF_MINUTE(dwTime),
190 MCI_TMSF_SECOND(dwTime), MCI_TMSF_FRAME(dwTime));
191 addr = toc.TrackData[wTrack - toc.FirstTrack].Address;
192 TRACE("TMSF trackpos[%u]=%d:%d:%d\n",
193 wTrack, addr[1], addr[2], addr[3]);
194 dwFrame = CDFRAMES_PERMIN * (addr[1] + MCI_TMSF_MINUTE(dwTime)) +
195 CDFRAMES_PERSEC * (addr[2] + MCI_TMSF_SECOND(dwTime)) +
196 addr[3] + MCI_TMSF_FRAME(dwTime);
202 /**************************************************************************
203 * MCICDA_CalcTime [internal]
205 static DWORD MCICDA_CalcTime(WINE_MCICDAUDIO* wmcda, DWORD tf, DWORD dwFrame, LPDWORD lpRet)
215 TRACE("(%p, %08lX, %lu);\n", wmcda, tf, dwFrame);
218 case MCI_FORMAT_MILLISECONDS:
219 dwTime = (dwFrame * 1000) / CDFRAMES_PERSEC + 1;
220 TRACE("MILLISECONDS %lu\n", dwTime);
224 wMinutes = dwFrame / CDFRAMES_PERMIN;
225 wSeconds = (dwFrame - CDFRAMES_PERMIN * wMinutes) / CDFRAMES_PERSEC;
226 wFrames = dwFrame - CDFRAMES_PERMIN * wMinutes - CDFRAMES_PERSEC * wSeconds;
227 dwTime = MCI_MAKE_MSF(wMinutes, wSeconds, wFrames);
228 TRACE("MSF %02u:%02u:%02u -> dwTime=%lu\n",
229 wMinutes, wSeconds, wFrames, dwTime);
230 *lpRet = MCI_COLONIZED3_RETURN;
232 case MCI_FORMAT_TMSF:
233 default: /* unknown format ! force TMSF ! ... */
234 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
235 &toc, sizeof(toc), &br, NULL))
237 if (dwFrame < FRAME_OF_TOC(toc, toc.FirstTrack) ||
238 dwFrame > FRAME_OF_TOC(toc, toc.LastTrack + 1)) {
239 ERR("Out of range value %lu [%u,%u]\n",
240 dwFrame, FRAME_OF_TOC(toc, toc.FirstTrack),
241 FRAME_OF_TOC(toc, toc.LastTrack + 1));
245 for (wTrack = toc.FirstTrack; wTrack <= toc.LastTrack; wTrack++) {
246 if (FRAME_OF_TOC(toc, wTrack) > dwFrame)
250 dwFrame -= FRAME_OF_TOC(toc, wTrack);
251 wMinutes = dwFrame / CDFRAMES_PERMIN;
252 wSeconds = (dwFrame - CDFRAMES_PERMIN * wMinutes) / CDFRAMES_PERSEC;
253 wFrames = dwFrame - CDFRAMES_PERMIN * wMinutes - CDFRAMES_PERSEC * wSeconds;
254 dwTime = MCI_MAKE_TMSF(wTrack, wMinutes, wSeconds, wFrames);
255 TRACE("%02u-%02u:%02u:%02u\n", wTrack, wMinutes, wSeconds, wFrames);
256 *lpRet = MCI_COLONIZED4_RETURN;
262 static DWORD MCICDA_Seek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms);
263 static DWORD MCICDA_Stop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms);
265 /**************************************************************************
266 * MCICDA_Open [internal]
268 static DWORD MCICDA_Open(UINT wDevID, DWORD dwFlags, LPMCI_OPEN_PARMSW lpOpenParms)
271 DWORD ret = MCIERR_HARDWARE;
272 WINE_MCICDAUDIO* wmcda = (WINE_MCICDAUDIO*)mciGetDriverData(wDevID);
273 WCHAR root[7], drive = 0;
276 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpOpenParms);
278 if (lpOpenParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
279 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
281 dwDeviceID = lpOpenParms->wDeviceID;
283 if (wmcda->nUseCount > 0) {
284 /* The driver is already open on this channel */
285 /* If the driver was opened shareable before and this open specifies */
286 /* shareable then increment the use count */
287 if (wmcda->fShareable && (dwFlags & MCI_OPEN_SHAREABLE))
290 return MCIERR_MUST_USE_SHAREABLE;
292 wmcda->nUseCount = 1;
293 wmcda->fShareable = dwFlags & MCI_OPEN_SHAREABLE;
295 if (dwFlags & MCI_OPEN_ELEMENT) {
296 if (dwFlags & MCI_OPEN_ELEMENT_ID) {
297 WARN("MCI_OPEN_ELEMENT_ID %8lx ! Abort\n", (DWORD)lpOpenParms->lpstrElementName);
298 return MCIERR_NO_ELEMENT_ALLOWED;
300 TRACE("MCI_OPEN_ELEMENT element name: %s\n", debugstr_w(lpOpenParms->lpstrElementName));
301 if (!isalpha(lpOpenParms->lpstrElementName[0]) || lpOpenParms->lpstrElementName[1] != ':' ||
302 (lpOpenParms->lpstrElementName[2] && lpOpenParms->lpstrElementName[2] != '\\'))
304 WARN("MCI_OPEN_ELEMENT unsupported format: %s\n",
305 debugstr_w(lpOpenParms->lpstrElementName));
306 ret = MCIERR_NO_ELEMENT_ALLOWED;
309 drive = toupper(lpOpenParms->lpstrElementName[0]);
310 root[0] = drive; root[1] = ':'; root[2] = '\\'; root[3] = '\0';
311 if (GetDriveTypeW(root) != DRIVE_CDROM)
313 ret = MCIERR_INVALID_DEVICE_NAME;
319 /* drive letter isn't passed... get the dwDeviceID'th cdrom in the system */
320 root[0] = 'A'; root[1] = ':'; root[2] = '\\'; root[3] = '\0';
321 for (count = 0; root[0] <= 'Z'; root[0]++)
323 if (GetDriveTypeW(root) == DRIVE_CDROM && ++count >= dwDeviceID)
331 ret = MCIERR_INVALID_DEVICE_ID;
336 wmcda->wNotifyDeviceID = dwDeviceID;
337 wmcda->dwTimeFormat = MCI_FORMAT_MSF;
339 /* now, open the handle */
340 root[0] = root[1] = '\\'; root[2] = '.'; root[3] = '\\'; root[4] = drive; root[5] = ':'; root[6] = '\0';
341 wmcda->handle = CreateFileW(root, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
342 if (wmcda->handle != INVALID_HANDLE_VALUE)
350 /**************************************************************************
351 * MCICDA_Close [internal]
353 static DWORD MCICDA_Close(UINT wDevID, DWORD dwParam, LPMCI_GENERIC_PARMS lpParms)
355 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
357 TRACE("(%04X, %08lX, %p);\n", wDevID, dwParam, lpParms);
359 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
361 if (--wmcda->nUseCount == 0) {
362 CloseHandle(wmcda->handle);
367 /**************************************************************************
368 * MCICDA_GetDevCaps [internal]
370 static DWORD MCICDA_GetDevCaps(UINT wDevID, DWORD dwFlags,
371 LPMCI_GETDEVCAPS_PARMS lpParms)
375 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
377 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
379 if (dwFlags & MCI_GETDEVCAPS_ITEM) {
380 TRACE("MCI_GETDEVCAPS_ITEM dwItem=%08lX;\n", lpParms->dwItem);
382 switch (lpParms->dwItem) {
383 case MCI_GETDEVCAPS_CAN_RECORD:
384 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
385 ret = MCI_RESOURCE_RETURNED;
387 case MCI_GETDEVCAPS_HAS_AUDIO:
388 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
389 ret = MCI_RESOURCE_RETURNED;
391 case MCI_GETDEVCAPS_HAS_VIDEO:
392 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
393 ret = MCI_RESOURCE_RETURNED;
395 case MCI_GETDEVCAPS_DEVICE_TYPE:
396 lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_CD_AUDIO, MCI_DEVTYPE_CD_AUDIO);
397 ret = MCI_RESOURCE_RETURNED;
399 case MCI_GETDEVCAPS_USES_FILES:
400 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
401 ret = MCI_RESOURCE_RETURNED;
403 case MCI_GETDEVCAPS_COMPOUND_DEVICE:
404 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
405 ret = MCI_RESOURCE_RETURNED;
407 case MCI_GETDEVCAPS_CAN_EJECT:
408 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
409 ret = MCI_RESOURCE_RETURNED;
411 case MCI_GETDEVCAPS_CAN_PLAY:
412 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
413 ret = MCI_RESOURCE_RETURNED;
415 case MCI_GETDEVCAPS_CAN_SAVE:
416 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
417 ret = MCI_RESOURCE_RETURNED;
420 ERR("Unsupported %lx devCaps item\n", lpParms->dwItem);
421 return MCIERR_UNRECOGNIZED_COMMAND;
424 TRACE("No GetDevCaps-Item !\n");
425 return MCIERR_UNRECOGNIZED_COMMAND;
427 TRACE("lpParms->dwReturn=%08lX;\n", lpParms->dwReturn);
431 static DWORD CDROM_Audio_GetSerial(CDROM_TOC* toc)
433 unsigned long serial = 0;
436 DWORD dwStart, dwEnd;
439 * wMagic collects the wFrames from track 1
440 * dwStart, dwEnd collect the beginning and end of the disc respectively, in
442 * There it is collected for correcting the serial when there are less than
445 wMagic = toc->TrackData[0].Address[3];
446 dwStart = FRAME_OF_TOC(*toc, toc->FirstTrack);
448 for (i = 0; i <= toc->LastTrack - toc->FirstTrack; i++) {
449 serial += (toc->TrackData[i].Address[1] << 16) |
450 (toc->TrackData[i].Address[2] << 8) | toc->TrackData[i].Address[3];
452 dwEnd = FRAME_OF_TOC(*toc, toc->LastTrack + 1);
454 if (toc->LastTrack - toc->FirstTrack + 1 < 3)
455 serial += wMagic + (dwEnd - dwStart);
461 /**************************************************************************
462 * MCICDA_Info [internal]
464 static DWORD MCICDA_Info(UINT wDevID, DWORD dwFlags, LPMCI_INFO_PARMSW lpParms)
467 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
471 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
473 if (lpParms == NULL || lpParms->lpstrReturn == NULL)
474 return MCIERR_NULL_PARAMETER_BLOCK;
475 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
477 TRACE("buf=%p, len=%lu\n", lpParms->lpstrReturn, lpParms->dwRetSize);
479 if (dwFlags & MCI_INFO_PRODUCT) {
480 static const WCHAR wszAudioCd[] = {'W','i','n','e','\'','s',' ','a','u','d','i','o',' ','C','D',0};
482 } else if (dwFlags & MCI_INFO_MEDIA_UPC) {
483 ret = MCIERR_NO_IDENTITY;
484 } else if (dwFlags & MCI_INFO_MEDIA_IDENTITY) {
488 static const WCHAR wszLu[] = {'%','l','u',0};
490 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
491 &toc, sizeof(toc), &br, NULL)) {
492 return MCICDA_GetError(wmcda);
495 res = CDROM_Audio_GetSerial(&toc);
496 sprintfW(buffer, wszLu, res);
499 WARN("Don't know this info command (%lu)\n", dwFlags);
500 ret = MCIERR_UNRECOGNIZED_COMMAND;
503 if (lpParms->dwRetSize <= strlenW(str)) {
504 lstrcpynW(lpParms->lpstrReturn, str, lpParms->dwRetSize - 1);
505 ret = MCIERR_PARAM_OVERFLOW;
507 strcpyW(lpParms->lpstrReturn, str);
510 *lpParms->lpstrReturn = 0;
512 TRACE("=> %s (%ld)\n", debugstr_w(lpParms->lpstrReturn), ret);
516 /**************************************************************************
517 * MCICDA_Status [internal]
519 static DWORD MCICDA_Status(UINT wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
521 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
524 CDROM_SUB_Q_DATA_FORMAT fmt;
525 SUB_Q_CHANNEL_DATA data;
529 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
531 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
532 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
534 if (dwFlags & MCI_NOTIFY) {
535 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
536 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
537 wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
539 if (dwFlags & MCI_STATUS_ITEM) {
540 TRACE("dwItem = %lx\n", lpParms->dwItem);
541 switch (lpParms->dwItem) {
542 case MCI_STATUS_CURRENT_TRACK:
543 fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
544 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
545 &data, sizeof(data), &br, NULL))
547 return MCICDA_GetError(wmcda);
549 lpParms->dwReturn = data.CurrentPosition.TrackNumber;
550 TRACE("CURRENT_TRACK=%lu!\n", lpParms->dwReturn);
552 case MCI_STATUS_LENGTH:
553 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
554 &toc, sizeof(toc), &br, NULL)) {
555 WARN("error reading TOC !\n");
556 return MCICDA_GetError(wmcda);
558 if (dwFlags & MCI_TRACK) {
559 TRACE("MCI_TRACK #%lu LENGTH=??? !\n", lpParms->dwTrack);
560 if (lpParms->dwTrack < toc.FirstTrack || lpParms->dwTrack > toc.LastTrack)
561 return MCIERR_OUTOFRANGE;
562 idx = lpParms->dwTrack - toc.FirstTrack;
563 lpParms->dwReturn = FRAME_OF_TOC(toc, lpParms->dwTrack + 1) -
564 FRAME_OF_TOC(toc, lpParms->dwTrack);
565 /* Windows returns one frame less than the total track length for the
566 last track on the CD. See CDDB HOWTO. Verified on Win95OSR2. */
567 if (lpParms->dwTrack == toc.LastTrack)
570 /* Sum of the lengths of all of the tracks. Inherits the
571 'off by one frame' behavior from the length of the last track.
572 See above comment. */
573 lpParms->dwReturn = FRAME_OF_TOC(toc, toc.LastTrack + 1) -
574 FRAME_OF_TOC(toc, toc.FirstTrack) - 1;
576 lpParms->dwReturn = MCICDA_CalcTime(wmcda,
577 (wmcda->dwTimeFormat == MCI_FORMAT_TMSF)
578 ? MCI_FORMAT_MSF : wmcda->dwTimeFormat,
581 TRACE("LENGTH=%lu !\n", lpParms->dwReturn);
583 case MCI_STATUS_MODE:
584 lpParms->dwReturn = MCICDA_GetStatus(wmcda);
585 TRACE("MCI_STATUS_MODE=%08lX !\n", lpParms->dwReturn);
586 lpParms->dwReturn = MAKEMCIRESOURCE(lpParms->dwReturn, lpParms->dwReturn);
587 ret = MCI_RESOURCE_RETURNED;
589 case MCI_STATUS_MEDIA_PRESENT:
590 lpParms->dwReturn = (MCICDA_GetStatus(wmcda) == MCI_MODE_OPEN) ?
591 MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
592 TRACE("MCI_STATUS_MEDIA_PRESENT =%c!\n", LOWORD(lpParms->dwReturn) ? 'Y' : 'N');
593 ret = MCI_RESOURCE_RETURNED;
595 case MCI_STATUS_NUMBER_OF_TRACKS:
596 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
597 &toc, sizeof(toc), &br, NULL)) {
598 WARN("error reading TOC !\n");
599 return MCICDA_GetError(wmcda);
601 lpParms->dwReturn = toc.LastTrack - toc.FirstTrack + 1;
602 TRACE("MCI_STATUS_NUMBER_OF_TRACKS = %lu !\n", lpParms->dwReturn);
603 if (lpParms->dwReturn == (WORD)-1)
604 return MCICDA_GetError(wmcda);
606 case MCI_STATUS_POSITION:
607 if (dwFlags & MCI_STATUS_START) {
608 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
609 &toc, sizeof(toc), &br, NULL)) {
610 WARN("error reading TOC !\n");
611 return MCICDA_GetError(wmcda);
613 lpParms->dwReturn = FRAME_OF_TOC(toc, toc.FirstTrack);
614 TRACE("get MCI_STATUS_START !\n");
615 } else if (dwFlags & MCI_TRACK) {
616 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
617 &toc, sizeof(toc), &br, NULL)) {
618 WARN("error reading TOC !\n");
619 return MCICDA_GetError(wmcda);
621 if (lpParms->dwTrack < toc.FirstTrack || lpParms->dwTrack > toc.LastTrack)
622 return MCIERR_OUTOFRANGE;
623 lpParms->dwReturn = FRAME_OF_TOC(toc, lpParms->dwTrack);
624 TRACE("get MCI_TRACK #%lu !\n", lpParms->dwTrack);
626 fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
627 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
628 &data, sizeof(data), &br, NULL)) {
629 return MCICDA_GetError(wmcda);
631 lpParms->dwReturn = FRAME_OF_ADDR(data.CurrentPosition.AbsoluteAddress);
633 lpParms->dwReturn = MCICDA_CalcTime(wmcda, wmcda->dwTimeFormat, lpParms->dwReturn, &ret);
634 TRACE("MCI_STATUS_POSITION=%08lX !\n", lpParms->dwReturn);
636 case MCI_STATUS_READY:
637 TRACE("MCI_STATUS_READY !\n");
638 switch (MCICDA_GetStatus(wmcda))
640 case MCI_MODE_NOT_READY:
642 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
645 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
648 TRACE("MCI_STATUS_READY=%u!\n", LOWORD(lpParms->dwReturn));
649 ret = MCI_RESOURCE_RETURNED;
651 case MCI_STATUS_TIME_FORMAT:
652 lpParms->dwReturn = MAKEMCIRESOURCE(wmcda->dwTimeFormat, MCI_FORMAT_RETURN_BASE + wmcda->dwTimeFormat);
653 TRACE("MCI_STATUS_TIME_FORMAT=%08x!\n", LOWORD(lpParms->dwReturn));
654 ret = MCI_RESOURCE_RETURNED;
656 case 4001: /* FIXME: for bogus FullCD */
657 case MCI_CDA_STATUS_TYPE_TRACK:
658 if (!(dwFlags & MCI_TRACK))
659 ret = MCIERR_MISSING_PARAMETER;
661 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
662 &toc, sizeof(toc), &br, NULL)) {
663 WARN("error reading TOC !\n");
664 return MCICDA_GetError(wmcda);
666 if (lpParms->dwTrack < toc.FirstTrack || lpParms->dwTrack > toc.LastTrack)
667 ret = MCIERR_OUTOFRANGE;
669 lpParms->dwReturn = (toc.TrackData[lpParms->dwTrack - toc.FirstTrack].Control & 0x04) ?
670 MCI_CDA_TRACK_OTHER : MCI_CDA_TRACK_AUDIO;
672 TRACE("MCI_CDA_STATUS_TYPE_TRACK[%ld]=%ld\n", lpParms->dwTrack, lpParms->dwReturn);
675 FIXME("unknown command %08lX !\n", lpParms->dwItem);
676 return MCIERR_UNRECOGNIZED_COMMAND;
679 WARN("not MCI_STATUS_ITEM !\n");
684 /**************************************************************************
685 * MCICDA_SkipDataTracks [internal]
687 static DWORD MCICDA_SkipDataTracks(WINE_MCICDAUDIO* wmcda,DWORD *frame)
692 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
693 &toc, sizeof(toc), &br, NULL)) {
694 WARN("error reading TOC !\n");
695 return MCICDA_GetError(wmcda);
697 /* Locate first track whose starting frame is bigger than frame */
698 for(i=toc.FirstTrack;i<=toc.LastTrack+1;i++)
699 if ( FRAME_OF_TOC(toc, i) > *frame ) break;
700 if (i <= toc.FirstTrack && i>toc.LastTrack+1) {
701 i = 0; /* requested address is out of range: go back to start */
702 *frame = FRAME_OF_TOC(toc,toc.FirstTrack);
706 /* i points to last track whose start address is not greater than frame.
707 * Now skip non-audio tracks */
708 for(;i<=toc.LastTrack+1;i++)
709 if ( ! (toc.TrackData[i-toc.FirstTrack].Control & 4) )
711 /* The frame will be an address in the next audio track or
712 * address of lead-out. */
713 if ( FRAME_OF_TOC(toc, i) > *frame )
714 *frame = FRAME_OF_TOC(toc, i);
718 /**************************************************************************
719 * MCICDA_Play [internal]
721 static DWORD MCICDA_Play(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
723 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
724 DWORD ret = 0, start, end;
726 CDROM_PLAY_AUDIO_MSF play;
727 CDROM_SUB_Q_DATA_FORMAT fmt;
728 SUB_Q_CHANNEL_DATA data;
731 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
734 return MCIERR_NULL_PARAMETER_BLOCK;
737 return MCIERR_INVALID_DEVICE_ID;
739 if (dwFlags & MCI_FROM) {
740 start = MCICDA_CalcFrame(wmcda, lpParms->dwFrom);
741 if ( (ret=MCICDA_SkipDataTracks(wmcda, &start)) )
743 TRACE("MCI_FROM=%08lX -> %lu \n", lpParms->dwFrom, start);
745 fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
746 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
747 &data, sizeof(data), &br, NULL)) {
748 return MCICDA_GetError(wmcda);
750 start = FRAME_OF_ADDR(data.CurrentPosition.AbsoluteAddress);
751 if ( (ret=MCICDA_SkipDataTracks(wmcda, &start)) )
754 if (dwFlags & MCI_TO) {
755 end = MCICDA_CalcFrame(wmcda, lpParms->dwTo);
756 TRACE("MCI_TO=%08lX -> %lu \n", lpParms->dwTo, end);
758 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
759 &toc, sizeof(toc), &br, NULL)) {
760 WARN("error reading TOC !\n");
761 return MCICDA_GetError(wmcda);
763 end = FRAME_OF_TOC(toc, toc.LastTrack + 1) - 1;
765 TRACE("Playing from %lu to %lu\n", start, end);
766 play.StartingM = start / CDFRAMES_PERMIN;
767 play.StartingS = (start / CDFRAMES_PERSEC) % 60;
768 play.StartingF = start % CDFRAMES_PERSEC;
769 play.EndingM = end / CDFRAMES_PERMIN;
770 play.EndingS = (end / CDFRAMES_PERSEC) % 60;
771 play.EndingF = end % CDFRAMES_PERSEC;
772 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_PLAY_AUDIO_MSF, &play, sizeof(play),
773 NULL, 0, &br, NULL)) {
774 ret = MCIERR_HARDWARE;
775 } else if (dwFlags & MCI_NOTIFY) {
776 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
778 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
779 wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
785 /**************************************************************************
786 * MCICDA_Stop [internal]
788 static DWORD MCICDA_Stop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
790 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
793 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
795 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
797 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_STOP_AUDIO, NULL, 0, NULL, 0, &br, NULL))
798 return MCIERR_HARDWARE;
800 if (lpParms && (dwFlags & MCI_NOTIFY)) {
801 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
802 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
803 wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
808 /**************************************************************************
809 * MCICDA_Pause [internal]
811 static DWORD MCICDA_Pause(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
813 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
816 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
818 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
820 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_PAUSE_AUDIO, NULL, 0, NULL, 0, &br, NULL))
821 return MCIERR_HARDWARE;
823 if (lpParms && (dwFlags & MCI_NOTIFY)) {
824 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
825 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
826 wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
831 /**************************************************************************
832 * MCICDA_Resume [internal]
834 static DWORD MCICDA_Resume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
836 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
839 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
841 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
843 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_RESUME_AUDIO, NULL, 0, NULL, 0, &br, NULL))
844 return MCIERR_HARDWARE;
846 if (lpParms && (dwFlags & MCI_NOTIFY)) {
847 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
848 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
849 wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
854 /**************************************************************************
855 * MCICDA_Seek [internal]
857 static DWORD MCICDA_Seek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
860 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
861 CDROM_SEEK_AUDIO_MSF seek;
865 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
867 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
868 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
870 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
871 &toc, sizeof(toc), &br, NULL)) {
872 WARN("error reading TOC !\n");
873 return MCICDA_GetError(wmcda);
875 switch (dwFlags & ~(MCI_NOTIFY|MCI_WAIT)) {
876 case MCI_SEEK_TO_START:
877 TRACE("Seeking to start\n");
878 at = FRAME_OF_TOC(toc,toc.FirstTrack);
879 if ( (ret=MCICDA_SkipDataTracks(wmcda, &at)) )
882 case MCI_SEEK_TO_END:
883 TRACE("Seeking to end\n");
884 at = FRAME_OF_TOC(toc, toc.LastTrack + 1) - 1;
885 if ( (ret=MCICDA_SkipDataTracks(wmcda, &at)) )
889 TRACE("Seeking to %lu\n", lpParms->dwTo);
890 at = MCICDA_CalcFrame(wmcda, lpParms->dwTo);
891 if ( (ret=MCICDA_SkipDataTracks(wmcda, &at)) )
895 TRACE("Unknown seek action %08lX\n",
896 (dwFlags & ~(MCI_NOTIFY|MCI_WAIT)));
897 return MCIERR_UNSUPPORTED_FUNCTION;
899 seek.M = at / CDFRAMES_PERMIN;
900 seek.S = (at / CDFRAMES_PERSEC) % 60;
901 seek.F = at % CDFRAMES_PERSEC;
902 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_SEEK_AUDIO_MSF, &seek, sizeof(seek),
904 return MCIERR_HARDWARE;
906 if (dwFlags & MCI_NOTIFY) {
907 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
908 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
909 wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
914 /**************************************************************************
915 * MCICDA_SetDoor [internal]
917 static DWORD MCICDA_SetDoor(UINT wDevID, BOOL open)
919 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
922 TRACE("(%04x, %s) !\n", wDevID, (open) ? "OPEN" : "CLOSE");
924 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
926 if (!DeviceIoControl(wmcda->handle,
927 (open) ? IOCTL_STORAGE_EJECT_MEDIA : IOCTL_STORAGE_LOAD_MEDIA,
928 NULL, 0, NULL, 0, &br, NULL))
929 return MCIERR_HARDWARE;
934 /**************************************************************************
935 * MCICDA_Set [internal]
937 static DWORD MCICDA_Set(UINT wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
939 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
941 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
943 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
945 if (dwFlags & MCI_SET_DOOR_OPEN) {
946 MCICDA_SetDoor(wDevID, TRUE);
948 if (dwFlags & MCI_SET_DOOR_CLOSED) {
949 MCICDA_SetDoor(wDevID, FALSE);
952 /* only functions which require valid lpParms below this line ! */
953 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
955 TRACE("dwTimeFormat=%08lX\n", lpParms->dwTimeFormat);
956 TRACE("dwAudio=%08lX\n", lpParms->dwAudio);
958 if (dwFlags & MCI_SET_TIME_FORMAT) {
959 switch (lpParms->dwTimeFormat) {
960 case MCI_FORMAT_MILLISECONDS:
961 TRACE("MCI_FORMAT_MILLISECONDS !\n");
964 TRACE("MCI_FORMAT_MSF !\n");
966 case MCI_FORMAT_TMSF:
967 TRACE("MCI_FORMAT_TMSF !\n");
970 WARN("bad time format !\n");
971 return MCIERR_BAD_TIME_FORMAT;
973 wmcda->dwTimeFormat = lpParms->dwTimeFormat;
975 if (dwFlags & MCI_SET_VIDEO) return MCIERR_UNSUPPORTED_FUNCTION;
976 if (dwFlags & MCI_SET_ON) return MCIERR_UNSUPPORTED_FUNCTION;
977 if (dwFlags & MCI_SET_OFF) return MCIERR_UNSUPPORTED_FUNCTION;
978 if (dwFlags & MCI_NOTIFY) {
979 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n",
980 lpParms->dwCallback);
981 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
982 wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
987 /**************************************************************************
988 * DriverProc (MCICDA.@)
990 LONG CALLBACK MCICDA_DriverProc(DWORD dwDevID, HDRVR hDriv, DWORD wMsg,
991 DWORD dwParam1, DWORD dwParam2)
994 case DRV_LOAD: return 1;
995 case DRV_FREE: return 1;
996 case DRV_OPEN: return MCICDA_drvOpen((LPCWSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSW)dwParam2);
997 case DRV_CLOSE: return MCICDA_drvClose(dwDevID);
998 case DRV_ENABLE: return 1;
999 case DRV_DISABLE: return 1;
1000 case DRV_QUERYCONFIGURE: return 1;
1001 case DRV_CONFIGURE: MessageBoxA(0, "MCI audio CD driver !", "Wine Driver", MB_OK); return 1;
1002 case DRV_INSTALL: return DRVCNF_RESTART;
1003 case DRV_REMOVE: return DRVCNF_RESTART;
1006 if (dwDevID == 0xFFFFFFFF) return MCIERR_UNSUPPORTED_FUNCTION;
1009 case MCI_OPEN_DRIVER: return MCICDA_Open(dwDevID, dwParam1, (LPMCI_OPEN_PARMSW)dwParam2);
1010 case MCI_CLOSE_DRIVER: return MCICDA_Close(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
1011 case MCI_GETDEVCAPS: return MCICDA_GetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS)dwParam2);
1012 case MCI_INFO: return MCICDA_Info(dwDevID, dwParam1, (LPMCI_INFO_PARMSW)dwParam2);
1013 case MCI_STATUS: return MCICDA_Status(dwDevID, dwParam1, (LPMCI_STATUS_PARMS)dwParam2);
1014 case MCI_SET: return MCICDA_Set(dwDevID, dwParam1, (LPMCI_SET_PARMS)dwParam2);
1015 case MCI_PLAY: return MCICDA_Play(dwDevID, dwParam1, (LPMCI_PLAY_PARMS)dwParam2);
1016 case MCI_STOP: return MCICDA_Stop(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
1017 case MCI_PAUSE: return MCICDA_Pause(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
1018 case MCI_RESUME: return MCICDA_Resume(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
1019 case MCI_SEEK: return MCICDA_Seek(dwDevID, dwParam1, (LPMCI_SEEK_PARMS)dwParam2);
1020 /* commands that should report an error as they are not supported in
1021 * the native version */
1022 case MCI_SET_DOOR_CLOSED:
1023 case MCI_SET_DOOR_OPEN:
1040 TRACE("Unsupported command [0x%lx]\n", wMsg);
1044 ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1047 TRACE("Sending msg [0x%lx] to default driver proc\n", wMsg);
1048 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1050 return MCIERR_UNRECOGNIZED_COMMAND;
1053 /*-----------------------------------------------------------------------*/