Now using the NT CD interface.

This commit is contained in:
Eric Pouech 2002-01-13 01:46:12 +00:00 committed by Alexandre Julliard
parent 5cc6105b39
commit f65c7d546d
3 changed files with 467 additions and 232 deletions

View File

@ -3,7 +3,6 @@ TOPOBJDIR = ../../..
SRCDIR = @srcdir@ SRCDIR = @srcdir@
VPATH = @srcdir@ VPATH = @srcdir@
MODULE = mcicda.drv MODULE = mcicda.drv
IMPORTS = ntdll
LDDLLFLAGS = @LDDLLFLAGS@ LDDLLFLAGS = @LDDLLFLAGS@
SYMBOLFILE = $(MODULE).tmp.o SYMBOLFILE = $(MODULE).tmp.o

View File

@ -16,11 +16,18 @@
#include "wingdi.h" #include "wingdi.h"
#include "winuser.h" #include "winuser.h"
#include "mmddk.h" #include "mmddk.h"
#include "cdrom.h" #include "winioctl.h"
#include "ntddstor.h"
#include "ntddcdrm.h"
#include "debugtools.h" #include "debugtools.h"
DEFAULT_DEBUG_CHANNEL(mcicda); DEFAULT_DEBUG_CHANNEL(mcicda);
#define CDFRAMES_PERSEC 75
#define CDFRAMES_PERMIN (CDFRAMES_PERSEC * 60)
#define FRAME_OF_ADDR(a) ((a)[1] * CDFRAMES_PERMIN + (a)[2] * CDFRAMES_PERSEC + (a)[3])
#define FRAME_OF_TOC(toc, idx) FRAME_OF_ADDR((toc).TrackData[idx - (toc).FirstTrack].Address)
typedef struct { typedef struct {
UINT wDevID; UINT wDevID;
int nUseCount; /* Incremented for each shared open */ int nUseCount; /* Incremented for each shared open */
@ -28,8 +35,7 @@ typedef struct {
WORD wNotifyDeviceID; /* MCI device ID with a pending notification */ WORD wNotifyDeviceID; /* MCI device ID with a pending notification */
HANDLE hCallback; /* Callback handle for pending notification */ HANDLE hCallback; /* Callback handle for pending notification */
DWORD dwTimeFormat; DWORD dwTimeFormat;
WINE_CDAUDIO wcda; HANDLE handle;
int mciMode;
} WINE_MCICDAUDIO; } WINE_MCICDAUDIO;
/*-----------------------------------------------------------------------*/ /*-----------------------------------------------------------------------*/
@ -80,21 +86,33 @@ static WINE_MCICDAUDIO* MCICDA_GetOpenDrv(UINT wDevID)
} }
/************************************************************************** /**************************************************************************
* MCICDA_Mode [internal] * MCICDA_GetStatus [internal]
*/ */
static int MCICDA_Mode(int wcdaMode) static DWORD MCICDA_GetStatus(WINE_MCICDAUDIO* wmcda)
{ {
switch (wcdaMode) { CDROM_SUB_Q_DATA_FORMAT fmt;
case WINE_CDA_DONTKNOW: return MCI_MODE_STOP; SUB_Q_CHANNEL_DATA data;
case WINE_CDA_NOTREADY: return MCI_MODE_STOP; DWORD br;
case WINE_CDA_OPEN: return MCI_MODE_OPEN; DWORD mode = MCI_MODE_NOT_READY;
case WINE_CDA_PLAY: return MCI_MODE_PLAY;
case WINE_CDA_STOP: return MCI_MODE_STOP; fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
case WINE_CDA_PAUSE: return MCI_MODE_PAUSE; if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
default: &data, sizeof(data), &br, NULL)) {
FIXME("Unknown mode %04x\n", wcdaMode); if (GetLastError() == STATUS_NO_MEDIA_IN_DEVICE) mode = MCI_MODE_OPEN;
} else {
switch (data.CurrentPosition.Header.AudioStatus)
{
case AUDIO_STATUS_IN_PROGRESS: mode = MCI_MODE_PLAY; break;
case AUDIO_STATUS_PAUSED: mode = MCI_MODE_PAUSE; break;
case AUDIO_STATUS_PLAY_COMPLETE: mode = MCI_MODE_STOP; break;
case AUDIO_STATUS_PLAY_ERROR:
case AUDIO_STATUS_NOT_SUPPORTED:
case AUDIO_STATUS_NO_STATUS:
default:
break;
}
} }
return MCI_MODE_STOP; return mode;
} }
/************************************************************************** /**************************************************************************
@ -102,15 +120,12 @@ static int MCICDA_Mode(int wcdaMode)
*/ */
static int MCICDA_GetError(WINE_MCICDAUDIO* wmcda) static int MCICDA_GetError(WINE_MCICDAUDIO* wmcda)
{ {
switch (wmcda->wcda.cdaMode) { switch (GetLastError())
case WINE_CDA_DONTKNOW: {
case WINE_CDA_NOTREADY: return MCIERR_DEVICE_NOT_READY; case STATUS_NO_MEDIA_IN_DEVICE: return MCIERR_DEVICE_NOT_READY;
case WINE_CDA_OPEN: return MCIERR_HARDWARE; case STATUS_IO_DEVICE_ERROR: return MCIERR_HARDWARE;
case WINE_CDA_PLAY:
case WINE_CDA_STOP:
case WINE_CDA_PAUSE: break;
default: default:
FIXME("Unknown mode %04x\n", wmcda->wcda.cdaMode); FIXME("Unknown mode %lx\n", GetLastError());
} }
return MCIERR_DRIVER_INTERNAL; return MCIERR_DRIVER_INTERNAL;
} }
@ -122,6 +137,9 @@ static DWORD MCICDA_CalcFrame(WINE_MCICDAUDIO* wmcda, DWORD dwTime)
{ {
DWORD dwFrame = 0; DWORD dwFrame = 0;
UINT wTrack; UINT wTrack;
CDROM_TOC toc;
DWORD br;
BYTE* addr;
TRACE("(%p, %08lX, %lu);\n", wmcda, wmcda->dwTimeFormat, dwTime); TRACE("(%p, %08lX, %lu);\n", wmcda, wmcda->dwTimeFormat, dwTime);
@ -140,15 +158,20 @@ static DWORD MCICDA_CalcFrame(WINE_MCICDAUDIO* wmcda, DWORD dwTime)
case MCI_FORMAT_TMSF: case MCI_FORMAT_TMSF:
default: /* unknown format ! force TMSF ! ... */ default: /* unknown format ! force TMSF ! ... */
wTrack = MCI_TMSF_TRACK(dwTime); wTrack = MCI_TMSF_TRACK(dwTime);
TRACE("MSF %02u-%02u:%02u:%02u\n", if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
MCI_TMSF_TRACK(dwTime), MCI_TMSF_MINUTE(dwTime), &toc, sizeof(toc), &br, NULL))
MCI_TMSF_SECOND(dwTime), MCI_TMSF_FRAME(dwTime)); return 0;
TRACE("TMSF trackpos[%u]=%lu\n", if (wTrack < toc.FirstTrack || wTrack > toc.LastTrack)
wTrack, wmcda->wcda.lpdwTrackPos[wTrack - 1]); return 0;
dwFrame = wmcda->wcda.lpdwTrackPos[wTrack - 1]; TRACE("MSF %02u-%02u:%02u:%02u\n",
dwFrame += CDFRAMES_PERMIN * MCI_TMSF_MINUTE(dwTime); MCI_TMSF_TRACK(dwTime), MCI_TMSF_MINUTE(dwTime),
dwFrame += CDFRAMES_PERSEC * MCI_TMSF_SECOND(dwTime); MCI_TMSF_SECOND(dwTime), MCI_TMSF_FRAME(dwTime));
dwFrame += MCI_TMSF_FRAME(dwTime); addr = toc.TrackData[wTrack - toc.FirstTrack].Address;
TRACE("TMSF trackpos[%u]=%d:%d:%d\n",
wTrack, addr[0], addr[1], addr[2]);
dwFrame = CDFRAMES_PERMIN * (addr[0] + MCI_TMSF_MINUTE(dwTime)) +
CDFRAMES_PERSEC * (addr[1] + MCI_TMSF_SECOND(dwTime)) +
addr[2] + MCI_TMSF_FRAME(dwTime);
break; break;
} }
return dwFrame; return dwFrame;
@ -157,14 +180,15 @@ static DWORD MCICDA_CalcFrame(WINE_MCICDAUDIO* wmcda, DWORD dwTime)
/************************************************************************** /**************************************************************************
* MCICDA_CalcTime [internal] * MCICDA_CalcTime [internal]
*/ */
static DWORD MCICDA_CalcTime(WINE_MCICDAUDIO* wmcda, DWORD tf, DWORD dwFrame, static DWORD MCICDA_CalcTime(WINE_MCICDAUDIO* wmcda, DWORD tf, DWORD dwFrame, LPDWORD lpRet)
LPDWORD lpRet)
{ {
DWORD dwTime = 0; DWORD dwTime = 0;
UINT wTrack; UINT wTrack;
UINT wMinutes; UINT wMinutes;
UINT wSeconds; UINT wSeconds;
UINT wFrames; UINT wFrames;
CDROM_TOC toc;
DWORD br;
TRACE("(%p, %08lX, %lu);\n", wmcda, tf, dwFrame); TRACE("(%p, %08lX, %lu);\n", wmcda, tf, dwFrame);
@ -185,17 +209,23 @@ static DWORD MCICDA_CalcTime(WINE_MCICDAUDIO* wmcda, DWORD tf, DWORD dwFrame,
break; break;
case MCI_FORMAT_TMSF: case MCI_FORMAT_TMSF:
default: /* unknown format ! force TMSF ! ... */ default: /* unknown format ! force TMSF ! ... */
if (dwFrame < wmcda->wcda.dwFirstFrame || dwFrame > wmcda->wcda.dwLastFrame) { if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
ERR("Out of range value %lu [%lu,%lu]\n", &toc, sizeof(toc), &br, NULL))
dwFrame, wmcda->wcda.dwFirstFrame, wmcda->wcda.dwLastFrame); return 0;
if (dwFrame < FRAME_OF_TOC(toc, toc.FirstTrack) ||
dwFrame > FRAME_OF_TOC(toc, toc.LastTrack + 1)) {
ERR("Out of range value %lu [%u,%u]\n",
dwFrame, FRAME_OF_TOC(toc, toc.FirstTrack),
FRAME_OF_TOC(toc, toc.LastTrack + 1));
*lpRet = 0; *lpRet = 0;
return 0; return 0;
} }
for (wTrack = 1; wTrack < wmcda->wcda.nTracks; wTrack++) { for (wTrack = toc.FirstTrack; wTrack <= toc.LastTrack; wTrack++) {
if (wmcda->wcda.lpdwTrackPos[wTrack] > dwFrame) if (FRAME_OF_TOC(toc, wTrack) > dwFrame)
break; break;
} }
dwFrame -= wmcda->wcda.lpdwTrackPos[wTrack - 1]; wTrack--;
dwFrame -= FRAME_OF_TOC(toc, wTrack);
wMinutes = dwFrame / CDFRAMES_PERMIN; wMinutes = dwFrame / CDFRAMES_PERMIN;
wSeconds = (dwFrame - CDFRAMES_PERMIN * wMinutes) / CDFRAMES_PERSEC; wSeconds = (dwFrame - CDFRAMES_PERMIN * wMinutes) / CDFRAMES_PERSEC;
wFrames = dwFrame - CDFRAMES_PERMIN * wMinutes - CDFRAMES_PERSEC * wSeconds; wFrames = dwFrame - CDFRAMES_PERMIN * wMinutes - CDFRAMES_PERSEC * wSeconds;
@ -216,9 +246,11 @@ static DWORD MCICDA_Stop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms
static DWORD MCICDA_Open(UINT wDevID, DWORD dwFlags, LPMCI_OPEN_PARMSA lpOpenParms) static DWORD MCICDA_Open(UINT wDevID, DWORD dwFlags, LPMCI_OPEN_PARMSA lpOpenParms)
{ {
DWORD dwDeviceID; DWORD dwDeviceID;
DWORD ret = MCIERR_HARDWARE;
WINE_MCICDAUDIO* wmcda = (WINE_MCICDAUDIO*)mciGetDriverData(wDevID); WINE_MCICDAUDIO* wmcda = (WINE_MCICDAUDIO*)mciGetDriverData(wDevID);
MCI_SEEK_PARMS seekParms; char root[7];
int dev; int count;
char drive = 0;
TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpOpenParms); TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpOpenParms);
@ -244,28 +276,54 @@ static DWORD MCICDA_Open(UINT wDevID, DWORD dwFlags, LPMCI_OPEN_PARMSA lpOpenPar
WARN("MCI_OPEN_ELEMENT_ID %8lx ! Abort\n", (DWORD)lpOpenParms->lpstrElementName); WARN("MCI_OPEN_ELEMENT_ID %8lx ! Abort\n", (DWORD)lpOpenParms->lpstrElementName);
return MCIERR_NO_ELEMENT_ALLOWED; return MCIERR_NO_ELEMENT_ALLOWED;
} }
WARN("MCI_OPEN_ELEMENT %s ignored\n",lpOpenParms->lpstrElementName); if (!isalpha(lpOpenParms->lpstrElementName[0]) || lpOpenParms->lpstrElementName[1] != ':' ||
/*return MCIERR_NO_ELEMENT_ALLOWED; lpOpenParms->lpstrElementName[2])
bon 19991106 allows cdplayer.exe to run*/ {
WARN("MCI_OPEN_ELEMENT unsupported format: %s\n", lpOpenParms->lpstrElementName);
ret = MCIERR_NO_ELEMENT_ALLOWED;
goto the_error;
}
drive = toupper(lpOpenParms->lpstrElementName[0]);
strcpy(root, "A:\\");
root[0] = drive;
if (GetDriveTypeA(root) != DRIVE_CDROM)
{
ret = MCIERR_INVALID_DEVICE_NAME;
goto the_error;
}
}
else
{
/* drive letter isn't passed... get the dwDeviceID'th cdrom in the system */
strcpy(root, "A:\\");
for (count = 0; root[0] <= 'Z'; root[0]++)
{
if (GetDriveTypeA(root) == DRIVE_CDROM && ++count >= dwDeviceID)
{
drive = root[0];
break;
}
}
if (!drive)
{
ret = MCIERR_INVALID_DEVICE_ID;
goto the_error;
}
} }
wmcda->wNotifyDeviceID = dwDeviceID; wmcda->wNotifyDeviceID = dwDeviceID;
if (CDROM_Open(&wmcda->wcda, -1) == -1) {
--wmcda->nUseCount;
return MCIERR_HARDWARE;
}
wmcda->mciMode = MCI_MODE_STOP;
wmcda->dwTimeFormat = MCI_FORMAT_MSF; wmcda->dwTimeFormat = MCI_FORMAT_MSF;
dev = CDROM_OpenDev(&wmcda->wcda);
if (!CDROM_Audio_GetTracksInfo(&wmcda->wcda, dev)) {
wmcda->mciMode = MCI_MODE_OPEN;
} else {
MCICDA_Seek(wDevID, MCI_SEEK_TO_START, &seekParms);
}
CDROM_CloseDev(dev);
return 0; /* now, open the handle */
strcpy(root, "\\\\.\\A:");
root[4] = drive;
wmcda->handle = CreateFileA(root, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
if (wmcda->handle != INVALID_HANDLE_VALUE)
return 0;
the_error:
--wmcda->nUseCount;
return ret;
} }
/************************************************************************** /**************************************************************************
@ -279,10 +337,9 @@ static DWORD MCICDA_Close(UINT wDevID, DWORD dwParam, LPMCI_GENERIC_PARMS lpParm
if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID; if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
if (wmcda->nUseCount == 1) { if (--wmcda->nUseCount == 0) {
CDROM_Close(&wmcda->wcda); CloseHandle(wmcda->handle);
} }
wmcda->nUseCount--;
return 0; return 0;
} }
@ -350,6 +407,36 @@ static DWORD MCICDA_GetDevCaps(UINT wDevID, DWORD dwFlags,
return ret; return ret;
} }
static DWORD CDROM_Audio_GetSerial(CDROM_TOC* toc)
{
unsigned long serial = 0;
int i;
WORD wMagic;
DWORD dwStart, dwEnd;
/*
* wMagic collects the wFrames from track 1
* dwStart, dwEnd collect the beginning and end of the disc respectively, in
* frames.
* There it is collected for correcting the serial when there are less than
* 3 tracks.
*/
wMagic = toc->TrackData[0].Address[3];
dwStart = FRAME_OF_TOC(*toc, toc->FirstTrack);
for (i = 0; i <= toc->LastTrack - toc->FirstTrack; i++) {
serial += (toc->TrackData[i].Address[1] << 16) |
(toc->TrackData[i].Address[2] << 8) | toc->TrackData[i].Address[3];
}
dwEnd = FRAME_OF_TOC(*toc, toc->LastTrack + 1);
if (toc->LastTrack - toc->FirstTrack + 1 < 3)
serial += wMagic + (dwEnd - dwStart);
return serial;
}
/************************************************************************** /**************************************************************************
* MCICDA_Info [internal] * MCICDA_Info [internal]
*/ */
@ -373,23 +460,16 @@ static DWORD MCICDA_Info(UINT wDevID, DWORD dwFlags, LPMCI_INFO_PARMSA lpParms)
} else if (dwFlags & MCI_INFO_MEDIA_UPC) { } else if (dwFlags & MCI_INFO_MEDIA_UPC) {
ret = MCIERR_NO_IDENTITY; ret = MCIERR_NO_IDENTITY;
} else if (dwFlags & MCI_INFO_MEDIA_IDENTITY) { } else if (dwFlags & MCI_INFO_MEDIA_IDENTITY) {
DWORD res = 0; DWORD res = 0;
int dev = CDROM_OpenDev(&wmcda->wcda); CDROM_TOC toc;
DWORD br;
if (!CDROM_Audio_GetCDStatus(&wmcda->wcda, dev)) { if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
CDROM_CloseDev(dev); &toc, sizeof(toc), &br, NULL)) {
return MCICDA_GetError(wmcda); return MCICDA_GetError(wmcda);
} }
CDROM_CloseDev(dev);
res = CDROM_Audio_GetSerial(&wmcda->wcda); res = CDROM_Audio_GetSerial(&toc);
if (wmcda->wcda.nTracks <= 2) {
/* there are some other values added when # of tracks < 3
* for most Audio CD it will do without
*/
FIXME("Value is not correct !! "
"Please report with full audio CD information (-debugmsg +cdrom,mcicda)\n");
}
sprintf(buffer, "%lu", res); sprintf(buffer, "%lu", res);
str = buffer; str = buffer;
} else { } else {
@ -415,8 +495,13 @@ static DWORD MCICDA_Info(UINT wDevID, DWORD dwFlags, LPMCI_INFO_PARMSA lpParms)
*/ */
static DWORD MCICDA_Status(UINT wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms) static DWORD MCICDA_Status(UINT wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
{ {
WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID); WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
DWORD ret = 0; DWORD idx;
DWORD ret = 0;
CDROM_SUB_Q_DATA_FORMAT fmt;
SUB_Q_CHANNEL_DATA data;
CDROM_TOC toc;
DWORD br;
TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms); TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
@ -432,33 +517,38 @@ static DWORD MCICDA_Status(UINT wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParm
TRACE("dwItem = %lx\n", lpParms->dwItem); TRACE("dwItem = %lx\n", lpParms->dwItem);
switch (lpParms->dwItem) { switch (lpParms->dwItem) {
case MCI_STATUS_CURRENT_TRACK: case MCI_STATUS_CURRENT_TRACK:
if (!CDROM_Audio_GetCDStatus(&wmcda->wcda, -1)) { fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
&data, sizeof(data), &br, NULL))
{
return MCICDA_GetError(wmcda); return MCICDA_GetError(wmcda);
} }
lpParms->dwReturn = wmcda->wcda.nCurTrack; lpParms->dwReturn = data.CurrentPosition.TrackNumber;
TRACE("CURRENT_TRACK=%lu!\n", lpParms->dwReturn); TRACE("CURRENT_TRACK=%lu!\n", lpParms->dwReturn);
break; break;
case MCI_STATUS_LENGTH: case MCI_STATUS_LENGTH:
if (wmcda->wcda.nTracks == 0) { if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
if (!CDROM_Audio_GetTracksInfo(&wmcda->wcda, -1)) { &toc, sizeof(toc), &br, NULL)) {
WARN("error reading TracksInfo !\n"); WARN("error reading TOC !\n");
return MCICDA_GetError(wmcda); return MCICDA_GetError(wmcda);
}
} }
if (dwFlags & MCI_TRACK) { if (dwFlags & MCI_TRACK) {
TRACE("MCI_TRACK #%lu LENGTH=??? !\n", lpParms->dwTrack); TRACE("MCI_TRACK #%lu LENGTH=??? !\n", lpParms->dwTrack);
if (lpParms->dwTrack > wmcda->wcda.nTracks || lpParms->dwTrack == 0) if (lpParms->dwTrack < toc.FirstTrack || lpParms->dwTrack > toc.LastTrack)
return MCIERR_OUTOFRANGE; return MCIERR_OUTOFRANGE;
lpParms->dwReturn = wmcda->wcda.lpdwTrackLen[lpParms->dwTrack - 1]; idx = lpParms->dwTrack - toc.FirstTrack;
lpParms->dwReturn = FRAME_OF_TOC(toc, lpParms->dwTrack + 1) -
FRAME_OF_TOC(toc, lpParms->dwTrack);
/* Windows returns one frame less than the total track length for the /* Windows returns one frame less than the total track length for the
last track on the CD. See CDDB HOWTO. Verified on Win95OSR2. */ last track on the CD. See CDDB HOWTO. Verified on Win95OSR2. */
if (lpParms->dwTrack == wmcda->wcda.nTracks) if (lpParms->dwTrack == toc.LastTrack)
lpParms->dwReturn--; lpParms->dwReturn--;
} else { } else {
/* Sum of the lengths of all of the tracks. Inherits the /* Sum of the lengths of all of the tracks. Inherits the
'off by one frame' behavior from the length of the last track. 'off by one frame' behavior from the length of the last track.
See above comment. */ See above comment. */
lpParms->dwReturn = wmcda->wcda.dwLastFrame - wmcda->wcda.dwFirstFrame - 1; lpParms->dwReturn = FRAME_OF_TOC(toc, toc.LastTrack + 1) -
FRAME_OF_TOC(toc, toc.FirstTrack) - 1;
} }
lpParms->dwReturn = MCICDA_CalcTime(wmcda, lpParms->dwReturn = MCICDA_CalcTime(wmcda,
(wmcda->dwTimeFormat == MCI_FORMAT_TMSF) (wmcda->dwTimeFormat == MCI_FORMAT_TMSF)
@ -468,51 +558,70 @@ static DWORD MCICDA_Status(UINT wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParm
TRACE("LENGTH=%lu !\n", lpParms->dwReturn); TRACE("LENGTH=%lu !\n", lpParms->dwReturn);
break; break;
case MCI_STATUS_MODE: case MCI_STATUS_MODE:
CDROM_Audio_GetCDStatus(&wmcda->wcda, -1); lpParms->dwReturn = MCICDA_GetStatus(wmcda);
lpParms->dwReturn = MCICDA_Mode(wmcda->wcda.cdaMode);
if (!lpParms->dwReturn) lpParms->dwReturn = wmcda->mciMode;
TRACE("MCI_STATUS_MODE=%08lX !\n", lpParms->dwReturn); TRACE("MCI_STATUS_MODE=%08lX !\n", lpParms->dwReturn);
lpParms->dwReturn = MAKEMCIRESOURCE(lpParms->dwReturn, lpParms->dwReturn); lpParms->dwReturn = MAKEMCIRESOURCE(lpParms->dwReturn, lpParms->dwReturn);
ret = MCI_RESOURCE_RETURNED; ret = MCI_RESOURCE_RETURNED;
break; break;
case MCI_STATUS_MEDIA_PRESENT: case MCI_STATUS_MEDIA_PRESENT:
CDROM_Audio_GetCDStatus(&wmcda->wcda, -1); lpParms->dwReturn = (MCICDA_GetStatus(wmcda) == MCI_MODE_OPEN) ?
lpParms->dwReturn = (wmcda->wcda.nTracks == 0 ||
wmcda->wcda.cdaMode == WINE_CDA_OPEN) ?
MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE); MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
TRACE("MCI_STATUS_MEDIA_PRESENT =%c!\n", LOWORD(lpParms->dwReturn) ? 'Y' : 'N'); TRACE("MCI_STATUS_MEDIA_PRESENT =%c!\n", LOWORD(lpParms->dwReturn) ? 'Y' : 'N');
ret = MCI_RESOURCE_RETURNED; ret = MCI_RESOURCE_RETURNED;
break; break;
case MCI_STATUS_NUMBER_OF_TRACKS: case MCI_STATUS_NUMBER_OF_TRACKS:
lpParms->dwReturn = CDROM_Audio_GetNumberOfTracks(&wmcda->wcda, -1); if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
&toc, sizeof(toc), &br, NULL)) {
WARN("error reading TOC !\n");
return MCICDA_GetError(wmcda);
}
lpParms->dwReturn = toc.LastTrack - toc.FirstTrack + 1;
TRACE("MCI_STATUS_NUMBER_OF_TRACKS = %lu !\n", lpParms->dwReturn); TRACE("MCI_STATUS_NUMBER_OF_TRACKS = %lu !\n", lpParms->dwReturn);
if (lpParms->dwReturn == (WORD)-1) if (lpParms->dwReturn == (WORD)-1)
return MCICDA_GetError(wmcda); return MCICDA_GetError(wmcda);
break; break;
case MCI_STATUS_POSITION: case MCI_STATUS_POSITION:
if (!CDROM_Audio_GetCDStatus(&wmcda->wcda, -1))
return MCICDA_GetError(wmcda);
if(wmcda->wcda.cdaMode == WINE_CDA_OPEN)
return MCIERR_HARDWARE;
lpParms->dwReturn = wmcda->wcda.dwCurFrame;
if (dwFlags & MCI_STATUS_START) { if (dwFlags & MCI_STATUS_START) {
lpParms->dwReturn = wmcda->wcda.dwFirstFrame; if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
&toc, sizeof(toc), &br, NULL)) {
WARN("error reading TOC !\n");
return MCICDA_GetError(wmcda);
}
lpParms->dwReturn = FRAME_OF_TOC(toc, toc.FirstTrack);
TRACE("get MCI_STATUS_START !\n"); TRACE("get MCI_STATUS_START !\n");
} } else if (dwFlags & MCI_TRACK) {
if (dwFlags & MCI_TRACK) { if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
if (lpParms->dwTrack > wmcda->wcda.nTracks || lpParms->dwTrack == 0) &toc, sizeof(toc), &br, NULL)) {
WARN("error reading TOC !\n");
return MCICDA_GetError(wmcda);
}
if (lpParms->dwTrack < toc.FirstTrack || lpParms->dwTrack > toc.LastTrack)
return MCIERR_OUTOFRANGE; return MCIERR_OUTOFRANGE;
lpParms->dwReturn = wmcda->wcda.lpdwTrackPos[lpParms->dwTrack - 1]; lpParms->dwReturn = FRAME_OF_TOC(toc, lpParms->dwTrack);
TRACE("get MCI_TRACK #%lu !\n", lpParms->dwTrack); TRACE("get MCI_TRACK #%lu !\n", lpParms->dwTrack);
} } else {
fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
&data, sizeof(data), &br, NULL)) {
return MCICDA_GetError(wmcda);
}
lpParms->dwReturn = FRAME_OF_ADDR(data.CurrentPosition.AbsoluteAddress);
}
lpParms->dwReturn = MCICDA_CalcTime(wmcda, wmcda->dwTimeFormat, lpParms->dwReturn, &ret); lpParms->dwReturn = MCICDA_CalcTime(wmcda, wmcda->dwTimeFormat, lpParms->dwReturn, &ret);
TRACE("MCI_STATUS_POSITION=%08lX !\n", lpParms->dwReturn); TRACE("MCI_STATUS_POSITION=%08lX !\n", lpParms->dwReturn);
break; break;
case MCI_STATUS_READY: case MCI_STATUS_READY:
TRACE("MCI_STATUS_READY !\n"); TRACE("MCI_STATUS_READY !\n");
lpParms->dwReturn = (wmcda->wcda.cdaMode == WINE_CDA_DONTKNOW || switch (MCICDA_GetStatus(wmcda))
wmcda->wcda.cdaMode == WINE_CDA_NOTREADY) ? {
MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE); case MCI_MODE_NOT_READY:
case MCI_MODE_OPEN:
lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
break;
default:
lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
break;
}
TRACE("MCI_STATUS_READY=%u!\n", LOWORD(lpParms->dwReturn)); TRACE("MCI_STATUS_READY=%u!\n", LOWORD(lpParms->dwReturn));
ret = MCI_RESOURCE_RETURNED; ret = MCI_RESOURCE_RETURNED;
break; break;
@ -526,15 +635,16 @@ static DWORD MCICDA_Status(UINT wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParm
if (!(dwFlags & MCI_TRACK)) if (!(dwFlags & MCI_TRACK))
ret = MCIERR_MISSING_PARAMETER; ret = MCIERR_MISSING_PARAMETER;
else { else {
if(!CDROM_Audio_GetTracksInfo(&wmcda->wcda, -1)) { if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
WARN("Error reading tracks info\n"); &toc, sizeof(toc), &br, NULL)) {
return MCICDA_GetError(wmcda); WARN("error reading TOC !\n");
} return MCICDA_GetError(wmcda);
if (lpParms->dwTrack > wmcda->wcda.nTracks || lpParms->dwTrack == 0) }
if (lpParms->dwTrack < toc.FirstTrack || lpParms->dwTrack > toc.LastTrack)
ret = MCIERR_OUTOFRANGE; ret = MCIERR_OUTOFRANGE;
else else
lpParms->dwReturn = (wmcda->wcda.lpbTrackFlags[lpParms->dwTrack - 1] & lpParms->dwReturn = (toc.TrackData[lpParms->dwTrack - toc.FirstTrack].Control & 0x04) ?
CDROM_DATA_TRACK) ? MCI_CDA_TRACK_OTHER : MCI_CDA_TRACK_AUDIO; MCI_CDA_TRACK_OTHER : MCI_CDA_TRACK_AUDIO;
} }
TRACE("MCI_CDA_STATUS_TYPE_TRACK[%ld]=%08lx\n", lpParms->dwTrack, lpParms->dwReturn); TRACE("MCI_CDA_STATUS_TYPE_TRACK[%ld]=%08lx\n", lpParms->dwTrack, lpParms->dwReturn);
break; break;
@ -553,11 +663,14 @@ static DWORD MCICDA_Status(UINT wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParm
*/ */
static DWORD MCICDA_Play(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms) static DWORD MCICDA_Play(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
{ {
int start, end; WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID); DWORD ret = 0, start, end;
DWORD ret = 0; CDROM_TOC toc;
int dev = -1; DWORD br;
CDROM_PLAY_AUDIO_MSF play;
CDROM_SUB_Q_DATA_FORMAT fmt;
SUB_Q_CHANNEL_DATA data;
TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms); TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
if (lpParms == NULL) if (lpParms == NULL)
@ -566,48 +679,45 @@ static DWORD MCICDA_Play(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
if (wmcda == NULL) if (wmcda == NULL)
return MCIERR_INVALID_DEVICE_ID; return MCIERR_INVALID_DEVICE_ID;
dev = CDROM_OpenDev(&wmcda->wcda);
if (wmcda->wcda.nTracks == 0) {
if (!CDROM_Audio_GetTracksInfo(&wmcda->wcda, dev)) {
WARN("error reading TracksInfo !\n");
ret = MCIERR_DRIVER_INTERNAL;
goto end;
}
}
wmcda->wcda.nCurTrack = 1;
if (dwFlags & MCI_FROM) { if (dwFlags & MCI_FROM) {
start = MCICDA_CalcFrame(wmcda, lpParms->dwFrom); start = MCICDA_CalcFrame(wmcda, lpParms->dwFrom);
TRACE("MCI_FROM=%08lX -> %u \n", lpParms->dwFrom, start); TRACE("MCI_FROM=%08lX -> %lu \n", lpParms->dwFrom, start);
} else { } else {
if (!CDROM_Audio_GetCDStatus(&wmcda->wcda, dev)) fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
{ if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
ret = MCIERR_DRIVER_INTERNAL; &data, sizeof(data), &br, NULL)) {
goto end; return MCICDA_GetError(wmcda);
} }
start = wmcda->wcda.dwCurFrame; start = FRAME_OF_ADDR(data.CurrentPosition.AbsoluteAddress);
} }
if (dwFlags & MCI_TO) { if (dwFlags & MCI_TO) {
end = MCICDA_CalcFrame(wmcda, lpParms->dwTo); end = MCICDA_CalcFrame(wmcda, lpParms->dwTo);
TRACE("MCI_TO=%08lX -> %u \n", lpParms->dwTo, end); TRACE("MCI_TO=%08lX -> %lu \n", lpParms->dwTo, end);
} else } else {
end = wmcda->wcda.dwLastFrame; if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
&toc, sizeof(toc), &br, NULL)) {
if (CDROM_Audio_Play(&wmcda->wcda, start, end, dev) == -1) WARN("error reading TOC !\n");
{ return MCICDA_GetError(wmcda);
ret = MCIERR_HARDWARE; }
goto end; end = FRAME_OF_TOC(toc, toc.LastTrack + 1) - 1;
} }
wmcda->mciMode = MCI_MODE_PLAY; TRACE("Playing from %lu to %lu\n", start, end);
if (dwFlags & MCI_NOTIFY) { play.StartingM = start / CDFRAMES_PERMIN;
play.StartingS = (start / CDFRAMES_PERSEC) % 60;
play.StartingF = start % CDFRAMES_PERSEC;
play.EndingM = end / CDFRAMES_PERMIN;
play.EndingS = (end / CDFRAMES_PERSEC) % 60;
play.EndingF = end % CDFRAMES_PERSEC;
if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_PLAY_AUDIO_MSF, &play, sizeof(play),
NULL, 0, &br, NULL)) {
ret = MCIERR_HARDWARE;
} else if (dwFlags & MCI_NOTIFY) {
TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback); TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
/* /*
mciDriverNotify((HWND)LOWORD(lpParms->dwCallback), mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL); wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
*/ */
} }
end:
if (dev != -1)
CDROM_CloseDev(dev);
return ret; return ret;
} }
@ -617,15 +727,15 @@ end:
static DWORD MCICDA_Stop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms) static DWORD MCICDA_Stop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
{ {
WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID); WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
DWORD br;
TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms); TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID; if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
if (CDROM_Audio_Stop(&wmcda->wcda, -1) == -1) if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_STOP_AUDIO, NULL, 0, NULL, 0, &br, NULL))
return MCIERR_HARDWARE; return MCIERR_HARDWARE;
wmcda->mciMode = MCI_MODE_STOP;
if (lpParms && (dwFlags & MCI_NOTIFY)) { if (lpParms && (dwFlags & MCI_NOTIFY)) {
TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback); TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
mciDriverNotify((HWND)LOWORD(lpParms->dwCallback), mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
@ -640,14 +750,15 @@ static DWORD MCICDA_Stop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms
static DWORD MCICDA_Pause(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms) static DWORD MCICDA_Pause(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
{ {
WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID); WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
DWORD br;
TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms); TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID; if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
if (CDROM_Audio_Pause(&wmcda->wcda, 1, -1) == -1) if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_PAUSE_AUDIO, NULL, 0, NULL, 0, &br, NULL))
return MCIERR_HARDWARE; return MCIERR_HARDWARE;
wmcda->mciMode = MCI_MODE_PAUSE;
if (lpParms && (dwFlags & MCI_NOTIFY)) { if (lpParms && (dwFlags & MCI_NOTIFY)) {
TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback); TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
mciDriverNotify((HWND)LOWORD(lpParms->dwCallback), mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
@ -662,14 +773,15 @@ static DWORD MCICDA_Pause(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParm
static DWORD MCICDA_Resume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms) static DWORD MCICDA_Resume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
{ {
WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID); WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
DWORD br;
TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms); TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID; if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
if (CDROM_Audio_Pause(&wmcda->wcda, 0, -1) == -1) if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_RESUME_AUDIO, NULL, 0, NULL, 0, &br, NULL))
return MCIERR_HARDWARE; return MCIERR_HARDWARE;
wmcda->mciMode = MCI_MODE_STOP;
if (lpParms && (dwFlags & MCI_NOTIFY)) { if (lpParms && (dwFlags & MCI_NOTIFY)) {
TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback); TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
mciDriverNotify((HWND)LOWORD(lpParms->dwCallback), mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
@ -683,23 +795,35 @@ static DWORD MCICDA_Resume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpPar
*/ */
static DWORD MCICDA_Seek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms) static DWORD MCICDA_Seek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
{ {
DWORD at; DWORD at;
WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID); WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
CDROM_SEEK_AUDIO_MSF seek;
CDROM_TOC toc;
DWORD br;
TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms); TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID; if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK; if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
wmcda->mciMode = MCI_MODE_SEEK;
switch (dwFlags & ~(MCI_NOTIFY|MCI_WAIT)) { switch (dwFlags & ~(MCI_NOTIFY|MCI_WAIT)) {
case MCI_SEEK_TO_START: case MCI_SEEK_TO_START:
TRACE("Seeking to start\n"); TRACE("Seeking to start\n");
at = wmcda->wcda.dwFirstFrame; if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
&toc, sizeof(toc), &br, NULL)) {
WARN("error reading TOC !\n");
return MCICDA_GetError(wmcda);
}
at = FRAME_OF_TOC(toc, toc.FirstTrack);
break; break;
case MCI_SEEK_TO_END: case MCI_SEEK_TO_END:
TRACE("Seeking to end\n"); TRACE("Seeking to end\n");
at = wmcda->wcda.dwLastFrame; if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
&toc, sizeof(toc), &br, NULL)) {
WARN("error reading TOC !\n");
return MCICDA_GetError(wmcda);
}
at = FRAME_OF_TOC(toc, toc.LastTrack + 1) - 1;
break; break;
case MCI_TO: case MCI_TO:
TRACE("Seeking to %lu\n", lpParms->dwTo); TRACE("Seeking to %lu\n", lpParms->dwTo);
@ -710,9 +834,13 @@ static DWORD MCICDA_Seek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
(dwFlags & ~(MCI_NOTIFY|MCI_WAIT))); (dwFlags & ~(MCI_NOTIFY|MCI_WAIT)));
return MCIERR_UNSUPPORTED_FUNCTION; return MCIERR_UNSUPPORTED_FUNCTION;
} }
if (CDROM_Audio_Seek(&wmcda->wcda, at, -1) == -1) { seek.M = at / CDFRAMES_PERMIN;
seek.S = (at / CDFRAMES_PERSEC) % 60;
seek.F = at % CDFRAMES_PERSEC;
if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_SEEK_AUDIO_MSF, &seek, sizeof(seek),
NULL, 0, &br, NULL))
return MCIERR_HARDWARE; return MCIERR_HARDWARE;
}
if (dwFlags & MCI_NOTIFY) { if (dwFlags & MCI_NOTIFY) {
TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback); TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
mciDriverNotify((HWND)LOWORD(lpParms->dwCallback), mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
@ -724,17 +852,20 @@ static DWORD MCICDA_Seek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
/************************************************************************** /**************************************************************************
* MCICDA_SetDoor [internal] * MCICDA_SetDoor [internal]
*/ */
static DWORD MCICDA_SetDoor(UINT wDevID, int open) static DWORD MCICDA_SetDoor(UINT wDevID, BOOL open)
{ {
WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID); WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
DWORD br;
TRACE("(%04x, %s) !\n", wDevID, (open) ? "OPEN" : "CLOSE"); TRACE("(%04x, %s) !\n", wDevID, (open) ? "OPEN" : "CLOSE");
if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID; if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
if (CDROM_SetDoor(&wmcda->wcda, open, -1) == -1) if (!DeviceIoControl(wmcda->handle,
(open) ? IOCTL_STORAGE_EJECT_MEDIA : IOCTL_STORAGE_LOAD_MEDIA,
NULL, 0, NULL, 0, &br, NULL))
return MCIERR_HARDWARE; return MCIERR_HARDWARE;
wmcda->mciMode = (open) ? MCI_MODE_OPEN : MCI_MODE_STOP;
return 0; return 0;
} }

View File

@ -4,7 +4,7 @@
* *
* Cdrom - device driver emulation - Audio features. * Cdrom - device driver emulation - Audio features.
* (c) 1998 Petr Tomasek <tomasek@etf.cuni.cz> * (c) 1998 Petr Tomasek <tomasek@etf.cuni.cz>
* (c) 1999 Eric Pouech * (c) 1999,2002 Eric Pouech
*/ */
#include "config.h" #include "config.h"
@ -15,7 +15,9 @@
#include "module.h" #include "module.h"
/* #define DEBUG_INT */ /* #define DEBUG_INT */
#include "debugtools.h" #include "debugtools.h"
#include "cdrom.h" #include "winioctl.h"
#include "ntddstor.h"
#include "ntddcdrm.h"
DEFAULT_DEBUG_CHANNEL(int); DEFAULT_DEBUG_CHANNEL(int);
@ -414,12 +416,19 @@ static void MSCDEX_Dump(char* pfx, BYTE* req, int dorealmode)
#define MSCDEX_Dump(pfx, req, drm) #define MSCDEX_Dump(pfx, req, drm)
#endif #endif
#define CDFRAMES_PERSEC 75
#define CDFRAMES_PERMIN (CDFRAMES_PERSEC * 60)
#define FRAME_OF_ADDR(a) ((a)[1] * CDFRAMES_PERMIN + (a)[2] * CDFRAMES_PERSEC + (a)[3])
#define FRAME_OF_TOC(toc, idx) FRAME_OF_ADDR((toc).TrackData[idx - (toc).FirstTrack].Address)
#define CTRL_OF_TOC(toc, idx) (((toc).TrackData[idx - (toc).FirstTrack].Control << 4) | \
(toc).TrackData[idx - (toc).FirstTrack].Adr)
static void MSCDEX_StoreMSF(DWORD frame, BYTE* val) static void MSCDEX_StoreMSF(DWORD frame, BYTE* val)
{ {
val[3] = 0; /* zero */ val[3] = 0; /* zero */
val[2] = frame / CDFRAMES_PERMIN; /* minutes */ val[2] = frame / CDFRAMES_PERMIN; /* minutes */
val[1] = (frame - CDFRAMES_PERMIN * val[2]) / CDFRAMES_PERSEC; /* seconds */ val[1] = (frame / CDFRAMES_PERSEC) % 60; /* seconds */
val[0] = frame - CDFRAMES_PERMIN * val[2] - CDFRAMES_PERSEC * val[1]; /* frames */ val[0] = frame % CDFRAMES_PERSEC; /* frames */
} }
static int is_cdrom( int drive) static int is_cdrom( int drive)
@ -434,7 +443,7 @@ static void MSCDEX_Handler(CONTEXT86* context)
int drive, count; int drive, count;
char* p; char* p;
switch(LOBYTE(context->Eax)) { switch (LOBYTE(context->Eax)) {
case 0x00: /* Installation check */ case 0x00: /* Installation check */
/* Count the number of contiguous CDROM drives /* Count the number of contiguous CDROM drives
*/ */
@ -470,13 +479,18 @@ static void MSCDEX_Handler(CONTEXT86* context)
case 0x10: /* direct driver access */ case 0x10: /* direct driver access */
{ {
static WINE_CDAUDIO wcda;
int dev = -1;
BYTE* driver_request; BYTE* driver_request;
BYTE* io_stru; BYTE* io_stru;
BYTE Error = 255; /* No Error */ BYTE Error = 255; /* No Error */
int dorealmode = ISV86(context); int dorealmode = ISV86(context);
char devName[] = "\\\\.\\@:";
HANDLE h;
CDROM_TOC toc;
CDROM_SUB_Q_DATA_FORMAT fmt;
SUB_Q_CHANNEL_DATA data;
DWORD br;
DWORD present = TRUE;
driver_request = CTX_SEG_OFF_TO_LIN(context, context->SegEs, context->Ebx); driver_request = CTX_SEG_OFF_TO_LIN(context, context->SegEs, context->Ebx);
if (!driver_request) { if (!driver_request) {
@ -489,16 +503,13 @@ static void MSCDEX_Handler(CONTEXT86* context)
return; return;
} }
/* FIXME /* FIXME
* - the current implementation only supports a single CD ROM * the following tests are wrong because lots of functions don't require the
* tray to be closed with a CD inside
*/ */
CDROM_Open(&wcda, -1);
dev = CDROM_OpenDev(&wcda);
TRACE("CDROM device driver -> command <%d>\n", (unsigned char)driver_request[2]); TRACE("CDROM device driver -> command <%d>\n", (unsigned char)driver_request[2]);
for (drive = 0; drive < 26; drive++) if (is_cdrom(drive)) break; if (!is_cdrom(CX_reg(context))) {
/* drive contains the first CD ROM */ WARN("Request made doesn't match a CD ROM drive (%d)\n", CX_reg(context));
if (CX_reg(context) != drive) {
WARN("Request made doesn't match a CD ROM drive (%d/%d)\n", CX_reg(context), drive);
driver_request[4] |= 0x80; driver_request[4] |= 0x80;
driver_request[3] = 1; /* unknown unit */ driver_request[3] = 1; /* unknown unit */
return; return;
@ -508,8 +519,39 @@ static void MSCDEX_Handler(CONTEXT86* context)
/* set status to 0 */ /* set status to 0 */
PTR_AT(driver_request, 3, WORD) = 0; PTR_AT(driver_request, 3, WORD) = 0;
CDROM_Audio_GetCDStatus(&wcda, dev); devName[4] = 'A' + CX_reg(context);
h = CreateFileA(devName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
if (!h) {
WARN("Couldn't open cdrom handle\n");
driver_request[4] |= 0x80;
driver_request[3] = 1; /* unknown unit */
return;
}
fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
if (!DeviceIoControl(h, IOCTL_CDROM_READ_TOC, NULL, 0, &toc, sizeof(toc), &br, NULL) ||
!DeviceIoControl(h, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
&data, sizeof(data), &br, NULL)) {
if (GetLastError() == STATUS_NO_MEDIA_IN_DEVICE)
{
if (driver_request[2] != 6 && driver_request[2] != 15)
{
driver_request[4] |= 0x80;
driver_request[3] = 2; /* drive not ready */
CloseHandle(h);
return;
}
present = FALSE;
}
else
{
driver_request[4] |= 0x80;
driver_request[3] = 1; /* unknown unit */
CloseHandle(h);
return;
}
}
switch (driver_request[2]) { switch (driver_request[2]) {
case 3: case 3:
io_stru = (dorealmode) ? io_stru = (dorealmode) ?
@ -548,10 +590,12 @@ static void MSCDEX_Handler(CONTEXT86* context)
case 1: /* location of head */ case 1: /* location of head */
switch (io_stru[1]) { switch (io_stru[1]) {
case 0: case 0:
PTR_AT(io_stru, 2, DWORD) = wcda.dwCurFrame; PTR_AT(io_stru, 2, DWORD) =
FRAME_OF_ADDR(data.CurrentPosition.AbsoluteAddress);
break; break;
case 1: case 1:
MSCDEX_StoreMSF(wcda.dwCurFrame, io_stru + 2); MSCDEX_StoreMSF(FRAME_OF_ADDR(data.CurrentPosition.AbsoluteAddress),
io_stru + 2);
break; break;
default: default:
ERR("CD-ROM driver: unsupported addressing mode !!\n"); ERR("CD-ROM driver: unsupported addressing mode !!\n");
@ -588,13 +632,13 @@ static void MSCDEX_Handler(CONTEXT86* context)
* 0 Door locked * 0 Door locked
* 0 see below (Door closed/opened) * 0 see below (Door closed/opened)
*/ */
if (wcda.cdaMode == WINE_CDA_OPEN) if (!present) PTR_AT(io_stru, 1, DWORD) |= 1;
io_stru[1] |= 1;
TRACE(" ----> DEVICE STATUS <0x%08lx>\n", PTR_AT(io_stru, 1, DWORD)); TRACE(" ----> DEVICE STATUS <0x%08lx>\n", PTR_AT(io_stru, 1, DWORD));
break; break;
case 8: /* Volume size */ case 8: /* Volume size */
PTR_AT(io_stru, 1, DWORD) = wcda.dwLastFrame; PTR_AT(io_stru, 1, DWORD) = FRAME_OF_TOC(toc, toc.LastTrack + 1) -
FRAME_OF_TOC(toc, toc.FirstTrack) - 1;
TRACE(" ----> VOLUME SIZE <%ld>\n", PTR_AT(io_stru, 1, DWORD)); TRACE(" ----> VOLUME SIZE <%ld>\n", PTR_AT(io_stru, 1, DWORD));
break; break;
@ -605,20 +649,20 @@ static void MSCDEX_Handler(CONTEXT86* context)
break; break;
case 10: /* audio disk info */ case 10: /* audio disk info */
io_stru[1] = wcda.nFirstTrack; /* starting track of the disc */ io_stru[1] = toc.FirstTrack; /* starting track of the disc */
io_stru[2] = wcda.nLastTrack; /* ending track */ io_stru[2] = toc.LastTrack; /* ending track */
MSCDEX_StoreMSF(wcda.dwLastFrame, io_stru + 3); MSCDEX_StoreMSF(FRAME_OF_TOC(toc, toc.LastTrack + 1) -
FRAME_OF_TOC(toc, toc.FirstTrack) - 1, io_stru + 3);
TRACE(" ----> AUDIO DISK INFO <%d-%d/%08lx>\n", TRACE(" ----> AUDIO DISK INFO <%d-%d/%08lx>\n",
io_stru[1], io_stru[2], PTR_AT(io_stru, 3, DWORD)); io_stru[1], io_stru[2], PTR_AT(io_stru, 3, DWORD));
break; break;
case 11: /* audio track info */ case 11: /* audio track info */
if (io_stru[1] >= wcda.nFirstTrack && io_stru[1] <= wcda.nLastTrack) { if (io_stru[1] >= toc.FirstTrack && io_stru[1] <= toc.LastTrack) {
int nt = io_stru[1] - wcda.nFirstTrack; MSCDEX_StoreMSF(FRAME_OF_TOC(toc, io_stru[1]), io_stru + 2);
MSCDEX_StoreMSF(wcda.lpdwTrackPos[nt], io_stru + 2);
/* starting point if the track */ /* starting point if the track */
io_stru[6] = (wcda.lpbTrackFlags[nt] & 0xF0) >> 4; io_stru[6] = CTRL_OF_TOC(toc, io_stru[1]);
} else { } else {
PTR_AT(io_stru, 2, DWORD) = 0; PTR_AT(io_stru, 2, DWORD) = 0;
io_stru[6] = 0; io_stru[6] = 0;
@ -628,42 +672,43 @@ static void MSCDEX_Handler(CONTEXT86* context)
break; break;
case 12: /* get Q-Channel info */ case 12: /* get Q-Channel info */
io_stru[1] = wcda.lpbTrackFlags[wcda.nCurTrack - 1]; io_stru[1] = CTRL_OF_TOC(toc, data.CurrentPosition.TrackNumber);
io_stru[2] = wcda.nCurTrack; io_stru[2] = data.CurrentPosition.TrackNumber;
io_stru[3] = 0; /* FIXME ?? */ io_stru[3] = 0; /* FIXME ?? */
/* why the heck did MS use another format for 0MSF information... sigh */ /* why the heck did MS use another format for 0MSF information... sigh */
{ {
BYTE bTmp[4]; BYTE bTmp[4];
MSCDEX_StoreMSF(wcda.dwCurFrame - wcda.lpdwTrackPos[wcda.nCurTrack - 1], bTmp); MSCDEX_StoreMSF(FRAME_OF_ADDR(data.CurrentPosition.TrackRelativeAddress), bTmp);
io_stru[ 4] = bTmp[2]; io_stru[ 4] = bTmp[2];
io_stru[ 5] = bTmp[1]; io_stru[ 5] = bTmp[1];
io_stru[ 6] = bTmp[0]; io_stru[ 6] = bTmp[0];
io_stru[ 7] = 0; io_stru[ 7] = 0;
MSCDEX_StoreMSF(wcda.dwCurFrame, bTmp); MSCDEX_StoreMSF(FRAME_OF_ADDR(data.CurrentPosition.AbsoluteAddress), bTmp);
io_stru[ 8] = bTmp[2]; io_stru[ 8] = bTmp[2];
io_stru[ 9] = bTmp[1]; io_stru[ 9] = bTmp[1];
io_stru[10] = bTmp[0]; io_stru[10] = bTmp[0];
io_stru[11] = 0; io_stru[11] = 0;
} }
TRACE("Q-Channel info: Ctrl/adr=%02x TNO=%02x X=%02x rtt=%02x:%02x:%02x rtd=%02x:%02x:%02x (cf=%08lx, tp=%08lx)\n", TRACE("Q-Channel info: Ctrl/adr=%02x TNO=%02x X=%02x rtt=%02x:%02x:%02x rtd=%02x:%02x:%02x (cf=%08x, tp=%08x)\n",
io_stru[ 1], io_stru[ 2], io_stru[ 3], io_stru[ 1], io_stru[ 2], io_stru[ 3],
io_stru[ 4], io_stru[ 5], io_stru[ 6], io_stru[ 4], io_stru[ 5], io_stru[ 6],
io_stru[ 8], io_stru[ 9], io_stru[10], io_stru[ 8], io_stru[ 9], io_stru[10],
wcda.dwCurFrame, wcda.lpdwTrackPos[wcda.nCurTrack - 1]); FRAME_OF_ADDR(data.CurrentPosition.AbsoluteAddress),
FRAME_OF_TOC(toc, data.CurrentPosition.TrackNumber));
break; break;
case 15: /* Audio status info */ case 15: /* Audio status info */
/* !!!! FIXME FIXME FIXME !! */ /* !!!! FIXME FIXME FIXME !! */
PTR_AT(io_stru, 1, WORD) = 2 | ((wcda.cdaMode == WINE_CDA_PAUSE) ? 1 : 0); PTR_AT(io_stru, 1, WORD) = 2 | ((data.CurrentPosition.Header.AudioStatus == AUDIO_STATUS_PAUSED) ? 1 : 0);
if (wcda.cdaMode == WINE_CDA_OPEN) { if (!present) {
PTR_AT(io_stru, 3, DWORD) = 0; PTR_AT(io_stru, 3, DWORD) = 0;
PTR_AT(io_stru, 7, DWORD) = 0; PTR_AT(io_stru, 7, DWORD) = 0;
} else { } else {
PTR_AT(io_stru, 3, DWORD) = wcda.lpdwTrackPos[0]; PTR_AT(io_stru, 3, DWORD) = FRAME_OF_TOC(toc, toc.FirstTrack);
PTR_AT(io_stru, 7, DWORD) = wcda.lpdwTrackPos[wcda.nTracks - 1]; PTR_AT(io_stru, 7, DWORD) = FRAME_OF_TOC(toc, toc.LastTrack + 1);
} }
TRACE("Audio status info: status=%04x startLoc=%ld endLoc=%ld\n", TRACE("Audio status info: status=%04x startLoc=%ld endLoc=%ld\n",
PTR_AT(io_stru, 1, WORD), PTR_AT(io_stru, 3, DWORD), PTR_AT(io_stru, 7, DWORD)); PTR_AT(io_stru, 1, WORD), PTR_AT(io_stru, 3, DWORD), PTR_AT(io_stru, 7, DWORD));
@ -684,18 +729,18 @@ static void MSCDEX_Handler(CONTEXT86* context)
TRACE(" --> IOCTL OUTPUT <%d>\n", io_stru[0]); TRACE(" --> IOCTL OUTPUT <%d>\n", io_stru[0]);
switch (io_stru[0]) { switch (io_stru[0]) {
case 0: /* eject */ case 0: /* eject */
CDROM_SetDoor(&wcda, 1, dev); DeviceIoControl(h, IOCTL_STORAGE_EJECT_MEDIA, NULL, 0, NULL, 0, &br, NULL);
TRACE(" ----> EJECT\n"); TRACE(" ----> EJECT\n");
break; break;
case 2: /* reset drive */ case 2: /* reset drive */
CDROM_Reset(&wcda, dev); DeviceIoControl(h, IOCTL_STORAGE_RESET_DEVICE, NULL, 0, NULL, 0, &br, NULL);
TRACE(" ----> RESET\n"); TRACE(" ----> RESET\n");
break; break;
case 3: /* Audio Channel Control */ case 3: /* Audio Channel Control */
FIXME(" ----> AUDIO CHANNEL CONTROL (NIY)\n"); FIXME(" ----> AUDIO CHANNEL CONTROL (NIY)\n");
break; break;
case 5: /* close tray */ case 5: /* close tray */
CDROM_SetDoor(&wcda, 0, dev); DeviceIoControl(h, IOCTL_STORAGE_LOAD_MEDIA, NULL, 0, NULL, 0, &br, NULL);
TRACE(" ----> CLOSE TRAY\n"); TRACE(" ----> CLOSE TRAY\n");
break; break;
default: default:
@ -705,10 +750,57 @@ static void MSCDEX_Handler(CONTEXT86* context)
} }
break; break;
case 128: /* read long */
{
LPVOID dst = MapSL(MAKESEGPTR(PTR_AT(driver_request, 16, WORD),
PTR_AT(driver_request, 14, WORD)));
DWORD at = PTR_AT(driver_request, 20, DWORD);
WORD num = PTR_AT(driver_request, 18, WORD);
RAW_READ_INFO rri;
switch (driver_request[13]) {
case 1: /* Red book addressing mode = 0:m:s:f */
/* FIXME : frame <=> msf conversion routines could be shared
* between mscdex and mcicda
*/
at = LOBYTE(HIWORD(at)) * CDFRAMES_PERMIN +
HIBYTE(LOWORD(at)) * CDFRAMES_PERSEC +
LOBYTE(LOWORD(at));
/* fall through */
case 0: /* HSG addressing mode */
switch (PTR_AT(driver_request, 24, BYTE))
{
case 0: /* cooked */
ReadFile(h, dst, num * 2048, &br, NULL);
break;
case 1:
/* FIXME: computation is wrong */
rri.DiskOffset.s.HighPart = 0;
rri.DiskOffset.s.LowPart = at << 11;
rri.TrackMode = YellowMode2;
rri.SectorCount = num;
DeviceIoControl(h, IOCTL_CDROM_RAW_READ, &rri, sizeof(rri),
dst, num * 2352, &br, NULL);
break;
default:
ERR("Unsupported read mode !!\n");
Error = 0x0c;
break;
}
break;
default:
ERR("Unsupported address mode !!\n");
Error = 0x0c;
break;
}
}
break;
case 131: /* seek */ case 131: /* seek */
{ {
DWORD at; DWORD at;
CDROM_SEEK_AUDIO_MSF seek;
at = PTR_AT(driver_request, 20, DWORD); at = PTR_AT(driver_request, 20, DWORD);
TRACE(" --> SEEK AUDIO mode :<0x%02X>, [%ld]\n", TRACE(" --> SEEK AUDIO mode :<0x%02X>, [%ld]\n",
@ -724,7 +816,11 @@ static void MSCDEX_Handler(CONTEXT86* context)
LOBYTE(LOWORD(at)); LOBYTE(LOWORD(at));
/* fall through */ /* fall through */
case 0: /* HSG addressing mode */ case 0: /* HSG addressing mode */
CDROM_Audio_Seek(&wcda, at, dev); seek.M = at / CDFRAMES_PERMIN;
seek.S = (at / CDFRAMES_PERSEC) % 60;
seek.F = at % CDFRAMES_PERSEC;
DeviceIoControl(h, IOCTL_CDROM_SEEK_AUDIO_MSF, &seek, sizeof(seek),
NULL, 0, &br, NULL);
break; break;
default: default:
ERR("Unsupported address mode !!\n"); ERR("Unsupported address mode !!\n");
@ -736,8 +832,9 @@ static void MSCDEX_Handler(CONTEXT86* context)
case 132: /* play */ case 132: /* play */
{ {
DWORD beg, end; DWORD beg, end;
CDROM_PLAY_AUDIO_MSF play;
beg = end = PTR_AT(driver_request, 14, DWORD); beg = end = PTR_AT(driver_request, 14, DWORD);
end += PTR_AT(driver_request, 18, DWORD); end += PTR_AT(driver_request, 18, DWORD);
@ -745,19 +842,27 @@ static void MSCDEX_Handler(CONTEXT86* context)
(BYTE)driver_request[13], beg, end); (BYTE)driver_request[13], beg, end);
switch (driver_request[13]) { switch (driver_request[13]) {
case 1: /* Red book addressing mode = 0:m:s:f */ case 1:
/* Red book addressing mode = 0:m:s:f */
/* FIXME : frame <=> msf conversion routines could be shared /* FIXME : frame <=> msf conversion routines could be shared
* between mscdex and mcicda * between mscdex and mcicda
*/ */
beg = LOBYTE(HIWORD(beg)) * CDFRAMES_PERMIN + beg = LOBYTE(LOWORD(beg)) * CDFRAMES_PERMIN +
HIBYTE(LOWORD(beg)) * CDFRAMES_PERSEC + HIBYTE(LOWORD(beg)) * CDFRAMES_PERSEC +
LOBYTE(LOWORD(beg)); LOBYTE(HIWORD(beg));
end = LOBYTE(HIWORD(end)) * CDFRAMES_PERMIN + end = LOBYTE(LOWORD(end)) * CDFRAMES_PERMIN +
HIBYTE(LOWORD(end)) * CDFRAMES_PERSEC + HIBYTE(LOWORD(end)) * CDFRAMES_PERSEC +
LOBYTE(LOWORD(end)); LOBYTE(HIWORD(end));
/* fall through */ /* fall through */
case 0: /* HSG addressing mode */ case 0: /* HSG addressing mode */
CDROM_Audio_Play(&wcda, beg, end, dev); play.StartingM = beg / CDFRAMES_PERMIN;
play.StartingS = (beg / CDFRAMES_PERSEC) % 60;
play.StartingF = beg % CDFRAMES_PERSEC;
play.EndingM = end / CDFRAMES_PERMIN;
play.EndingS = (end / CDFRAMES_PERSEC) % 60;
play.EndingF = end % CDFRAMES_PERSEC;
DeviceIoControl(h, IOCTL_CDROM_PLAY_AUDIO_MSF, &play, sizeof(play),
NULL, 0, &br, NULL);
break; break;
default: default:
ERR("Unsupported address mode !!\n"); ERR("Unsupported address mode !!\n");
@ -768,18 +873,18 @@ static void MSCDEX_Handler(CONTEXT86* context)
break; break;
case 133: case 133:
if (wcda.cdaMode == WINE_CDA_PLAY) { if (data.CurrentPosition.Header.AudioStatus == AUDIO_STATUS_IN_PROGRESS) {
CDROM_Audio_Pause(&wcda, 1, dev); DeviceIoControl(h, IOCTL_CDROM_PAUSE_AUDIO, NULL, 0, NULL, 0, &br, NULL);
TRACE(" --> STOP AUDIO (Paused)\n"); TRACE(" --> STOP AUDIO (Paused)\n");
} else { } else {
CDROM_Audio_Stop(&wcda, dev); DeviceIoControl(h, IOCTL_CDROM_STOP_AUDIO, NULL, 0, NULL, 0, &br, NULL);
TRACE(" --> STOP AUDIO (Stopped)\n"); TRACE(" --> STOP AUDIO (Stopped)\n");
} }
break; break;
case 136: case 136:
TRACE(" --> RESUME AUDIO\n"); TRACE(" --> RESUME AUDIO\n");
CDROM_Audio_Pause(&wcda, 0, dev); DeviceIoControl(h, IOCTL_CDROM_PAUSE_AUDIO, NULL, 0, NULL, 0, &br, NULL);
break; break;
default: default:
@ -792,14 +897,14 @@ static void MSCDEX_Handler(CONTEXT86* context)
driver_request[4] |= 0x80; driver_request[4] |= 0x80;
driver_request[3] = Error; driver_request[3] = Error;
} }
CDROM_CloseDev(dev); CloseHandle(h);
CDROM_Close(&wcda);
/* setting status bits /* setting status bits
* 3 == playing && done * 3 == playing && done
* 1 == done * 1 == done
*/ */
driver_request[4] |= (wcda.cdaMode == WINE_CDA_PLAY) ? 3 : 1; driver_request[4] |=
(data.CurrentPosition.Header.AudioStatus == AUDIO_STATUS_IN_PROGRESS) ? 3 : 1;
MSCDEX_Dump("End", driver_request, dorealmode); MSCDEX_Dump("End", driver_request, dorealmode);
} }