mcicda.c 36.4 KB
Newer Older
Alexandre Julliard's avatar
Alexandre Julliard committed
1
/*
2
 * MCI driver for audio CD (MCICDA)
Alexandre Julliard's avatar
Alexandre Julliard committed
3
 *
4 5
 * Copyright 1994    Martin Ayotte
 * Copyright 1998-99 Eric Pouech
6
 * Copyright 2000    Andreas Mohr
7 8 9 10 11 12 13 14 15 16 17 18 19
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
20
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
Alexandre Julliard's avatar
Alexandre Julliard committed
21 22
 */

23
#include "config.h"
24
#include <stdarg.h>
25
#include <stdio.h>
26
#include <string.h>
27

28 29
#include "ntstatus.h"
#define WIN32_NO_STATUS
30
#include "windef.h"
31
#include "winbase.h"
32
#include "wingdi.h"
33
#include "winuser.h"
34
#include "wownt32.h"
35
#include "mmddk.h"
36 37 38
#include "winioctl.h"
#include "ntddstor.h"
#include "ntddcdrm.h"
39
#include "winternl.h"
40
#include "wine/debug.h"
41
#include "wine/unicode.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
42

43
WINE_DEFAULT_DEBUG_CHANNEL(mcicda);
44

45 46 47 48 49
#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)

Alexandre Julliard's avatar
Alexandre Julliard committed
50
typedef struct {
51
    UINT		wDevID;
52
    int     		nUseCount;          /* Incremented for each shared open */
53
    BOOL  		fShareable;         /* TRUE if first open was shareable */
54
    WORD    		wNotifyDeviceID;    /* MCI device ID with a pending notification */
55
    HANDLE 		hCallback;          /* Callback handle for pending notification */
56
    DWORD		dwTimeFormat;
57
    HANDLE              handle;
58
} WINE_MCICDAUDIO;
Alexandre Julliard's avatar
Alexandre Julliard committed
59 60 61

/*-----------------------------------------------------------------------*/

62
/**************************************************************************
63
 * 				MCICDA_drvOpen			[internal]
64
 */
65
static	DWORD	MCICDA_drvOpen(LPCWSTR str, LPMCI_OPEN_DRIVER_PARMSW modp)
66
{
67 68 69 70
    WINE_MCICDAUDIO*	wmcda;

    if (!modp) return 0xFFFFFFFF;

71
    wmcda = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCICDAUDIO));
72 73 74 75 76 77

    if (!wmcda)
	return 0;

    wmcda->wDevID = modp->wDeviceID;
    mciSetDriverData(wmcda->wDevID, (DWORD)wmcda);
78
    modp->wCustomCommandTable = MCI_NO_COMMAND_TABLE;
79
    modp->wType = MCI_DEVTYPE_CD_AUDIO;
80
    return modp->wDeviceID;
81 82 83
}

/**************************************************************************
84
 * 				MCICDA_drvClose			[internal]
85
 */
86
static	DWORD	MCICDA_drvClose(DWORD dwDevID)
87
{
88
    WINE_MCICDAUDIO*  wmcda = (WINE_MCICDAUDIO*)mciGetDriverData(dwDevID);
89 90

    if (wmcda) {
91 92
	HeapFree(GetProcessHeap(), 0, wmcda);
	mciSetDriverData(dwDevID, 0);
93
    }
94
    return (dwDevID == 0xFFFFFFFF) ? 1 : 0;
95 96
}

97
/**************************************************************************
98
 * 				MCICDA_GetOpenDrv		[internal]
99
 */
100
static WINE_MCICDAUDIO*  MCICDA_GetOpenDrv(UINT wDevID)
101
{
102
    WINE_MCICDAUDIO*	wmcda = (WINE_MCICDAUDIO*)mciGetDriverData(wDevID);
103

104
    if (wmcda == NULL || wmcda->nUseCount == 0) {
105
	WARN("Invalid wDevID=%u\n", wDevID);
106 107
	return 0;
    }
108
    return wmcda;
109
}
Alexandre Julliard's avatar
Alexandre Julliard committed
110

Alexandre Julliard's avatar
Alexandre Julliard committed
111
/**************************************************************************
112
 * 				MCICDA_GetStatus		[internal]
113
 */
114
static	DWORD    MCICDA_GetStatus(WINE_MCICDAUDIO* wmcda)
Alexandre Julliard's avatar
Alexandre Julliard committed
115
{
116 117 118 119 120 121
    CDROM_SUB_Q_DATA_FORMAT     fmt;
    SUB_Q_CHANNEL_DATA          data;
    DWORD                       br;
    DWORD                       mode = MCI_MODE_NOT_READY;

    fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
122
    if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
123 124 125 126 127 128 129
                         &data, sizeof(data), &br, NULL)) {
        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;
130
        case AUDIO_STATUS_NO_STATUS:
131 132 133 134 135 136
        case AUDIO_STATUS_PLAY_COMPLETE:        mode = MCI_MODE_STOP;   break;
        case AUDIO_STATUS_PLAY_ERROR:
        case AUDIO_STATUS_NOT_SUPPORTED:
        default:
            break;
        }
137
    }
138
    return mode;
Alexandre Julliard's avatar
Alexandre Julliard committed
139 140 141
}

/**************************************************************************
142
 * 				MCICDA_GetError			[internal]
143
 */
144
static	int	MCICDA_GetError(WINE_MCICDAUDIO* wmcda)
Alexandre Julliard's avatar
Alexandre Julliard committed
145
{
146 147 148 149
    switch (GetLastError())
    {
    case STATUS_NO_MEDIA_IN_DEVICE:     return MCIERR_DEVICE_NOT_READY;
    case STATUS_IO_DEVICE_ERROR:        return MCIERR_HARDWARE;
150
    default:
151
	FIXME("Unknown mode %lx\n", GetLastError());
152
    }
153
    return MCIERR_DRIVER_INTERNAL;
Alexandre Julliard's avatar
Alexandre Julliard committed
154 155
}

Alexandre Julliard's avatar
Alexandre Julliard committed
156
/**************************************************************************
157
 * 			MCICDA_CalcFrame			[internal]
158
 */
159
static DWORD MCICDA_CalcFrame(WINE_MCICDAUDIO* wmcda, DWORD dwTime)
Alexandre Julliard's avatar
Alexandre Julliard committed
160
{
161
    DWORD	dwFrame = 0;
162
    UINT	wTrack;
163 164 165
    CDROM_TOC   toc;
    DWORD       br;
    BYTE*       addr;
166

167
    TRACE("(%p, %08lX, %lu);\n", wmcda, wmcda->dwTimeFormat, dwTime);
168

169
    switch (wmcda->dwTimeFormat) {
170
    case MCI_FORMAT_MILLISECONDS:
171
	dwFrame = ((dwTime - 1) * CDFRAMES_PERSEC + 500) / 1000;
172
	TRACE("MILLISECONDS %lu\n", dwFrame);
173 174
	break;
    case MCI_FORMAT_MSF:
175
	TRACE("MSF %02u:%02u:%02u\n",
176
	      MCI_MSF_MINUTE(dwTime), MCI_MSF_SECOND(dwTime), MCI_MSF_FRAME(dwTime));
177 178 179 180
	dwFrame += CDFRAMES_PERMIN * MCI_MSF_MINUTE(dwTime);
	dwFrame += CDFRAMES_PERSEC * MCI_MSF_SECOND(dwTime);
	dwFrame += MCI_MSF_FRAME(dwTime);
	break;
181
    case MCI_FORMAT_TMSF:
182
    default: /* unknown format ! force TMSF ! ... */
183
	wTrack = MCI_TMSF_TRACK(dwTime);
184
        if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
185 186 187 188 189
                             &toc, sizeof(toc), &br, NULL))
            return 0;
        if (wTrack < toc.FirstTrack || wTrack > toc.LastTrack)
            return 0;
        TRACE("MSF %02u-%02u:%02u:%02u\n",
190
              MCI_TMSF_TRACK(dwTime), MCI_TMSF_MINUTE(dwTime),
191 192 193
              MCI_TMSF_SECOND(dwTime), MCI_TMSF_FRAME(dwTime));
        addr = toc.TrackData[wTrack - toc.FirstTrack].Address;
        TRACE("TMSF trackpos[%u]=%d:%d:%d\n",
194 195 196 197
              wTrack, addr[1], addr[2], addr[3]);
        dwFrame = CDFRAMES_PERMIN * (addr[1] + MCI_TMSF_MINUTE(dwTime)) +
            CDFRAMES_PERSEC * (addr[2] + MCI_TMSF_SECOND(dwTime)) +
            addr[3] + MCI_TMSF_FRAME(dwTime);
198 199 200
	break;
    }
    return dwFrame;
Alexandre Julliard's avatar
Alexandre Julliard committed
201 202
}

Alexandre Julliard's avatar
Alexandre Julliard committed
203
/**************************************************************************
204
 * 			MCICDA_CalcTime				[internal]
205
 */
206
static DWORD MCICDA_CalcTime(WINE_MCICDAUDIO* wmcda, DWORD tf, DWORD dwFrame, LPDWORD lpRet)
Alexandre Julliard's avatar
Alexandre Julliard committed
207
{
208
    DWORD	dwTime = 0;
209 210 211 212
    UINT	wTrack;
    UINT	wMinutes;
    UINT	wSeconds;
    UINT	wFrames;
213 214
    CDROM_TOC   toc;
    DWORD       br;
215

216
    TRACE("(%p, %08lX, %lu);\n", wmcda, tf, dwFrame);
217

218
    switch (tf) {
219
    case MCI_FORMAT_MILLISECONDS:
220
	dwTime = (dwFrame * 1000) / CDFRAMES_PERSEC + 1;
221
	TRACE("MILLISECONDS %lu\n", dwTime);
222
	*lpRet = 0;
223 224 225 226
	break;
    case MCI_FORMAT_MSF:
	wMinutes = dwFrame / CDFRAMES_PERMIN;
	wSeconds = (dwFrame - CDFRAMES_PERMIN * wMinutes) / CDFRAMES_PERSEC;
227
	wFrames = dwFrame - CDFRAMES_PERMIN * wMinutes - CDFRAMES_PERSEC * wSeconds;
228
	dwTime = MCI_MAKE_MSF(wMinutes, wSeconds, wFrames);
229 230
	TRACE("MSF %02u:%02u:%02u -> dwTime=%lu\n",
	      wMinutes, wSeconds, wFrames, dwTime);
231
	*lpRet = MCI_COLONIZED3_RETURN;
232
	break;
233
    case MCI_FORMAT_TMSF:
234
    default:	/* unknown format ! force TMSF ! ... */
235
        if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
236 237
                             &toc, sizeof(toc), &br, NULL))
            return 0;
238
	if (dwFrame < FRAME_OF_TOC(toc, toc.FirstTrack) ||
239
            dwFrame > FRAME_OF_TOC(toc, toc.LastTrack + 1)) {
240 241
	    ERR("Out of range value %lu [%u,%u]\n",
		dwFrame, FRAME_OF_TOC(toc, toc.FirstTrack),
242
                FRAME_OF_TOC(toc, toc.LastTrack + 1));
243 244 245
	    *lpRet = 0;
	    return 0;
	}
246 247
	for (wTrack = toc.FirstTrack; wTrack <= toc.LastTrack; wTrack++) {
	    if (FRAME_OF_TOC(toc, wTrack) > dwFrame)
248
		break;
249
	}
250 251
        wTrack--;
	dwFrame -= FRAME_OF_TOC(toc, wTrack);
252 253
	wMinutes = dwFrame / CDFRAMES_PERMIN;
	wSeconds = (dwFrame - CDFRAMES_PERMIN * wMinutes) / CDFRAMES_PERSEC;
254
	wFrames = dwFrame - CDFRAMES_PERMIN * wMinutes - CDFRAMES_PERSEC * wSeconds;
255
	dwTime = MCI_MAKE_TMSF(wTrack, wMinutes, wSeconds, wFrames);
256
	TRACE("%02u-%02u:%02u:%02u\n", wTrack, wMinutes, wSeconds, wFrames);
257
	*lpRet = MCI_COLONIZED4_RETURN;
258 259 260
	break;
    }
    return dwTime;
Alexandre Julliard's avatar
Alexandre Julliard committed
261 262
}

263 264
static DWORD MCICDA_Seek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms);
static DWORD MCICDA_Stop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms);
265 266

/**************************************************************************
267
 * 				MCICDA_Open			[internal]
268
 */
269
static DWORD MCICDA_Open(UINT wDevID, DWORD dwFlags, LPMCI_OPEN_PARMSW lpOpenParms)
270 271
{
    DWORD		dwDeviceID;
272
    DWORD               ret = MCIERR_HARDWARE;
273
    WINE_MCICDAUDIO* 	wmcda = (WINE_MCICDAUDIO*)mciGetDriverData(wDevID);
274
    WCHAR               root[7], drive = 0;
275
    int                 count;
276

277
    TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpOpenParms);
278

279
    if (lpOpenParms == NULL) 		return MCIERR_NULL_PARAMETER_BLOCK;
280
    if (wmcda == NULL)			return MCIERR_INVALID_DEVICE_ID;
281 282 283

    dwDeviceID = lpOpenParms->wDeviceID;

284 285
    if (wmcda->nUseCount > 0) {
	/* The driver is already open on this channel */
286 287
	/* If the driver was opened shareable before and this open specifies */
	/* shareable then increment the use count */
288 289
	if (wmcda->fShareable && (dwFlags & MCI_OPEN_SHAREABLE))
	    ++wmcda->nUseCount;
290 291 292
	else
	    return MCIERR_MUST_USE_SHAREABLE;
    } else {
293 294
	wmcda->nUseCount = 1;
	wmcda->fShareable = dwFlags & MCI_OPEN_SHAREABLE;
295 296
    }
    if (dwFlags & MCI_OPEN_ELEMENT) {
297
        if (dwFlags & MCI_OPEN_ELEMENT_ID) {
298
            WARN("MCI_OPEN_ELEMENT_ID %8lx ! Abort\n", (DWORD)lpOpenParms->lpstrElementName);
299 300
            return MCIERR_NO_ELEMENT_ALLOWED;
        }
301
        TRACE("MCI_OPEN_ELEMENT element name: %s\n", debugstr_w(lpOpenParms->lpstrElementName));
302
        if (!isalpha(lpOpenParms->lpstrElementName[0]) || lpOpenParms->lpstrElementName[1] != ':' ||
303
            (lpOpenParms->lpstrElementName[2] && lpOpenParms->lpstrElementName[2] != '\\'))
304
        {
305 306
            WARN("MCI_OPEN_ELEMENT unsupported format: %s\n", 
                 debugstr_w(lpOpenParms->lpstrElementName));
307
            ret = MCIERR_NO_ELEMENT_ALLOWED;
308 309 310
            goto the_error;
        }
        drive = toupper(lpOpenParms->lpstrElementName[0]);
311 312
        root[0] = drive; root[1] = ':'; root[2] = '\\'; root[3] = '\0';
        if (GetDriveTypeW(root) != DRIVE_CDROM)
313 314 315 316 317 318 319 320
        {
            ret = MCIERR_INVALID_DEVICE_NAME;
            goto the_error;
        }
    }
    else
    {
        /* drive letter isn't passed... get the dwDeviceID'th cdrom in the system */
321
        root[0] = 'A'; root[1] = ':'; root[2] = '\\'; root[3] = '\0';
322 323
        for (count = 0; root[0] <= 'Z'; root[0]++)
        {
324
            if (GetDriveTypeW(root) == DRIVE_CDROM && ++count >= dwDeviceID)
325 326 327 328 329 330 331 332 333 334
            {
                drive = root[0];
                break;
            }
        }
        if (!drive)
        {
            ret = MCIERR_INVALID_DEVICE_ID;
            goto the_error;
        }
335 336
    }

337
    wmcda->wNotifyDeviceID = dwDeviceID;
338
    wmcda->dwTimeFormat = MCI_FORMAT_MSF;
339

340
    /* now, open the handle */
341 342
    root[0] = root[1] = '\\'; root[2] = '.'; root[3] = '\\'; root[4] = drive; root[5] = ':'; root[6] = '\0';
    wmcda->handle = CreateFileW(root, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
343 344 345 346 347 348
    if (wmcda->handle != INVALID_HANDLE_VALUE)
        return 0;

 the_error:
    --wmcda->nUseCount;
    return ret;
349 350 351
}

/**************************************************************************
352
 * 				MCICDA_Close			[internal]
353
 */
354
static DWORD MCICDA_Close(UINT wDevID, DWORD dwParam, LPMCI_GENERIC_PARMS lpParms)
355
{
356
    WINE_MCICDAUDIO*	wmcda = MCICDA_GetOpenDrv(wDevID);
357

358
    TRACE("(%04X, %08lX, %p);\n", wDevID, dwParam, lpParms);
359

360
    if (wmcda == NULL) 	return MCIERR_INVALID_DEVICE_ID;
361

362 363
    if (--wmcda->nUseCount == 0) {
	CloseHandle(wmcda->handle);
364 365 366 367 368
    }
    return 0;
}

/**************************************************************************
369
 * 				MCICDA_GetDevCaps		[internal]
370
 */
371
static DWORD MCICDA_GetDevCaps(UINT wDevID, DWORD dwFlags,
372 373
				   LPMCI_GETDEVCAPS_PARMS lpParms)
{
374 375
    DWORD	ret = 0;

376
    TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
377 378 379 380

    if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;

    if (dwFlags & MCI_GETDEVCAPS_ITEM) {
381
	TRACE("MCI_GETDEVCAPS_ITEM dwItem=%08lX;\n", lpParms->dwItem);
382

383
	switch (lpParms->dwItem) {
384
	case MCI_GETDEVCAPS_CAN_RECORD:
385 386
	    lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
	    ret = MCI_RESOURCE_RETURNED;
387 388
	    break;
	case MCI_GETDEVCAPS_HAS_AUDIO:
389 390
	    lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
	    ret = MCI_RESOURCE_RETURNED;
391 392
	    break;
	case MCI_GETDEVCAPS_HAS_VIDEO:
393 394
	    lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
	    ret = MCI_RESOURCE_RETURNED;
395 396
	    break;
	case MCI_GETDEVCAPS_DEVICE_TYPE:
397 398
	    lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_CD_AUDIO, MCI_DEVTYPE_CD_AUDIO);
	    ret = MCI_RESOURCE_RETURNED;
399 400
	    break;
	case MCI_GETDEVCAPS_USES_FILES:
401 402
	    lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
	    ret = MCI_RESOURCE_RETURNED;
403 404
	    break;
	case MCI_GETDEVCAPS_COMPOUND_DEVICE:
405 406
	    lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
	    ret = MCI_RESOURCE_RETURNED;
407 408
	    break;
	case MCI_GETDEVCAPS_CAN_EJECT:
409 410
	    lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
	    ret = MCI_RESOURCE_RETURNED;
411 412
	    break;
	case MCI_GETDEVCAPS_CAN_PLAY:
413 414
	    lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
	    ret = MCI_RESOURCE_RETURNED;
415 416
	    break;
	case MCI_GETDEVCAPS_CAN_SAVE:
417 418
	    lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
	    ret = MCI_RESOURCE_RETURNED;
419 420
	    break;
	default:
421
	    ERR("Unsupported %lx devCaps item\n", lpParms->dwItem);
422 423
	    return MCIERR_UNRECOGNIZED_COMMAND;
	}
424 425 426
    } else {
	TRACE("No GetDevCaps-Item !\n");
	return MCIERR_UNRECOGNIZED_COMMAND;
427
    }
428
    TRACE("lpParms->dwReturn=%08lX;\n", lpParms->dwReturn);
429
    return ret;
430 431
}

432 433 434 435 436 437
static DWORD CDROM_Audio_GetSerial(CDROM_TOC* toc)
{
    unsigned long serial = 0;
    int i;
    WORD wMagic;
    DWORD dwStart, dwEnd;
438

439 440 441 442 443 444 445 446 447 448 449
    /*
     * 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++) {
450
        serial += (toc->TrackData[i].Address[1] << 16) |
451 452 453
            (toc->TrackData[i].Address[2] << 8) | toc->TrackData[i].Address[3];
    }
    dwEnd = FRAME_OF_TOC(*toc, toc->LastTrack + 1);
454

455 456 457 458 459
    if (toc->LastTrack - toc->FirstTrack + 1 < 3)
        serial += wMagic + (dwEnd - dwStart);

    return serial;
}
460

461

462
/**************************************************************************
463
 * 				MCICDA_Info			[internal]
464
 */
465
static DWORD MCICDA_Info(UINT wDevID, DWORD dwFlags, LPMCI_INFO_PARMSW lpParms)
466
{
467
    LPCWSTR		str = NULL;
468
    WINE_MCICDAUDIO*	wmcda = MCICDA_GetOpenDrv(wDevID);
469
    DWORD		ret = 0;
470
    WCHAR		buffer[16];
471

472
    TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
473

474 475 476 477 478
    if (lpParms == NULL || lpParms->lpstrReturn == NULL)
	return MCIERR_NULL_PARAMETER_BLOCK;
    if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;

    TRACE("buf=%p, len=%lu\n", lpParms->lpstrReturn, lpParms->dwRetSize);
479

480
    if (dwFlags & MCI_INFO_PRODUCT) {
481 482
        static const WCHAR wszAudioCd[] = {'W','i','n','e','\'','s',' ','a','u','d','i','o',' ','C','D',0};
        str = wszAudioCd;
483 484 485
    } else if (dwFlags & MCI_INFO_MEDIA_UPC) {
	ret = MCIERR_NO_IDENTITY;
    } else if (dwFlags & MCI_INFO_MEDIA_IDENTITY) {
486 487 488
	DWORD	    res = 0;
        CDROM_TOC   toc;
        DWORD       br;
489
	static const WCHAR wszLu[] = {'%','l','u',0};
490

491
        if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
492
                             &toc, sizeof(toc), &br, NULL)) {
493
	    return MCICDA_GetError(wmcda);
494
	}
495 496

	res = CDROM_Audio_GetSerial(&toc);
497
	sprintfW(buffer, wszLu, res);
498 499
	str = buffer;
    } else {
500
	WARN("Don't know this info command (%lu)\n", dwFlags);
501
	ret = MCIERR_UNRECOGNIZED_COMMAND;
502
    }
503
    if (str) {
504 505
	if (lpParms->dwRetSize <= strlenW(str)) {
	    lstrcpynW(lpParms->lpstrReturn, str, lpParms->dwRetSize - 1);
506 507
	    ret = MCIERR_PARAM_OVERFLOW;
	} else {
508
	    strcpyW(lpParms->lpstrReturn, str);
509
	}
510 511 512
    } else {
	*lpParms->lpstrReturn = 0;
    }
513
    TRACE("=> %s (%ld)\n", debugstr_w(lpParms->lpstrReturn), ret);
514
    return ret;
515
}
Alexandre Julliard's avatar
Alexandre Julliard committed
516 517

/**************************************************************************
518
 * 				MCICDA_Status			[internal]
519
 */
520
static DWORD MCICDA_Status(UINT wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
Alexandre Julliard's avatar
Alexandre Julliard committed
521
{
522 523 524 525 526 527 528
    WINE_MCICDAUDIO*	        wmcda = MCICDA_GetOpenDrv(wDevID);
    DWORD                       idx;
    DWORD	                ret = 0;
    CDROM_SUB_Q_DATA_FORMAT     fmt;
    SUB_Q_CHANNEL_DATA          data;
    CDROM_TOC                   toc;
    DWORD                       br;
529

530
    TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
531

532 533 534 535 536
    if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
    if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;

    if (dwFlags & MCI_NOTIFY) {
	TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
537
	mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
538
			wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
539 540
    }
    if (dwFlags & MCI_STATUS_ITEM) {
541
	TRACE("dwItem = %lx\n", lpParms->dwItem);
542 543
	switch (lpParms->dwItem) {
	case MCI_STATUS_CURRENT_TRACK:
544
            fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
545
            if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
546 547
                                 &data, sizeof(data), &br, NULL))
            {
548
		return MCICDA_GetError(wmcda);
549
	    }
550
	    lpParms->dwReturn = data.CurrentPosition.TrackNumber;
551 552 553
	    TRACE("CURRENT_TRACK=%lu!\n", lpParms->dwReturn);
	    break;
	case MCI_STATUS_LENGTH:
554
            if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
555 556 557
                                 &toc, sizeof(toc), &br, NULL)) {
                WARN("error reading TOC !\n");
                return MCICDA_GetError(wmcda);
558 559 560
	    }
	    if (dwFlags & MCI_TRACK) {
		TRACE("MCI_TRACK #%lu LENGTH=??? !\n", lpParms->dwTrack);
561
		if (lpParms->dwTrack < toc.FirstTrack || lpParms->dwTrack > toc.LastTrack)
562
		    return MCIERR_OUTOFRANGE;
563
                idx = lpParms->dwTrack - toc.FirstTrack;
564
		lpParms->dwReturn = FRAME_OF_TOC(toc, lpParms->dwTrack + 1) -
565
                    FRAME_OF_TOC(toc, lpParms->dwTrack);
566 567
		/* Windows returns one frame less than the total track length for the
		   last track on the CD.  See CDDB HOWTO.  Verified on Win95OSR2. */
568
		if (lpParms->dwTrack == toc.LastTrack)
569
		    lpParms->dwReturn--;
570
	    } else {
571 572 573
		/* Sum of the lengths of all of the tracks.  Inherits the
		   'off by one frame' behavior from the length of the last track.
		   See above comment. */
574 575
		lpParms->dwReturn = FRAME_OF_TOC(toc, toc.LastTrack + 1) -
                    FRAME_OF_TOC(toc, toc.FirstTrack) - 1;
576
	    }
577 578
	    lpParms->dwReturn = MCICDA_CalcTime(wmcda,
						 (wmcda->dwTimeFormat == MCI_FORMAT_TMSF)
579 580 581
						    ? MCI_FORMAT_MSF : wmcda->dwTimeFormat,
						 lpParms->dwReturn,
						 &ret);
582 583 584
	    TRACE("LENGTH=%lu !\n", lpParms->dwReturn);
	    break;
	case MCI_STATUS_MODE:
585
            lpParms->dwReturn = MCICDA_GetStatus(wmcda);
586 587 588 589 590
	    TRACE("MCI_STATUS_MODE=%08lX !\n", lpParms->dwReturn);
	    lpParms->dwReturn = MAKEMCIRESOURCE(lpParms->dwReturn, lpParms->dwReturn);
	    ret = MCI_RESOURCE_RETURNED;
	    break;
	case MCI_STATUS_MEDIA_PRESENT:
591
	    lpParms->dwReturn = (MCICDA_GetStatus(wmcda) == MCI_MODE_OPEN) ?
592
		MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
593
	    TRACE("MCI_STATUS_MEDIA_PRESENT =%c!\n", LOWORD(lpParms->dwReturn) ? 'Y' : 'N');
594 595 596
	    ret = MCI_RESOURCE_RETURNED;
	    break;
	case MCI_STATUS_NUMBER_OF_TRACKS:
597
            if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
598 599 600 601 602
                                 &toc, sizeof(toc), &br, NULL)) {
                WARN("error reading TOC !\n");
                return MCICDA_GetError(wmcda);
	    }
	    lpParms->dwReturn = toc.LastTrack - toc.FirstTrack + 1;
603
	    TRACE("MCI_STATUS_NUMBER_OF_TRACKS = %lu !\n", lpParms->dwReturn);
604
	    if (lpParms->dwReturn == (WORD)-1)
605
		return MCICDA_GetError(wmcda);
606 607 608
	    break;
	case MCI_STATUS_POSITION:
	    if (dwFlags & MCI_STATUS_START) {
609
                if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
610 611 612 613 614
                                     &toc, sizeof(toc), &br, NULL)) {
                    WARN("error reading TOC !\n");
                    return MCICDA_GetError(wmcda);
                }
		lpParms->dwReturn = FRAME_OF_TOC(toc, toc.FirstTrack);
615
		TRACE("get MCI_STATUS_START !\n");
616
	    } else if (dwFlags & MCI_TRACK) {
617
                if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
618 619 620 621 622
                                     &toc, sizeof(toc), &br, NULL)) {
                    WARN("error reading TOC !\n");
                    return MCICDA_GetError(wmcda);
                }
		if (lpParms->dwTrack < toc.FirstTrack || lpParms->dwTrack > toc.LastTrack)
623
		    return MCIERR_OUTOFRANGE;
624
		lpParms->dwReturn = FRAME_OF_TOC(toc, lpParms->dwTrack);
625
		TRACE("get MCI_TRACK #%lu !\n", lpParms->dwTrack);
626 627
            } else {
                fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
628
                if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
629 630 631 632 633
                                     &data, sizeof(data), &br, NULL)) {
                    return MCICDA_GetError(wmcda);
                }
                lpParms->dwReturn = FRAME_OF_ADDR(data.CurrentPosition.AbsoluteAddress);
            }
634
	    lpParms->dwReturn = MCICDA_CalcTime(wmcda, wmcda->dwTimeFormat, lpParms->dwReturn, &ret);
635 636 637 638
	    TRACE("MCI_STATUS_POSITION=%08lX !\n", lpParms->dwReturn);
	    break;
	case MCI_STATUS_READY:
	    TRACE("MCI_STATUS_READY !\n");
639 640 641 642 643 644 645 646 647 648
            switch (MCICDA_GetStatus(wmcda))
            {
            case MCI_MODE_NOT_READY:
            case MCI_MODE_OPEN:
                lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
                break;
            default:
                lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
                break;
            }
649 650 651 652
	    TRACE("MCI_STATUS_READY=%u!\n", LOWORD(lpParms->dwReturn));
	    ret = MCI_RESOURCE_RETURNED;
	    break;
	case MCI_STATUS_TIME_FORMAT:
653
	    lpParms->dwReturn = MAKEMCIRESOURCE(wmcda->dwTimeFormat, MCI_FORMAT_RETURN_BASE + wmcda->dwTimeFormat);
654 655 656
	    TRACE("MCI_STATUS_TIME_FORMAT=%08x!\n", LOWORD(lpParms->dwReturn));
	    ret = MCI_RESOURCE_RETURNED;
	    break;
657
	case 4001: /* FIXME: for bogus FullCD */
658
	case MCI_CDA_STATUS_TYPE_TRACK:
659
	    if (!(dwFlags & MCI_TRACK))
660
		ret = MCIERR_MISSING_PARAMETER;
661
	    else {
662
                if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
663 664 665 666 667
                                     &toc, sizeof(toc), &br, NULL)) {
                    WARN("error reading TOC !\n");
                    return MCICDA_GetError(wmcda);
                }
		if (lpParms->dwTrack < toc.FirstTrack || lpParms->dwTrack > toc.LastTrack)
668 669
		    ret = MCIERR_OUTOFRANGE;
		else
670 671
		    lpParms->dwReturn = (toc.TrackData[lpParms->dwTrack - toc.FirstTrack].Control & 0x04) ?
                                         MCI_CDA_TRACK_OTHER : MCI_CDA_TRACK_AUDIO;
672
	    }
673
	    TRACE("MCI_CDA_STATUS_TYPE_TRACK[%ld]=%ld\n", lpParms->dwTrack, lpParms->dwReturn);
674 675 676 677
	    break;
	default:
	    FIXME("unknown command %08lX !\n", lpParms->dwItem);
	    return MCIERR_UNRECOGNIZED_COMMAND;
678
	}
679 680
    } else {
	WARN("not MCI_STATUS_ITEM !\n");
681
    }
682
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
683 684
}

685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718
/**************************************************************************
 * 				MCICDA_SkipDataTracks		[internal]
 */
static DWORD MCICDA_SkipDataTracks(WINE_MCICDAUDIO* wmcda,DWORD *frame)
{
  int i;
  DWORD br;
  CDROM_TOC toc;
  if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
                      &toc, sizeof(toc), &br, NULL)) {
    WARN("error reading TOC !\n");
    return MCICDA_GetError(wmcda);
  }
  /* Locate first track whose starting frame is bigger than frame */
  for(i=toc.FirstTrack;i<=toc.LastTrack+1;i++) 
    if ( FRAME_OF_TOC(toc, i) > *frame ) break;
  if (i <= toc.FirstTrack && i>toc.LastTrack+1) {
    i = 0; /* requested address is out of range: go back to start */
    *frame = FRAME_OF_TOC(toc,toc.FirstTrack);
  }
  else
    i--;
  /* i points to last track whose start address is not greater than frame.
   * Now skip non-audio tracks */
  for(;i<=toc.LastTrack+1;i++)
    if ( ! (toc.TrackData[i-toc.FirstTrack].Control & 4) )
      break;
  /* The frame will be an address in the next audio track or
   * address of lead-out. */
  if ( FRAME_OF_TOC(toc, i) > *frame )
    *frame = FRAME_OF_TOC(toc, i);
  return 0;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
719
/**************************************************************************
720
 * 				MCICDA_Play			[internal]
721
 */
722
static DWORD MCICDA_Play(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
Alexandre Julliard's avatar
Alexandre Julliard committed
723
{
724 725 726 727 728 729
    WINE_MCICDAUDIO*	        wmcda = MCICDA_GetOpenDrv(wDevID);
    DWORD		        ret = 0, start, end;
    DWORD                       br;
    CDROM_PLAY_AUDIO_MSF        play;
    CDROM_SUB_Q_DATA_FORMAT     fmt;
    SUB_Q_CHANNEL_DATA          data;
730
    CDROM_TOC			toc;
731

732
    TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
733

734 735 736 737 738 739 740 741
    if (lpParms == NULL)
	return MCIERR_NULL_PARAMETER_BLOCK;

    if (wmcda == NULL)
	return MCIERR_INVALID_DEVICE_ID;

    if (dwFlags & MCI_FROM) {
	start = MCICDA_CalcFrame(wmcda, lpParms->dwFrom);
742 743
	if ( (ret=MCICDA_SkipDataTracks(wmcda, &start)) )
	  return ret;
744
	TRACE("MCI_FROM=%08lX -> %lu\n", lpParms->dwFrom, start);
745
    } else {
746
        fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
747
        if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
748 749 750 751
                             &data, sizeof(data), &br, NULL)) {
            return MCICDA_GetError(wmcda);
        }
        start = FRAME_OF_ADDR(data.CurrentPosition.AbsoluteAddress);
752 753
	if ( (ret=MCICDA_SkipDataTracks(wmcda, &start)) )
	  return ret;
754 755 756
    }
    if (dwFlags & MCI_TO) {
	end = MCICDA_CalcFrame(wmcda, lpParms->dwTo);
757
	TRACE("MCI_TO=%08lX -> %lu\n", lpParms->dwTo, end);
758
    } else {
759
        if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
760 761 762 763 764
                             &toc, sizeof(toc), &br, NULL)) {
            WARN("error reading TOC !\n");
            return MCICDA_GetError(wmcda);
        }
	end = FRAME_OF_TOC(toc, toc.LastTrack + 1) - 1;
765
    }
766 767 768 769 770 771 772
    TRACE("Playing from %lu to %lu\n", start, end);
    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;
773
    if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_PLAY_AUDIO_MSF, &play, sizeof(play),
774 775 776
                         NULL, 0, &br, NULL)) {
	ret = MCIERR_HARDWARE;
    } else if (dwFlags & MCI_NOTIFY) {
777 778
	TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
	/*
779
	  mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
780 781
	  wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
	*/
782 783
    }
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
784 785 786
}

/**************************************************************************
787
 * 				MCICDA_Stop			[internal]
788
 */
789
static DWORD MCICDA_Stop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
Alexandre Julliard's avatar
Alexandre Julliard committed
790
{
791
    WINE_MCICDAUDIO*	wmcda = MCICDA_GetOpenDrv(wDevID);
792 793
    DWORD               br;

794
    TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
795

796
    if (wmcda == NULL)	return MCIERR_INVALID_DEVICE_ID;
797

798
    if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_STOP_AUDIO, NULL, 0, NULL, 0, &br, NULL))
799
	return MCIERR_HARDWARE;
800

801
    if (lpParms && (dwFlags & MCI_NOTIFY)) {
802
	TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
803
	mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
804
			wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
805 806
    }
    return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
807 808 809
}

/**************************************************************************
810
 * 				MCICDA_Pause			[internal]
811
 */
812
static DWORD MCICDA_Pause(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
Alexandre Julliard's avatar
Alexandre Julliard committed
813
{
814
    WINE_MCICDAUDIO*	wmcda = MCICDA_GetOpenDrv(wDevID);
815 816
    DWORD               br;

817
    TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
818

819
    if (wmcda == NULL)	return MCIERR_INVALID_DEVICE_ID;
820

821
    if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_PAUSE_AUDIO, NULL, 0, NULL, 0, &br, NULL))
822
	return MCIERR_HARDWARE;
823

824
    if (lpParms && (dwFlags & MCI_NOTIFY)) {
825
        TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
826
	mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
827
			wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
828 829
    }
    return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
830 831 832
}

/**************************************************************************
833
 * 				MCICDA_Resume			[internal]
834
 */
835
static DWORD MCICDA_Resume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
Alexandre Julliard's avatar
Alexandre Julliard committed
836
{
837
    WINE_MCICDAUDIO*	wmcda = MCICDA_GetOpenDrv(wDevID);
838
    DWORD               br;
839

840
    TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
841

842
    if (wmcda == NULL)	return MCIERR_INVALID_DEVICE_ID;
843

844
    if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_RESUME_AUDIO, NULL, 0, NULL, 0, &br, NULL))
845
	return MCIERR_HARDWARE;
846

847
    if (lpParms && (dwFlags & MCI_NOTIFY)) {
848
	TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
849
	mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
850
			wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
851 852
    }
    return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
853 854 855
}

/**************************************************************************
856
 * 				MCICDA_Seek			[internal]
857
 */
858
static DWORD MCICDA_Seek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
Alexandre Julliard's avatar
Alexandre Julliard committed
859
{
860 861 862
    DWORD		        at;
    WINE_MCICDAUDIO*	        wmcda = MCICDA_GetOpenDrv(wDevID);
    CDROM_SEEK_AUDIO_MSF        seek;
863 864
    DWORD                       br, ret;
    CDROM_TOC			toc;
865

866
    TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
867

868
    if (wmcda == NULL)	return MCIERR_INVALID_DEVICE_ID;
869
    if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
870

871 872 873 874 875
    if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
                         &toc, sizeof(toc), &br, NULL)) {
        WARN("error reading TOC !\n");
        return MCICDA_GetError(wmcda);
    }
876
    switch (dwFlags & ~(MCI_NOTIFY|MCI_WAIT)) {
877
    case MCI_SEEK_TO_START:
878
	TRACE("Seeking to start\n");
879 880 881
	at = FRAME_OF_TOC(toc,toc.FirstTrack);
	if ( (ret=MCICDA_SkipDataTracks(wmcda, &at)) )
	  return ret;
882 883
	break;
    case MCI_SEEK_TO_END:
884
	TRACE("Seeking to end\n");
885
	at = FRAME_OF_TOC(toc, toc.LastTrack + 1) - 1;
886 887
	if ( (ret=MCICDA_SkipDataTracks(wmcda, &at)) )
	  return ret;
888 889
	break;
    case MCI_TO:
890
	TRACE("Seeking to %lu\n", lpParms->dwTo);
891 892 893
        at = MCICDA_CalcFrame(wmcda, lpParms->dwTo);
	if ( (ret=MCICDA_SkipDataTracks(wmcda, &at)) )
	  return ret;
894
	break;
895
    default:
Jeff Garzik's avatar
Jeff Garzik committed
896 897
	TRACE("Unknown seek action %08lX\n",
	      (dwFlags & ~(MCI_NOTIFY|MCI_WAIT)));
898
	return MCIERR_UNSUPPORTED_FUNCTION;
899
    }
900 901 902
    seek.M = at / CDFRAMES_PERMIN;
    seek.S = (at / CDFRAMES_PERSEC) % 60;
    seek.F = at % CDFRAMES_PERSEC;
903
    if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_SEEK_AUDIO_MSF, &seek, sizeof(seek),
904
                         NULL, 0, &br, NULL))
905
	return MCIERR_HARDWARE;
906

907
    if (dwFlags & MCI_NOTIFY) {
908
	TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
909
	mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
910
			  wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
911
    }
912
    return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
913 914
}

915
/**************************************************************************
916
 * 				MCICDA_SetDoor			[internal]
917
 */
918
static DWORD	MCICDA_SetDoor(UINT wDevID, BOOL open)
919
{
920
    WINE_MCICDAUDIO*	wmcda = MCICDA_GetOpenDrv(wDevID);
921 922
    DWORD               br;

923
    TRACE("(%04x, %s) !\n", wDevID, (open) ? "OPEN" : "CLOSE");
924

925
    if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
926 927

    if (!DeviceIoControl(wmcda->handle,
928 929
                         (open) ? IOCTL_STORAGE_EJECT_MEDIA : IOCTL_STORAGE_LOAD_MEDIA,
                         NULL, 0, NULL, 0, &br, NULL))
930
	return MCIERR_HARDWARE;
931

932 933
    return 0;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
934 935

/**************************************************************************
936
 * 				MCICDA_Set			[internal]
937
 */
938
static DWORD MCICDA_Set(UINT wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
Alexandre Julliard's avatar
Alexandre Julliard committed
939
{
940
    WINE_MCICDAUDIO*	wmcda = MCICDA_GetOpenDrv(wDevID);
941

942
    TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
943

944
    if (wmcda == NULL)	return MCIERR_INVALID_DEVICE_ID;
945 946 947 948 949 950 951 952 953

    if (dwFlags & MCI_SET_DOOR_OPEN) {
	MCICDA_SetDoor(wDevID, TRUE);
    }
    if (dwFlags & MCI_SET_DOOR_CLOSED) {
	MCICDA_SetDoor(wDevID, FALSE);
    }

    /* only functions which require valid lpParms below this line ! */
954
    if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
955
    /*
956 957
      TRACE("dwTimeFormat=%08lX\n", lpParms->dwTimeFormat);
      TRACE("dwAudio=%08lX\n", lpParms->dwAudio);
958 959 960 961
    */
    if (dwFlags & MCI_SET_TIME_FORMAT) {
	switch (lpParms->dwTimeFormat) {
	case MCI_FORMAT_MILLISECONDS:
962
	    TRACE("MCI_FORMAT_MILLISECONDS !\n");
963 964
	    break;
	case MCI_FORMAT_MSF:
965
	    TRACE("MCI_FORMAT_MSF !\n");
966 967
	    break;
	case MCI_FORMAT_TMSF:
968
	    TRACE("MCI_FORMAT_TMSF !\n");
969 970
	    break;
	default:
971
	    WARN("bad time format !\n");
972 973
	    return MCIERR_BAD_TIME_FORMAT;
	}
974
	wmcda->dwTimeFormat = lpParms->dwTimeFormat;
975 976 977 978 979
    }
    if (dwFlags & MCI_SET_VIDEO) return MCIERR_UNSUPPORTED_FUNCTION;
    if (dwFlags & MCI_SET_ON) return MCIERR_UNSUPPORTED_FUNCTION;
    if (dwFlags & MCI_SET_OFF) return MCIERR_UNSUPPORTED_FUNCTION;
    if (dwFlags & MCI_NOTIFY) {
980
	TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n",
981
	      lpParms->dwCallback);
982
	mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
983
			wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
984 985
    }
    return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
986 987 988
}

/**************************************************************************
Patrik Stridvall's avatar
Patrik Stridvall committed
989
 * 			DriverProc (MCICDA.@)
990
 */
991 992
LRESULT CALLBACK MCICDA_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
                                   LPARAM dwParam1, LPARAM dwParam2)
993 994 995 996
{
    switch(wMsg) {
    case DRV_LOAD:		return 1;
    case DRV_FREE:		return 1;
997
    case DRV_OPEN:		return MCICDA_drvOpen((LPCWSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSW)dwParam2);
998
    case DRV_CLOSE:		return MCICDA_drvClose(dwDevID);
999
    case DRV_ENABLE:		return 1;
1000 1001
    case DRV_DISABLE:		return 1;
    case DRV_QUERYCONFIGURE:	return 1;
1002
    case DRV_CONFIGURE:		MessageBoxA(0, "MCI audio CD driver !", "Wine Driver", MB_OK); return 1;
1003 1004
    case DRV_INSTALL:		return DRVCNF_RESTART;
    case DRV_REMOVE:		return DRVCNF_RESTART;
1005
    }
1006

1007 1008 1009
    if (dwDevID == 0xFFFFFFFF) return MCIERR_UNSUPPORTED_FUNCTION;

    switch (wMsg) {
1010
    case MCI_OPEN_DRIVER:	return MCICDA_Open(dwDevID, dwParam1, (LPMCI_OPEN_PARMSW)dwParam2);
1011 1012
    case MCI_CLOSE_DRIVER:	return MCICDA_Close(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
    case MCI_GETDEVCAPS:	return MCICDA_GetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS)dwParam2);
1013
    case MCI_INFO:		return MCICDA_Info(dwDevID, dwParam1, (LPMCI_INFO_PARMSW)dwParam2);
1014 1015 1016 1017 1018 1019 1020
    case MCI_STATUS:		return MCICDA_Status(dwDevID, dwParam1, (LPMCI_STATUS_PARMS)dwParam2);
    case MCI_SET:		return MCICDA_Set(dwDevID, dwParam1, (LPMCI_SET_PARMS)dwParam2);
    case MCI_PLAY:		return MCICDA_Play(dwDevID, dwParam1, (LPMCI_PLAY_PARMS)dwParam2);
    case MCI_STOP:		return MCICDA_Stop(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
    case MCI_PAUSE:		return MCICDA_Pause(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
    case MCI_RESUME:		return MCICDA_Resume(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
    case MCI_SEEK:		return MCICDA_Seek(dwDevID, dwParam1, (LPMCI_SEEK_PARMS)dwParam2);
1021 1022 1023 1024
    /* commands that should report an error as they are not supported in
     * the native version */
    case MCI_SET_DOOR_CLOSED:
    case MCI_SET_DOOR_OPEN:
1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040
    case MCI_LOAD:
    case MCI_SAVE:
    case MCI_FREEZE:
    case MCI_PUT:
    case MCI_REALIZE:
    case MCI_UNFREEZE:
    case MCI_UPDATE:
    case MCI_WHERE:
    case MCI_STEP:
    case MCI_SPIN:
    case MCI_ESCAPE:
    case MCI_COPY:
    case MCI_CUT:
    case MCI_DELETE:
    case MCI_PASTE:
    case MCI_WINDOW:
1041
	TRACE("Unsupported command [0x%x]\n", wMsg);
1042 1043 1044
	break;
    case MCI_OPEN:
    case MCI_CLOSE:
1045
	ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1046
	break;
1047
    default:
1048
	TRACE("Sending msg [0x%x] to default driver proc\n", wMsg);
1049
	return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1050
    }
1051
    return MCIERR_UNRECOGNIZED_COMMAND;
Alexandre Julliard's avatar
Alexandre Julliard committed
1052 1053 1054
}

/*-----------------------------------------------------------------------*/