mcicda.c 46 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"
40
#include "dsound.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
41

42
WINE_DEFAULT_DEBUG_CHANNEL(mcicda);
43

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

49 50 51 52 53 54 55 56
/* Defined by red-book standard; do not change! */
#define RAW_SECTOR_SIZE  (2352)

/* Must be >= RAW_SECTOR_SIZE */
#define CDDA_FRAG_SIZE   (32768)
/* Must be >= 2 */
#define CDDA_FRAG_COUNT  (3)

Alexandre Julliard's avatar
Alexandre Julliard committed
57
typedef struct {
58
    UINT		wDevID;
59
    int     		nUseCount;          /* Incremented for each shared open */
60
    BOOL  		fShareable;         /* TRUE if first open was shareable */
61
    WORD    		wNotifyDeviceID;    /* MCI device ID with a pending notification */
62
    HANDLE 		hCallback;          /* Callback handle for pending notification */
63
    DWORD		dwTimeFormat;
64
    HANDLE              handle;
65 66 67 68 69 70 71 72 73 74

    /* The following are used for digital playback only */
    HANDLE hThread;
    HANDLE stopEvent;
    DWORD start, end;

    IDirectSound *dsObj;
    IDirectSoundBuffer *dsBuf;

    CRITICAL_SECTION cs;
75
} WINE_MCICDAUDIO;
Alexandre Julliard's avatar
Alexandre Julliard committed
76 77 78

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

79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
typedef HRESULT(WINAPI*LPDIRECTSOUNDCREATE)(LPCGUID,LPDIRECTSOUND*,LPUNKNOWN);
static LPDIRECTSOUNDCREATE pDirectSoundCreate;

static DWORD CALLBACK MCICDA_playLoop(void *ptr)
{
    WINE_MCICDAUDIO *wmcda = (WINE_MCICDAUDIO*)ptr;
    DWORD lastPos, curPos, endPos, br;
    void *cdData;
    DWORD lockLen, fragLen;
    DSBCAPS caps;
    RAW_READ_INFO rdInfo;
    HRESULT hr = DS_OK;

    memset(&caps, 0, sizeof(caps));
    caps.dwSize = sizeof(caps);
    hr = IDirectSoundBuffer_GetCaps(wmcda->dsBuf, &caps);

    fragLen = caps.dwBufferBytes/CDDA_FRAG_COUNT;
    curPos = lastPos = 0;
    endPos = ~0u;
    while (SUCCEEDED(hr) && endPos != lastPos &&
           WaitForSingleObject(wmcda->stopEvent, 0) != WAIT_OBJECT_0) {
        hr = IDirectSoundBuffer_GetCurrentPosition(wmcda->dsBuf, &curPos, NULL);
        if ((curPos-lastPos+caps.dwBufferBytes)%caps.dwBufferBytes < fragLen) {
            Sleep(1);
            continue;
        }

        EnterCriticalSection(&wmcda->cs);
        rdInfo.DiskOffset.QuadPart = wmcda->start<<11;
        rdInfo.SectorCount = min(fragLen/RAW_SECTOR_SIZE, wmcda->end-wmcda->start);
        rdInfo.TrackMode = CDDA;

        hr = IDirectSoundBuffer_Lock(wmcda->dsBuf, lastPos, fragLen, &cdData, &lockLen, NULL, NULL, 0);
        if (hr == DSERR_BUFFERLOST) {
            if(FAILED(IDirectSoundBuffer_Restore(wmcda->dsBuf)) ||
               FAILED(IDirectSoundBuffer_Play(wmcda->dsBuf, 0, 0, DSBPLAY_LOOPING))) {
                LeaveCriticalSection(&wmcda->cs);
                break;
            }
            hr = IDirectSoundBuffer_Lock(wmcda->dsBuf, lastPos, fragLen, &cdData, &lockLen, NULL, NULL, 0);
        }

        if (SUCCEEDED(hr)) {
            if (rdInfo.SectorCount > 0) {
                if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_RAW_READ, &rdInfo, sizeof(rdInfo), cdData, lockLen, &br, NULL))
                    WARN("CD read failed at sector %d: 0x%x\n", wmcda->start, GetLastError());
            }
            if (rdInfo.SectorCount*RAW_SECTOR_SIZE < lockLen) {
                if(endPos == ~0u) endPos = lastPos;
                memset((BYTE*)cdData + rdInfo.SectorCount*RAW_SECTOR_SIZE, 0,
                       lockLen - rdInfo.SectorCount*RAW_SECTOR_SIZE);
            }
            hr = IDirectSoundBuffer_Unlock(wmcda->dsBuf, cdData, lockLen, NULL, 0);
        }

        lastPos += fragLen;
        lastPos %= caps.dwBufferBytes;
        wmcda->start += rdInfo.SectorCount;

        LeaveCriticalSection(&wmcda->cs);
    }
    IDirectSoundBuffer_Stop(wmcda->dsBuf);
    SetEvent(wmcda->stopEvent);

144 145 146 147 148 149 150 151 152 153
    EnterCriticalSection(&wmcda->cs);
    if (wmcda->hCallback) {
        mciDriverNotify(wmcda->hCallback, wmcda->wNotifyDeviceID,
                        FAILED(hr) ? MCI_NOTIFY_FAILURE :
                        ((endPos!=lastPos) ? MCI_NOTIFY_ABORTED :
                         MCI_NOTIFY_SUCCESSFUL));
        wmcda->hCallback = NULL;
    }
    LeaveCriticalSection(&wmcda->cs);

154 155 156 157 158 159
    ExitThread(0);
    return 0;
}



160
/**************************************************************************
161
 * 				MCICDA_drvOpen			[internal]
162
 */
163
static	DWORD	MCICDA_drvOpen(LPCWSTR str, LPMCI_OPEN_DRIVER_PARMSW modp)
164
{
165
    static HMODULE dsHandle;
166 167 168 169
    WINE_MCICDAUDIO*	wmcda;

    if (!modp) return 0xFFFFFFFF;

170
    wmcda = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCICDAUDIO));
171 172 173 174

    if (!wmcda)
	return 0;

175 176 177 178 179 180
    if (!dsHandle) {
        dsHandle = LoadLibraryA("dsound.dll");
        if(dsHandle)
            pDirectSoundCreate = (LPDIRECTSOUNDCREATE)GetProcAddress(dsHandle, "DirectSoundCreate");
    }

181
    wmcda->wDevID = modp->wDeviceID;
182
    mciSetDriverData(wmcda->wDevID, (DWORD_PTR)wmcda);
183
    modp->wCustomCommandTable = MCI_NO_COMMAND_TABLE;
184
    modp->wType = MCI_DEVTYPE_CD_AUDIO;
185
    InitializeCriticalSection(&wmcda->cs);
186
    return modp->wDeviceID;
187 188 189
}

/**************************************************************************
190
 * 				MCICDA_drvClose			[internal]
191
 */
192
static	DWORD	MCICDA_drvClose(DWORD dwDevID)
193
{
194
    WINE_MCICDAUDIO*  wmcda = (WINE_MCICDAUDIO*)mciGetDriverData(dwDevID);
195 196

    if (wmcda) {
197
	DeleteCriticalSection(&wmcda->cs);
198 199
	HeapFree(GetProcessHeap(), 0, wmcda);
	mciSetDriverData(dwDevID, 0);
200
    }
201
    return (dwDevID == 0xFFFFFFFF) ? 1 : 0;
202 203
}

204
/**************************************************************************
205
 * 				MCICDA_GetOpenDrv		[internal]
206
 */
207
static WINE_MCICDAUDIO*  MCICDA_GetOpenDrv(UINT wDevID)
208
{
209
    WINE_MCICDAUDIO*	wmcda = (WINE_MCICDAUDIO*)mciGetDriverData(wDevID);
210

211
    if (wmcda == NULL || wmcda->nUseCount == 0) {
212
	WARN("Invalid wDevID=%u\n", wDevID);
213 214
	return 0;
    }
215
    return wmcda;
216
}
Alexandre Julliard's avatar
Alexandre Julliard committed
217

Alexandre Julliard's avatar
Alexandre Julliard committed
218
/**************************************************************************
219
 * 				MCICDA_GetStatus		[internal]
220
 */
221
static	DWORD    MCICDA_GetStatus(WINE_MCICDAUDIO* wmcda)
Alexandre Julliard's avatar
Alexandre Julliard committed
222
{
223 224 225 226 227 228
    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;
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
    if(wmcda->hThread != 0) {
        DWORD status;
        HRESULT hr;

        hr = IDirectSoundBuffer_GetStatus(wmcda->dsBuf, &status);
        if(SUCCEEDED(hr)) {
            if(!(status&DSBSTATUS_PLAYING)) {
                if(WaitForSingleObject(wmcda->stopEvent, 0) == WAIT_OBJECT_0)
                    mode = MCI_MODE_STOP;
                else
                    mode = MCI_MODE_PAUSE;
            }
            else
                mode = MCI_MODE_PLAY;
        }
    }
    else if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
                              &data, sizeof(data), &br, NULL)) {
247
        if (GetLastError() == ERROR_NOT_READY) mode = MCI_MODE_OPEN;
248 249 250 251 252
    } 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;
253
        case AUDIO_STATUS_NO_STATUS:
254 255 256 257 258 259
        case AUDIO_STATUS_PLAY_COMPLETE:        mode = MCI_MODE_STOP;   break;
        case AUDIO_STATUS_PLAY_ERROR:
        case AUDIO_STATUS_NOT_SUPPORTED:
        default:
            break;
        }
260
    }
261
    return mode;
Alexandre Julliard's avatar
Alexandre Julliard committed
262 263 264
}

/**************************************************************************
265
 * 				MCICDA_GetError			[internal]
266
 */
267
static	int	MCICDA_GetError(WINE_MCICDAUDIO* wmcda)
Alexandre Julliard's avatar
Alexandre Julliard committed
268
{
269 270
    switch (GetLastError())
    {
271 272
    case ERROR_NOT_READY:     return MCIERR_DEVICE_NOT_READY;
    case ERROR_IO_DEVICE:     return MCIERR_HARDWARE;
273
    default:
274
	FIXME("Unknown mode %u\n", GetLastError());
275
    }
276
    return MCIERR_DRIVER_INTERNAL;
Alexandre Julliard's avatar
Alexandre Julliard committed
277 278
}

Alexandre Julliard's avatar
Alexandre Julliard committed
279
/**************************************************************************
280
 * 			MCICDA_CalcFrame			[internal]
281
 */
282
static DWORD MCICDA_CalcFrame(WINE_MCICDAUDIO* wmcda, DWORD dwTime)
Alexandre Julliard's avatar
Alexandre Julliard committed
283
{
284
    DWORD	dwFrame = 0;
285
    UINT	wTrack;
286 287 288
    CDROM_TOC   toc;
    DWORD       br;
    BYTE*       addr;
289

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

292
    switch (wmcda->dwTimeFormat) {
293
    case MCI_FORMAT_MILLISECONDS:
294
	dwFrame = ((dwTime - 1) * CDFRAMES_PERSEC + 500) / 1000;
295
	TRACE("MILLISECONDS %u\n", dwFrame);
296 297
	break;
    case MCI_FORMAT_MSF:
298
	TRACE("MSF %02u:%02u:%02u\n",
299
	      MCI_MSF_MINUTE(dwTime), MCI_MSF_SECOND(dwTime), MCI_MSF_FRAME(dwTime));
300 301 302 303
	dwFrame += CDFRAMES_PERMIN * MCI_MSF_MINUTE(dwTime);
	dwFrame += CDFRAMES_PERSEC * MCI_MSF_SECOND(dwTime);
	dwFrame += MCI_MSF_FRAME(dwTime);
	break;
304
    case MCI_FORMAT_TMSF:
305
    default: /* unknown format ! force TMSF ! ... */
306
	wTrack = MCI_TMSF_TRACK(dwTime);
307
        if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
308 309 310 311 312
                             &toc, sizeof(toc), &br, NULL))
            return 0;
        if (wTrack < toc.FirstTrack || wTrack > toc.LastTrack)
            return 0;
        TRACE("MSF %02u-%02u:%02u:%02u\n",
313
              MCI_TMSF_TRACK(dwTime), MCI_TMSF_MINUTE(dwTime),
314 315 316
              MCI_TMSF_SECOND(dwTime), MCI_TMSF_FRAME(dwTime));
        addr = toc.TrackData[wTrack - toc.FirstTrack].Address;
        TRACE("TMSF trackpos[%u]=%d:%d:%d\n",
317 318 319 320
              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);
321 322 323
	break;
    }
    return dwFrame;
Alexandre Julliard's avatar
Alexandre Julliard committed
324 325
}

Alexandre Julliard's avatar
Alexandre Julliard committed
326
/**************************************************************************
327
 * 			MCICDA_CalcTime				[internal]
328
 */
329
static DWORD MCICDA_CalcTime(WINE_MCICDAUDIO* wmcda, DWORD tf, DWORD dwFrame, LPDWORD lpRet)
Alexandre Julliard's avatar
Alexandre Julliard committed
330
{
331
    DWORD	dwTime = 0;
332 333 334 335
    UINT	wTrack;
    UINT	wMinutes;
    UINT	wSeconds;
    UINT	wFrames;
336 337
    CDROM_TOC   toc;
    DWORD       br;
338

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

341
    switch (tf) {
342
    case MCI_FORMAT_MILLISECONDS:
343
	dwTime = (dwFrame * 1000) / CDFRAMES_PERSEC + 1;
344
	TRACE("MILLISECONDS %u\n", dwTime);
345
	*lpRet = 0;
346 347 348 349
	break;
    case MCI_FORMAT_MSF:
	wMinutes = dwFrame / CDFRAMES_PERMIN;
	wSeconds = (dwFrame - CDFRAMES_PERMIN * wMinutes) / CDFRAMES_PERSEC;
350
	wFrames = dwFrame - CDFRAMES_PERMIN * wMinutes - CDFRAMES_PERSEC * wSeconds;
351
	dwTime = MCI_MAKE_MSF(wMinutes, wSeconds, wFrames);
352
	TRACE("MSF %02u:%02u:%02u -> dwTime=%u\n",
353
	      wMinutes, wSeconds, wFrames, dwTime);
354
	*lpRet = MCI_COLONIZED3_RETURN;
355
	break;
356
    case MCI_FORMAT_TMSF:
357
    default:	/* unknown format ! force TMSF ! ... */
358
        if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
359 360
                             &toc, sizeof(toc), &br, NULL))
            return 0;
361
	if (dwFrame < FRAME_OF_TOC(toc, toc.FirstTrack) ||
362
            dwFrame > FRAME_OF_TOC(toc, toc.LastTrack + 1)) {
363
            ERR("Out of range value %u [%u,%u]\n",
364
		dwFrame, FRAME_OF_TOC(toc, toc.FirstTrack),
365
                FRAME_OF_TOC(toc, toc.LastTrack + 1));
366 367 368
	    *lpRet = 0;
	    return 0;
	}
369 370
	for (wTrack = toc.FirstTrack; wTrack <= toc.LastTrack; wTrack++) {
	    if (FRAME_OF_TOC(toc, wTrack) > dwFrame)
371
		break;
372
	}
373 374
        wTrack--;
	dwFrame -= FRAME_OF_TOC(toc, wTrack);
375 376
	wMinutes = dwFrame / CDFRAMES_PERMIN;
	wSeconds = (dwFrame - CDFRAMES_PERMIN * wMinutes) / CDFRAMES_PERSEC;
377
	wFrames = dwFrame - CDFRAMES_PERMIN * wMinutes - CDFRAMES_PERSEC * wSeconds;
378
	dwTime = MCI_MAKE_TMSF(wTrack, wMinutes, wSeconds, wFrames);
379
	TRACE("%02u-%02u:%02u:%02u\n", wTrack, wMinutes, wSeconds, wFrames);
380
	*lpRet = MCI_COLONIZED4_RETURN;
381 382 383
	break;
    }
    return dwTime;
Alexandre Julliard's avatar
Alexandre Julliard committed
384 385
}

386 387
static DWORD MCICDA_Seek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms);
static DWORD MCICDA_Stop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms);
388 389

/**************************************************************************
390
 * 				MCICDA_Open			[internal]
391
 */
392
static DWORD MCICDA_Open(UINT wDevID, DWORD dwFlags, LPMCI_OPEN_PARMSW lpOpenParms)
393 394
{
    DWORD		dwDeviceID;
395
    DWORD               ret = MCIERR_HARDWARE;
396
    WINE_MCICDAUDIO* 	wmcda = (WINE_MCICDAUDIO*)mciGetDriverData(wDevID);
397
    WCHAR               root[7], drive = 0;
398
    unsigned int        count;
399

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

402
    if (lpOpenParms == NULL) 		return MCIERR_NULL_PARAMETER_BLOCK;
403
    if (wmcda == NULL)			return MCIERR_INVALID_DEVICE_ID;
404 405 406

    dwDeviceID = lpOpenParms->wDeviceID;

407 408
    if (wmcda->nUseCount > 0) {
	/* The driver is already open on this channel */
409 410
	/* If the driver was opened shareable before and this open specifies */
	/* shareable then increment the use count */
411 412
	if (wmcda->fShareable && (dwFlags & MCI_OPEN_SHAREABLE))
	    ++wmcda->nUseCount;
413 414 415
	else
	    return MCIERR_MUST_USE_SHAREABLE;
    } else {
416 417
	wmcda->nUseCount = 1;
	wmcda->fShareable = dwFlags & MCI_OPEN_SHAREABLE;
418 419
    }
    if (dwFlags & MCI_OPEN_ELEMENT) {
420
        if (dwFlags & MCI_OPEN_ELEMENT_ID) {
421
            WARN("MCI_OPEN_ELEMENT_ID %p! Abort\n", lpOpenParms->lpstrElementName);
422 423
            return MCIERR_NO_ELEMENT_ALLOWED;
        }
424
        TRACE("MCI_OPEN_ELEMENT element name: %s\n", debugstr_w(lpOpenParms->lpstrElementName));
425
        if (!isalpha(lpOpenParms->lpstrElementName[0]) || lpOpenParms->lpstrElementName[1] != ':' ||
426
            (lpOpenParms->lpstrElementName[2] && lpOpenParms->lpstrElementName[2] != '\\'))
427
        {
428 429
            WARN("MCI_OPEN_ELEMENT unsupported format: %s\n", 
                 debugstr_w(lpOpenParms->lpstrElementName));
430
            ret = MCIERR_NO_ELEMENT_ALLOWED;
431 432 433
            goto the_error;
        }
        drive = toupper(lpOpenParms->lpstrElementName[0]);
434 435
        root[0] = drive; root[1] = ':'; root[2] = '\\'; root[3] = '\0';
        if (GetDriveTypeW(root) != DRIVE_CDROM)
436 437 438 439 440 441 442 443
        {
            ret = MCIERR_INVALID_DEVICE_NAME;
            goto the_error;
        }
    }
    else
    {
        /* drive letter isn't passed... get the dwDeviceID'th cdrom in the system */
444
        root[0] = 'A'; root[1] = ':'; root[2] = '\\'; root[3] = '\0';
445 446
        for (count = 0; root[0] <= 'Z'; root[0]++)
        {
447
            if (GetDriveTypeW(root) == DRIVE_CDROM && ++count >= dwDeviceID)
448 449 450 451 452 453 454 455 456 457
            {
                drive = root[0];
                break;
            }
        }
        if (!drive)
        {
            ret = MCIERR_INVALID_DEVICE_ID;
            goto the_error;
        }
458 459
    }

460
    wmcda->wNotifyDeviceID = dwDeviceID;
461
    wmcda->dwTimeFormat = MCI_FORMAT_MSF;
462

463
    /* now, open the handle */
464 465
    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);
466
    if (wmcda->handle != INVALID_HANDLE_VALUE)
467 468 469 470 471
        return 0;

 the_error:
    --wmcda->nUseCount;
    return ret;
472 473 474
}

/**************************************************************************
475
 * 				MCICDA_Close			[internal]
476
 */
477
static DWORD MCICDA_Close(UINT wDevID, DWORD dwParam, LPMCI_GENERIC_PARMS lpParms)
478
{
479
    WINE_MCICDAUDIO*	wmcda = MCICDA_GetOpenDrv(wDevID);
480

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

483
    if (wmcda == NULL) 	return MCIERR_INVALID_DEVICE_ID;
484

485 486
    if (--wmcda->nUseCount == 0) {
	CloseHandle(wmcda->handle);
487 488 489 490 491
    }
    return 0;
}

/**************************************************************************
492
 * 				MCICDA_GetDevCaps		[internal]
493
 */
494
static DWORD MCICDA_GetDevCaps(UINT wDevID, DWORD dwFlags,
495 496
				   LPMCI_GETDEVCAPS_PARMS lpParms)
{
497 498
    DWORD	ret = 0;

499
    TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
500 501 502 503

    if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;

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

506
	switch (lpParms->dwItem) {
507
	case MCI_GETDEVCAPS_CAN_RECORD:
508 509
	    lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
	    ret = MCI_RESOURCE_RETURNED;
510 511
	    break;
	case MCI_GETDEVCAPS_HAS_AUDIO:
512 513
	    lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
	    ret = MCI_RESOURCE_RETURNED;
514 515
	    break;
	case MCI_GETDEVCAPS_HAS_VIDEO:
516 517
	    lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
	    ret = MCI_RESOURCE_RETURNED;
518 519
	    break;
	case MCI_GETDEVCAPS_DEVICE_TYPE:
520 521
	    lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_CD_AUDIO, MCI_DEVTYPE_CD_AUDIO);
	    ret = MCI_RESOURCE_RETURNED;
522 523
	    break;
	case MCI_GETDEVCAPS_USES_FILES:
524 525
	    lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
	    ret = MCI_RESOURCE_RETURNED;
526 527
	    break;
	case MCI_GETDEVCAPS_COMPOUND_DEVICE:
528 529
	    lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
	    ret = MCI_RESOURCE_RETURNED;
530 531
	    break;
	case MCI_GETDEVCAPS_CAN_EJECT:
532 533
	    lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
	    ret = MCI_RESOURCE_RETURNED;
534 535
	    break;
	case MCI_GETDEVCAPS_CAN_PLAY:
536 537
	    lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
	    ret = MCI_RESOURCE_RETURNED;
538 539
	    break;
	case MCI_GETDEVCAPS_CAN_SAVE:
540 541
	    lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
	    ret = MCI_RESOURCE_RETURNED;
542 543
	    break;
	default:
544
            ERR("Unsupported %x devCaps item\n", lpParms->dwItem);
545 546
	    return MCIERR_UNRECOGNIZED_COMMAND;
	}
547 548 549
    } else {
	TRACE("No GetDevCaps-Item !\n");
	return MCIERR_UNRECOGNIZED_COMMAND;
550
    }
551
    TRACE("lpParms->dwReturn=%08X;\n", lpParms->dwReturn);
552
    return ret;
553 554
}

555 556 557 558 559 560
static DWORD CDROM_Audio_GetSerial(CDROM_TOC* toc)
{
    unsigned long serial = 0;
    int i;
    WORD wMagic;
    DWORD dwStart, dwEnd;
561

562 563 564 565 566 567 568 569 570 571 572
    /*
     * 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++) {
573
        serial += (toc->TrackData[i].Address[1] << 16) |
574 575 576
            (toc->TrackData[i].Address[2] << 8) | toc->TrackData[i].Address[3];
    }
    dwEnd = FRAME_OF_TOC(*toc, toc->LastTrack + 1);
577

578 579 580 581 582
    if (toc->LastTrack - toc->FirstTrack + 1 < 3)
        serial += wMagic + (dwEnd - dwStart);

    return serial;
}
583

584

585
/**************************************************************************
586
 * 				MCICDA_Info			[internal]
587
 */
588
static DWORD MCICDA_Info(UINT wDevID, DWORD dwFlags, LPMCI_INFO_PARMSW lpParms)
589
{
590
    LPCWSTR		str = NULL;
591
    WINE_MCICDAUDIO*	wmcda = MCICDA_GetOpenDrv(wDevID);
592
    DWORD		ret = 0;
593
    WCHAR		buffer[16];
594

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

597 598 599 600
    if (lpParms == NULL || lpParms->lpstrReturn == NULL)
	return MCIERR_NULL_PARAMETER_BLOCK;
    if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;

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

603
    if (dwFlags & MCI_INFO_PRODUCT) {
604 605
        static const WCHAR wszAudioCd[] = {'W','i','n','e','\'','s',' ','a','u','d','i','o',' ','C','D',0};
        str = wszAudioCd;
606 607 608
    } else if (dwFlags & MCI_INFO_MEDIA_UPC) {
	ret = MCIERR_NO_IDENTITY;
    } else if (dwFlags & MCI_INFO_MEDIA_IDENTITY) {
609 610 611
	DWORD	    res = 0;
        CDROM_TOC   toc;
        DWORD       br;
612
	static const WCHAR wszLu[] = {'%','l','u',0};
613

614
        if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
615
                             &toc, sizeof(toc), &br, NULL)) {
616
	    return MCICDA_GetError(wmcda);
617
	}
618 619

	res = CDROM_Audio_GetSerial(&toc);
620
	sprintfW(buffer, wszLu, res);
621 622
	str = buffer;
    } else {
623
	WARN("Don't know this info command (%u)\n", dwFlags);
624
	ret = MCIERR_UNRECOGNIZED_COMMAND;
625
    }
626
    if (str) {
627 628
	if (lpParms->dwRetSize <= strlenW(str)) {
	    lstrcpynW(lpParms->lpstrReturn, str, lpParms->dwRetSize - 1);
629 630
	    ret = MCIERR_PARAM_OVERFLOW;
	} else {
631
	    strcpyW(lpParms->lpstrReturn, str);
632
	}
633 634 635
    } else {
	*lpParms->lpstrReturn = 0;
    }
636
    TRACE("=> %s (%d)\n", debugstr_w(lpParms->lpstrReturn), ret);
637
    return ret;
638
}
Alexandre Julliard's avatar
Alexandre Julliard committed
639 640

/**************************************************************************
641
 * 				MCICDA_Status			[internal]
642
 */
643
static DWORD MCICDA_Status(UINT wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
Alexandre Julliard's avatar
Alexandre Julliard committed
644
{
645 646 647 648 649 650
    WINE_MCICDAUDIO*	        wmcda = MCICDA_GetOpenDrv(wDevID);
    DWORD	                ret = 0;
    CDROM_SUB_Q_DATA_FORMAT     fmt;
    SUB_Q_CHANNEL_DATA          data;
    CDROM_TOC                   toc;
    DWORD                       br;
651

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

654 655 656 657 658
    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);
659
	mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
660
			wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
661 662
    }
    if (dwFlags & MCI_STATUS_ITEM) {
663
	TRACE("dwItem = %x\n", lpParms->dwItem);
664 665
	switch (lpParms->dwItem) {
	case MCI_STATUS_CURRENT_TRACK:
666
            fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
667
            if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
668 669
                                 &data, sizeof(data), &br, NULL))
            {
670
		return MCICDA_GetError(wmcda);
671
	    }
672
	    lpParms->dwReturn = data.CurrentPosition.TrackNumber;
673
            TRACE("CURRENT_TRACK=%lu\n", lpParms->dwReturn);
674 675
	    break;
	case MCI_STATUS_LENGTH:
676
            if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
677 678 679
                                 &toc, sizeof(toc), &br, NULL)) {
                WARN("error reading TOC !\n");
                return MCICDA_GetError(wmcda);
680 681
	    }
	    if (dwFlags & MCI_TRACK) {
682
		TRACE("MCI_TRACK #%u LENGTH=??? !\n", lpParms->dwTrack);
683
		if (lpParms->dwTrack < toc.FirstTrack || lpParms->dwTrack > toc.LastTrack)
684
		    return MCIERR_OUTOFRANGE;
685
		lpParms->dwReturn = FRAME_OF_TOC(toc, lpParms->dwTrack + 1) -
686
                    FRAME_OF_TOC(toc, lpParms->dwTrack);
687 688
		/* Windows returns one frame less than the total track length for the
		   last track on the CD.  See CDDB HOWTO.  Verified on Win95OSR2. */
689
		if (lpParms->dwTrack == toc.LastTrack)
690
		    lpParms->dwReturn--;
691
	    } else {
692 693 694
		/* 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. */
695 696
		lpParms->dwReturn = FRAME_OF_TOC(toc, toc.LastTrack + 1) -
                    FRAME_OF_TOC(toc, toc.FirstTrack) - 1;
697
	    }
698 699
	    lpParms->dwReturn = MCICDA_CalcTime(wmcda,
						 (wmcda->dwTimeFormat == MCI_FORMAT_TMSF)
700 701 702
						    ? MCI_FORMAT_MSF : wmcda->dwTimeFormat,
						 lpParms->dwReturn,
						 &ret);
703
            TRACE("LENGTH=%lu\n", lpParms->dwReturn);
704 705
	    break;
	case MCI_STATUS_MODE:
706
            lpParms->dwReturn = MCICDA_GetStatus(wmcda);
707
            TRACE("MCI_STATUS_MODE=%08lX\n", lpParms->dwReturn);
708 709 710 711
	    lpParms->dwReturn = MAKEMCIRESOURCE(lpParms->dwReturn, lpParms->dwReturn);
	    ret = MCI_RESOURCE_RETURNED;
	    break;
	case MCI_STATUS_MEDIA_PRESENT:
712
	    lpParms->dwReturn = (MCICDA_GetStatus(wmcda) == MCI_MODE_OPEN) ?
713
		MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
714
	    TRACE("MCI_STATUS_MEDIA_PRESENT =%c!\n", LOWORD(lpParms->dwReturn) ? 'Y' : 'N');
715 716 717
	    ret = MCI_RESOURCE_RETURNED;
	    break;
	case MCI_STATUS_NUMBER_OF_TRACKS:
718
            if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
719 720 721 722 723
                                 &toc, sizeof(toc), &br, NULL)) {
                WARN("error reading TOC !\n");
                return MCICDA_GetError(wmcda);
	    }
	    lpParms->dwReturn = toc.LastTrack - toc.FirstTrack + 1;
724
            TRACE("MCI_STATUS_NUMBER_OF_TRACKS = %lu\n", lpParms->dwReturn);
725
	    if (lpParms->dwReturn == (WORD)-1)
726
		return MCICDA_GetError(wmcda);
727 728 729
	    break;
	case MCI_STATUS_POSITION:
	    if (dwFlags & MCI_STATUS_START) {
730
                if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
731 732 733 734 735
                                     &toc, sizeof(toc), &br, NULL)) {
                    WARN("error reading TOC !\n");
                    return MCICDA_GetError(wmcda);
                }
		lpParms->dwReturn = FRAME_OF_TOC(toc, toc.FirstTrack);
736
		TRACE("get MCI_STATUS_START !\n");
737
	    } else if (dwFlags & MCI_TRACK) {
738
                if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
739 740 741 742 743
                                     &toc, sizeof(toc), &br, NULL)) {
                    WARN("error reading TOC !\n");
                    return MCICDA_GetError(wmcda);
                }
		if (lpParms->dwTrack < toc.FirstTrack || lpParms->dwTrack > toc.LastTrack)
744
		    return MCIERR_OUTOFRANGE;
745
		lpParms->dwReturn = FRAME_OF_TOC(toc, lpParms->dwTrack);
746
		TRACE("get MCI_TRACK #%u !\n", lpParms->dwTrack);
747 748
            } else {
                fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
749
                if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
750 751 752 753 754
                                     &data, sizeof(data), &br, NULL)) {
                    return MCICDA_GetError(wmcda);
                }
                lpParms->dwReturn = FRAME_OF_ADDR(data.CurrentPosition.AbsoluteAddress);
            }
755
	    lpParms->dwReturn = MCICDA_CalcTime(wmcda, wmcda->dwTimeFormat, lpParms->dwReturn, &ret);
756
            TRACE("MCI_STATUS_POSITION=%08lX\n", lpParms->dwReturn);
757 758 759
	    break;
	case MCI_STATUS_READY:
	    TRACE("MCI_STATUS_READY !\n");
760 761 762 763 764 765 766 767 768 769
            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;
            }
770 771 772 773
	    TRACE("MCI_STATUS_READY=%u!\n", LOWORD(lpParms->dwReturn));
	    ret = MCI_RESOURCE_RETURNED;
	    break;
	case MCI_STATUS_TIME_FORMAT:
774
	    lpParms->dwReturn = MAKEMCIRESOURCE(wmcda->dwTimeFormat, MCI_FORMAT_RETURN_BASE + wmcda->dwTimeFormat);
775 776 777
	    TRACE("MCI_STATUS_TIME_FORMAT=%08x!\n", LOWORD(lpParms->dwReturn));
	    ret = MCI_RESOURCE_RETURNED;
	    break;
778
	case 4001: /* FIXME: for bogus FullCD */
779
	case MCI_CDA_STATUS_TYPE_TRACK:
780
	    if (!(dwFlags & MCI_TRACK))
781
		ret = MCIERR_MISSING_PARAMETER;
782
	    else {
783
                if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
784 785 786 787 788
                                     &toc, sizeof(toc), &br, NULL)) {
                    WARN("error reading TOC !\n");
                    return MCICDA_GetError(wmcda);
                }
		if (lpParms->dwTrack < toc.FirstTrack || lpParms->dwTrack > toc.LastTrack)
789 790
		    ret = MCIERR_OUTOFRANGE;
		else
791 792
		    lpParms->dwReturn = (toc.TrackData[lpParms->dwTrack - toc.FirstTrack].Control & 0x04) ?
                                         MCI_CDA_TRACK_OTHER : MCI_CDA_TRACK_AUDIO;
793
	    }
794
            TRACE("MCI_CDA_STATUS_TYPE_TRACK[%d]=%ld\n", lpParms->dwTrack, lpParms->dwReturn);
795 796
	    break;
	default:
797
            FIXME("unknown command %08X !\n", lpParms->dwItem);
798
	    return MCIERR_UNRECOGNIZED_COMMAND;
799
	}
800 801
    } else {
	WARN("not MCI_STATUS_ITEM !\n");
802
    }
803
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
804 805
}

806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839
/**************************************************************************
 * 				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
840
/**************************************************************************
841
 * 				MCICDA_Play			[internal]
842
 */
843
static DWORD MCICDA_Play(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
Alexandre Julliard's avatar
Alexandre Julliard committed
844
{
845 846 847 848 849 850
    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;
851
    CDROM_TOC			toc;
852

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

855 856 857 858 859 860
    if (lpParms == NULL)
	return MCIERR_NULL_PARAMETER_BLOCK;

    if (wmcda == NULL)
	return MCIERR_INVALID_DEVICE_ID;

861 862 863 864 865 866
    if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
                         &toc, sizeof(toc), &br, NULL)) {
        WARN("error reading TOC !\n");
        return MCICDA_GetError(wmcda);
    }

867 868
    if (dwFlags & MCI_FROM) {
	start = MCICDA_CalcFrame(wmcda, lpParms->dwFrom);
869 870
	if ( (ret=MCICDA_SkipDataTracks(wmcda, &start)) )
	  return ret;
871
	TRACE("MCI_FROM=%08X -> %u\n", lpParms->dwFrom, start);
872
    } else {
873
        fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
874
        if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
875 876 877 878
                             &data, sizeof(data), &br, NULL)) {
            return MCICDA_GetError(wmcda);
        }
        start = FRAME_OF_ADDR(data.CurrentPosition.AbsoluteAddress);
879 880
	if ( (ret=MCICDA_SkipDataTracks(wmcda, &start)) )
	  return ret;
881 882 883
    }
    if (dwFlags & MCI_TO) {
	end = MCICDA_CalcFrame(wmcda, lpParms->dwTo);
884
	TRACE("MCI_TO=%08X -> %u\n", lpParms->dwTo, end);
885 886
    } else {
	end = FRAME_OF_TOC(toc, toc.LastTrack + 1) - 1;
887
    }
888
    TRACE("Playing from %u to %u\n", start, end);
889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904

    if (wmcda->hThread != 0) {
        SetEvent(wmcda->stopEvent);
        WaitForSingleObject(wmcda->hThread, INFINITE);

        CloseHandle(wmcda->hThread);
        wmcda->hThread = 0;
        CloseHandle(wmcda->stopEvent);
        wmcda->stopEvent = 0;

        IDirectSoundBuffer_Stop(wmcda->dsBuf);
        IDirectSoundBuffer_Release(wmcda->dsBuf);
        wmcda->dsBuf = NULL;
        IDirectSound_Release(wmcda->dsObj);
        wmcda->dsObj = NULL;
    }
905 906 907 908 909 910 911 912
    else if(wmcda->hCallback) {
        mciDriverNotify(wmcda->hCallback, wmcda->wNotifyDeviceID,
                        MCI_NOTIFY_ABORTED);
        wmcda->hCallback = NULL;
    }

    if ((dwFlags&MCI_NOTIFY))
        wmcda->hCallback = HWND_32(LOWORD(lpParms->dwCallback));
913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995

    if (pDirectSoundCreate) {
        WAVEFORMATEX format;
        DSBUFFERDESC desc;
        DWORD lockLen;
        void *cdData;
        HRESULT hr;

        hr = pDirectSoundCreate(NULL, &wmcda->dsObj, NULL);
        if (SUCCEEDED(hr)) {
            IDirectSound_SetCooperativeLevel(wmcda->dsObj, GetDesktopWindow(), DSSCL_PRIORITY);

            /* The "raw" frame is relative to the start of the first track */
            wmcda->start = start - FRAME_OF_TOC(toc, toc.FirstTrack);
            wmcda->end = end - FRAME_OF_TOC(toc, toc.FirstTrack);

            memset(&format, 0, sizeof(format));
            format.wFormatTag = WAVE_FORMAT_PCM;
            format.nChannels = 2;
            format.nSamplesPerSec = 44100;
            format.wBitsPerSample = 16;
            format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8;
            format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;
            format.cbSize = 0;

            memset(&desc, 0, sizeof(desc));
            desc.dwSize = sizeof(desc);
            desc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS;
            desc.dwBufferBytes = (CDDA_FRAG_SIZE - (CDDA_FRAG_SIZE%RAW_SECTOR_SIZE)) * CDDA_FRAG_COUNT;
            desc.lpwfxFormat = &format;

            hr = IDirectSound_CreateSoundBuffer(wmcda->dsObj, &desc, &wmcda->dsBuf, NULL);
        }
        if (SUCCEEDED(hr)) {
            hr = IDirectSoundBuffer_Lock(wmcda->dsBuf, 0, 0, &cdData, &lockLen,
                                         NULL, NULL, DSBLOCK_ENTIREBUFFER);
        }
        if (SUCCEEDED(hr)) {
            RAW_READ_INFO rdInfo;
            int readok;

            rdInfo.DiskOffset.QuadPart = wmcda->start<<11;
            rdInfo.SectorCount = min(desc.dwBufferBytes/RAW_SECTOR_SIZE,
                                     wmcda->end-wmcda->start);
            rdInfo.TrackMode = CDDA;

            readok = DeviceIoControl(wmcda->handle, IOCTL_CDROM_RAW_READ,
                                     &rdInfo, sizeof(rdInfo), cdData, lockLen,
                                     &br, NULL);
            IDirectSoundBuffer_Unlock(wmcda->dsBuf, cdData, lockLen, NULL, 0);

            if (readok) {
                wmcda->start += rdInfo.SectorCount;
                wmcda->stopEvent = CreateEventA(NULL, TRUE, FALSE, NULL);
            }
            if (wmcda->stopEvent != 0)
                wmcda->hThread = CreateThread(NULL, 0, MCICDA_playLoop, wmcda, 0, &br);
            if (wmcda->hThread != 0) {
                hr = IDirectSoundBuffer_Play(wmcda->dsBuf, 0, 0, DSBPLAY_LOOPING);
                if (SUCCEEDED(hr))
                    return ret;

                SetEvent(wmcda->stopEvent);
                WaitForSingleObject(wmcda->hThread, INFINITE);
                CloseHandle(wmcda->hThread);
                wmcda->hThread = 0;
            }
        }

        if (wmcda->stopEvent != 0) {
            CloseHandle(wmcda->stopEvent);
            wmcda->stopEvent = 0;
        }
        if (wmcda->dsBuf) {
            IDirectSoundBuffer_Release(wmcda->dsBuf);
            wmcda->dsBuf = NULL;
        }
        if (wmcda->dsObj) {
            IDirectSound_Release(wmcda->dsObj);
            wmcda->dsObj = NULL;
        }
    }

996 997 998 999 1000 1001
    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;
1002
    if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_PLAY_AUDIO_MSF, &play, sizeof(play),
1003
                         NULL, 0, &br, NULL)) {
1004
	wmcda->hCallback = NULL;
1005 1006
	ret = MCIERR_HARDWARE;
    } else if (dwFlags & MCI_NOTIFY) {
1007 1008
	TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
	/*
1009
	  mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1010 1011
	  wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
	*/
1012 1013
    }
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
1014 1015 1016
}

/**************************************************************************
1017
 * 				MCICDA_Stop			[internal]
1018
 */
1019
static DWORD MCICDA_Stop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
Alexandre Julliard's avatar
Alexandre Julliard committed
1020
{
1021
    WINE_MCICDAUDIO*	wmcda = MCICDA_GetOpenDrv(wDevID);
1022 1023
    DWORD               br;

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

1026
    if (wmcda == NULL)	return MCIERR_INVALID_DEVICE_ID;
1027

1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043
    if (wmcda->hThread != 0) {
        SetEvent(wmcda->stopEvent);
        WaitForSingleObject(wmcda->hThread, INFINITE);

        CloseHandle(wmcda->hThread);
        wmcda->hThread = 0;
        CloseHandle(wmcda->stopEvent);
        wmcda->stopEvent = 0;

        IDirectSoundBuffer_Release(wmcda->dsBuf);
        wmcda->dsBuf = NULL;
        IDirectSound_Release(wmcda->dsObj);
        wmcda->dsObj = NULL;
    }
    else if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_STOP_AUDIO, NULL, 0, NULL, 0, &br, NULL))
        return MCIERR_HARDWARE;
1044

1045 1046 1047 1048 1049
    if (wmcda->hCallback) {
        mciDriverNotify(wmcda->hCallback, wmcda->wNotifyDeviceID, MCI_NOTIFY_ABORTED);
        wmcda->hCallback = NULL;
    }

1050
    if (lpParms && (dwFlags & MCI_NOTIFY)) {
1051
	TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
1052
	mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1053
			wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
1054 1055
    }
    return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1056 1057 1058
}

/**************************************************************************
1059
 * 				MCICDA_Pause			[internal]
1060
 */
1061
static DWORD MCICDA_Pause(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
Alexandre Julliard's avatar
Alexandre Julliard committed
1062
{
1063
    WINE_MCICDAUDIO*	wmcda = MCICDA_GetOpenDrv(wDevID);
1064 1065
    DWORD               br;

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

1068
    if (wmcda == NULL)	return MCIERR_INVALID_DEVICE_ID;
1069

1070 1071 1072 1073 1074 1075 1076 1077
    if (wmcda->hThread != 0) {
        /* Don't bother calling stop if the playLoop thread has already stopped */
        if(WaitForSingleObject(wmcda->stopEvent, 0) != WAIT_OBJECT_0 &&
           FAILED(IDirectSoundBuffer_Stop(wmcda->dsBuf)))
            return MCIERR_HARDWARE;
    }
    else if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_PAUSE_AUDIO, NULL, 0, NULL, 0, &br, NULL))
        return MCIERR_HARDWARE;
1078

1079 1080 1081 1082 1083 1084 1085
    EnterCriticalSection(&wmcda->cs);
    if (wmcda->hCallback) {
        mciDriverNotify(wmcda->hCallback, wmcda->wNotifyDeviceID, MCI_NOTIFY_SUPERSEDED);
        wmcda->hCallback = NULL;
    }
    LeaveCriticalSection(&wmcda->cs);

1086
    if (lpParms && (dwFlags & MCI_NOTIFY)) {
1087
        TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
1088
	mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1089
			wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
1090 1091
    }
    return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1092 1093 1094
}

/**************************************************************************
1095
 * 				MCICDA_Resume			[internal]
1096
 */
1097
static DWORD MCICDA_Resume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
Alexandre Julliard's avatar
Alexandre Julliard committed
1098
{
1099
    WINE_MCICDAUDIO*	wmcda = MCICDA_GetOpenDrv(wDevID);
1100
    DWORD               br;
1101

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

1104
    if (wmcda == NULL)	return MCIERR_INVALID_DEVICE_ID;
1105

1106 1107 1108 1109 1110 1111 1112 1113
    if (wmcda->hThread != 0) {
        /* Don't restart if the playLoop thread has already stopped */
        if(WaitForSingleObject(wmcda->stopEvent, 0) != WAIT_OBJECT_0 &&
           FAILED(IDirectSoundBuffer_Play(wmcda->dsBuf, 0, 0, DSBPLAY_LOOPING)))
            return MCIERR_HARDWARE;
    }
    else if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_RESUME_AUDIO, NULL, 0, NULL, 0, &br, NULL))
        return MCIERR_HARDWARE;
1114

1115
    if (lpParms && (dwFlags & MCI_NOTIFY)) {
1116
	TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
1117
	mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1118
			wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
1119 1120
    }
    return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1121 1122 1123
}

/**************************************************************************
1124
 * 				MCICDA_Seek			[internal]
1125
 */
1126
static DWORD MCICDA_Seek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
Alexandre Julliard's avatar
Alexandre Julliard committed
1127
{
1128 1129 1130
    DWORD		        at;
    WINE_MCICDAUDIO*	        wmcda = MCICDA_GetOpenDrv(wDevID);
    CDROM_SEEK_AUDIO_MSF        seek;
1131 1132
    DWORD                       br, ret;
    CDROM_TOC			toc;
1133

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

1136
    if (wmcda == NULL)	return MCIERR_INVALID_DEVICE_ID;
1137
    if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1138

1139 1140 1141 1142 1143
    if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
                         &toc, sizeof(toc), &br, NULL)) {
        WARN("error reading TOC !\n");
        return MCICDA_GetError(wmcda);
    }
1144
    switch (dwFlags & ~(MCI_NOTIFY|MCI_WAIT)) {
1145
    case MCI_SEEK_TO_START:
1146
	TRACE("Seeking to start\n");
1147 1148 1149
	at = FRAME_OF_TOC(toc,toc.FirstTrack);
	if ( (ret=MCICDA_SkipDataTracks(wmcda, &at)) )
	  return ret;
1150 1151
	break;
    case MCI_SEEK_TO_END:
1152
	TRACE("Seeking to end\n");
1153
	at = FRAME_OF_TOC(toc, toc.LastTrack + 1) - 1;
1154 1155
	if ( (ret=MCICDA_SkipDataTracks(wmcda, &at)) )
	  return ret;
1156 1157
	break;
    case MCI_TO:
1158
	TRACE("Seeking to %u\n", lpParms->dwTo);
1159 1160 1161
        at = MCICDA_CalcFrame(wmcda, lpParms->dwTo);
	if ( (ret=MCICDA_SkipDataTracks(wmcda, &at)) )
	  return ret;
1162
	break;
1163
    default:
Jeff Garzik's avatar
Jeff Garzik committed
1164 1165
	TRACE("Unknown seek action %08lX\n",
	      (dwFlags & ~(MCI_NOTIFY|MCI_WAIT)));
1166
	return MCIERR_UNSUPPORTED_FUNCTION;
1167
    }
1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182

    if (wmcda->hThread != 0) {
        EnterCriticalSection(&wmcda->cs);
        wmcda->start = at - FRAME_OF_TOC(toc, toc.FirstTrack);
        /* Flush remaining data, or just let it play into the new data? */
        LeaveCriticalSection(&wmcda->cs);
    }
    else {
        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;
    }
1183

1184
    if (dwFlags & MCI_NOTIFY) {
1185
	TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
1186
	mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1187
			  wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
1188
    }
1189
    return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1190 1191
}

1192
/**************************************************************************
1193
 * 				MCICDA_SetDoor			[internal]
1194
 */
1195
static DWORD	MCICDA_SetDoor(UINT wDevID, BOOL open)
1196
{
1197
    WINE_MCICDAUDIO*	wmcda = MCICDA_GetOpenDrv(wDevID);
1198 1199
    DWORD               br;

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

1202
    if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
1203 1204

    if (!DeviceIoControl(wmcda->handle,
1205 1206
                         (open) ? IOCTL_STORAGE_EJECT_MEDIA : IOCTL_STORAGE_LOAD_MEDIA,
                         NULL, 0, NULL, 0, &br, NULL))
1207
	return MCIERR_HARDWARE;
1208

1209 1210
    return 0;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1211 1212

/**************************************************************************
1213
 * 				MCICDA_Set			[internal]
1214
 */
1215
static DWORD MCICDA_Set(UINT wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
Alexandre Julliard's avatar
Alexandre Julliard committed
1216
{
1217
    WINE_MCICDAUDIO*	wmcda = MCICDA_GetOpenDrv(wDevID);
1218

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

1221
    if (wmcda == NULL)	return MCIERR_INVALID_DEVICE_ID;
1222 1223 1224 1225 1226 1227 1228 1229 1230

    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 ! */
1231
    if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1232
    /*
1233 1234
      TRACE("dwTimeFormat=%08lX\n", lpParms->dwTimeFormat);
      TRACE("dwAudio=%08lX\n", lpParms->dwAudio);
1235 1236 1237 1238
    */
    if (dwFlags & MCI_SET_TIME_FORMAT) {
	switch (lpParms->dwTimeFormat) {
	case MCI_FORMAT_MILLISECONDS:
1239
	    TRACE("MCI_FORMAT_MILLISECONDS !\n");
1240 1241
	    break;
	case MCI_FORMAT_MSF:
1242
	    TRACE("MCI_FORMAT_MSF !\n");
1243 1244
	    break;
	case MCI_FORMAT_TMSF:
1245
	    TRACE("MCI_FORMAT_TMSF !\n");
1246 1247
	    break;
	default:
1248
	    WARN("bad time format !\n");
1249 1250
	    return MCIERR_BAD_TIME_FORMAT;
	}
1251
	wmcda->dwTimeFormat = lpParms->dwTimeFormat;
1252 1253 1254 1255 1256
    }
    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) {
1257
	TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n",
1258
	      lpParms->dwCallback);
1259
	mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1260
			wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
1261 1262
    }
    return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1263 1264 1265
}

/**************************************************************************
Patrik Stridvall's avatar
Patrik Stridvall committed
1266
 * 			DriverProc (MCICDA.@)
1267
 */
1268 1269
LRESULT CALLBACK MCICDA_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
                                   LPARAM dwParam1, LPARAM dwParam2)
1270 1271 1272 1273
{
    switch(wMsg) {
    case DRV_LOAD:		return 1;
    case DRV_FREE:		return 1;
1274
    case DRV_OPEN:		return MCICDA_drvOpen((LPCWSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSW)dwParam2);
1275
    case DRV_CLOSE:		return MCICDA_drvClose(dwDevID);
1276
    case DRV_ENABLE:		return 1;
1277 1278
    case DRV_DISABLE:		return 1;
    case DRV_QUERYCONFIGURE:	return 1;
1279
    case DRV_CONFIGURE:		MessageBoxA(0, "MCI audio CD driver !", "Wine Driver", MB_OK); return 1;
1280 1281
    case DRV_INSTALL:		return DRVCNF_RESTART;
    case DRV_REMOVE:		return DRVCNF_RESTART;
1282
    }
1283

1284 1285 1286
    if (dwDevID == 0xFFFFFFFF) return MCIERR_UNSUPPORTED_FUNCTION;

    switch (wMsg) {
1287
    case MCI_OPEN_DRIVER:	return MCICDA_Open(dwDevID, dwParam1, (LPMCI_OPEN_PARMSW)dwParam2);
1288 1289
    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);
1290
    case MCI_INFO:		return MCICDA_Info(dwDevID, dwParam1, (LPMCI_INFO_PARMSW)dwParam2);
1291 1292 1293 1294 1295 1296 1297
    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);
1298 1299 1300 1301
    /* 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:
1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317
    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:
1318
	TRACE("Unsupported command [0x%x]\n", wMsg);
1319 1320 1321
	break;
    case MCI_OPEN:
    case MCI_CLOSE:
1322
	ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1323
	break;
1324
    default:
1325
	TRACE("Sending msg [0x%x] to default driver proc\n", wMsg);
1326
	return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1327
    }
1328
    return MCIERR_UNRECOGNIZED_COMMAND;
Alexandre Julliard's avatar
Alexandre Julliard committed
1329 1330 1331
}

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