ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

mcicda.c
Go to the documentation of this file.
00001 /*
00002  * MCI driver for audio CD (MCICDA)
00003  *
00004  * Copyright 1994    Martin Ayotte
00005  * Copyright 1998-99 Eric Pouech
00006  * Copyright 2000    Andreas Mohr
00007  *
00008  * This library is free software; you can redistribute it and/or
00009  * modify it under the terms of the GNU Lesser General Public
00010  * License as published by the Free Software Foundation; either
00011  * version 2.1 of the License, or (at your option) any later version.
00012  *
00013  * This library is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00016  * Lesser General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU Lesser General Public
00019  * License along with this library; if not, write to the Free Software
00020  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
00021  */
00022 
00023 #include "config.h"
00024 #include <stdarg.h>
00025 #include <stdio.h>
00026 #include <string.h>
00027 
00028 #define WIN32_NO_STATUS
00029 #include "windef.h"
00030 #include "winbase.h"
00031 #include "wingdi.h"
00032 #include "winuser.h"
00033 #include "wownt32.h"
00034 #include "mmddk.h"
00035 #include "winioctl.h"
00036 #include "ntddcdrm.h"
00037 #include "winternl.h"
00038 #include "wine/debug.h"
00039 #include "wine/unicode.h"
00040 #include "dsound.h"
00041 
00042 WINE_DEFAULT_DEBUG_CHANNEL(mcicda);
00043 
00044 #define CDFRAMES_PERSEC                 75
00045 #define CDFRAMES_PERMIN                 (CDFRAMES_PERSEC * 60)
00046 #define FRAME_OF_ADDR(a) ((a)[1] * CDFRAMES_PERMIN + (a)[2] * CDFRAMES_PERSEC + (a)[3])
00047 #define FRAME_OF_TOC(toc, idx)  FRAME_OF_ADDR((toc).TrackData[idx - (toc).FirstTrack].Address)
00048 
00049 /* Defined by red-book standard; do not change! */
00050 #define RAW_SECTOR_SIZE  (2352)
00051 
00052 /* Must be >= RAW_SECTOR_SIZE */
00053 #define CDDA_FRAG_SIZE   (32768)
00054 /* Must be >= 2 */
00055 #define CDDA_FRAG_COUNT  (3)
00056 
00057 typedef struct {
00058     UINT        wDevID;
00059     int             nUseCount;          /* Incremented for each shared open */
00060     BOOL        fShareable;         /* TRUE if first open was shareable */
00061     MCIDEVICEID     wNotifyDeviceID;    /* MCI device ID with a pending notification */
00062     HANDLE      hCallback;          /* Callback handle for pending notification */
00063     DWORD       dwTimeFormat;
00064     HANDLE              handle;
00065 
00066     /* The following are used for digital playback only */
00067     HANDLE hThread;
00068     HANDLE stopEvent;
00069     DWORD start, end;
00070 
00071     IDirectSound *dsObj;
00072     IDirectSoundBuffer *dsBuf;
00073 
00074     CRITICAL_SECTION cs;
00075 } WINE_MCICDAUDIO;
00076 
00077 /*-----------------------------------------------------------------------*/
00078 
00079 typedef HRESULT(WINAPI*LPDIRECTSOUNDCREATE)(LPCGUID,LPDIRECTSOUND*,LPUNKNOWN);
00080 static LPDIRECTSOUNDCREATE pDirectSoundCreate;
00081 
00082 static DWORD CALLBACK MCICDA_playLoop(void *ptr)
00083 {
00084     WINE_MCICDAUDIO *wmcda = (WINE_MCICDAUDIO*)ptr;
00085     DWORD lastPos, curPos, endPos, br;
00086     void *cdData;
00087     DWORD lockLen, fragLen;
00088     DSBCAPS caps;
00089     RAW_READ_INFO rdInfo;
00090     HRESULT hr = DS_OK;
00091 
00092     memset(&caps, 0, sizeof(caps));
00093     caps.dwSize = sizeof(caps);
00094     hr = IDirectSoundBuffer_GetCaps(wmcda->dsBuf, &caps);
00095 
00096     fragLen = caps.dwBufferBytes/CDDA_FRAG_COUNT;
00097     curPos = lastPos = 0;
00098     endPos = ~0u;
00099     while (SUCCEEDED(hr) && endPos != lastPos &&
00100            WaitForSingleObject(wmcda->stopEvent, 0) != WAIT_OBJECT_0) {
00101         hr = IDirectSoundBuffer_GetCurrentPosition(wmcda->dsBuf, &curPos, NULL);
00102         if ((curPos-lastPos+caps.dwBufferBytes)%caps.dwBufferBytes < fragLen) {
00103             Sleep(1);
00104             continue;
00105         }
00106 
00107         EnterCriticalSection(&wmcda->cs);
00108         rdInfo.DiskOffset.QuadPart = wmcda->start<<11;
00109         rdInfo.SectorCount = min(fragLen/RAW_SECTOR_SIZE, wmcda->end-wmcda->start);
00110         rdInfo.TrackMode = CDDA;
00111 
00112         hr = IDirectSoundBuffer_Lock(wmcda->dsBuf, lastPos, fragLen, &cdData, &lockLen, NULL, NULL, 0);
00113         if (hr == DSERR_BUFFERLOST) {
00114             if(FAILED(IDirectSoundBuffer_Restore(wmcda->dsBuf)) ||
00115                FAILED(IDirectSoundBuffer_Play(wmcda->dsBuf, 0, 0, DSBPLAY_LOOPING))) {
00116                 LeaveCriticalSection(&wmcda->cs);
00117                 break;
00118             }
00119             hr = IDirectSoundBuffer_Lock(wmcda->dsBuf, lastPos, fragLen, &cdData, &lockLen, NULL, NULL, 0);
00120         }
00121 
00122         if (SUCCEEDED(hr)) {
00123             if (rdInfo.SectorCount > 0) {
00124                 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_RAW_READ, &rdInfo, sizeof(rdInfo), cdData, lockLen, &br, NULL))
00125                     WARN("CD read failed at sector %d: 0x%x\n", wmcda->start, GetLastError());
00126             }
00127             if (rdInfo.SectorCount*RAW_SECTOR_SIZE < lockLen) {
00128                 if(endPos == ~0u) endPos = lastPos;
00129                 memset((BYTE*)cdData + rdInfo.SectorCount*RAW_SECTOR_SIZE, 0,
00130                        lockLen - rdInfo.SectorCount*RAW_SECTOR_SIZE);
00131             }
00132             hr = IDirectSoundBuffer_Unlock(wmcda->dsBuf, cdData, lockLen, NULL, 0);
00133         }
00134 
00135         lastPos += fragLen;
00136         lastPos %= caps.dwBufferBytes;
00137         wmcda->start += rdInfo.SectorCount;
00138 
00139         LeaveCriticalSection(&wmcda->cs);
00140     }
00141     IDirectSoundBuffer_Stop(wmcda->dsBuf);
00142     SetEvent(wmcda->stopEvent);
00143 
00144     /* A design bug in native: the independent CD player called by the
00145      * MCI has no means to signal end of playing, therefore the MCI
00146      * notification is left hanging.  MCI_NOTIFY_SUPERSEDED will be
00147      * signaled by the next command that has MCI_NOTIFY set (or
00148      * MCI_NOTIFY_ABORTED for MCI_PLAY). */
00149 
00150     return 0;
00151 }
00152 
00153 
00154 
00155 /**************************************************************************
00156  *              MCICDA_drvOpen          [internal]
00157  */
00158 static  DWORD   MCICDA_drvOpen(LPCWSTR str, LPMCI_OPEN_DRIVER_PARMSW modp)
00159 {
00160     static HMODULE dsHandle;
00161     WINE_MCICDAUDIO*    wmcda;
00162 
00163     if (!modp) return 0xFFFFFFFF;
00164     /* FIXME: MCIERR_CANNOT_LOAD_DRIVER if there's no drive of type CD-ROM */
00165 
00166     wmcda = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCICDAUDIO));
00167 
00168     if (!wmcda)
00169     return 0;
00170 
00171     if (!dsHandle) {
00172         dsHandle = LoadLibraryA("dsound.dll");
00173         if(dsHandle)
00174             pDirectSoundCreate = (LPDIRECTSOUNDCREATE)GetProcAddress(dsHandle, "DirectSoundCreate");
00175     }
00176 
00177     wmcda->wDevID = modp->wDeviceID;
00178     mciSetDriverData(wmcda->wDevID, (DWORD_PTR)wmcda);
00179     modp->wCustomCommandTable = MCI_NO_COMMAND_TABLE;
00180     modp->wType = MCI_DEVTYPE_CD_AUDIO;
00181     InitializeCriticalSection(&wmcda->cs);
00182     wmcda->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": WINE_MCICDAUDIO.cs");
00183     return modp->wDeviceID;
00184 }
00185 
00186 /**************************************************************************
00187  *              MCICDA_drvClose         [internal]
00188  */
00189 static  DWORD   MCICDA_drvClose(DWORD dwDevID)
00190 {
00191     WINE_MCICDAUDIO*  wmcda = (WINE_MCICDAUDIO*)mciGetDriverData(dwDevID);
00192 
00193     if (wmcda) {
00194     wmcda->cs.DebugInfo->Spare[0] = 0;
00195     DeleteCriticalSection(&wmcda->cs);
00196     HeapFree(GetProcessHeap(), 0, wmcda);
00197     mciSetDriverData(dwDevID, 0);
00198     }
00199     return (dwDevID == 0xFFFFFFFF) ? 1 : 0;
00200 }
00201 
00202 /**************************************************************************
00203  *              MCICDA_GetOpenDrv       [internal]
00204  */
00205 static WINE_MCICDAUDIO*  MCICDA_GetOpenDrv(UINT wDevID)
00206 {
00207     WINE_MCICDAUDIO*    wmcda = (WINE_MCICDAUDIO*)mciGetDriverData(wDevID);
00208 
00209     if (wmcda == NULL || wmcda->nUseCount == 0) {
00210     WARN("Invalid wDevID=%u\n", wDevID);
00211     return 0;
00212     }
00213     return wmcda;
00214 }
00215 
00216 /**************************************************************************
00217  *              MCICDA_mciNotify        [internal]
00218  *
00219  * Notifications in MCI work like a 1-element queue.
00220  * Each new notification request supersedes the previous one.
00221  */
00222 static void MCICDA_Notify(DWORD_PTR hWndCallBack, WINE_MCICDAUDIO* wmcda, UINT wStatus)
00223 {
00224     MCIDEVICEID wDevID = wmcda->wNotifyDeviceID;
00225     HANDLE old = InterlockedExchangePointer(&wmcda->hCallback, NULL);
00226     if (old) mciDriverNotify(old, wDevID, MCI_NOTIFY_SUPERSEDED);
00227     mciDriverNotify(HWND_32(LOWORD(hWndCallBack)), wDevID, wStatus);
00228 }
00229 
00230 /**************************************************************************
00231  *              MCICDA_GetStatus        [internal]
00232  */
00233 static  DWORD    MCICDA_GetStatus(WINE_MCICDAUDIO* wmcda)
00234 {
00235     CDROM_SUB_Q_DATA_FORMAT     fmt;
00236     SUB_Q_CHANNEL_DATA          data;
00237     DWORD                       br;
00238     DWORD                       mode = MCI_MODE_NOT_READY;
00239 
00240     fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
00241     if(wmcda->hThread != 0) {
00242         DWORD status;
00243         HRESULT hr;
00244 
00245         hr = IDirectSoundBuffer_GetStatus(wmcda->dsBuf, &status);
00246         if(SUCCEEDED(hr)) {
00247             if(!(status&DSBSTATUS_PLAYING)) {
00248                 if(WaitForSingleObject(wmcda->stopEvent, 0) == WAIT_OBJECT_0)
00249                     mode = MCI_MODE_STOP;
00250                 else
00251                     mode = MCI_MODE_PAUSE;
00252             }
00253             else
00254                 mode = MCI_MODE_PLAY;
00255         }
00256     }
00257     else if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
00258                               &data, sizeof(data), &br, NULL)) {
00259         if (GetLastError() == ERROR_NOT_READY) mode = MCI_MODE_OPEN;
00260     } else {
00261         switch (data.CurrentPosition.Header.AudioStatus)
00262         {
00263         case AUDIO_STATUS_IN_PROGRESS:          mode = MCI_MODE_PLAY;   break;
00264         case AUDIO_STATUS_PAUSED:               mode = MCI_MODE_PAUSE;  break;
00265         case AUDIO_STATUS_NO_STATUS:
00266         case AUDIO_STATUS_PLAY_COMPLETE:        mode = MCI_MODE_STOP;   break;
00267         case AUDIO_STATUS_PLAY_ERROR:
00268         case AUDIO_STATUS_NOT_SUPPORTED:
00269         default:
00270             break;
00271         }
00272     }
00273     return mode;
00274 }
00275 
00276 /**************************************************************************
00277  *              MCICDA_GetError         [internal]
00278  */
00279 static  int MCICDA_GetError(WINE_MCICDAUDIO* wmcda)
00280 {
00281     switch (GetLastError())
00282     {
00283     case ERROR_NOT_READY:     return MCIERR_DEVICE_NOT_READY;
00284     case ERROR_NOT_SUPPORTED:
00285     case ERROR_IO_DEVICE:     return MCIERR_HARDWARE;
00286     default:
00287     FIXME("Unknown mode %u\n", GetLastError());
00288     }
00289     return MCIERR_DRIVER_INTERNAL;
00290 }
00291 
00292 /**************************************************************************
00293  *          MCICDA_CalcFrame            [internal]
00294  */
00295 static DWORD MCICDA_CalcFrame(WINE_MCICDAUDIO* wmcda, DWORD dwTime)
00296 {
00297     DWORD   dwFrame = 0;
00298     UINT    wTrack;
00299     CDROM_TOC   toc;
00300     DWORD       br;
00301     BYTE*       addr;
00302 
00303     TRACE("(%p, %08X, %u);\n", wmcda, wmcda->dwTimeFormat, dwTime);
00304 
00305     switch (wmcda->dwTimeFormat) {
00306     case MCI_FORMAT_MILLISECONDS:
00307     dwFrame = ((dwTime - 1) * CDFRAMES_PERSEC + 500) / 1000;
00308     TRACE("MILLISECONDS %u\n", dwFrame);
00309     break;
00310     case MCI_FORMAT_MSF:
00311     TRACE("MSF %02u:%02u:%02u\n",
00312           MCI_MSF_MINUTE(dwTime), MCI_MSF_SECOND(dwTime), MCI_MSF_FRAME(dwTime));
00313     dwFrame += CDFRAMES_PERMIN * MCI_MSF_MINUTE(dwTime);
00314     dwFrame += CDFRAMES_PERSEC * MCI_MSF_SECOND(dwTime);
00315     dwFrame += MCI_MSF_FRAME(dwTime);
00316     break;
00317     case MCI_FORMAT_TMSF:
00318     default: /* unknown format ! force TMSF ! ... */
00319     wTrack = MCI_TMSF_TRACK(dwTime);
00320         if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
00321                              &toc, sizeof(toc), &br, NULL))
00322             return 0;
00323         if (wTrack < toc.FirstTrack || wTrack > toc.LastTrack)
00324             return 0;
00325         TRACE("MSF %02u-%02u:%02u:%02u\n",
00326               MCI_TMSF_TRACK(dwTime), MCI_TMSF_MINUTE(dwTime),
00327               MCI_TMSF_SECOND(dwTime), MCI_TMSF_FRAME(dwTime));
00328         addr = toc.TrackData[wTrack - toc.FirstTrack].Address;
00329         TRACE("TMSF trackpos[%u]=%d:%d:%d\n",
00330               wTrack, addr[1], addr[2], addr[3]);
00331         dwFrame = CDFRAMES_PERMIN * (addr[1] + MCI_TMSF_MINUTE(dwTime)) +
00332             CDFRAMES_PERSEC * (addr[2] + MCI_TMSF_SECOND(dwTime)) +
00333             addr[3] + MCI_TMSF_FRAME(dwTime);
00334     break;
00335     }
00336     return dwFrame;
00337 }
00338 
00339 /**************************************************************************
00340  *          MCICDA_CalcTime             [internal]
00341  */
00342 static DWORD MCICDA_CalcTime(WINE_MCICDAUDIO* wmcda, DWORD tf, DWORD dwFrame, LPDWORD lpRet)
00343 {
00344     DWORD   dwTime = 0;
00345     UINT    wTrack;
00346     UINT    wMinutes;
00347     UINT    wSeconds;
00348     UINT    wFrames;
00349     CDROM_TOC   toc;
00350     DWORD       br;
00351 
00352     TRACE("(%p, %08X, %u);\n", wmcda, tf, dwFrame);
00353 
00354     switch (tf) {
00355     case MCI_FORMAT_MILLISECONDS:
00356     dwTime = (dwFrame * 1000) / CDFRAMES_PERSEC + 1;
00357     TRACE("MILLISECONDS %u\n", dwTime);
00358     *lpRet = 0;
00359     break;
00360     case MCI_FORMAT_MSF:
00361     wMinutes = dwFrame / CDFRAMES_PERMIN;
00362     wSeconds = (dwFrame - CDFRAMES_PERMIN * wMinutes) / CDFRAMES_PERSEC;
00363     wFrames = dwFrame - CDFRAMES_PERMIN * wMinutes - CDFRAMES_PERSEC * wSeconds;
00364     dwTime = MCI_MAKE_MSF(wMinutes, wSeconds, wFrames);
00365     TRACE("MSF %02u:%02u:%02u -> dwTime=%u\n",
00366           wMinutes, wSeconds, wFrames, dwTime);
00367     *lpRet = MCI_COLONIZED3_RETURN;
00368     break;
00369     case MCI_FORMAT_TMSF:
00370     default:    /* unknown format ! force TMSF ! ... */
00371         if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
00372                              &toc, sizeof(toc), &br, NULL))
00373             return 0;
00374     if (dwFrame < FRAME_OF_TOC(toc, toc.FirstTrack) ||
00375             dwFrame > FRAME_OF_TOC(toc, toc.LastTrack + 1)) {
00376             ERR("Out of range value %u [%u,%u]\n",
00377         dwFrame, FRAME_OF_TOC(toc, toc.FirstTrack),
00378                 FRAME_OF_TOC(toc, toc.LastTrack + 1));
00379         *lpRet = 0;
00380         return 0;
00381     }
00382     for (wTrack = toc.FirstTrack; wTrack <= toc.LastTrack; wTrack++) {
00383         if (FRAME_OF_TOC(toc, wTrack) > dwFrame)
00384         break;
00385     }
00386         wTrack--;
00387     dwFrame -= FRAME_OF_TOC(toc, wTrack);
00388     wMinutes = dwFrame / CDFRAMES_PERMIN;
00389     wSeconds = (dwFrame - CDFRAMES_PERMIN * wMinutes) / CDFRAMES_PERSEC;
00390     wFrames = dwFrame - CDFRAMES_PERMIN * wMinutes - CDFRAMES_PERSEC * wSeconds;
00391     dwTime = MCI_MAKE_TMSF(wTrack, wMinutes, wSeconds, wFrames);
00392     TRACE("%02u-%02u:%02u:%02u\n", wTrack, wMinutes, wSeconds, wFrames);
00393     *lpRet = MCI_COLONIZED4_RETURN;
00394     break;
00395     }
00396     return dwTime;
00397 }
00398 
00399 static DWORD MCICDA_Stop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms);
00400 
00401 /**************************************************************************
00402  *              MCICDA_Open         [internal]
00403  */
00404 static DWORD MCICDA_Open(UINT wDevID, DWORD dwFlags, LPMCI_OPEN_PARMSW lpOpenParms)
00405 {
00406     MCIDEVICEID     dwDeviceID;
00407     DWORD               ret;
00408     WINE_MCICDAUDIO*    wmcda = (WINE_MCICDAUDIO*)mciGetDriverData(wDevID);
00409     WCHAR               root[7], drive = 0;
00410 
00411     TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpOpenParms);
00412 
00413     if (lpOpenParms == NULL)        return MCIERR_NULL_PARAMETER_BLOCK;
00414     if (wmcda == NULL)          return MCIERR_INVALID_DEVICE_ID;
00415 
00416     dwDeviceID = lpOpenParms->wDeviceID;
00417 
00418     if (wmcda->nUseCount > 0) {
00419     /* The driver is already open on this channel */
00420     /* If the driver was opened shareable before and this open specifies */
00421     /* shareable then increment the use count */
00422     if (wmcda->fShareable && (dwFlags & MCI_OPEN_SHAREABLE))
00423         ++wmcda->nUseCount;
00424     else
00425         return MCIERR_MUST_USE_SHAREABLE;
00426     } else {
00427     wmcda->nUseCount = 1;
00428     wmcda->fShareable = dwFlags & MCI_OPEN_SHAREABLE;
00429     }
00430     if (dwFlags & MCI_OPEN_ELEMENT) {
00431         if (dwFlags & MCI_OPEN_ELEMENT_ID) {
00432             WARN("MCI_OPEN_ELEMENT_ID %p! Abort\n", lpOpenParms->lpstrElementName);
00433             ret = MCIERR_FLAGS_NOT_COMPATIBLE;
00434             goto the_error;
00435         }
00436         TRACE("MCI_OPEN_ELEMENT element name: %s\n", debugstr_w(lpOpenParms->lpstrElementName));
00437         /* Only the first letter counts since w2k
00438          * Win9x-NT accept only d: and w98SE accepts d:\foobar as well.
00439          * Play d:\Track03.cda plays from the first track, not #3. */
00440         if (!isalpha(lpOpenParms->lpstrElementName[0]))
00441         {
00442             ret = MCIERR_INVALID_FILE;
00443             goto the_error;
00444         }
00445         drive = toupper(lpOpenParms->lpstrElementName[0]);
00446         root[0] = drive; root[1] = ':'; root[2] = '\\'; root[3] = '\0';
00447         if (GetDriveTypeW(root) != DRIVE_CDROM)
00448         {
00449             ret = MCIERR_INVALID_FILE;
00450             goto the_error;
00451         }
00452     }
00453     else
00454     {
00455         root[0] = 'A'; root[1] = ':'; root[2] = '\\'; root[3] = '\0';
00456         for ( ; root[0] <= 'Z'; root[0]++)
00457         {
00458             if (GetDriveTypeW(root) == DRIVE_CDROM)
00459             {
00460                 drive = root[0];
00461                 break;
00462             }
00463         }
00464         if (!drive)
00465         {
00466             ret = MCIERR_CANNOT_LOAD_DRIVER; /* drvOpen should return this */
00467             goto the_error;
00468         }
00469     }
00470 
00471     wmcda->wNotifyDeviceID = dwDeviceID;
00472     wmcda->dwTimeFormat = MCI_FORMAT_MSF;
00473 
00474     /* now, open the handle */
00475     root[0] = root[1] = '\\'; root[2] = '.'; root[3] = '\\'; root[4] = drive; root[5] = ':'; root[6] = '\0';
00476     wmcda->handle = CreateFileW(root, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
00477     if (wmcda->handle == INVALID_HANDLE_VALUE)
00478     {
00479         ret = MCIERR_MUST_USE_SHAREABLE;
00480         goto the_error;
00481     }
00482 
00483     if (dwFlags & MCI_NOTIFY) {
00484     mciDriverNotify(HWND_32(LOWORD(lpOpenParms->dwCallback)),
00485             dwDeviceID, MCI_NOTIFY_SUCCESSFUL);
00486     }
00487     return 0;
00488 
00489  the_error:
00490     --wmcda->nUseCount;
00491     return ret;
00492 }
00493 
00494 /**************************************************************************
00495  *              MCICDA_Close            [internal]
00496  */
00497 static DWORD MCICDA_Close(UINT wDevID, DWORD dwParam, LPMCI_GENERIC_PARMS lpParms)
00498 {
00499     WINE_MCICDAUDIO*    wmcda = MCICDA_GetOpenDrv(wDevID);
00500 
00501     TRACE("(%04X, %08X, %p);\n", wDevID, dwParam, lpParms);
00502 
00503     if (wmcda == NULL)  return MCIERR_INVALID_DEVICE_ID;
00504 
00505     MCICDA_Stop(wDevID, MCI_WAIT, NULL);
00506 
00507     if (--wmcda->nUseCount == 0) {
00508     CloseHandle(wmcda->handle);
00509     }
00510     if ((dwParam & MCI_NOTIFY) && lpParms)
00511     MCICDA_Notify(lpParms->dwCallback, wmcda, MCI_NOTIFY_SUCCESSFUL);
00512     return 0;
00513 }
00514 
00515 /**************************************************************************
00516  *              MCICDA_GetDevCaps       [internal]
00517  */
00518 static DWORD MCICDA_GetDevCaps(UINT wDevID, DWORD dwFlags,
00519                    LPMCI_GETDEVCAPS_PARMS lpParms)
00520 {
00521     WINE_MCICDAUDIO*    wmcda = (WINE_MCICDAUDIO*)mciGetDriverData(wDevID);
00522     DWORD   ret = 0;
00523 
00524     TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
00525 
00526     if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
00527     if (wmcda == NULL)          return MCIERR_INVALID_DEVICE_ID;
00528 
00529     if (dwFlags & MCI_GETDEVCAPS_ITEM) {
00530     TRACE("MCI_GETDEVCAPS_ITEM dwItem=%08X;\n", lpParms->dwItem);
00531 
00532     switch (lpParms->dwItem) {
00533     case MCI_GETDEVCAPS_CAN_RECORD:
00534         lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
00535         ret = MCI_RESOURCE_RETURNED;
00536         break;
00537     case MCI_GETDEVCAPS_HAS_AUDIO:
00538         lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
00539         ret = MCI_RESOURCE_RETURNED;
00540         break;
00541     case MCI_GETDEVCAPS_HAS_VIDEO:
00542         lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
00543         ret = MCI_RESOURCE_RETURNED;
00544         break;
00545     case MCI_GETDEVCAPS_DEVICE_TYPE:
00546         lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_CD_AUDIO, MCI_DEVTYPE_CD_AUDIO);
00547         ret = MCI_RESOURCE_RETURNED;
00548         break;
00549     case MCI_GETDEVCAPS_USES_FILES:
00550         lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
00551         ret = MCI_RESOURCE_RETURNED;
00552         break;
00553     case MCI_GETDEVCAPS_COMPOUND_DEVICE:
00554         lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
00555         ret = MCI_RESOURCE_RETURNED;
00556         break;
00557     case MCI_GETDEVCAPS_CAN_EJECT:
00558         lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
00559         ret = MCI_RESOURCE_RETURNED;
00560         break;
00561     case MCI_GETDEVCAPS_CAN_PLAY:
00562         lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
00563         ret = MCI_RESOURCE_RETURNED;
00564         break;
00565     case MCI_GETDEVCAPS_CAN_SAVE:
00566         lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
00567         ret = MCI_RESOURCE_RETURNED;
00568         break;
00569     default:
00570             WARN("Unsupported %x devCaps item\n", lpParms->dwItem);
00571         return MCIERR_UNSUPPORTED_FUNCTION;
00572     }
00573     } else {
00574     TRACE("No GetDevCaps-Item !\n");
00575     return MCIERR_MISSING_PARAMETER;
00576     }
00577     TRACE("lpParms->dwReturn=%08X;\n", lpParms->dwReturn);
00578     if (dwFlags & MCI_NOTIFY) {
00579     MCICDA_Notify(lpParms->dwCallback, wmcda, MCI_NOTIFY_SUCCESSFUL);
00580     }
00581     return ret;
00582 }
00583 
00584 static DWORD CDROM_Audio_GetSerial(CDROM_TOC* toc)
00585 {
00586     DWORD serial = 0;
00587     int i;
00588     WORD wMagic;
00589     DWORD dwStart, dwEnd;
00590 
00591     /*
00592      * wMagic collects the wFrames from track 1
00593      * dwStart, dwEnd collect the beginning and end of the disc respectively, in
00594      * frames.
00595      * There it is collected for correcting the serial when there are less than
00596      * 3 tracks.
00597      */
00598     wMagic = toc->TrackData[0].Address[3];
00599     dwStart = FRAME_OF_TOC(*toc, toc->FirstTrack);
00600 
00601     for (i = 0; i <= toc->LastTrack - toc->FirstTrack; i++) {
00602         serial += (toc->TrackData[i].Address[1] << 16) |
00603             (toc->TrackData[i].Address[2] << 8) | toc->TrackData[i].Address[3];
00604     }
00605     dwEnd = FRAME_OF_TOC(*toc, toc->LastTrack + 1);
00606 
00607     if (toc->LastTrack - toc->FirstTrack + 1 < 3)
00608         serial += wMagic + (dwEnd - dwStart);
00609 
00610     return serial;
00611 }
00612 
00613 
00614 /**************************************************************************
00615  *              MCICDA_Info         [internal]
00616  */
00617 static DWORD MCICDA_Info(UINT wDevID, DWORD dwFlags, LPMCI_INFO_PARMSW lpParms)
00618 {
00619     LPCWSTR     str = NULL;
00620     WINE_MCICDAUDIO*    wmcda = MCICDA_GetOpenDrv(wDevID);
00621     DWORD       ret = 0;
00622     WCHAR       buffer[16];
00623 
00624     TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
00625 
00626     if (lpParms == NULL || lpParms->lpstrReturn == NULL)
00627     return MCIERR_NULL_PARAMETER_BLOCK;
00628     if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
00629 
00630     TRACE("buf=%p, len=%u\n", lpParms->lpstrReturn, lpParms->dwRetSize);
00631 
00632     if (dwFlags & MCI_INFO_PRODUCT) {
00633         static const WCHAR wszAudioCd[] = {'W','i','n','e','\'','s',' ','a','u','d','i','o',' ','C','D',0};
00634         str = wszAudioCd;
00635     } else if (dwFlags & MCI_INFO_MEDIA_UPC) {
00636     ret = MCIERR_NO_IDENTITY;
00637     } else if (dwFlags & MCI_INFO_MEDIA_IDENTITY) {
00638     DWORD       res = 0;
00639         CDROM_TOC   toc;
00640         DWORD       br;
00641     static const WCHAR wszLu[] = {'%','l','u',0};
00642 
00643         if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
00644                              &toc, sizeof(toc), &br, NULL)) {
00645         return MCICDA_GetError(wmcda);
00646     }
00647 
00648     res = CDROM_Audio_GetSerial(&toc);
00649     sprintfW(buffer, wszLu, res);
00650     str = buffer;
00651     } else {
00652     WARN("Don't know this info command (%u)\n", dwFlags);
00653     ret = MCIERR_MISSING_PARAMETER;
00654     }
00655     if (!ret) {
00656     TRACE("=> %s\n", debugstr_w(str));
00657     if (lpParms->dwRetSize) {
00658         WCHAR zero = 0;
00659         /* FIXME? Since NT, mciwave, mciseq and mcicda set dwRetSize
00660          *        to the number of characters written, excluding \0. */
00661         lstrcpynW(lpParms->lpstrReturn, str ? str : &zero, lpParms->dwRetSize);
00662     } else ret = MCIERR_PARAM_OVERFLOW;
00663     }
00664     if (MMSYSERR_NOERROR==ret && (dwFlags & MCI_NOTIFY))
00665     MCICDA_Notify(lpParms->dwCallback, wmcda, MCI_NOTIFY_SUCCESSFUL);
00666     return ret;
00667 }
00668 
00669 /**************************************************************************
00670  *              MCICDA_Status           [internal]
00671  */
00672 static DWORD MCICDA_Status(UINT wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
00673 {
00674     WINE_MCICDAUDIO*            wmcda = MCICDA_GetOpenDrv(wDevID);
00675     DWORD                   ret = 0;
00676     CDROM_SUB_Q_DATA_FORMAT     fmt;
00677     SUB_Q_CHANNEL_DATA          data;
00678     CDROM_TOC                   toc;
00679     DWORD                       br;
00680 
00681     TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
00682 
00683     if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
00684     if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
00685 
00686     if (dwFlags & MCI_STATUS_ITEM) {
00687     TRACE("dwItem = %x\n", lpParms->dwItem);
00688     switch (lpParms->dwItem) {
00689     case MCI_STATUS_CURRENT_TRACK:
00690             fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
00691             if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
00692                                  &data, sizeof(data), &br, NULL))
00693             {
00694         return MCICDA_GetError(wmcda);
00695         /* alt. data.CurrentPosition.TrackNumber = 1; -- what native yields */
00696         }
00697         lpParms->dwReturn = data.CurrentPosition.TrackNumber;
00698             TRACE("CURRENT_TRACK=%lu\n", lpParms->dwReturn);
00699         break;
00700     case MCI_STATUS_LENGTH:
00701             if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
00702                                  &toc, sizeof(toc), &br, NULL)) {
00703                 WARN("error reading TOC !\n");
00704                 return MCICDA_GetError(wmcda);
00705         }
00706         if (dwFlags & MCI_TRACK) {
00707         TRACE("MCI_TRACK #%u LENGTH=??? !\n", lpParms->dwTrack);
00708         if (lpParms->dwTrack < toc.FirstTrack || lpParms->dwTrack > toc.LastTrack)
00709             return MCIERR_OUTOFRANGE;
00710         lpParms->dwReturn = FRAME_OF_TOC(toc, lpParms->dwTrack + 1) -
00711                     FRAME_OF_TOC(toc, lpParms->dwTrack);
00712         /* Windows returns one frame less than the total track length for the
00713            last track on the CD.  See CDDB HOWTO.  Verified on Win95OSR2. */
00714         if (lpParms->dwTrack == toc.LastTrack)
00715             lpParms->dwReturn--;
00716         } else {
00717         /* Sum of the lengths of all of the tracks.  Inherits the
00718            'off by one frame' behavior from the length of the last track.
00719            See above comment. */
00720         lpParms->dwReturn = FRAME_OF_TOC(toc, toc.LastTrack + 1) -
00721                     FRAME_OF_TOC(toc, toc.FirstTrack) - 1;
00722         }
00723         lpParms->dwReturn = MCICDA_CalcTime(wmcda,
00724                          (wmcda->dwTimeFormat == MCI_FORMAT_TMSF)
00725                             ? MCI_FORMAT_MSF : wmcda->dwTimeFormat,
00726                          lpParms->dwReturn,
00727                          &ret);
00728             TRACE("LENGTH=%lu\n", lpParms->dwReturn);
00729         break;
00730     case MCI_STATUS_MODE:
00731             lpParms->dwReturn = MCICDA_GetStatus(wmcda);
00732             TRACE("MCI_STATUS_MODE=%08lX\n", lpParms->dwReturn);
00733         lpParms->dwReturn = MAKEMCIRESOURCE(lpParms->dwReturn, lpParms->dwReturn);
00734         ret = MCI_RESOURCE_RETURNED;
00735         break;
00736     case MCI_STATUS_MEDIA_PRESENT:
00737         lpParms->dwReturn = (MCICDA_GetStatus(wmcda) == MCI_MODE_OPEN) ?
00738         MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
00739         TRACE("MCI_STATUS_MEDIA_PRESENT =%c!\n", LOWORD(lpParms->dwReturn) ? 'Y' : 'N');
00740         ret = MCI_RESOURCE_RETURNED;
00741         break;
00742     case MCI_STATUS_NUMBER_OF_TRACKS:
00743             if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
00744                                  &toc, sizeof(toc), &br, NULL)) {
00745                 WARN("error reading TOC !\n");
00746                 return MCICDA_GetError(wmcda);
00747         }
00748         lpParms->dwReturn = toc.LastTrack - toc.FirstTrack + 1;
00749             TRACE("MCI_STATUS_NUMBER_OF_TRACKS = %lu\n", lpParms->dwReturn);
00750         if (lpParms->dwReturn == (WORD)-1)
00751         return MCICDA_GetError(wmcda);
00752         break;
00753     case MCI_STATUS_POSITION:
00754             switch (dwFlags & (MCI_STATUS_START | MCI_TRACK)) {
00755             case MCI_STATUS_START:
00756                 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
00757                                      &toc, sizeof(toc), &br, NULL)) {
00758                     WARN("error reading TOC !\n");
00759                     return MCICDA_GetError(wmcda);
00760                 }
00761         lpParms->dwReturn = FRAME_OF_TOC(toc, toc.FirstTrack);
00762         TRACE("get MCI_STATUS_START !\n");
00763                 break;
00764             case MCI_TRACK:
00765                 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
00766                                      &toc, sizeof(toc), &br, NULL)) {
00767                     WARN("error reading TOC !\n");
00768                     return MCICDA_GetError(wmcda);
00769                 }
00770         if (lpParms->dwTrack < toc.FirstTrack || lpParms->dwTrack > toc.LastTrack)
00771             return MCIERR_OUTOFRANGE;
00772         lpParms->dwReturn = FRAME_OF_TOC(toc, lpParms->dwTrack);
00773         TRACE("get MCI_TRACK #%u !\n", lpParms->dwTrack);
00774                 break;
00775             case 0:
00776                 fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
00777                 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
00778                                      &data, sizeof(data), &br, NULL)) {
00779                     return MCICDA_GetError(wmcda);
00780                 }
00781                 lpParms->dwReturn = FRAME_OF_ADDR(data.CurrentPosition.AbsoluteAddress);
00782                 break;
00783             default:
00784                 return MCIERR_FLAGS_NOT_COMPATIBLE;
00785             }
00786         lpParms->dwReturn = MCICDA_CalcTime(wmcda, wmcda->dwTimeFormat, lpParms->dwReturn, &ret);
00787             TRACE("MCI_STATUS_POSITION=%08lX\n", lpParms->dwReturn);
00788         break;
00789     case MCI_STATUS_READY:
00790         TRACE("MCI_STATUS_READY !\n");
00791             switch (MCICDA_GetStatus(wmcda))
00792             {
00793             case MCI_MODE_NOT_READY:
00794             case MCI_MODE_OPEN:
00795                 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
00796                 break;
00797             default:
00798                 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
00799                 break;
00800             }
00801         TRACE("MCI_STATUS_READY=%u!\n", LOWORD(lpParms->dwReturn));
00802         ret = MCI_RESOURCE_RETURNED;
00803         break;
00804     case MCI_STATUS_TIME_FORMAT:
00805         lpParms->dwReturn = MAKEMCIRESOURCE(wmcda->dwTimeFormat, MCI_FORMAT_RETURN_BASE + wmcda->dwTimeFormat);
00806         TRACE("MCI_STATUS_TIME_FORMAT=%08x!\n", LOWORD(lpParms->dwReturn));
00807         ret = MCI_RESOURCE_RETURNED;
00808         break;
00809     case 4001: /* FIXME: for bogus FullCD */
00810     case MCI_CDA_STATUS_TYPE_TRACK:
00811         if (!(dwFlags & MCI_TRACK))
00812         ret = MCIERR_MISSING_PARAMETER;
00813         else {
00814                 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
00815                                      &toc, sizeof(toc), &br, NULL)) {
00816                     WARN("error reading TOC !\n");
00817                     return MCICDA_GetError(wmcda);
00818                 }
00819         if (lpParms->dwTrack < toc.FirstTrack || lpParms->dwTrack > toc.LastTrack)
00820             ret = MCIERR_OUTOFRANGE;
00821         else
00822             lpParms->dwReturn = (toc.TrackData[lpParms->dwTrack - toc.FirstTrack].Control & 0x04) ?
00823                                          MCI_CDA_TRACK_OTHER : MCI_CDA_TRACK_AUDIO;
00824             /* FIXME: MAKEMCIRESOURCE "audio" | "other", localised */
00825         }
00826             TRACE("MCI_CDA_STATUS_TYPE_TRACK[%d]=%ld\n", lpParms->dwTrack, lpParms->dwReturn);
00827         break;
00828     default:
00829             FIXME("unknown command %08X !\n", lpParms->dwItem);
00830         return MCIERR_UNSUPPORTED_FUNCTION;
00831     }
00832     } else return MCIERR_MISSING_PARAMETER;
00833     if ((dwFlags & MCI_NOTIFY) && HRESULT_CODE(ret)==0)
00834     MCICDA_Notify(lpParms->dwCallback, wmcda, MCI_NOTIFY_SUCCESSFUL);
00835     return ret;
00836 }
00837 
00838 /**************************************************************************
00839  *              MCICDA_SkipDataTracks       [internal]
00840  */
00841 static DWORD MCICDA_SkipDataTracks(WINE_MCICDAUDIO* wmcda,DWORD *frame)
00842 {
00843   int i;
00844   DWORD br;
00845   CDROM_TOC toc;
00846   if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
00847                       &toc, sizeof(toc), &br, NULL)) {
00848     WARN("error reading TOC !\n");
00849     return MCICDA_GetError(wmcda);
00850   }
00851   if (*frame < FRAME_OF_TOC(toc,toc.FirstTrack) ||
00852       *frame >= FRAME_OF_TOC(toc,toc.LastTrack+1)) /* lead-out */
00853     return MCIERR_OUTOFRANGE;
00854   for(i=toc.LastTrack+1;i>toc.FirstTrack;i--)
00855     if ( FRAME_OF_TOC(toc, i) <= *frame ) break;
00856   /* i points to last track whose start address is not greater than frame.
00857    * Now skip non-audio tracks */
00858   for(;i<=toc.LastTrack;i++)
00859     if ( ! (toc.TrackData[i-toc.FirstTrack].Control & 4) )
00860       break;
00861   /* The frame will be an address in the next audio track or
00862    * address of lead-out. */
00863   if ( FRAME_OF_TOC(toc, i) > *frame )
00864     *frame = FRAME_OF_TOC(toc, i);
00865   /* Lead-out is an invalid seek position (on Linux as well). */
00866   if (*frame == FRAME_OF_TOC(toc,toc.LastTrack+1))
00867      (*frame)--;
00868   return 0;
00869 }
00870 
00871 /**************************************************************************
00872  *              MCICDA_Play         [internal]
00873  */
00874 static DWORD MCICDA_Play(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
00875 {
00876     WINE_MCICDAUDIO*            wmcda = MCICDA_GetOpenDrv(wDevID);
00877     DWORD               ret = 0, start, end;
00878     HANDLE                      oldcb;
00879     DWORD                       br;
00880     CDROM_PLAY_AUDIO_MSF        play;
00881     CDROM_SUB_Q_DATA_FORMAT     fmt;
00882     SUB_Q_CHANNEL_DATA          data;
00883     CDROM_TOC           toc;
00884 
00885     TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
00886 
00887     if (lpParms == NULL)
00888     return MCIERR_NULL_PARAMETER_BLOCK;
00889 
00890     if (wmcda == NULL)
00891     return MCIERR_INVALID_DEVICE_ID;
00892 
00893     if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
00894                          &toc, sizeof(toc), &br, NULL)) {
00895         WARN("error reading TOC !\n");
00896         return MCICDA_GetError(wmcda);
00897     }
00898 
00899     if (dwFlags & MCI_FROM) {
00900     start = MCICDA_CalcFrame(wmcda, lpParms->dwFrom);
00901     if ( (ret=MCICDA_SkipDataTracks(wmcda, &start)) )
00902       return ret;
00903     TRACE("MCI_FROM=%08X -> %u\n", lpParms->dwFrom, start);
00904     } else {
00905         fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
00906         if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
00907                              &data, sizeof(data), &br, NULL)) {
00908             return MCICDA_GetError(wmcda);
00909         }
00910         start = FRAME_OF_ADDR(data.CurrentPosition.AbsoluteAddress);
00911     if ( (ret=MCICDA_SkipDataTracks(wmcda, &start)) )
00912       return ret;
00913     }
00914     if (dwFlags & MCI_TO) {
00915     end = MCICDA_CalcFrame(wmcda, lpParms->dwTo);
00916     if ( (ret=MCICDA_SkipDataTracks(wmcda, &end)) )
00917       return ret;
00918     TRACE("MCI_TO=%08X -> %u\n", lpParms->dwTo, end);
00919     } else {
00920     end = FRAME_OF_TOC(toc, toc.LastTrack + 1) - 1;
00921     }
00922     if (end < start) return MCIERR_OUTOFRANGE;
00923     TRACE("Playing from %u to %u\n", start, end);
00924 
00925     oldcb = InterlockedExchangePointer(&wmcda->hCallback,
00926     (dwFlags & MCI_NOTIFY) ? HWND_32(LOWORD(lpParms->dwCallback)) : NULL);
00927     if (oldcb) mciDriverNotify(oldcb, wmcda->wNotifyDeviceID, MCI_NOTIFY_ABORTED);
00928 
00929     if (start == end || start == FRAME_OF_TOC(toc,toc.LastTrack+1)-1) {
00930         if (dwFlags & MCI_NOTIFY) {
00931             oldcb = InterlockedExchangePointer(&wmcda->hCallback, NULL);
00932             if (oldcb) mciDriverNotify(oldcb, wDevID, MCI_NOTIFY_SUCCESSFUL);
00933         }
00934         return MMSYSERR_NOERROR;
00935     }
00936 
00937     if (wmcda->hThread != 0) {
00938         SetEvent(wmcda->stopEvent);
00939         WaitForSingleObject(wmcda->hThread, INFINITE);
00940 
00941         CloseHandle(wmcda->hThread);
00942         wmcda->hThread = 0;
00943         CloseHandle(wmcda->stopEvent);
00944         wmcda->stopEvent = 0;
00945 
00946         IDirectSoundBuffer_Stop(wmcda->dsBuf);
00947         IDirectSoundBuffer_Release(wmcda->dsBuf);
00948         wmcda->dsBuf = NULL;
00949         IDirectSound_Release(wmcda->dsObj);
00950         wmcda->dsObj = NULL;
00951     }
00952 
00953     if (pDirectSoundCreate) {
00954         WAVEFORMATEX format;
00955         DSBUFFERDESC desc;
00956         DWORD lockLen;
00957         void *cdData;
00958         HRESULT hr;
00959 
00960         hr = pDirectSoundCreate(NULL, &wmcda->dsObj, NULL);
00961         if (SUCCEEDED(hr)) {
00962             IDirectSound_SetCooperativeLevel(wmcda->dsObj, GetDesktopWindow(), DSSCL_PRIORITY);
00963 
00964             /* The "raw" frame is relative to the start of the first track */
00965             wmcda->start = start - FRAME_OF_TOC(toc, toc.FirstTrack);
00966             wmcda->end = end - FRAME_OF_TOC(toc, toc.FirstTrack);
00967 
00968             memset(&format, 0, sizeof(format));
00969             format.wFormatTag = WAVE_FORMAT_PCM;
00970             format.nChannels = 2;
00971             format.nSamplesPerSec = 44100;
00972             format.wBitsPerSample = 16;
00973             format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8;
00974             format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;
00975             format.cbSize = 0;
00976 
00977             memset(&desc, 0, sizeof(desc));
00978             desc.dwSize = sizeof(desc);
00979             desc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS;
00980             desc.dwBufferBytes = (CDDA_FRAG_SIZE - (CDDA_FRAG_SIZE%RAW_SECTOR_SIZE)) * CDDA_FRAG_COUNT;
00981             desc.lpwfxFormat = &format;
00982 
00983             hr = IDirectSound_CreateSoundBuffer(wmcda->dsObj, &desc, &wmcda->dsBuf, NULL);
00984         }
00985         if (SUCCEEDED(hr)) {
00986             hr = IDirectSoundBuffer_Lock(wmcda->dsBuf, 0, 0, &cdData, &lockLen,
00987                                          NULL, NULL, DSBLOCK_ENTIREBUFFER);
00988         }
00989         if (SUCCEEDED(hr)) {
00990             RAW_READ_INFO rdInfo;
00991             int readok;
00992 
00993             rdInfo.DiskOffset.QuadPart = wmcda->start<<11;
00994             rdInfo.SectorCount = min(desc.dwBufferBytes/RAW_SECTOR_SIZE,
00995                                      wmcda->end-wmcda->start);
00996             rdInfo.TrackMode = CDDA;
00997 
00998             readok = DeviceIoControl(wmcda->handle, IOCTL_CDROM_RAW_READ,
00999                                      &rdInfo, sizeof(rdInfo), cdData, lockLen,
01000                                      &br, NULL);
01001             IDirectSoundBuffer_Unlock(wmcda->dsBuf, cdData, lockLen, NULL, 0);
01002 
01003             if (readok) {
01004                 wmcda->start += rdInfo.SectorCount;
01005                 wmcda->stopEvent = CreateEventA(NULL, TRUE, FALSE, NULL);
01006             }
01007             if (wmcda->stopEvent != 0)
01008                 wmcda->hThread = CreateThread(NULL, 0, MCICDA_playLoop, wmcda, 0, &br);
01009             if (wmcda->hThread != 0) {
01010                 hr = IDirectSoundBuffer_Play(wmcda->dsBuf, 0, 0, DSBPLAY_LOOPING);
01011                 if (SUCCEEDED(hr)) {
01012                     /* FIXME: implement MCI_WAIT and send notification only in that case */
01013                     if (0) {
01014                         oldcb = InterlockedExchangePointer(&wmcda->hCallback, NULL);
01015                         if (oldcb) mciDriverNotify(oldcb, wmcda->wNotifyDeviceID,
01016                             FAILED(hr) ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
01017                     }
01018                     return ret;
01019                 }
01020 
01021                 SetEvent(wmcda->stopEvent);
01022                 WaitForSingleObject(wmcda->hThread, INFINITE);
01023                 CloseHandle(wmcda->hThread);
01024                 wmcda->hThread = 0;
01025             }
01026         }
01027 
01028         if (wmcda->stopEvent != 0) {
01029             CloseHandle(wmcda->stopEvent);
01030             wmcda->stopEvent = 0;
01031         }
01032         if (wmcda->dsBuf) {
01033             IDirectSoundBuffer_Release(wmcda->dsBuf);
01034             wmcda->dsBuf = NULL;
01035         }
01036         if (wmcda->dsObj) {
01037             IDirectSound_Release(wmcda->dsObj);
01038             wmcda->dsObj = NULL;
01039         }
01040     }
01041 
01042     play.StartingM = start / CDFRAMES_PERMIN;
01043     play.StartingS = (start / CDFRAMES_PERSEC) % 60;
01044     play.StartingF = start % CDFRAMES_PERSEC;
01045     play.EndingM   = end / CDFRAMES_PERMIN;
01046     play.EndingS   = (end / CDFRAMES_PERSEC) % 60;
01047     play.EndingF   = end % CDFRAMES_PERSEC;
01048     if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_PLAY_AUDIO_MSF, &play, sizeof(play),
01049                          NULL, 0, &br, NULL)) {
01050     wmcda->hCallback = NULL;
01051     ret = MCIERR_HARDWARE;
01052     }
01053     /* The independent CD player has no means to signal MCI_NOTIFY when it's done.
01054      * Native sends a notification with MCI_WAIT only. */
01055     return ret;
01056 }
01057 
01058 /**************************************************************************
01059  *              MCICDA_Stop         [internal]
01060  */
01061 static DWORD MCICDA_Stop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
01062 {
01063     WINE_MCICDAUDIO*    wmcda = MCICDA_GetOpenDrv(wDevID);
01064     HANDLE      oldcb;
01065     DWORD               br;
01066 
01067     TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
01068 
01069     if (wmcda == NULL)  return MCIERR_INVALID_DEVICE_ID;
01070 
01071     oldcb = InterlockedExchangePointer(&wmcda->hCallback, NULL);
01072     if (oldcb) mciDriverNotify(oldcb, wmcda->wNotifyDeviceID, MCI_NOTIFY_ABORTED);
01073 
01074     if (wmcda->hThread != 0) {
01075         SetEvent(wmcda->stopEvent);
01076         WaitForSingleObject(wmcda->hThread, INFINITE);
01077 
01078         CloseHandle(wmcda->hThread);
01079         wmcda->hThread = 0;
01080         CloseHandle(wmcda->stopEvent);
01081         wmcda->stopEvent = 0;
01082 
01083         IDirectSoundBuffer_Release(wmcda->dsBuf);
01084         wmcda->dsBuf = NULL;
01085         IDirectSound_Release(wmcda->dsObj);
01086         wmcda->dsObj = NULL;
01087     }
01088     else if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_STOP_AUDIO, NULL, 0, NULL, 0, &br, NULL))
01089         return MCIERR_HARDWARE;
01090 
01091     if ((dwFlags & MCI_NOTIFY) && lpParms)
01092     MCICDA_Notify(lpParms->dwCallback, wmcda, MCI_NOTIFY_SUCCESSFUL);
01093     return 0;
01094 }
01095 
01096 /**************************************************************************
01097  *              MCICDA_Pause            [internal]
01098  */
01099 static DWORD MCICDA_Pause(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
01100 {
01101     WINE_MCICDAUDIO*    wmcda = MCICDA_GetOpenDrv(wDevID);
01102     HANDLE      oldcb;
01103     DWORD               br;
01104 
01105     TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
01106 
01107     if (wmcda == NULL)  return MCIERR_INVALID_DEVICE_ID;
01108 
01109     oldcb = InterlockedExchangePointer(&wmcda->hCallback, NULL);
01110     if (oldcb) mciDriverNotify(oldcb, wmcda->wNotifyDeviceID, MCI_NOTIFY_ABORTED);
01111 
01112     if (wmcda->hThread != 0) {
01113         /* Don't bother calling stop if the playLoop thread has already stopped */
01114         if(WaitForSingleObject(wmcda->stopEvent, 0) != WAIT_OBJECT_0 &&
01115            FAILED(IDirectSoundBuffer_Stop(wmcda->dsBuf)))
01116             return MCIERR_HARDWARE;
01117     }
01118     else if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_PAUSE_AUDIO, NULL, 0, NULL, 0, &br, NULL))
01119         return MCIERR_HARDWARE;
01120 
01121     if ((dwFlags & MCI_NOTIFY) && lpParms)
01122     MCICDA_Notify(lpParms->dwCallback, wmcda, MCI_NOTIFY_SUCCESSFUL);
01123     return 0;
01124 }
01125 
01126 /**************************************************************************
01127  *              MCICDA_Resume           [internal]
01128  */
01129 static DWORD MCICDA_Resume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
01130 {
01131     WINE_MCICDAUDIO*    wmcda = MCICDA_GetOpenDrv(wDevID);
01132     DWORD               br;
01133 
01134     TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
01135 
01136     if (wmcda == NULL)  return MCIERR_INVALID_DEVICE_ID;
01137 
01138     if (wmcda->hThread != 0) {
01139         /* Don't restart if the playLoop thread has already stopped */
01140         if(WaitForSingleObject(wmcda->stopEvent, 0) != WAIT_OBJECT_0 &&
01141            FAILED(IDirectSoundBuffer_Play(wmcda->dsBuf, 0, 0, DSBPLAY_LOOPING)))
01142             return MCIERR_HARDWARE;
01143     }
01144     else if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_RESUME_AUDIO, NULL, 0, NULL, 0, &br, NULL))
01145         return MCIERR_HARDWARE;
01146 
01147     if ((dwFlags & MCI_NOTIFY) && lpParms)
01148     MCICDA_Notify(lpParms->dwCallback, wmcda, MCI_NOTIFY_SUCCESSFUL);
01149     return 0;
01150 }
01151 
01152 /**************************************************************************
01153  *              MCICDA_Seek         [internal]
01154  */
01155 static DWORD MCICDA_Seek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
01156 {
01157     DWORD               at;
01158     WINE_MCICDAUDIO*            wmcda = MCICDA_GetOpenDrv(wDevID);
01159     CDROM_SEEK_AUDIO_MSF        seek;
01160     DWORD                       br, position, ret;
01161     CDROM_TOC           toc;
01162 
01163     TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
01164 
01165     if (wmcda == NULL)  return MCIERR_INVALID_DEVICE_ID;
01166     if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
01167 
01168     position = dwFlags & (MCI_SEEK_TO_START|MCI_SEEK_TO_END|MCI_TO);
01169     if (!position)      return MCIERR_MISSING_PARAMETER;
01170     if (position&(position-1))  return MCIERR_FLAGS_NOT_COMPATIBLE;
01171 
01172     /* Stop sends MCI_NOTIFY_ABORTED when needed.
01173      * Tests show that native first sends ABORTED and reads the TOC,
01174      * then only checks the position flags, then stops and seeks. */
01175     MCICDA_Stop(wDevID, MCI_WAIT, 0);
01176 
01177     if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
01178                          &toc, sizeof(toc), &br, NULL)) {
01179         WARN("error reading TOC !\n");
01180         return MCICDA_GetError(wmcda);
01181     }
01182     switch (position) {
01183     case MCI_SEEK_TO_START:
01184     TRACE("Seeking to start\n");
01185     at = FRAME_OF_TOC(toc,toc.FirstTrack);
01186     if ( (ret=MCICDA_SkipDataTracks(wmcda, &at)) )
01187       return ret;
01188     break;
01189     case MCI_SEEK_TO_END:
01190     TRACE("Seeking to end\n");
01191     /* End is prior to lead-out
01192      * yet Win9X seeks to even one frame less than that. */
01193     at = FRAME_OF_TOC(toc, toc.LastTrack + 1) - 1;
01194     if ( (ret=MCICDA_SkipDataTracks(wmcda, &at)) )
01195       return ret;
01196     break;
01197     case MCI_TO:
01198     TRACE("Seeking to %u\n", lpParms->dwTo);
01199         at = MCICDA_CalcFrame(wmcda, lpParms->dwTo);
01200     if ( (ret=MCICDA_SkipDataTracks(wmcda, &at)) )
01201       return ret;
01202     break;
01203     default:
01204     return MCIERR_FLAGS_NOT_COMPATIBLE;
01205     }
01206 
01207     {
01208         seek.M = at / CDFRAMES_PERMIN;
01209         seek.S = (at / CDFRAMES_PERSEC) % 60;
01210         seek.F = at % CDFRAMES_PERSEC;
01211         if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_SEEK_AUDIO_MSF, &seek, sizeof(seek),
01212                              NULL, 0, &br, NULL))
01213             return MCIERR_HARDWARE;
01214     }
01215 
01216     if (dwFlags & MCI_NOTIFY)
01217     MCICDA_Notify(lpParms->dwCallback, wmcda, MCI_NOTIFY_SUCCESSFUL);
01218     return 0;
01219 }
01220 
01221 /**************************************************************************
01222  *              MCICDA_SetDoor          [internal]
01223  */
01224 static DWORD    MCICDA_SetDoor(UINT wDevID, BOOL open)
01225 {
01226     WINE_MCICDAUDIO*    wmcda = MCICDA_GetOpenDrv(wDevID);
01227     DWORD               br;
01228 
01229     TRACE("(%04x, %s) !\n", wDevID, (open) ? "OPEN" : "CLOSE");
01230 
01231     if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
01232 
01233     if (!DeviceIoControl(wmcda->handle,
01234                          (open) ? IOCTL_STORAGE_EJECT_MEDIA : IOCTL_STORAGE_LOAD_MEDIA,
01235                          NULL, 0, NULL, 0, &br, NULL))
01236     return MCIERR_HARDWARE;
01237 
01238     return 0;
01239 }
01240 
01241 /**************************************************************************
01242  *              MCICDA_Set          [internal]
01243  */
01244 static DWORD MCICDA_Set(UINT wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
01245 {
01246     WINE_MCICDAUDIO*    wmcda = MCICDA_GetOpenDrv(wDevID);
01247 
01248     TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
01249 
01250     if (wmcda == NULL)  return MCIERR_INVALID_DEVICE_ID;
01251 
01252     if (dwFlags & MCI_SET_DOOR_OPEN) {
01253     MCICDA_SetDoor(wDevID, TRUE);
01254     }
01255     if (dwFlags & MCI_SET_DOOR_CLOSED) {
01256     MCICDA_SetDoor(wDevID, FALSE);
01257     }
01258 
01259     /* only functions which require valid lpParms below this line ! */
01260     if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
01261     /*
01262       TRACE("dwTimeFormat=%08lX\n", lpParms->dwTimeFormat);
01263     */
01264     if (dwFlags & MCI_SET_TIME_FORMAT) {
01265     switch (lpParms->dwTimeFormat) {
01266     case MCI_FORMAT_MILLISECONDS:
01267         TRACE("MCI_FORMAT_MILLISECONDS !\n");
01268         break;
01269     case MCI_FORMAT_MSF:
01270         TRACE("MCI_FORMAT_MSF !\n");
01271         break;
01272     case MCI_FORMAT_TMSF:
01273         TRACE("MCI_FORMAT_TMSF !\n");
01274         break;
01275     default:
01276         return MCIERR_BAD_TIME_FORMAT;
01277     }
01278     wmcda->dwTimeFormat = lpParms->dwTimeFormat;
01279     }
01280     if (dwFlags & MCI_SET_AUDIO) /* one xp machine ignored it */
01281     TRACE("SET_AUDIO %X %x\n", dwFlags, lpParms->dwAudio);
01282 
01283     if (dwFlags & MCI_NOTIFY)
01284     MCICDA_Notify(lpParms->dwCallback, wmcda, MCI_NOTIFY_SUCCESSFUL);
01285     return 0;
01286 }
01287 
01288 /**************************************************************************
01289  *          DriverProc (MCICDA.@)
01290  */
01291 LRESULT CALLBACK MCICDA_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
01292                                    LPARAM dwParam1, LPARAM dwParam2)
01293 {
01294     switch(wMsg) {
01295     case DRV_LOAD:      return 1;
01296     case DRV_FREE:      return 1;
01297     case DRV_OPEN:      return MCICDA_drvOpen((LPCWSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSW)dwParam2);
01298     case DRV_CLOSE:     return MCICDA_drvClose(dwDevID);
01299     case DRV_ENABLE:        return 1;
01300     case DRV_DISABLE:       return 1;
01301     case DRV_QUERYCONFIGURE:    return 1;
01302     case DRV_CONFIGURE:     MessageBoxA(0, "MCI audio CD driver !", "Wine Driver", MB_OK); return 1;
01303     case DRV_INSTALL:       return DRVCNF_RESTART;
01304     case DRV_REMOVE:        return DRVCNF_RESTART;
01305     }
01306 
01307     if (dwDevID == 0xFFFFFFFF) return MCIERR_UNSUPPORTED_FUNCTION;
01308 
01309     switch (wMsg) {
01310     case MCI_OPEN_DRIVER:   return MCICDA_Open(dwDevID, dwParam1, (LPMCI_OPEN_PARMSW)dwParam2);
01311     case MCI_CLOSE_DRIVER:  return MCICDA_Close(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
01312     case MCI_GETDEVCAPS:    return MCICDA_GetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS)dwParam2);
01313     case MCI_INFO:      return MCICDA_Info(dwDevID, dwParam1, (LPMCI_INFO_PARMSW)dwParam2);
01314     case MCI_STATUS:        return MCICDA_Status(dwDevID, dwParam1, (LPMCI_STATUS_PARMS)dwParam2);
01315     case MCI_SET:       return MCICDA_Set(dwDevID, dwParam1, (LPMCI_SET_PARMS)dwParam2);
01316     case MCI_PLAY:      return MCICDA_Play(dwDevID, dwParam1, (LPMCI_PLAY_PARMS)dwParam2);
01317     case MCI_STOP:      return MCICDA_Stop(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
01318     case MCI_PAUSE:     return MCICDA_Pause(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
01319     case MCI_RESUME:        return MCICDA_Resume(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
01320     case MCI_SEEK:      return MCICDA_Seek(dwDevID, dwParam1, (LPMCI_SEEK_PARMS)dwParam2);
01321     /* commands that should report an error as they are not supported in
01322      * the native version */
01323     case MCI_RECORD:
01324     case MCI_LOAD:
01325     case MCI_SAVE:
01326     return MCIERR_UNSUPPORTED_FUNCTION;
01327     case MCI_BREAK:
01328     case MCI_FREEZE:
01329     case MCI_PUT:
01330     case MCI_REALIZE:
01331     case MCI_UNFREEZE:
01332     case MCI_UPDATE:
01333     case MCI_WHERE:
01334     case MCI_STEP:
01335     case MCI_SPIN:
01336     case MCI_ESCAPE:
01337     case MCI_COPY:
01338     case MCI_CUT:
01339     case MCI_DELETE:
01340     case MCI_PASTE:
01341     case MCI_WINDOW:
01342     TRACE("Unsupported command [0x%x]\n", wMsg);
01343     break;
01344     case MCI_OPEN:
01345     case MCI_CLOSE:
01346     ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
01347     break;
01348     default:
01349     TRACE("Sending msg [0x%x] to default driver proc\n", wMsg);
01350     return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
01351     }
01352     return MCIERR_UNRECOGNIZED_COMMAND;
01353 }
01354 
01355 /*-----------------------------------------------------------------------*/

Generated on Fri May 25 2012 04:22:48 for ReactOS by doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.