PATCH: to implement toc and position shadowing in cdrom

Waldeck Schutzer schutzer at math.rutgers.edu
Sat Feb 15 21:53:46 CST 2003


Hi Eric,

Sorry to keep you on hold for so long. Here is my proposal for toc and 
position shadowing that should address those performance and functional 
issues we have already discussed and are summarized below. I've tested 
it with cdplayer and it seems to be working pretty well now. To my 
surprise and complete dismay it even worked the first time! It's ugly, I 
know, but it should do it. I don't dare to delve into the BSD parts. 
I'll leave these to a BSD expert.

Best,
Waldeck

Issues addressed by this patch
1. cdrom.c
1.1 Some systems/drives are very slow to read the TOC. To address this 
issue, we are shadowing it inside the driver.
1.2 Windows will seek while not playing, Linux will not. We are 
providing better compatibility with Windows by also shadowing the 
current position.

2. mcicda.c
Windows will play data tracks as audio silently, Linux won't play at 
all. With this patch we are simply avoiding playing data tracks. This is 
important, otherwise CDs beginning with a data track will not start 
playing unless you seek to the next audio track first. Since seeking to 
the next track while stopped was impossible before the above patch to 
cdrom.c, these disks would not play.
-------------- next part --------------
Index: wine/dlls/ntdll/cdrom.c
===================================================================
RCS file: /home/wine/wine/dlls/ntdll/cdrom.c,v
retrieving revision 1.24
diff -u -p -r1.24 cdrom.c
--- wine/dlls/ntdll/cdrom.c	23 Jan 2003 21:32:36 -0000	1.24
+++ wine/dlls/ntdll/cdrom.c	16 Feb 2003 03:32:48 -0000
@@ -33,6 +33,7 @@
 # include <unistd.h>
 #endif
 #include <fcntl.h>
+#include <stdlib.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 
@@ -72,8 +73,55 @@
 #include "file.h"
 #include "wine/debug.h"
 
+
+static struct iocodexs 
+{ 
+  DWORD code; 
+  char *codex; 
+} iocodextable[] = {
+{IOCTL_CDROM_UNLOAD_DRIVER, "IOCTL_CDROM_UNLOAD_DRIVER"},
+{IOCTL_CDROM_READ_TOC, "IOCTL_CDROM_READ_TOC"},
+{IOCTL_CDROM_GET_CONTROL, "IOCTL_CDROM_GET_CONTROL"},
+{IOCTL_CDROM_PLAY_AUDIO_MSF, "IOCTL_CDROM_PLAY_AUDIO_MSF"},
+{IOCTL_CDROM_SEEK_AUDIO_MSF, "IOCTL_CDROM_SEEK_AUDIO_MSF"},
+{IOCTL_CDROM_STOP_AUDIO, "IOCTL_CDROM_STOP_AUDIO"},
+{IOCTL_CDROM_PAUSE_AUDIO, "IOCTL_CDROM_PAUSE_AUDIO"},
+{IOCTL_CDROM_RESUME_AUDIO, "IOCTL_CDROM_RESUME_AUDIO"},
+{IOCTL_CDROM_GET_VOLUME, "IOCTL_CDROM_GET_VOLUME"},
+{IOCTL_CDROM_SET_VOLUME, "IOCTL_CDROM_SET_VOLUME"},
+{IOCTL_CDROM_READ_Q_CHANNEL, "IOCTL_CDROM_READ_Q_CHANNEL"},
+{IOCTL_CDROM_GET_LAST_SESSION, "IOCTL_CDROM_GET_LAST_SESSION"},
+{IOCTL_CDROM_RAW_READ, "IOCTL_CDROM_RAW_READ"},
+{IOCTL_CDROM_DISK_TYPE, "IOCTL_CDROM_DISK_TYPE"},
+{IOCTL_CDROM_GET_DRIVE_GEOMETRY, "IOCTL_CDROM_GET_DRIVE_GEOMETRY"},
+{IOCTL_CDROM_CHECK_VERIFY, "IOCTL_CDROM_CHECK_VERIFY"},
+{IOCTL_CDROM_MEDIA_REMOVAL, "IOCTL_CDROM_MEDIA_REMOVAL"},
+{IOCTL_CDROM_EJECT_MEDIA, "IOCTL_CDROM_EJECT_MEDIA"},
+{IOCTL_CDROM_LOAD_MEDIA, "IOCTL_CDROM_LOAD_MEDIA"},
+{IOCTL_CDROM_RESERVE, "IOCTL_CDROM_RESERVE"},
+{IOCTL_CDROM_RELEASE, "IOCTL_CDROM_RELEASE"},
+{IOCTL_CDROM_FIND_NEW_DEVICES, "IOCTL_CDROM_FIND_NEW_DEVICES"}
+};
+char *iocodex(DWORD code)
+{
+   int i;
+   static char buffer[25];
+   for(i=0; i<sizeof(iocodextable)/sizeof(struct iocodexs); i++)
+      if (code==iocodextable[i].code)
+	 return iocodextable[i].codex;
+   sprintf(buffer, "IOCTL_CODE_%x", (int)code);
+   return buffer;
+}
+
 WINE_DEFAULT_DEBUG_CHANNEL(cdrom);
 
+#define FRAME_OF_ADDR(a) (((int)(a)[1] * CD_SECS + (a)[2]) * CD_FRAMES + (a)[3])
+#define FRAME_OF_MSF(a) (((int)(a).M * CD_SECS + (a).S) * CD_FRAMES + (a).F)
+#define FRAME_OF_TOC(toc, idx)  FRAME_OF_ADDR((toc).TrackData[idx - (toc).FirstTrack].Address)
+static DWORD CDROM_ReadTOC(int, CDROM_TOC*);
+static DWORD CDROM_GetStatusCode(int);
+
+
 #ifdef linux
 
 # ifndef IDE6_MAJOR
@@ -105,13 +153,119 @@ struct linux_cdrom_generic_command
 
 /* FIXME: this is needed because we can't open simultaneously several times /dev/cdrom
  * this should be removed when a proper device interface is implemented
+ * 
+ * (WS) We need this to keep track of current position and to safely
+ * detect media changes. Besides this should provide a great speed up
+ * for toc inquiries.
  */
 struct cdrom_cache {
     int fd;
     int count;
+    struct cdrom_tochdr	hdr;
+    struct cdrom_tocentry *entry;
+    SUB_Q_CURRENT_POSITION CurrentPosition;
 };
 static struct cdrom_cache cdrom_cache[26];
 
+/* Proposed media change function: not really needed at this time */
+/* This is a 1 or 0 type of function */
+#if 0
+static int CDROM_MediaChanged(int dev)
+{
+   int i;
+
+   struct cdrom_tochdr	hdr;
+   struct cdrom_tocentry entry;
+
+   if (dev < 0 || dev >= 26)
+      return 0;
+   if ( ioctl(dev, CDROMREADTOCHDR, &hdr) == -1 )
+      return 0;
+
+   if ( memcmp(&hdr, &cdrom_cache[dev].hdr, sizeof(struct cdrom_tochdr)) )
+      return 1;
+
+   for (i=hdr.cdth_trk0; i<=hdr.cdth_trk1+1; i++)
+   {
+      if (i == hdr.cdth_trk1 + 1)
+      {
+	 entry.cdte_track = CDROM_LEADOUT;
+      } else {
+         entry.cdte_track = i;
+      }
+      entry.cdte_format = CDROM_MSF;
+      if ( ioctl(dev, CDROMREADTOCENTRY, &entry) == -1)
+	 return 0;
+      if ( memcmp(&entry, cdrom_cache[dev].entry+i-hdr.cdth_trk0,
+			      sizeof(struct cdrom_tocentry)) )
+	 return 1;
+   }
+   return 0;
+}
+#endif
+static int CDROM_SyncCache(int dev)
+{
+   int i, io = 0;
+   struct cdrom_tochdr		*hdr;
+   struct cdrom_tocentry	*entry;
+
+   if (dev < 0 || dev >= 26)
+      return STATUS_INVALID_PARAMETER;
+
+   hdr = &cdrom_cache[dev].hdr;
+   io = ioctl(dev, CDROMREADTOCHDR, hdr);
+   if (io == -1)
+   {
+      WARN("(%d) -- Error occurred (%s)!\n", dev, strerror(errno));
+      goto end;
+   }
+   
+   TRACE("caching toc from=%d to=%d\n", hdr->cdth_trk0, hdr->cdth_trk1);
+
+   if ( cdrom_cache[dev].entry )
+      free( cdrom_cache[dev].entry );
+   entry =
+      malloc( (hdr->cdth_trk1-hdr->cdth_trk0+2)*sizeof(struct cdrom_tocentry) );
+   cdrom_cache[dev].entry = entry;
+   if (!entry) {
+      hdr->cdth_trk0 = 0;
+      hdr->cdth_trk1 = -1;
+      return STATUS_NO_MEMORY;
+   }
+
+   for (i = hdr->cdth_trk0; i <= hdr->cdth_trk1 + 1; i++)
+   {
+      if (i == hdr->cdth_trk1 + 1)
+      {
+         entry->cdte_track = CDROM_LEADOUT;
+      } else {
+         entry->cdte_track = i;
+      }
+      entry->cdte_format = CDROM_MSF;
+      io = ioctl(dev, CDROMREADTOCENTRY, entry);
+      if (io == -1) {
+         WARN("error read entry (%s)\n", strerror(errno));
+         goto end;
+      }
+      entry++;
+    }
+end:
+   return CDROM_GetStatusCode(io);
+}
+
+static void CDROM_ClearCacheEntry(int dev)
+{
+   if (dev >= 0 && dev < 26) {
+      if ( cdrom_cache[dev].entry )
+	 free( cdrom_cache[dev].entry );
+      cdrom_cache[dev].entry = NULL;
+      cdrom_cache[dev].hdr.cdth_trk0 = 0;
+      cdrom_cache[dev].hdr.cdth_trk1 = -1;
+   }
+}
+
+
+
 /******************************************************************
  *		CDROM_GetIdeInterface
  *
@@ -327,6 +481,7 @@ static int CDROM_Open(HANDLE hDevice, DW
         }
     }
     cdrom_cache[dev].count++;
+    TRACE("%d, %d, %d\n", dev, cdrom_cache[dev].fd, cdrom_cache[dev].count);
     return cdrom_cache[dev].fd;
 }
 
@@ -361,6 +516,9 @@ static DWORD CDROM_GetStatusCode(int io)
 	    return STATUS_NO_MEDIA_IN_DEVICE;
     case EPERM:
 	    return STATUS_ACCESS_DENIED;
+    case EINVAL:
+	    return STATUS_INVALID_PARAMETER;
+    /* case EBADF: Bad file descriptor */
     }
     FIXME("Unmapped error code %d: %s\n", errno, strerror(errno));
     return STATUS_IO_DEVICE_ERROR;
@@ -380,15 +538,22 @@ static DWORD CDROM_GetDeviceNumber(int d
 
 static DWORD CDROM_GetDriveGeometry(int dev, DISK_GEOMETRY* dg)
 {
-#if 0
-    dg->Cylinders.s.LowPart = 1; /* FIXME */
-    dg->Cylinders.s.HighPart = 0; /* FIXME */
-    dg->MediaType = 1; /* FIXME */
-    dg->TracksPerCylinder = 1; /* FIXME */
-    dg->SectorsPerTrack = 1; /* FIXME */
-    dg->BytesPerSector= 1; /* FIXME */
-#endif
-    return STATUS_NOT_SUPPORTED;
+  CDROM_TOC   toc;
+  DWORD ret = 0;
+  int fsize=0;
+
+  if ((ret = CDROM_ReadTOC(dev, &toc)) != 0) return ret;
+
+  fsize = FRAME_OF_TOC(toc, toc.LastTrack+1)
+        - FRAME_OF_TOC(toc, 1); /* Total size in frames */
+  
+  dg->Cylinders.s.LowPart = fsize / (64 * 32); 
+  dg->Cylinders.s.HighPart = 0; 
+  dg->MediaType = RemovableMedia;  
+  dg->TracksPerCylinder = 64; 
+  dg->SectorsPerTrack = 32;  
+  dg->BytesPerSector= 2048; 
+  return ret;
 }
 
 /**************************************************************************
@@ -449,23 +614,39 @@ static DWORD CDROM_ReadTOC(int dev, CDRO
     DWORD       ret = STATUS_NOT_SUPPORTED;
 
 #if defined(linux)
-    int                         i, io = -1;
-    struct cdrom_tochdr		hdr;
-    struct cdrom_tocentry	entry;
+    int                         i, tsz;
+    struct cdrom_tochdr		*hdr;
+    struct cdrom_tocentry	*entry;
 
+#if 0
     io = ioctl(dev, CDROMREADTOCHDR, &hdr);
     if (io == -1)
     {
         WARN("(%d) -- Error occurred (%s)!\n", dev, strerror(errno));
         goto end;
     }
-    toc->FirstTrack = hdr.cdth_trk0;
-    toc->LastTrack  = hdr.cdth_trk1;
+#endif
+    if (dev < 0 || dev >= 26)
+       return STATUS_INVALID_PARAMETER;
+    if ( !cdrom_cache[dev].entry ) {
+       int ret;
+       ret = CDROM_SyncCache(dev);
+       if ( ret )
+	  return ret;
+    }
+    hdr = &cdrom_cache[dev].hdr;
+    toc->FirstTrack = hdr->cdth_trk0;
+    toc->LastTrack  = hdr->cdth_trk1;
+    tsz = sizeof(toc->FirstTrack) + sizeof(toc->LastTrack)
+       + sizeof(TRACK_DATA) * (toc->LastTrack-toc->FirstTrack+2);
+    toc->Length[0] = tsz >> 8;
+    toc->Length[1] = tsz;
 
     TRACE("from=%d to=%d\n", toc->FirstTrack, toc->LastTrack);
 
     for (i = toc->FirstTrack; i <= toc->LastTrack + 1; i++)
     {
+#if 0	    
 	if (i == toc->LastTrack + 1)
         {
 	    entry.cdte_track = CDROM_LEADOUT;
@@ -478,17 +659,22 @@ static DWORD CDROM_ReadTOC(int dev, CDRO
 	    WARN("error read entry (%s)\n", strerror(errno));
 	    goto end;
 	}
-        toc->TrackData[i - toc->FirstTrack].Control = entry.cdte_ctrl;
-        toc->TrackData[i - toc->FirstTrack].Adr = entry.cdte_adr;
+#endif	
+	entry = cdrom_cache[dev].entry+i-toc->FirstTrack;
+        toc->TrackData[i - toc->FirstTrack].Control = entry->cdte_ctrl;
+        toc->TrackData[i - toc->FirstTrack].Adr = entry->cdte_adr;
         /* marking last track with leadout value as index */
-        toc->TrackData[i - toc->FirstTrack].TrackNumber = entry.cdte_track;
+        toc->TrackData[i - toc->FirstTrack].TrackNumber = entry->cdte_track;
         toc->TrackData[i - toc->FirstTrack].Address[0] = 0;
-        toc->TrackData[i - toc->FirstTrack].Address[1] = entry.cdte_addr.msf.minute;
-        toc->TrackData[i - toc->FirstTrack].Address[2] = entry.cdte_addr.msf.second;
-        toc->TrackData[i - toc->FirstTrack].Address[3] = entry.cdte_addr.msf.frame;
+        toc->TrackData[i - toc->FirstTrack].Address[1] = entry->cdte_addr.msf.minute;
+        toc->TrackData[i - toc->FirstTrack].Address[2] = entry->cdte_addr.msf.second;
+        toc->TrackData[i - toc->FirstTrack].Address[3] = entry->cdte_addr.msf.frame;
     }
+    ret = 0;
+#if 0
 end:
     ret = CDROM_GetStatusCode(io);
+#endif
 #elif defined(__FreeBSD__) || defined(__NetBSD__)
     int                         i, io = -1;
     struct ioc_toc_header	hdr;
@@ -503,6 +689,10 @@ end:
     }
     toc->FirstTrack = hdr.starting_track;
     toc->LastTrack  = hdr.ending_track;
+    tsz = sizeof(toc->FirstTrack) + sizeof(toc->LastTrack)
+        + sizeof(TRACK_DATA) * (toc->LastTrack-toc->FirstTrack+2);
+    toc->Length[0] = tsz >> 8;
+    toc->Length[1] = tsz;
 
     TRACE("from=%d to=%d\n", toc->FirstTrack, toc->LastTrack);
 
@@ -555,7 +745,7 @@ static DWORD CDROM_GetDiskData(int dev, 
     if ((ret = CDROM_ReadTOC(dev, &toc)) != 0) return ret;
     data->DiskData = 0;
     for (i = toc.FirstTrack; i <= toc.LastTrack; i++) {
-        if (toc.TrackData[i].Control & 0x04)
+        if (toc.TrackData[i-toc.FirstTrack].Control & 0x04)
             data->DiskData |= CDROM_DISK_DATA_TRACK;
         else
             data->DiskData |= CDROM_DISK_AUDIO_TRACK;
@@ -584,6 +774,7 @@ static DWORD CDROM_ReadQChannel(int dev,
     {
 	TRACE("opened or no_media (%s)!\n", strerror(errno));
 	hdr->AudioStatus = AUDIO_STATUS_NO_STATUS;
+	CDROM_ClearCacheEntry(dev);
 	goto end;
     }
 
@@ -591,9 +782,11 @@ static DWORD CDROM_ReadQChannel(int dev,
 
     switch (sc.cdsc_audiostatus) {
     case CDROM_AUDIO_INVALID:
+	CDROM_ClearCacheEntry(dev);
 	hdr->AudioStatus = AUDIO_STATUS_NOT_SUPPORTED;
 	break;
     case CDROM_AUDIO_NO_STATUS:
+	CDROM_ClearCacheEntry(dev);
 	hdr->AudioStatus = AUDIO_STATUS_NO_STATUS;
 	break;
     case CDROM_AUDIO_PLAY:
@@ -616,21 +809,30 @@ static DWORD CDROM_ReadQChannel(int dev,
     {
     case IOCTL_CDROM_CURRENT_POSITION:
         size = sizeof(SUB_Q_CURRENT_POSITION);
-        data->CurrentPosition.FormatCode = sc.cdsc_format;
-        data->CurrentPosition.Control = sc.cdsc_ctrl;
-        data->CurrentPosition.ADR = sc.cdsc_adr;
-        data->CurrentPosition.TrackNumber = sc.cdsc_trk;
-        data->CurrentPosition.IndexNumber = sc.cdsc_ind;
-
-        data->CurrentPosition.AbsoluteAddress[0] = 0;
-        data->CurrentPosition.AbsoluteAddress[1] = sc.cdsc_absaddr.msf.minute;
-        data->CurrentPosition.AbsoluteAddress[2] = sc.cdsc_absaddr.msf.second;
-        data->CurrentPosition.AbsoluteAddress[3] = sc.cdsc_absaddr.msf.frame;
+	if (hdr->AudioStatus==AUDIO_STATUS_IN_PROGRESS) {
+          data->CurrentPosition.FormatCode = sc.cdsc_format; 
+          data->CurrentPosition.Control = sc.cdsc_ctrl; 
+          data->CurrentPosition.ADR = sc.cdsc_adr; 
+          data->CurrentPosition.TrackNumber = sc.cdsc_trk; 
+          data->CurrentPosition.IndexNumber = sc.cdsc_ind; 
+
+          data->CurrentPosition.AbsoluteAddress[0] = 0; 
+          data->CurrentPosition.AbsoluteAddress[1] = sc.cdsc_absaddr.msf.minute; 
+          data->CurrentPosition.AbsoluteAddress[2] = sc.cdsc_absaddr.msf.second;
+          data->CurrentPosition.AbsoluteAddress[3] = sc.cdsc_absaddr.msf.frame;
+ 
+          data->CurrentPosition.TrackRelativeAddress[0] = 0; 
+          data->CurrentPosition.TrackRelativeAddress[1] = sc.cdsc_reladdr.msf.minute; 
+          data->CurrentPosition.TrackRelativeAddress[2] = sc.cdsc_reladdr.msf.second;
+          data->CurrentPosition.TrackRelativeAddress[3] = sc.cdsc_reladdr.msf.frame;
 
-        data->CurrentPosition.TrackRelativeAddress[0] = 0;
-        data->CurrentPosition.TrackRelativeAddress[1] = sc.cdsc_reladdr.msf.minute;
-        data->CurrentPosition.TrackRelativeAddress[2] = sc.cdsc_reladdr.msf.second;
-        data->CurrentPosition.TrackRelativeAddress[3] = sc.cdsc_reladdr.msf.frame;
+	  cdrom_cache[dev].CurrentPosition = data->CurrentPosition;
+	}
+	else /* not playing */
+	{
+	  cdrom_cache[dev].CurrentPosition.Header = *hdr; /* Preserve header info */
+	  data->CurrentPosition = cdrom_cache[dev].CurrentPosition;
+	}
         break;
     case IOCTL_CDROM_MEDIA_CATALOG:
         size = sizeof(SUB_Q_MEDIA_CATALOG_NUMBER);
@@ -843,11 +1045,56 @@ static DWORD CDROM_SeekAudioMSF(int dev,
 {
 #if defined(linux)
     struct cdrom_msf0	msf;
-    msf.minute = audio_msf->M;
-    msf.second = audio_msf->S;
-    msf.frame  = audio_msf->F;
+    CDROM_TOC   toc;
+    int i, frame, ret;
+    struct cdrom_subchnl sc;
+    SUB_Q_CURRENT_POSITION *cp;
+    
+    sc.cdsc_format = CDROM_MSF;
+
+    ret = ioctl(dev, CDROMSUBCHNL, &sc);
+    if (ret == -1)
+    {
+	TRACE("opened or no_media (%s)!\n", strerror(errno));
+	CDROM_ClearCacheEntry(dev);
+        return CDROM_GetStatusCode(ret);
+    }
+    if (dev>=0 && dev<26)
+    {
+      frame = FRAME_OF_MSF(*audio_msf);
+      cp = &cdrom_cache[dev].CurrentPosition;
+      if ((ret = CDROM_ReadTOC(dev, &toc)) != 0) return ret;
+     
+      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)
+	 return STATUS_INVALID_PARAMETER;
+      i--;
+      cp->FormatCode = CDROM_MSF; 
+      cp->Control = toc.TrackData[i-toc.FirstTrack].Control; 
+      cp->ADR = toc.TrackData[i-toc.FirstTrack].Adr; 
+      cp->TrackNumber = toc.TrackData[i-toc.FirstTrack].TrackNumber;
+      cp->IndexNumber = 0; /* FIXME: how do I keep these? */
+      cp->AbsoluteAddress[0] = 0; 
+      cp->AbsoluteAddress[1] = toc.TrackData[i-toc.FirstTrack].Address[1];
+      cp->AbsoluteAddress[2] = toc.TrackData[i-toc.FirstTrack].Address[2];
+      cp->AbsoluteAddress[3] = toc.TrackData[i-toc.FirstTrack].Address[3];
+      frame -= FRAME_OF_TOC(toc,i);
+      cp->TrackRelativeAddress[0] = 0; 
+      cp->TrackRelativeAddress[3] = frame % CD_FRAMES;
+      frame /= CD_FRAMES;
+      cp->TrackRelativeAddress[2] = frame % CD_SECS;
+      cp->TrackRelativeAddress[1] = frame / CD_SECS;
+    }
+    if (sc.cdsc_audiostatus==CDROM_AUDIO_PLAY) 
+    {
+      msf.minute = audio_msf->M;
+      msf.second = audio_msf->S;
+      msf.frame  = audio_msf->F;
+      return CDROM_GetStatusCode(ioctl(dev, CDROMSEEK, &msf));
+    }
+    return 0;
 
-    return CDROM_GetStatusCode(ioctl(dev, CDROMSEEK, &msf));
 #elif defined(__FreeBSD__) || defined(__NetBSD__)
     FIXME("Could a BSD expert implement the seek function ?\n");
     return STATUS_NOT_SUPPORTED;
@@ -1243,8 +1490,8 @@ BOOL CDROM_DeviceIoControl(DWORD clientI
     DWORD       error = 0;
     int         dev;
 
-    TRACE("%lx[%c] %lx %lx %ld %lx %ld %lx %lx\n",
-          (DWORD)hDevice, 'A' + LOWORD(clientID), dwIoControlCode, (DWORD)lpInBuffer, nInBufferSize,
+    TRACE("%lx[%c] %s %lx %ld %lx %ld %lx %lx\n",
+          (DWORD)hDevice, 'A' + LOWORD(clientID), iocodex(dwIoControlCode), (DWORD)lpInBuffer, nInBufferSize,
           (DWORD)lpOutBuffer, nOutBufferSize, (DWORD)lpBytesReturned, (DWORD)lpOverlapped);
 
     if (lpBytesReturned) *lpBytesReturned = 0;
@@ -1268,6 +1515,7 @@ BOOL CDROM_DeviceIoControl(DWORD clientI
     case IOCTL_STORAGE_CHECK_VERIFY:
     case IOCTL_CDROM_CHECK_VERIFY:
         sz = 0;
+	CDROM_ClearCacheEntry(dev);
         if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
             error = STATUS_INVALID_PARAMETER;
         else error = CDROM_Verify(dev);
@@ -1281,12 +1529,14 @@ BOOL CDROM_DeviceIoControl(DWORD clientI
     case IOCTL_STORAGE_LOAD_MEDIA:
     case IOCTL_CDROM_LOAD_MEDIA:
         sz = 0;
+	CDROM_ClearCacheEntry(dev);
         if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
             error = STATUS_INVALID_PARAMETER;
         else error = CDROM_SetTray(dev, FALSE);
         break;
      case IOCTL_STORAGE_EJECT_MEDIA:
         sz = 0;
+	CDROM_ClearCacheEntry(dev);
         if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
             error = STATUS_INVALID_PARAMETER;
         else error = CDROM_SetTray(dev, TRUE);
@@ -1299,6 +1549,7 @@ BOOL CDROM_DeviceIoControl(DWORD clientI
         /* FIXME the last ioctl:s is not the same as the two others...
          * lockcount/owner should be handled */
         sz = 0;
+	CDROM_ClearCacheEntry(dev);
         if (lpOutBuffer != NULL || nOutBufferSize != 0) error = STATUS_INVALID_PARAMETER;
         else if (nInBufferSize < sizeof(PREVENT_MEDIA_REMOVAL)) error = STATUS_BUFFER_TOO_SMALL;
         else error = CDROM_ControlEjection(dev, (const PREVENT_MEDIA_REMOVAL*)lpInBuffer);
@@ -1315,6 +1566,7 @@ BOOL CDROM_DeviceIoControl(DWORD clientI
 
     case IOCTL_STORAGE_RESET_DEVICE:
         sz = 0;
+	CDROM_ClearCacheEntry(dev);
         if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
             error = STATUS_INVALID_PARAMETER;
         else error = CDROM_ResetAudio(dev);
@@ -1336,6 +1588,7 @@ BOOL CDROM_DeviceIoControl(DWORD clientI
 
     case IOCTL_CDROM_DISK_TYPE:
         sz = sizeof(CDROM_DISK_DATA);
+	/* CDROM_ClearCacheEntry(dev); */
         if (lpInBuffer != NULL || nInBufferSize != 0) error = STATUS_INVALID_PARAMETER;
         else if (nOutBufferSize < sz) error = STATUS_BUFFER_TOO_SMALL;
         else error = CDROM_GetDiskData(dev, (CDROM_DISK_DATA*)lpOutBuffer);
@@ -1387,6 +1640,7 @@ BOOL CDROM_DeviceIoControl(DWORD clientI
         break;
     case IOCTL_CDROM_STOP_AUDIO:
         sz = 0;
+	CDROM_ClearCacheEntry(dev); /* Maybe intention is to change media */
         if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
             error = STATUS_INVALID_PARAMETER;
         else error = CDROM_StopAudio(dev);
@@ -1399,6 +1653,7 @@ BOOL CDROM_DeviceIoControl(DWORD clientI
         break;
     case IOCTL_CDROM_SET_VOLUME:
         sz = 0;
+	CDROM_ClearCacheEntry(dev);
         if (lpInBuffer == NULL || nInBufferSize < sizeof(VOLUME_CONTROL) || lpOutBuffer != NULL)
             error = STATUS_INVALID_PARAMETER;
         else error = CDROM_SetVolume(dev, (const VOLUME_CONTROL*)lpInBuffer);
-------------- next part --------------
Index: wine/dlls/winmm/mcicda/mcicda.c
===================================================================
RCS file: /home/wine/wine/dlls/winmm/mcicda/mcicda.c,v
retrieving revision 1.26
diff -u -p -r1.26 mcicda.c
--- wine/dlls/winmm/mcicda/mcicda.c	17 Oct 2002 16:43:43 -0000	1.26
+++ wine/dlls/winmm/mcicda/mcicda.c	16 Feb 2003 03:25:12 -0000
@@ -187,10 +187,10 @@ static DWORD MCICDA_CalcFrame(WINE_MCICD
               MCI_TMSF_SECOND(dwTime), MCI_TMSF_FRAME(dwTime));
         addr = toc.TrackData[wTrack - toc.FirstTrack].Address;
         TRACE("TMSF trackpos[%u]=%d:%d:%d\n",
-              wTrack, addr[0], addr[1], addr[2]);
-        dwFrame = CDFRAMES_PERMIN * (addr[0] + MCI_TMSF_MINUTE(dwTime)) +
-            CDFRAMES_PERSEC * (addr[1] + MCI_TMSF_SECOND(dwTime)) +
-            addr[2] + MCI_TMSF_FRAME(dwTime);
+              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);
 	break;
     }
     return dwFrame;
@@ -689,6 +689,7 @@ static DWORD MCICDA_Play(UINT wDevID, DW
     CDROM_PLAY_AUDIO_MSF        play;
     CDROM_SUB_Q_DATA_FORMAT     fmt;
     SUB_Q_CHANNEL_DATA          data;
+    int i;
 
     TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
 
@@ -699,7 +700,20 @@ static DWORD MCICDA_Play(UINT wDevID, DW
 	return MCIERR_INVALID_DEVICE_ID;
 
     if (dwFlags & MCI_FROM) {
+        if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
+                             &toc, sizeof(toc), &br, NULL)) {
+            WARN("error reading TOC !\n");
+            return MCICDA_GetError(wmcda);
+        }
 	start = MCICDA_CalcFrame(wmcda, lpParms->dwFrom);
+	for(i=toc.FirstTrack;i<=toc.LastTrack;i++) 
+           if ( FRAME_OF_TOC(toc, i) > start ) break;
+	if (i > toc.FirstTrack) i--;
+	for(;i<=toc.LastTrack;i++)
+	   if ( ! (toc.TrackData[i-toc.FirstTrack].Control & 4) )
+	      break;
+	if ( FRAME_OF_TOC(toc, i) > start )
+	   start = FRAME_OF_TOC(toc, i);
 	TRACE("MCI_FROM=%08lX -> %lu \n", lpParms->dwFrom, start);
     } else {
         fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
@@ -819,34 +833,42 @@ static DWORD MCICDA_Seek(UINT wDevID, DW
     CDROM_SEEK_AUDIO_MSF        seek;
     CDROM_TOC                   toc;
     DWORD                       br;
+    int i;
 
     TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
 
     if (wmcda == NULL)	return MCIERR_INVALID_DEVICE_ID;
     if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
 
+    if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
+                         &toc, sizeof(toc), &br, NULL)) {
+        WARN("error reading TOC !\n");
+        return MCICDA_GetError(wmcda);
+    }
     switch (dwFlags & ~(MCI_NOTIFY|MCI_WAIT)) {
     case MCI_SEEK_TO_START:
 	TRACE("Seeking to start\n");
-        if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
-                             &toc, sizeof(toc), &br, NULL)) {
-            WARN("error reading TOC !\n");
-            return MCICDA_GetError(wmcda);
-        }
-        at = FRAME_OF_TOC(toc, toc.FirstTrack);
+	for(i=toc.FirstTrack;i<=toc.LastTrack;i++)
+	   if (!(toc.TrackData[i-toc.FirstTrack].Control & 4))
+              break;
+        at = FRAME_OF_TOC(toc, i);
+	/* FIXME: should return error if there are no audio tracks */
 	break;
     case MCI_SEEK_TO_END:
 	TRACE("Seeking to end\n");
-        if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
-                             &toc, sizeof(toc), &br, NULL)) {
-            WARN("error reading TOC !\n");
-            return MCICDA_GetError(wmcda);
-        }
 	at = FRAME_OF_TOC(toc, toc.LastTrack + 1) - 1;
 	break;
     case MCI_TO:
 	TRACE("Seeking to %lu\n", lpParms->dwTo);
-	at = lpParms->dwTo;
+        at = MCICDA_CalcFrame(wmcda, lpParms->dwTo);
+	for(i=toc.FirstTrack;i<=toc.LastTrack;i++) 
+           if ( FRAME_OF_TOC(toc, i) > at ) break;
+	if (i > toc.FirstTrack) i--;
+	for(;i<=toc.LastTrack;i++)
+	   if ( ! (toc.TrackData[i-toc.FirstTrack].Control & 4) )
+	      break;
+	if ( FRAME_OF_TOC(toc, i) > at )
+	   at = FRAME_OF_TOC(toc, i);
 	break;
     default:
 	TRACE("Unknown seek action %08lX\n",


More information about the wine-patches mailing list