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
37 #include "wine/debug.h"
39 WINE_DEFAULT_DEBUG_CHANNEL(mcicda
);
41 #define CDFRAMES_PERSEC 75
42 #define CDFRAMES_PERMIN (CDFRAMES_PERSEC * 60)
43 #define FRAME_OF_ADDR(a) ((a)[1] * CDFRAMES_PERMIN + (a)[2] * CDFRAMES_PERSEC + (a)[3])
44 #define FRAME_OF_TOC(toc, idx) FRAME_OF_ADDR((toc).TrackData[idx - (toc).FirstTrack].Address)
48 int nUseCount
; /* Incremented for each shared open */
49 BOOL fShareable
; /* TRUE if first open was shareable */
50 WORD wNotifyDeviceID
; /* MCI device ID with a pending notification */
51 HANDLE hCallback
; /* Callback handle for pending notification */
56 /*-----------------------------------------------------------------------*/
58 /**************************************************************************
59 * MCICDA_drvOpen [internal]
61 static DWORD
MCICDA_drvOpen(LPSTR str
, LPMCI_OPEN_DRIVER_PARMSA modp
)
63 WINE_MCICDAUDIO
* wmcda
;
65 if (!modp
) return 0xFFFFFFFF;
67 wmcda
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(WINE_MCICDAUDIO
));
72 wmcda
->wDevID
= modp
->wDeviceID
;
73 mciSetDriverData(wmcda
->wDevID
, (DWORD
)wmcda
);
74 modp
->wCustomCommandTable
= MCI_NO_COMMAND_TABLE
;
75 modp
->wType
= MCI_DEVTYPE_CD_AUDIO
;
76 return modp
->wDeviceID
;
79 /**************************************************************************
80 * MCICDA_drvClose [internal]
82 static DWORD
MCICDA_drvClose(DWORD dwDevID
)
84 WINE_MCICDAUDIO
* wmcda
= (WINE_MCICDAUDIO
*)mciGetDriverData(dwDevID
);
87 HeapFree(GetProcessHeap(), 0, wmcda
);
88 mciSetDriverData(dwDevID
, 0);
90 return (dwDevID
== 0xFFFFFFFF) ? 1 : 0;
93 /**************************************************************************
94 * MCICDA_GetOpenDrv [internal]
96 static WINE_MCICDAUDIO
* MCICDA_GetOpenDrv(UINT wDevID
)
98 WINE_MCICDAUDIO
* wmcda
= (WINE_MCICDAUDIO
*)mciGetDriverData(wDevID
);
100 if (wmcda
== NULL
|| wmcda
->nUseCount
== 0) {
101 WARN("Invalid wDevID=%u\n", wDevID
);
107 /**************************************************************************
108 * MCICDA_GetStatus [internal]
110 static DWORD
MCICDA_GetStatus(WINE_MCICDAUDIO
* wmcda
)
112 CDROM_SUB_Q_DATA_FORMAT fmt
;
113 SUB_Q_CHANNEL_DATA data
;
115 DWORD mode
= MCI_MODE_NOT_READY
;
117 fmt
.Format
= IOCTL_CDROM_CURRENT_POSITION
;
118 if (!DeviceIoControl(wmcda
->handle
, IOCTL_CDROM_READ_Q_CHANNEL
, &fmt
, sizeof(fmt
),
119 &data
, sizeof(data
), &br
, NULL
)) {
120 if (GetLastError() == STATUS_NO_MEDIA_IN_DEVICE
) mode
= MCI_MODE_OPEN
;
122 switch (data
.CurrentPosition
.Header
.AudioStatus
)
124 case AUDIO_STATUS_IN_PROGRESS
: mode
= MCI_MODE_PLAY
; break;
125 case AUDIO_STATUS_PAUSED
: mode
= MCI_MODE_PAUSE
; break;
126 case AUDIO_STATUS_NO_STATUS
:
127 case AUDIO_STATUS_PLAY_COMPLETE
: mode
= MCI_MODE_STOP
; break;
128 case AUDIO_STATUS_PLAY_ERROR
:
129 case AUDIO_STATUS_NOT_SUPPORTED
:
137 /**************************************************************************
138 * MCICDA_GetError [internal]
140 static int MCICDA_GetError(WINE_MCICDAUDIO
* wmcda
)
142 switch (GetLastError())
144 case STATUS_NO_MEDIA_IN_DEVICE
: return MCIERR_DEVICE_NOT_READY
;
145 case STATUS_IO_DEVICE_ERROR
: return MCIERR_HARDWARE
;
147 FIXME("Unknown mode %lx\n", GetLastError());
149 return MCIERR_DRIVER_INTERNAL
;
152 /**************************************************************************
153 * MCICDA_CalcFrame [internal]
155 static DWORD
MCICDA_CalcFrame(WINE_MCICDAUDIO
* wmcda
, DWORD dwTime
)
163 TRACE("(%p, %08lX, %lu);\n", wmcda
, wmcda
->dwTimeFormat
, dwTime
);
165 switch (wmcda
->dwTimeFormat
) {
166 case MCI_FORMAT_MILLISECONDS
:
167 dwFrame
= ((dwTime
- 1) * CDFRAMES_PERSEC
+ 500) / 1000;
168 TRACE("MILLISECONDS %lu\n", dwFrame
);
171 TRACE("MSF %02u:%02u:%02u\n",
172 MCI_MSF_MINUTE(dwTime
), MCI_MSF_SECOND(dwTime
), MCI_MSF_FRAME(dwTime
));
173 dwFrame
+= CDFRAMES_PERMIN
* MCI_MSF_MINUTE(dwTime
);
174 dwFrame
+= CDFRAMES_PERSEC
* MCI_MSF_SECOND(dwTime
);
175 dwFrame
+= MCI_MSF_FRAME(dwTime
);
177 case MCI_FORMAT_TMSF
:
178 default: /* unknown format ! force TMSF ! ... */
179 wTrack
= MCI_TMSF_TRACK(dwTime
);
180 if (!DeviceIoControl(wmcda
->handle
, IOCTL_CDROM_READ_TOC
, NULL
, 0,
181 &toc
, sizeof(toc
), &br
, NULL
))
183 if (wTrack
< toc
.FirstTrack
|| wTrack
> toc
.LastTrack
)
185 TRACE("MSF %02u-%02u:%02u:%02u\n",
186 MCI_TMSF_TRACK(dwTime
), MCI_TMSF_MINUTE(dwTime
),
187 MCI_TMSF_SECOND(dwTime
), MCI_TMSF_FRAME(dwTime
));
188 addr
= toc
.TrackData
[wTrack
- toc
.FirstTrack
].Address
;
189 TRACE("TMSF trackpos[%u]=%d:%d:%d\n",
190 wTrack
, addr
[1], addr
[2], addr
[3]);
191 dwFrame
= CDFRAMES_PERMIN
* (addr
[1] + MCI_TMSF_MINUTE(dwTime
)) +
192 CDFRAMES_PERSEC
* (addr
[2] + MCI_TMSF_SECOND(dwTime
)) +
193 addr
[3] + MCI_TMSF_FRAME(dwTime
);
199 /**************************************************************************
200 * MCICDA_CalcTime [internal]
202 static DWORD
MCICDA_CalcTime(WINE_MCICDAUDIO
* wmcda
, DWORD tf
, DWORD dwFrame
, LPDWORD lpRet
)
212 TRACE("(%p, %08lX, %lu);\n", wmcda
, tf
, dwFrame
);
215 case MCI_FORMAT_MILLISECONDS
:
216 dwTime
= (dwFrame
* 1000) / CDFRAMES_PERSEC
+ 1;
217 TRACE("MILLISECONDS %lu\n", dwTime
);
221 wMinutes
= dwFrame
/ CDFRAMES_PERMIN
;
222 wSeconds
= (dwFrame
- CDFRAMES_PERMIN
* wMinutes
) / CDFRAMES_PERSEC
;
223 wFrames
= dwFrame
- CDFRAMES_PERMIN
* wMinutes
- CDFRAMES_PERSEC
* wSeconds
;
224 dwTime
= MCI_MAKE_MSF(wMinutes
, wSeconds
, wFrames
);
225 TRACE("MSF %02u:%02u:%02u -> dwTime=%lu\n",
226 wMinutes
, wSeconds
, wFrames
, dwTime
);
227 *lpRet
= MCI_COLONIZED3_RETURN
;
229 case MCI_FORMAT_TMSF
:
230 default: /* unknown format ! force TMSF ! ... */
231 if (!DeviceIoControl(wmcda
->handle
, IOCTL_CDROM_READ_TOC
, NULL
, 0,
232 &toc
, sizeof(toc
), &br
, NULL
))
234 if (dwFrame
< FRAME_OF_TOC(toc
, toc
.FirstTrack
) ||
235 dwFrame
> FRAME_OF_TOC(toc
, toc
.LastTrack
+ 1)) {
236 ERR("Out of range value %lu [%u,%u]\n",
237 dwFrame
, FRAME_OF_TOC(toc
, toc
.FirstTrack
),
238 FRAME_OF_TOC(toc
, toc
.LastTrack
+ 1));
242 for (wTrack
= toc
.FirstTrack
; wTrack
<= toc
.LastTrack
; wTrack
++) {
243 if (FRAME_OF_TOC(toc
, wTrack
) > dwFrame
)
247 dwFrame
-= FRAME_OF_TOC(toc
, wTrack
);
248 wMinutes
= dwFrame
/ CDFRAMES_PERMIN
;
249 wSeconds
= (dwFrame
- CDFRAMES_PERMIN
* wMinutes
) / CDFRAMES_PERSEC
;
250 wFrames
= dwFrame
- CDFRAMES_PERMIN
* wMinutes
- CDFRAMES_PERSEC
* wSeconds
;
251 dwTime
= MCI_MAKE_TMSF(wTrack
, wMinutes
, wSeconds
, wFrames
);
252 TRACE("%02u-%02u:%02u:%02u\n", wTrack
, wMinutes
, wSeconds
, wFrames
);
253 *lpRet
= MCI_COLONIZED4_RETURN
;
259 static DWORD
MCICDA_Seek(UINT wDevID
, DWORD dwFlags
, LPMCI_SEEK_PARMS lpParms
);
260 static DWORD
MCICDA_Stop(UINT wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
);
262 /**************************************************************************
263 * MCICDA_Open [internal]
265 static DWORD
MCICDA_Open(UINT wDevID
, DWORD dwFlags
, LPMCI_OPEN_PARMSA lpOpenParms
)
268 DWORD ret
= MCIERR_HARDWARE
;
269 WINE_MCICDAUDIO
* wmcda
= (WINE_MCICDAUDIO
*)mciGetDriverData(wDevID
);
274 TRACE("(%04X, %08lX, %p);\n", wDevID
, dwFlags
, lpOpenParms
);
276 if (lpOpenParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
277 if (wmcda
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
279 dwDeviceID
= lpOpenParms
->wDeviceID
;
281 if (wmcda
->nUseCount
> 0) {
282 /* The driver is already open on this channel */
283 /* If the driver was opened shareable before and this open specifies */
284 /* shareable then increment the use count */
285 if (wmcda
->fShareable
&& (dwFlags
& MCI_OPEN_SHAREABLE
))
288 return MCIERR_MUST_USE_SHAREABLE
;
290 wmcda
->nUseCount
= 1;
291 wmcda
->fShareable
= dwFlags
& MCI_OPEN_SHAREABLE
;
293 if (dwFlags
& MCI_OPEN_ELEMENT
) {
294 if (dwFlags
& MCI_OPEN_ELEMENT_ID
) {
295 WARN("MCI_OPEN_ELEMENT_ID %8lx ! Abort\n", (DWORD
)lpOpenParms
->lpstrElementName
);
296 return MCIERR_NO_ELEMENT_ALLOWED
;
298 if (!isalpha(lpOpenParms
->lpstrElementName
[0]) || lpOpenParms
->lpstrElementName
[1] != ':' ||
299 lpOpenParms
->lpstrElementName
[2])
301 WARN("MCI_OPEN_ELEMENT unsupported format: %s\n", lpOpenParms
->lpstrElementName
);
302 ret
= MCIERR_NO_ELEMENT_ALLOWED
;
305 drive
= toupper(lpOpenParms
->lpstrElementName
[0]);
306 strcpy(root
, "A:\\");
308 if (GetDriveTypeA(root
) != DRIVE_CDROM
)
310 ret
= MCIERR_INVALID_DEVICE_NAME
;
316 /* drive letter isn't passed... get the dwDeviceID'th cdrom in the system */
317 strcpy(root
, "A:\\");
318 for (count
= 0; root
[0] <= 'Z'; root
[0]++)
320 if (GetDriveTypeA(root
) == DRIVE_CDROM
&& ++count
>= dwDeviceID
)
328 ret
= MCIERR_INVALID_DEVICE_ID
;
333 wmcda
->wNotifyDeviceID
= dwDeviceID
;
334 wmcda
->dwTimeFormat
= MCI_FORMAT_MSF
;
336 /* now, open the handle */
337 strcpy(root
, "\\\\.\\A:");
339 wmcda
->handle
= CreateFileA(root
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, 0, 0);
340 if (wmcda
->handle
!= INVALID_HANDLE_VALUE
)
348 /**************************************************************************
349 * MCICDA_Close [internal]
351 static DWORD
MCICDA_Close(UINT wDevID
, DWORD dwParam
, LPMCI_GENERIC_PARMS lpParms
)
353 WINE_MCICDAUDIO
* wmcda
= MCICDA_GetOpenDrv(wDevID
);
355 TRACE("(%04X, %08lX, %p);\n", wDevID
, dwParam
, lpParms
);
357 if (wmcda
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
359 if (--wmcda
->nUseCount
== 0) {
360 CloseHandle(wmcda
->handle
);
365 /**************************************************************************
366 * MCICDA_GetDevCaps [internal]
368 static DWORD
MCICDA_GetDevCaps(UINT wDevID
, DWORD dwFlags
,
369 LPMCI_GETDEVCAPS_PARMS lpParms
)
373 TRACE("(%04X, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
375 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
377 if (dwFlags
& MCI_GETDEVCAPS_ITEM
) {
378 TRACE("MCI_GETDEVCAPS_ITEM dwItem=%08lX;\n", lpParms
->dwItem
);
380 switch (lpParms
->dwItem
) {
381 case MCI_GETDEVCAPS_CAN_RECORD
:
382 lpParms
->dwReturn
= MAKEMCIRESOURCE(FALSE
, MCI_FALSE
);
383 ret
= MCI_RESOURCE_RETURNED
;
385 case MCI_GETDEVCAPS_HAS_AUDIO
:
386 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
387 ret
= MCI_RESOURCE_RETURNED
;
389 case MCI_GETDEVCAPS_HAS_VIDEO
:
390 lpParms
->dwReturn
= MAKEMCIRESOURCE(FALSE
, MCI_FALSE
);
391 ret
= MCI_RESOURCE_RETURNED
;
393 case MCI_GETDEVCAPS_DEVICE_TYPE
:
394 lpParms
->dwReturn
= MAKEMCIRESOURCE(MCI_DEVTYPE_CD_AUDIO
, MCI_DEVTYPE_CD_AUDIO
);
395 ret
= MCI_RESOURCE_RETURNED
;
397 case MCI_GETDEVCAPS_USES_FILES
:
398 lpParms
->dwReturn
= MAKEMCIRESOURCE(FALSE
, MCI_FALSE
);
399 ret
= MCI_RESOURCE_RETURNED
;
401 case MCI_GETDEVCAPS_COMPOUND_DEVICE
:
402 lpParms
->dwReturn
= MAKEMCIRESOURCE(FALSE
, MCI_FALSE
);
403 ret
= MCI_RESOURCE_RETURNED
;
405 case MCI_GETDEVCAPS_CAN_EJECT
:
406 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
407 ret
= MCI_RESOURCE_RETURNED
;
409 case MCI_GETDEVCAPS_CAN_PLAY
:
410 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
411 ret
= MCI_RESOURCE_RETURNED
;
413 case MCI_GETDEVCAPS_CAN_SAVE
:
414 lpParms
->dwReturn
= MAKEMCIRESOURCE(FALSE
, MCI_FALSE
);
415 ret
= MCI_RESOURCE_RETURNED
;
418 ERR("Unsupported %lx devCaps item\n", lpParms
->dwItem
);
419 return MCIERR_UNRECOGNIZED_COMMAND
;
422 TRACE("No GetDevCaps-Item !\n");
423 return MCIERR_UNRECOGNIZED_COMMAND
;
425 TRACE("lpParms->dwReturn=%08lX;\n", lpParms
->dwReturn
);
429 static DWORD
CDROM_Audio_GetSerial(CDROM_TOC
* toc
)
431 unsigned long serial
= 0;
434 DWORD dwStart
, dwEnd
;
437 * wMagic collects the wFrames from track 1
438 * dwStart, dwEnd collect the beginning and end of the disc respectively, in
440 * There it is collected for correcting the serial when there are less than
443 wMagic
= toc
->TrackData
[0].Address
[3];
444 dwStart
= FRAME_OF_TOC(*toc
, toc
->FirstTrack
);
446 for (i
= 0; i
<= toc
->LastTrack
- toc
->FirstTrack
; i
++) {
447 serial
+= (toc
->TrackData
[i
].Address
[1] << 16) |
448 (toc
->TrackData
[i
].Address
[2] << 8) | toc
->TrackData
[i
].Address
[3];
450 dwEnd
= FRAME_OF_TOC(*toc
, toc
->LastTrack
+ 1);
452 if (toc
->LastTrack
- toc
->FirstTrack
+ 1 < 3)
453 serial
+= wMagic
+ (dwEnd
- dwStart
);
459 /**************************************************************************
460 * MCICDA_Info [internal]
462 static DWORD
MCICDA_Info(UINT wDevID
, DWORD dwFlags
, LPMCI_INFO_PARMSA lpParms
)
465 WINE_MCICDAUDIO
* wmcda
= MCICDA_GetOpenDrv(wDevID
);
469 TRACE("(%04X, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
471 if (lpParms
== NULL
|| lpParms
->lpstrReturn
== NULL
)
472 return MCIERR_NULL_PARAMETER_BLOCK
;
473 if (wmcda
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
475 TRACE("buf=%p, len=%lu\n", lpParms
->lpstrReturn
, lpParms
->dwRetSize
);
477 if (dwFlags
& MCI_INFO_PRODUCT
) {
478 str
= "Wine's audio CD";
479 } else if (dwFlags
& MCI_INFO_MEDIA_UPC
) {
480 ret
= MCIERR_NO_IDENTITY
;
481 } else if (dwFlags
& MCI_INFO_MEDIA_IDENTITY
) {
486 if (!DeviceIoControl(wmcda
->handle
, IOCTL_CDROM_READ_TOC
, NULL
, 0,
487 &toc
, sizeof(toc
), &br
, NULL
)) {
488 return MCICDA_GetError(wmcda
);
491 res
= CDROM_Audio_GetSerial(&toc
);
492 sprintf(buffer
, "%lu", res
);
495 WARN("Don't know this info command (%lu)\n", dwFlags
);
496 ret
= MCIERR_UNRECOGNIZED_COMMAND
;
499 if (lpParms
->dwRetSize
<= strlen(str
)) {
500 lstrcpynA(lpParms
->lpstrReturn
, str
, lpParms
->dwRetSize
- 1);
501 ret
= MCIERR_PARAM_OVERFLOW
;
503 strcpy(lpParms
->lpstrReturn
, str
);
506 *lpParms
->lpstrReturn
= 0;
508 TRACE("=> %s (%ld)\n", lpParms
->lpstrReturn
, ret
);
512 /**************************************************************************
513 * MCICDA_Status [internal]
515 static DWORD
MCICDA_Status(UINT wDevID
, DWORD dwFlags
, LPMCI_STATUS_PARMS lpParms
)
517 WINE_MCICDAUDIO
* wmcda
= MCICDA_GetOpenDrv(wDevID
);
520 CDROM_SUB_Q_DATA_FORMAT fmt
;
521 SUB_Q_CHANNEL_DATA data
;
525 TRACE("(%04X, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
527 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
528 if (wmcda
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
530 if (dwFlags
& MCI_NOTIFY
) {
531 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms
->dwCallback
);
532 mciDriverNotify(HWND_32(LOWORD(lpParms
->dwCallback
)),
533 wmcda
->wNotifyDeviceID
, MCI_NOTIFY_SUCCESSFUL
);
535 if (dwFlags
& MCI_STATUS_ITEM
) {
536 TRACE("dwItem = %lx\n", lpParms
->dwItem
);
537 switch (lpParms
->dwItem
) {
538 case MCI_STATUS_CURRENT_TRACK
:
539 fmt
.Format
= IOCTL_CDROM_CURRENT_POSITION
;
540 if (!DeviceIoControl(wmcda
->handle
, IOCTL_CDROM_READ_Q_CHANNEL
, &fmt
, sizeof(fmt
),
541 &data
, sizeof(data
), &br
, NULL
))
543 return MCICDA_GetError(wmcda
);
545 lpParms
->dwReturn
= data
.CurrentPosition
.TrackNumber
;
546 TRACE("CURRENT_TRACK=%lu!\n", lpParms
->dwReturn
);
548 case MCI_STATUS_LENGTH
:
549 if (!DeviceIoControl(wmcda
->handle
, IOCTL_CDROM_READ_TOC
, NULL
, 0,
550 &toc
, sizeof(toc
), &br
, NULL
)) {
551 WARN("error reading TOC !\n");
552 return MCICDA_GetError(wmcda
);
554 if (dwFlags
& MCI_TRACK
) {
555 TRACE("MCI_TRACK #%lu LENGTH=??? !\n", lpParms
->dwTrack
);
556 if (lpParms
->dwTrack
< toc
.FirstTrack
|| lpParms
->dwTrack
> toc
.LastTrack
)
557 return MCIERR_OUTOFRANGE
;
558 idx
= lpParms
->dwTrack
- toc
.FirstTrack
;
559 lpParms
->dwReturn
= FRAME_OF_TOC(toc
, lpParms
->dwTrack
+ 1) -
560 FRAME_OF_TOC(toc
, lpParms
->dwTrack
);
561 /* Windows returns one frame less than the total track length for the
562 last track on the CD. See CDDB HOWTO. Verified on Win95OSR2. */
563 if (lpParms
->dwTrack
== toc
.LastTrack
)
566 /* Sum of the lengths of all of the tracks. Inherits the
567 'off by one frame' behavior from the length of the last track.
568 See above comment. */
569 lpParms
->dwReturn
= FRAME_OF_TOC(toc
, toc
.LastTrack
+ 1) -
570 FRAME_OF_TOC(toc
, toc
.FirstTrack
) - 1;
572 lpParms
->dwReturn
= MCICDA_CalcTime(wmcda
,
573 (wmcda
->dwTimeFormat
== MCI_FORMAT_TMSF
)
574 ? MCI_FORMAT_MSF
: wmcda
->dwTimeFormat
,
577 TRACE("LENGTH=%lu !\n", lpParms
->dwReturn
);
579 case MCI_STATUS_MODE
:
580 lpParms
->dwReturn
= MCICDA_GetStatus(wmcda
);
581 TRACE("MCI_STATUS_MODE=%08lX !\n", lpParms
->dwReturn
);
582 lpParms
->dwReturn
= MAKEMCIRESOURCE(lpParms
->dwReturn
, lpParms
->dwReturn
);
583 ret
= MCI_RESOURCE_RETURNED
;
585 case MCI_STATUS_MEDIA_PRESENT
:
586 lpParms
->dwReturn
= (MCICDA_GetStatus(wmcda
) == MCI_MODE_OPEN
) ?
587 MAKEMCIRESOURCE(FALSE
, MCI_FALSE
) : MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
588 TRACE("MCI_STATUS_MEDIA_PRESENT =%c!\n", LOWORD(lpParms
->dwReturn
) ? 'Y' : 'N');
589 ret
= MCI_RESOURCE_RETURNED
;
591 case MCI_STATUS_NUMBER_OF_TRACKS
:
592 if (!DeviceIoControl(wmcda
->handle
, IOCTL_CDROM_READ_TOC
, NULL
, 0,
593 &toc
, sizeof(toc
), &br
, NULL
)) {
594 WARN("error reading TOC !\n");
595 return MCICDA_GetError(wmcda
);
597 lpParms
->dwReturn
= toc
.LastTrack
- toc
.FirstTrack
+ 1;
598 TRACE("MCI_STATUS_NUMBER_OF_TRACKS = %lu !\n", lpParms
->dwReturn
);
599 if (lpParms
->dwReturn
== (WORD
)-1)
600 return MCICDA_GetError(wmcda
);
602 case MCI_STATUS_POSITION
:
603 if (dwFlags
& MCI_STATUS_START
) {
604 if (!DeviceIoControl(wmcda
->handle
, IOCTL_CDROM_READ_TOC
, NULL
, 0,
605 &toc
, sizeof(toc
), &br
, NULL
)) {
606 WARN("error reading TOC !\n");
607 return MCICDA_GetError(wmcda
);
609 lpParms
->dwReturn
= FRAME_OF_TOC(toc
, toc
.FirstTrack
);
610 TRACE("get MCI_STATUS_START !\n");
611 } else if (dwFlags
& MCI_TRACK
) {
612 if (!DeviceIoControl(wmcda
->handle
, IOCTL_CDROM_READ_TOC
, NULL
, 0,
613 &toc
, sizeof(toc
), &br
, NULL
)) {
614 WARN("error reading TOC !\n");
615 return MCICDA_GetError(wmcda
);
617 if (lpParms
->dwTrack
< toc
.FirstTrack
|| lpParms
->dwTrack
> toc
.LastTrack
)
618 return MCIERR_OUTOFRANGE
;
619 lpParms
->dwReturn
= FRAME_OF_TOC(toc
, lpParms
->dwTrack
);
620 TRACE("get MCI_TRACK #%lu !\n", lpParms
->dwTrack
);
622 fmt
.Format
= IOCTL_CDROM_CURRENT_POSITION
;
623 if (!DeviceIoControl(wmcda
->handle
, IOCTL_CDROM_READ_Q_CHANNEL
, &fmt
, sizeof(fmt
),
624 &data
, sizeof(data
), &br
, NULL
)) {
625 return MCICDA_GetError(wmcda
);
627 lpParms
->dwReturn
= FRAME_OF_ADDR(data
.CurrentPosition
.AbsoluteAddress
);
629 lpParms
->dwReturn
= MCICDA_CalcTime(wmcda
, wmcda
->dwTimeFormat
, lpParms
->dwReturn
, &ret
);
630 TRACE("MCI_STATUS_POSITION=%08lX !\n", lpParms
->dwReturn
);
632 case MCI_STATUS_READY
:
633 TRACE("MCI_STATUS_READY !\n");
634 switch (MCICDA_GetStatus(wmcda
))
636 case MCI_MODE_NOT_READY
:
638 lpParms
->dwReturn
= MAKEMCIRESOURCE(FALSE
, MCI_FALSE
);
641 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
644 TRACE("MCI_STATUS_READY=%u!\n", LOWORD(lpParms
->dwReturn
));
645 ret
= MCI_RESOURCE_RETURNED
;
647 case MCI_STATUS_TIME_FORMAT
:
648 lpParms
->dwReturn
= MAKEMCIRESOURCE(wmcda
->dwTimeFormat
, wmcda
->dwTimeFormat
);
649 TRACE("MCI_STATUS_TIME_FORMAT=%08x!\n", LOWORD(lpParms
->dwReturn
));
650 ret
= MCI_RESOURCE_RETURNED
;
652 case 4001: /* FIXME: for bogus FullCD */
653 case MCI_CDA_STATUS_TYPE_TRACK
:
654 if (!(dwFlags
& MCI_TRACK
))
655 ret
= MCIERR_MISSING_PARAMETER
;
657 if (!DeviceIoControl(wmcda
->handle
, IOCTL_CDROM_READ_TOC
, NULL
, 0,
658 &toc
, sizeof(toc
), &br
, NULL
)) {
659 WARN("error reading TOC !\n");
660 return MCICDA_GetError(wmcda
);
662 if (lpParms
->dwTrack
< toc
.FirstTrack
|| lpParms
->dwTrack
> toc
.LastTrack
)
663 ret
= MCIERR_OUTOFRANGE
;
665 lpParms
->dwReturn
= (toc
.TrackData
[lpParms
->dwTrack
- toc
.FirstTrack
].Control
& 0x04) ?
666 MCI_CDA_TRACK_OTHER
: MCI_CDA_TRACK_AUDIO
;
668 TRACE("MCI_CDA_STATUS_TYPE_TRACK[%ld]=%08lx\n", lpParms
->dwTrack
, lpParms
->dwReturn
);
671 FIXME("unknown command %08lX !\n", lpParms
->dwItem
);
672 return MCIERR_UNRECOGNIZED_COMMAND
;
675 WARN("not MCI_STATUS_ITEM !\n");
680 /**************************************************************************
681 * MCICDA_SkipDataTracks [internal]
683 static DWORD
MCICDA_SkipDataTracks(WINE_MCICDAUDIO
* wmcda
,DWORD
*frame
)
688 if (!DeviceIoControl(wmcda
->handle
, IOCTL_CDROM_READ_TOC
, NULL
, 0,
689 &toc
, sizeof(toc
), &br
, NULL
)) {
690 WARN("error reading TOC !\n");
691 return MCICDA_GetError(wmcda
);
693 /* Locate first track whose starting frame is bigger than frame */
694 for(i
=toc
.FirstTrack
;i
<=toc
.LastTrack
+1;i
++)
695 if ( FRAME_OF_TOC(toc
, i
) > *frame
) break;
696 if (i
<= toc
.FirstTrack
&& i
>toc
.LastTrack
+1) {
697 i
= 0; /* requested address is out of range: go back to start */
698 *frame
= FRAME_OF_TOC(toc
,toc
.FirstTrack
);
702 /* i points to last track whose start address is not greater than frame.
703 * Now skip non-audio tracks */
704 for(;i
<=toc
.LastTrack
+1;i
++)
705 if ( ! (toc
.TrackData
[i
-toc
.FirstTrack
].Control
& 4) )
707 /* The frame will be an address in the next audio track or
708 * address of lead-out. */
709 if ( FRAME_OF_TOC(toc
, i
) > *frame
)
710 *frame
= FRAME_OF_TOC(toc
, i
);
714 /**************************************************************************
715 * MCICDA_Play [internal]
717 static DWORD
MCICDA_Play(UINT wDevID
, DWORD dwFlags
, LPMCI_PLAY_PARMS lpParms
)
719 WINE_MCICDAUDIO
* wmcda
= MCICDA_GetOpenDrv(wDevID
);
720 DWORD ret
= 0, start
, end
;
722 CDROM_PLAY_AUDIO_MSF play
;
723 CDROM_SUB_Q_DATA_FORMAT fmt
;
724 SUB_Q_CHANNEL_DATA data
;
727 TRACE("(%04X, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
730 return MCIERR_NULL_PARAMETER_BLOCK
;
733 return MCIERR_INVALID_DEVICE_ID
;
735 if (dwFlags
& MCI_FROM
) {
736 start
= MCICDA_CalcFrame(wmcda
, lpParms
->dwFrom
);
737 if ( (ret
=MCICDA_SkipDataTracks(wmcda
, &start
)) )
739 TRACE("MCI_FROM=%08lX -> %lu \n", lpParms
->dwFrom
, start
);
741 fmt
.Format
= IOCTL_CDROM_CURRENT_POSITION
;
742 if (!DeviceIoControl(wmcda
->handle
, IOCTL_CDROM_READ_Q_CHANNEL
, &fmt
, sizeof(fmt
),
743 &data
, sizeof(data
), &br
, NULL
)) {
744 return MCICDA_GetError(wmcda
);
746 start
= FRAME_OF_ADDR(data
.CurrentPosition
.AbsoluteAddress
);
747 if ( (ret
=MCICDA_SkipDataTracks(wmcda
, &start
)) )
750 if (dwFlags
& MCI_TO
) {
751 end
= MCICDA_CalcFrame(wmcda
, lpParms
->dwTo
);
752 TRACE("MCI_TO=%08lX -> %lu \n", lpParms
->dwTo
, end
);
754 if (!DeviceIoControl(wmcda
->handle
, IOCTL_CDROM_READ_TOC
, NULL
, 0,
755 &toc
, sizeof(toc
), &br
, NULL
)) {
756 WARN("error reading TOC !\n");
757 return MCICDA_GetError(wmcda
);
759 end
= FRAME_OF_TOC(toc
, toc
.LastTrack
+ 1) - 1;
761 TRACE("Playing from %lu to %lu\n", start
, end
);
762 play
.StartingM
= start
/ CDFRAMES_PERMIN
;
763 play
.StartingS
= (start
/ CDFRAMES_PERSEC
) % 60;
764 play
.StartingF
= start
% CDFRAMES_PERSEC
;
765 play
.EndingM
= end
/ CDFRAMES_PERMIN
;
766 play
.EndingS
= (end
/ CDFRAMES_PERSEC
) % 60;
767 play
.EndingF
= end
% CDFRAMES_PERSEC
;
768 if (!DeviceIoControl(wmcda
->handle
, IOCTL_CDROM_PLAY_AUDIO_MSF
, &play
, sizeof(play
),
769 NULL
, 0, &br
, NULL
)) {
770 ret
= MCIERR_HARDWARE
;
771 } else if (dwFlags
& MCI_NOTIFY
) {
772 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms
->dwCallback
);
774 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
775 wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
781 /**************************************************************************
782 * MCICDA_Stop [internal]
784 static DWORD
MCICDA_Stop(UINT wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
786 WINE_MCICDAUDIO
* wmcda
= MCICDA_GetOpenDrv(wDevID
);
789 TRACE("(%04X, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
791 if (wmcda
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
793 if (!DeviceIoControl(wmcda
->handle
, IOCTL_CDROM_STOP_AUDIO
, NULL
, 0, NULL
, 0, &br
, NULL
))
794 return MCIERR_HARDWARE
;
796 if (lpParms
&& (dwFlags
& MCI_NOTIFY
)) {
797 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms
->dwCallback
);
798 mciDriverNotify(HWND_32(LOWORD(lpParms
->dwCallback
)),
799 wmcda
->wNotifyDeviceID
, MCI_NOTIFY_SUCCESSFUL
);
804 /**************************************************************************
805 * MCICDA_Pause [internal]
807 static DWORD
MCICDA_Pause(UINT wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
809 WINE_MCICDAUDIO
* wmcda
= MCICDA_GetOpenDrv(wDevID
);
812 TRACE("(%04X, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
814 if (wmcda
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
816 if (!DeviceIoControl(wmcda
->handle
, IOCTL_CDROM_PAUSE_AUDIO
, NULL
, 0, NULL
, 0, &br
, NULL
))
817 return MCIERR_HARDWARE
;
819 if (lpParms
&& (dwFlags
& MCI_NOTIFY
)) {
820 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms
->dwCallback
);
821 mciDriverNotify(HWND_32(LOWORD(lpParms
->dwCallback
)),
822 wmcda
->wNotifyDeviceID
, MCI_NOTIFY_SUCCESSFUL
);
827 /**************************************************************************
828 * MCICDA_Resume [internal]
830 static DWORD
MCICDA_Resume(UINT wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
832 WINE_MCICDAUDIO
* wmcda
= MCICDA_GetOpenDrv(wDevID
);
835 TRACE("(%04X, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
837 if (wmcda
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
839 if (!DeviceIoControl(wmcda
->handle
, IOCTL_CDROM_RESUME_AUDIO
, NULL
, 0, NULL
, 0, &br
, NULL
))
840 return MCIERR_HARDWARE
;
842 if (lpParms
&& (dwFlags
& MCI_NOTIFY
)) {
843 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms
->dwCallback
);
844 mciDriverNotify(HWND_32(LOWORD(lpParms
->dwCallback
)),
845 wmcda
->wNotifyDeviceID
, MCI_NOTIFY_SUCCESSFUL
);
850 /**************************************************************************
851 * MCICDA_Seek [internal]
853 static DWORD
MCICDA_Seek(UINT wDevID
, DWORD dwFlags
, LPMCI_SEEK_PARMS lpParms
)
856 WINE_MCICDAUDIO
* wmcda
= MCICDA_GetOpenDrv(wDevID
);
857 CDROM_SEEK_AUDIO_MSF seek
;
861 TRACE("(%04X, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
863 if (wmcda
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
864 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
866 if (!DeviceIoControl(wmcda
->handle
, IOCTL_CDROM_READ_TOC
, NULL
, 0,
867 &toc
, sizeof(toc
), &br
, NULL
)) {
868 WARN("error reading TOC !\n");
869 return MCICDA_GetError(wmcda
);
871 switch (dwFlags
& ~(MCI_NOTIFY
|MCI_WAIT
)) {
872 case MCI_SEEK_TO_START
:
873 TRACE("Seeking to start\n");
874 at
= FRAME_OF_TOC(toc
,toc
.FirstTrack
);
875 if ( (ret
=MCICDA_SkipDataTracks(wmcda
, &at
)) )
878 case MCI_SEEK_TO_END
:
879 TRACE("Seeking to end\n");
880 at
= FRAME_OF_TOC(toc
, toc
.LastTrack
+ 1) - 1;
881 if ( (ret
=MCICDA_SkipDataTracks(wmcda
, &at
)) )
885 TRACE("Seeking to %lu\n", lpParms
->dwTo
);
886 at
= MCICDA_CalcFrame(wmcda
, lpParms
->dwTo
);
887 if ( (ret
=MCICDA_SkipDataTracks(wmcda
, &at
)) )
891 TRACE("Unknown seek action %08lX\n",
892 (dwFlags
& ~(MCI_NOTIFY
|MCI_WAIT
)));
893 return MCIERR_UNSUPPORTED_FUNCTION
;
895 seek
.M
= at
/ CDFRAMES_PERMIN
;
896 seek
.S
= (at
/ CDFRAMES_PERSEC
) % 60;
897 seek
.F
= at
% CDFRAMES_PERSEC
;
898 if (!DeviceIoControl(wmcda
->handle
, IOCTL_CDROM_SEEK_AUDIO_MSF
, &seek
, sizeof(seek
),
900 return MCIERR_HARDWARE
;
902 if (dwFlags
& MCI_NOTIFY
) {
903 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms
->dwCallback
);
904 mciDriverNotify(HWND_32(LOWORD(lpParms
->dwCallback
)),
905 wmcda
->wNotifyDeviceID
, MCI_NOTIFY_SUCCESSFUL
);
910 /**************************************************************************
911 * MCICDA_SetDoor [internal]
913 static DWORD
MCICDA_SetDoor(UINT wDevID
, BOOL open
)
915 WINE_MCICDAUDIO
* wmcda
= MCICDA_GetOpenDrv(wDevID
);
918 TRACE("(%04x, %s) !\n", wDevID
, (open
) ? "OPEN" : "CLOSE");
920 if (wmcda
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
922 if (!DeviceIoControl(wmcda
->handle
,
923 (open
) ? IOCTL_STORAGE_EJECT_MEDIA
: IOCTL_STORAGE_LOAD_MEDIA
,
924 NULL
, 0, NULL
, 0, &br
, NULL
))
925 return MCIERR_HARDWARE
;
930 /**************************************************************************
931 * MCICDA_Set [internal]
933 static DWORD
MCICDA_Set(UINT wDevID
, DWORD dwFlags
, LPMCI_SET_PARMS lpParms
)
935 WINE_MCICDAUDIO
* wmcda
= MCICDA_GetOpenDrv(wDevID
);
937 TRACE("(%04X, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
939 if (wmcda
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
941 if (dwFlags
& MCI_SET_DOOR_OPEN
) {
942 MCICDA_SetDoor(wDevID
, TRUE
);
944 if (dwFlags
& MCI_SET_DOOR_CLOSED
) {
945 MCICDA_SetDoor(wDevID
, FALSE
);
948 /* only functions which require valid lpParms below this line ! */
949 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
951 TRACE("dwTimeFormat=%08lX\n", lpParms->dwTimeFormat);
952 TRACE("dwAudio=%08lX\n", lpParms->dwAudio);
954 if (dwFlags
& MCI_SET_TIME_FORMAT
) {
955 switch (lpParms
->dwTimeFormat
) {
956 case MCI_FORMAT_MILLISECONDS
:
957 TRACE("MCI_FORMAT_MILLISECONDS !\n");
960 TRACE("MCI_FORMAT_MSF !\n");
962 case MCI_FORMAT_TMSF
:
963 TRACE("MCI_FORMAT_TMSF !\n");
966 WARN("bad time format !\n");
967 return MCIERR_BAD_TIME_FORMAT
;
969 wmcda
->dwTimeFormat
= lpParms
->dwTimeFormat
;
971 if (dwFlags
& MCI_SET_VIDEO
) return MCIERR_UNSUPPORTED_FUNCTION
;
972 if (dwFlags
& MCI_SET_ON
) return MCIERR_UNSUPPORTED_FUNCTION
;
973 if (dwFlags
& MCI_SET_OFF
) return MCIERR_UNSUPPORTED_FUNCTION
;
974 if (dwFlags
& MCI_NOTIFY
) {
975 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n",
976 lpParms
->dwCallback
);
977 mciDriverNotify(HWND_32(LOWORD(lpParms
->dwCallback
)),
978 wmcda
->wNotifyDeviceID
, MCI_NOTIFY_SUCCESSFUL
);
983 /**************************************************************************
984 * DriverProc (MCICDA.@)
986 LONG CALLBACK
MCICDA_DriverProc(DWORD dwDevID
, HDRVR hDriv
, DWORD wMsg
,
987 DWORD dwParam1
, DWORD dwParam2
)
990 case DRV_LOAD
: return 1;
991 case DRV_FREE
: return 1;
992 case DRV_OPEN
: return MCICDA_drvOpen((LPSTR
)dwParam1
, (LPMCI_OPEN_DRIVER_PARMSA
)dwParam2
);
993 case DRV_CLOSE
: return MCICDA_drvClose(dwDevID
);
994 case DRV_ENABLE
: return 1;
995 case DRV_DISABLE
: return 1;
996 case DRV_QUERYCONFIGURE
: return 1;
997 case DRV_CONFIGURE
: MessageBoxA(0, "MCI audio CD driver !", "Wine Driver", MB_OK
); return 1;
998 case DRV_INSTALL
: return DRVCNF_RESTART
;
999 case DRV_REMOVE
: return DRVCNF_RESTART
;
1002 if (dwDevID
== 0xFFFFFFFF) return MCIERR_UNSUPPORTED_FUNCTION
;
1005 case MCI_OPEN_DRIVER
: return MCICDA_Open(dwDevID
, dwParam1
, (LPMCI_OPEN_PARMSA
)dwParam2
);
1006 case MCI_CLOSE_DRIVER
: return MCICDA_Close(dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
)dwParam2
);
1007 case MCI_GETDEVCAPS
: return MCICDA_GetDevCaps(dwDevID
, dwParam1
, (LPMCI_GETDEVCAPS_PARMS
)dwParam2
);
1008 case MCI_INFO
: return MCICDA_Info(dwDevID
, dwParam1
, (LPMCI_INFO_PARMSA
)dwParam2
);
1009 case MCI_STATUS
: return MCICDA_Status(dwDevID
, dwParam1
, (LPMCI_STATUS_PARMS
)dwParam2
);
1010 case MCI_SET
: return MCICDA_Set(dwDevID
, dwParam1
, (LPMCI_SET_PARMS
)dwParam2
);
1011 case MCI_PLAY
: return MCICDA_Play(dwDevID
, dwParam1
, (LPMCI_PLAY_PARMS
)dwParam2
);
1012 case MCI_STOP
: return MCICDA_Stop(dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
)dwParam2
);
1013 case MCI_PAUSE
: return MCICDA_Pause(dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
)dwParam2
);
1014 case MCI_RESUME
: return MCICDA_Resume(dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
)dwParam2
);
1015 case MCI_SEEK
: return MCICDA_Seek(dwDevID
, dwParam1
, (LPMCI_SEEK_PARMS
)dwParam2
);
1016 /* FIXME: I wonder if those two next items are really called ? */
1017 case MCI_SET_DOOR_OPEN
: FIXME("MCI_SET_DOOR_OPEN called. Please report this.\n");
1018 return MCICDA_SetDoor(dwDevID
, TRUE
);
1019 case MCI_SET_DOOR_CLOSED
: FIXME("MCI_SET_DOOR_CLOSED called. Please report this.\n");
1020 return MCICDA_SetDoor(dwDevID
, FALSE
);
1021 /* commands that should be supported */
1037 FIXME("Unsupported yet command [%lu]\n", wMsg
);
1039 /* commands that should report an error */
1041 TRACE("Unsupported command [%lu]\n", wMsg
);
1045 ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1048 TRACE("Sending msg [%lu] to default driver proc\n", wMsg
);
1049 return DefDriverProc(dwDevID
, hDriv
, wMsg
, dwParam1
, dwParam2
);
1051 return MCIERR_UNRECOGNIZED_COMMAND
;
1054 /*-----------------------------------------------------------------------*/