mcicda.c 36.3 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
#define WIN32_NO_STATUS
29
#include "windef.h"
30
#include "winbase.h"
31
#include "wingdi.h"
32
#include "winuser.h"
33
#include "wownt32.h"
34
#include "mmddk.h"
35 36
#include "winioctl.h"
#include "ntddcdrm.h"
37
#include "winternl.h"
38
#include "wine/debug.h"
39
#include "wine/unicode.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
40

41
WINE_DEFAULT_DEBUG_CHANNEL(mcicda);
42

43 44 45 46 47
#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
48
typedef struct {
49
    UINT		wDevID;
50
    int     		nUseCount;          /* Incremented for each shared open */
51
    BOOL  		fShareable;         /* TRUE if first open was shareable */
52
    WORD    		wNotifyDeviceID;    /* MCI device ID with a pending notification */
53
    HANDLE 		hCallback;          /* Callback handle for pending notification */
54
    DWORD		dwTimeFormat;
55
    HANDLE              handle;
56
} WINE_MCICDAUDIO;
Alexandre Julliard's avatar
Alexandre Julliard committed
57 58 59

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

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

    if (!modp) return 0xFFFFFFFF;

69
    wmcda = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCICDAUDIO));
70 71 72 73 74

    if (!wmcda)
	return 0;

    wmcda->wDevID = modp->wDeviceID;
75
    mciSetDriverData(wmcda->wDevID, (DWORD_PTR)wmcda);
76
    modp->wCustomCommandTable = MCI_NO_COMMAND_TABLE;
77
    modp->wType = MCI_DEVTYPE_CD_AUDIO;
78
    return modp->wDeviceID;
79 80 81
}

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

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

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

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

Alexandre Julliard's avatar
Alexandre Julliard committed
109
/**************************************************************************
110
 * 				MCICDA_GetStatus		[internal]
111
 */
112
static	DWORD    MCICDA_GetStatus(WINE_MCICDAUDIO* wmcda)
Alexandre Julliard's avatar
Alexandre Julliard committed
113
{
114 115 116 117 118 119
    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;
120
    if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
121
                         &data, sizeof(data), &br, NULL)) {
122
        if (GetLastError() == ERROR_NOT_READY) mode = MCI_MODE_OPEN;
123 124 125 126 127
    } 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;
128
        case AUDIO_STATUS_NO_STATUS:
129 130 131 132 133 134
        case AUDIO_STATUS_PLAY_COMPLETE:        mode = MCI_MODE_STOP;   break;
        case AUDIO_STATUS_PLAY_ERROR:
        case AUDIO_STATUS_NOT_SUPPORTED:
        default:
            break;
        }
135
    }
136
    return mode;
Alexandre Julliard's avatar
Alexandre Julliard committed
137 138 139
}

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

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

165
    TRACE("(%p, %08X, %u);\n", wmcda, wmcda->dwTimeFormat, dwTime);
166

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

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

214
    TRACE("(%p, %08X, %u);\n", wmcda, tf, dwFrame);
215

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

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

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

275
    TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpOpenParms);
276

277
    if (lpOpenParms == NULL) 		return MCIERR_NULL_PARAMETER_BLOCK;
278
    if (wmcda == NULL)			return MCIERR_INVALID_DEVICE_ID;
279 280 281

    dwDeviceID = lpOpenParms->wDeviceID;

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

335
    wmcda->wNotifyDeviceID = dwDeviceID;
336
    wmcda->dwTimeFormat = MCI_FORMAT_MSF;
337

338
    /* now, open the handle */
339 340
    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);
341 342 343 344 345 346
    if (wmcda->handle != INVALID_HANDLE_VALUE)
        return 0;

 the_error:
    --wmcda->nUseCount;
    return ret;
347 348 349
}

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

356
    TRACE("(%04X, %08X, %p);\n", wDevID, dwParam, lpParms);
357

358
    if (wmcda == NULL) 	return MCIERR_INVALID_DEVICE_ID;
359

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

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

374
    TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
375 376 377 378

    if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;

    if (dwFlags & MCI_GETDEVCAPS_ITEM) {
379
	TRACE("MCI_GETDEVCAPS_ITEM dwItem=%08X;\n", lpParms->dwItem);
380

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

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

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

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

    return serial;
}
458

459

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

470
    TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
471

472 473 474 475
    if (lpParms == NULL || lpParms->lpstrReturn == NULL)
	return MCIERR_NULL_PARAMETER_BLOCK;
    if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;

476
    TRACE("buf=%p, len=%u\n", lpParms->lpstrReturn, lpParms->dwRetSize);
477

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

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

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

/**************************************************************************
516
 * 				MCICDA_Status			[internal]
517
 */
518
static DWORD MCICDA_Status(UINT wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
Alexandre Julliard's avatar
Alexandre Julliard committed
519
{
520 521 522 523 524 525 526
    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;
527

528
    TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
529

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

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
/**************************************************************************
 * 				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
717
/**************************************************************************
718
 * 				MCICDA_Play			[internal]
719
 */
720
static DWORD MCICDA_Play(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
Alexandre Julliard's avatar
Alexandre Julliard committed
721
{
722 723 724 725 726 727
    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;
728
    CDROM_TOC			toc;
729

730
    TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
731

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

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

792
    TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
793

794
    if (wmcda == NULL)	return MCIERR_INVALID_DEVICE_ID;
795

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

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

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

815
    TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
816

817
    if (wmcda == NULL)	return MCIERR_INVALID_DEVICE_ID;
818

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

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

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

838
    TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
839

840
    if (wmcda == NULL)	return MCIERR_INVALID_DEVICE_ID;
841

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

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

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

864
    TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
865

866
    if (wmcda == NULL)	return MCIERR_INVALID_DEVICE_ID;
867
    if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
868

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

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

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

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

923
    if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
924 925

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

930 931
    return 0;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
932 933

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

940
    TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
941

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

    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 ! */
952
    if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
953
    /*
954 955
      TRACE("dwTimeFormat=%08lX\n", lpParms->dwTimeFormat);
      TRACE("dwAudio=%08lX\n", lpParms->dwAudio);
956 957 958 959
    */
    if (dwFlags & MCI_SET_TIME_FORMAT) {
	switch (lpParms->dwTimeFormat) {
	case MCI_FORMAT_MILLISECONDS:
960
	    TRACE("MCI_FORMAT_MILLISECONDS !\n");
961 962
	    break;
	case MCI_FORMAT_MSF:
963
	    TRACE("MCI_FORMAT_MSF !\n");
964 965
	    break;
	case MCI_FORMAT_TMSF:
966
	    TRACE("MCI_FORMAT_TMSF !\n");
967 968
	    break;
	default:
969
	    WARN("bad time format !\n");
970 971
	    return MCIERR_BAD_TIME_FORMAT;
	}
972
	wmcda->dwTimeFormat = lpParms->dwTimeFormat;
973 974 975 976 977
    }
    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) {
978
	TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n",
979
	      lpParms->dwCallback);
980
	mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
981
			wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
982 983
    }
    return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
984 985 986
}

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

1005 1006 1007
    if (dwDevID == 0xFFFFFFFF) return MCIERR_UNSUPPORTED_FUNCTION;

    switch (wMsg) {
1008
    case MCI_OPEN_DRIVER:	return MCICDA_Open(dwDevID, dwParam1, (LPMCI_OPEN_PARMSW)dwParam2);
1009 1010
    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);
1011
    case MCI_INFO:		return MCICDA_Info(dwDevID, dwParam1, (LPMCI_INFO_PARMSW)dwParam2);
1012 1013 1014 1015 1016 1017 1018
    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);
1019 1020 1021 1022
    /* 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:
1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038
    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:
1039
	TRACE("Unsupported command [0x%x]\n", wMsg);
1040 1041 1042
	break;
    case MCI_OPEN:
    case MCI_CLOSE:
1043
	ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1044
	break;
1045
    default:
1046
	TRACE("Sending msg [0x%x] to default driver proc\n", wMsg);
1047
	return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1048
    }
1049
    return MCIERR_UNRECOGNIZED_COMMAND;
Alexandre Julliard's avatar
Alexandre Julliard committed
1050 1051 1052
}

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