Re: PATCH: to implement toc and position shadowing in cdrom

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Eric,

I followed your suggestions which greatly simplified the code. Thank you. Now, since we are using the fixed-size windows TOC, memory allocation is no longer an issue. Some of the sanity checks over dev were removed. I also made an attemt at making the code more portable and also fixed the BSD code. However, I couldn't find a seek function in the BSD cd driver, so I used the play function instead. Is there a BSD expert out there who could check this out? On the mcicda side, I factored out that piece of code you suggested but because my box is presently busy on a week-long calculation I couldn't run any tests on Windows.

Best,
Waldeck

Eric Pouech wrote:

several comments:
In cdrom.diff
* in SyncCache (and some other functions), I don't understand why you test dev against the [0,26[ range. Either, dev is the driver letter (A..Z) to which the cdrom is attached, and in that case I don't see how you could use this value as a valid fd in ioctl. Or, it's the fd opened on the cdrom, and there's no reason it would be in the [0,26[ range.
Yes, the code is misleading. The dev in CDROM_Open means the drive letter, and most of the other functions take a fd in their dev function.
* for memory allocation, better use the HeapAlloc/HeapFree instead of the malloc/free. We'll be better off in the long run (especially in debugging, monitoring...)
* I wouldn't use in the cdrom_cache structure the Linux structures. As it is, it won't compile on non Linux platforms. I'd rather use the Windows one (so that it'll ease porting to other OSes). Moreover, this would allow a better coding in cdrom.c. We could split the low-level OS dependent functions on one hand (reading toc, position...) from the highest level (including caching, seeking...). (Unless we only need caching, and seeking functionalities in Linux, which I'm not sure of).

In mcicda.diff:
* you could perhaps factorize the code that computes the correct frame address (when skipping non audio tracks).
* btw, did you test what windows was returning in this case as the set of tracks on the disk. In wonder whether all the tracks (including the non audio) are returned thru the mcicda interface (or perhaps the leading non-audio are not reported).

A+


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	17 Feb 2003 04:23:55 -0000
@@ -72,8 +72,57 @@
 #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)
+#define MSF_OF_FRAME(m,fr) {int f=(fr); ((UCHAR *)&(m))[2]=f%CD_FRAMES;f/=CD_FRAMES;((UCHAR *)&(m))[1]=f%CD_SECS;((UCHAR *)&(m))[0]=f/CD_SECS;}
+
+static DWORD CDROM_ReadTOC(int, CDROM_TOC*);
+static DWORD CDROM_GetStatusCode(int);
+
+
 #ifdef linux
 
 # ifndef IDE6_MAJOR
@@ -105,13 +154,183 @@ 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;
+    char toc_good; /* if false, will reread TOC from disk */
+    CDROM_TOC toc;
+    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
+
+/******************************************************************
+ *		CDROM_SyncCache                          [internal]
+ *
+ * Read the TOC in and store it in the cdrom_cache structure.
+ * Further requests for the TOC will be copied from the cache
+ * unless certain events like disk ejection is detected, in which
+ * case the cache will be cleared, causing it to be resynced.
+ *
+ */
+static int CDROM_SyncCache(int dev)
+{
+   int i, io = 0, tsz;
+#ifdef linux
+   struct cdrom_tochdr		hdr;
+   struct cdrom_tocentry	entry;
+#elif defined(__FreeBSD__) || defined(__NetBSD__)
+   struct ioc_toc_header	hdr;
+   struct ioc_read_toc_entry	entry;
+   struct cd_toc_entry         toc_buffer;
+#endif
+   CDROM_TOC *toc = &cdrom_cache[dev].toc;
+   cdrom_cache[dev].toc_good = 0;
+
+#ifdef linux
+
+   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);
+
+   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 (i == toc->LastTrack + 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;
+     }
+     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].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;
+    }
+    cdrom_cache[dev].toc_good = 1;
+    io = 0;
+#elif defined(__FreeBSD__) || defined(__NetBSD__)
+
+    io = ioctl(dev, CDIOREADTOCHEADER, &hdr);
+    if (io == -1)
+    {
+        WARN("(%d) -- Error occurred (%s)!\n", dev, strerror(errno));
+        goto 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("caching toc from=%d to=%d\n", hdr.cdth_trk0, hdr.cdth_trk1);
+
+    for (i = toc->FirstTrack; i <= toc->LastTrack + 1; i++)
+    {
+	if (i == toc->LastTrack + 1)
+        {
+#define LEADOUT 0xaa
+	    entry.starting_track = LEADOUT;
+        } else {
+            entry.starting_track = i;
+        }
+	memset((char *)&toc_buffer, 0, sizeof(toc_buffer));
+	entry.address_format = CD_MSF_FORMAT;
+	entry.data_len = sizeof(toc_buffer);
+	entry.data = &toc_buffer;
+	io = ioctl(dev, CDIOREADTOCENTRYS, &entry);
+	if (io == -1) {
+	    WARN("error read entry (%s)\n", strerror(errno));
+	    goto end;
+	}
+        toc->TrackData[i - toc->FirstTrack].Control = toc_buffer.control;
+        toc->TrackData[i - toc->FirstTrack].Adr = toc_buffer.addr_type;
+        /* marking last track with leadout value as index */
+        toc->TrackData[i - toc->FirstTrack].TrackNumber = entry.starting_track;
+        toc->TrackData[i - toc->FirstTrack].Address[0] = 0;
+        toc->TrackData[i - toc->FirstTrack].Address[1] = toc_buffer.addr.msf.minute;
+        toc->TrackData[i - toc->FirstTrack].Address[2] = toc_buffer.addr.msf.second;
+        toc->TrackData[i - toc->FirstTrack].Address[3] = toc_buffer.addr.msf.frame;
+    }
+    cdrom_cache[dev].toc_good = 1;
+    io = 0;
+#else
+    io = STATUS_NOT_SUPPORTED;
+#endif
+end:
+   return CDROM_GetStatusCode(io);
+}
+
+static void CDROM_ClearCacheEntry(int dev)
+{
+  cdrom_cache[dev].toc_good = 0;
+}
+
+
+
 /******************************************************************
  *		CDROM_GetIdeInterface
  *
@@ -327,6 +546,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 +581,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 +603,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;
 }
 
 /**************************************************************************
@@ -448,97 +678,15 @@ 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;
-
-    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;
-
-    TRACE("from=%d to=%d\n", toc->FirstTrack, toc->LastTrack);
-
-    for (i = toc->FirstTrack; i <= toc->LastTrack + 1; i++)
-    {
-	if (i == toc->LastTrack + 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;
-	}
-        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].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;
-    }
-end:
-    ret = CDROM_GetStatusCode(io);
-#elif defined(__FreeBSD__) || defined(__NetBSD__)
-    int                         i, io = -1;
-    struct ioc_toc_header	hdr;
-    struct ioc_read_toc_entry	entry;
-    struct cd_toc_entry         toc_buffer;
-
-    io = ioctl(dev, CDIOREADTOCHEADER, &hdr);
-    if (io == -1)
-    {
-        WARN("(%d) -- Error occurred (%s)!\n", dev, strerror(errno));
-        goto end;
-    }
-    toc->FirstTrack = hdr.starting_track;
-    toc->LastTrack  = hdr.ending_track;
-
-    TRACE("from=%d to=%d\n", toc->FirstTrack, toc->LastTrack);
-
-    for (i = toc->FirstTrack; i <= toc->LastTrack + 1; i++)
-    {
-	if (i == toc->LastTrack + 1)
-        {
-#define LEADOUT 0xaa
-	    entry.starting_track = LEADOUT;
-        } else {
-            entry.starting_track = i;
-        }
-	memset((char *)&toc_buffer, 0, sizeof(toc_buffer));
-	entry.address_format = CD_MSF_FORMAT;
-	entry.data_len = sizeof(toc_buffer);
-	entry.data = &toc_buffer;
-	io = ioctl(dev, CDIOREADTOCENTRYS, &entry);
-	if (io == -1) {
-	    WARN("error read entry (%s)\n", strerror(errno));
-	    goto end;
-	}
-        toc->TrackData[i - toc->FirstTrack].Control = toc_buffer.control;
-        toc->TrackData[i - toc->FirstTrack].Adr = toc_buffer.addr_type;
-        /* marking last track with leadout value as index */
-        toc->TrackData[i - toc->FirstTrack].TrackNumber = entry.starting_track;
-        toc->TrackData[i - toc->FirstTrack].Address[0] = 0;
-        toc->TrackData[i - toc->FirstTrack].Address[1] = toc_buffer.addr.msf.minute;
-        toc->TrackData[i - toc->FirstTrack].Address[2] = toc_buffer.addr.msf.second;
-        toc->TrackData[i - toc->FirstTrack].Address[3] = toc_buffer.addr.msf.frame;
+    if (dev < 0 || dev >= 26)
+       return STATUS_INVALID_PARAMETER;
+    if ( !cdrom_cache[dev].toc_good ) {
+       ret = CDROM_SyncCache(dev);
+       if ( ret )
+	  return ret;
     }
-end:
-    ret = CDROM_GetStatusCode(io);
-#else
-    ret = STATUS_NOT_SUPPORTED;
-#endif
-    return ret;
+    *toc = cdrom_cache[dev].toc;
+    return 0;
 }
 
 /******************************************************************
@@ -555,7 +703,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 +732,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 +740,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 +767,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;
-
-        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;
+	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;
+
+	  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);
@@ -684,6 +844,7 @@ static DWORD CDROM_ReadQChannel(int dev,
     if (io == -1)
     {
 	TRACE("opened or no_media (%s)!\n", strerror(errno));
+	CDROM_ClearCacheEntry(dev);
 	hdr->AudioStatus = AUDIO_STATUS_NO_STATUS;
 	goto end;
     }
@@ -692,9 +853,11 @@ static DWORD CDROM_ReadQChannel(int dev,
 
     switch (sc.header.audio_status) {
     case CD_AS_AUDIO_INVALID:
+	CDROM_ClearCacheEntry(dev);
 	hdr->AudioStatus = AUDIO_STATUS_NOT_SUPPORTED;
 	break;
     case CD_AS_NO_STATUS:
+	CDROM_ClearCacheEntry(dev);
 	hdr->AudioStatus = AUDIO_STATUS_NO_STATUS;
         break;
     case CD_AS_PLAY_IN_PROGRESS:
@@ -716,20 +879,27 @@ static DWORD CDROM_ReadQChannel(int dev,
     {
     case IOCTL_CDROM_CURRENT_POSITION:
         size = sizeof(SUB_Q_CURRENT_POSITION);
-        data->CurrentPosition.FormatCode = sc.what.position.data_format;
-        data->CurrentPosition.Control = sc.what.position.control;
-        data->CurrentPosition.ADR = sc.what.position.addr_type;
-        data->CurrentPosition.TrackNumber = sc.what.position.track_number;
-        data->CurrentPosition.IndexNumber = sc.what.position.index_number;
-
-        data->CurrentPosition.AbsoluteAddress[0] = 0;
-        data->CurrentPosition.AbsoluteAddress[1] = sc.what.position.absaddr.msf.minute;
-        data->CurrentPosition.AbsoluteAddress[2] = sc.what.position.absaddr.msf.second;
-        data->CurrentPosition.AbsoluteAddress[3] = sc.what.position.absaddr.msf.frame;
-        data->CurrentPosition.TrackRelativeAddress[0] = 0;
-        data->CurrentPosition.TrackRelativeAddress[1] = sc.what.position.reladdr.msf.minute;
-        data->CurrentPosition.TrackRelativeAddress[2] = sc.what.position.reladdr.msf.second;
-        data->CurrentPosition.TrackRelativeAddress[3] = sc.what.position.reladdr.msf.frame;
+	if (hdr->AudioStatus==AUDIO_STATUS_IN_PROGRESS) {
+          data->CurrentPosition.FormatCode = sc.what.position.data_format;
+          data->CurrentPosition.Control = sc.what.position.control;
+          data->CurrentPosition.ADR = sc.what.position.addr_type;
+          data->CurrentPosition.TrackNumber = sc.what.position.track_number;
+          data->CurrentPosition.IndexNumber = sc.what.position.index_number;
+
+          data->CurrentPosition.AbsoluteAddress[0] = 0;
+          data->CurrentPosition.AbsoluteAddress[1] = sc.what.position.absaddr.msf.minute;
+          data->CurrentPosition.AbsoluteAddress[2] = sc.what.position.absaddr.msf.second;
+          data->CurrentPosition.AbsoluteAddress[3] = sc.what.position.absaddr.msf.frame;
+          data->CurrentPosition.TrackRelativeAddress[0] = 0;
+          data->CurrentPosition.TrackRelativeAddress[1] = sc.what.position.reladdr.msf.minute;
+          data->CurrentPosition.TrackRelativeAddress[2] = sc.what.position.reladdr.msf.second;
+          data->CurrentPosition.TrackRelativeAddress[3] = sc.what.position.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);
@@ -841,16 +1011,88 @@ end:
  */
 static DWORD CDROM_SeekAudioMSF(int dev, const CDROM_SEEK_AUDIO_MSF* audio_msf)
 {
+    CDROM_TOC toc;
+    int i, io, frame;
+    SUB_Q_CURRENT_POSITION *cp;
 #if defined(linux)
     struct cdrom_msf0	msf;
-    msf.minute = audio_msf->M;
-    msf.second = audio_msf->S;
-    msf.frame  = audio_msf->F;
+    struct cdrom_subchnl sc;
+#elif defined(__FreeBSD__) || defined(__NetBSD__)
+    struct ioc_play_msf	msf;
+    struct ioc_read_subchannel	read_sc;
+    struct cd_sub_channel_info	sc;
+    int final_frame;
+#endif
 
-    return CDROM_GetStatusCode(ioctl(dev, CDROMSEEK, &msf));
+    /* Use the information on the TOC to compute the new current
+     * position, which is shadowed on the cache. [Portable]. */
+    frame = FRAME_OF_MSF(*audio_msf);
+    cp = &cdrom_cache[dev].CurrentPosition;
+    if ((io = CDROM_ReadTOC(dev, &toc)) != 0) return io;
+     
+    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: where do they 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;
+    MSF_OF_FRAME(cp->TrackRelativeAddress[1], frame); 
+
+    /* If playing, then issue a seek command, otherwise do nothing */
+#ifdef linux
+    sc.cdsc_format = CDROM_MSF;
+
+    io = ioctl(dev, CDROMSUBCHNL, &sc);
+    if (io == -1)
+    {
+	TRACE("opened or no_media (%s)!\n", strerror(errno));
+	CDROM_ClearCacheEntry(dev);
+        return CDROM_GetStatusCode(io);
+    }
+    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;
 #elif defined(__FreeBSD__) || defined(__NetBSD__)
-    FIXME("Could a BSD expert implement the seek function ?\n");
-    return STATUS_NOT_SUPPORTED;
+    read_sc.address_format = CD_MSF_FORMAT;
+    read_sc.track          = 0;
+    read_sc.data_len       = sizeof(sc);
+    read_sc.data           = &sc;
+    read_sc.data_format    = CD_CURRENT_POSITION;
+
+    io = ioctl(dev, CDIOCREADSUBCHANNEL, &read_sc);
+    if (io == -1)
+    {
+	TRACE("opened or no_media (%s)!\n", strerror(errno));
+	CDROM_ClearCacheEntry(dev);
+        return CDROM_GetStatusCode(io);
+    }
+    if (sc.header.audio_status==CD_AS_PLAY_IN_PROGRESS) 
+    {
+
+      msf.start_m      = audio_msf->M;
+      msf.start_s      = audio_msf->S;
+      msf.start_f      = audio_msf->F;
+      final_frame = FRAME_OF_TOC(toc,toc.LastTrack+1)-1;
+      MSF_OF_FRAME(msf.end_m, final_frame);
+
+      return CDROM_GetStatusCode(ioctl(dev, CDIOCPLAYMSF, &msf));
+    }
+    return 0;
 #else
     return STATUS_NOT_SUPPORTED;
 #endif
@@ -1243,8 +1485,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 +1510,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 +1524,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 +1544,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 +1561,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 +1583,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 +1635,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 +1648,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);
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	17 Feb 2003 04:23:55 -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;
@@ -678,17 +678,51 @@ static DWORD MCICDA_Status(UINT wDevID, 
 }
 
 /**************************************************************************
+ * 				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;
+}
+
+/**************************************************************************
  * 				MCICDA_Play			[internal]
  */
 static DWORD MCICDA_Play(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
 {
     WINE_MCICDAUDIO*	        wmcda = MCICDA_GetOpenDrv(wDevID);
     DWORD		        ret = 0, start, end;
-    CDROM_TOC                   toc;
     DWORD                       br;
     CDROM_PLAY_AUDIO_MSF        play;
     CDROM_SUB_Q_DATA_FORMAT     fmt;
     SUB_Q_CHANNEL_DATA          data;
+    CDROM_TOC			toc;
 
     TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
 
@@ -700,6 +734,8 @@ static DWORD MCICDA_Play(UINT wDevID, DW
 
     if (dwFlags & MCI_FROM) {
 	start = MCICDA_CalcFrame(wmcda, lpParms->dwFrom);
+	if ( (ret=MCICDA_SkipDataTracks(wmcda, &start)) )
+	  return ret;
 	TRACE("MCI_FROM=%08lX -> %lu \n", lpParms->dwFrom, start);
     } else {
         fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
@@ -708,6 +744,8 @@ static DWORD MCICDA_Play(UINT wDevID, DW
             return MCICDA_GetError(wmcda);
         }
         start = FRAME_OF_ADDR(data.CurrentPosition.AbsoluteAddress);
+	if ( (ret=MCICDA_SkipDataTracks(wmcda, &start)) )
+	  return ret;
     }
     if (dwFlags & MCI_TO) {
 	end = MCICDA_CalcFrame(wmcda, lpParms->dwTo);
@@ -817,36 +855,37 @@ static DWORD MCICDA_Seek(UINT wDevID, DW
     DWORD		        at;
     WINE_MCICDAUDIO*	        wmcda = MCICDA_GetOpenDrv(wDevID);
     CDROM_SEEK_AUDIO_MSF        seek;
-    CDROM_TOC                   toc;
-    DWORD                       br;
+    DWORD                       br, ret;
+    CDROM_TOC			toc;
 
     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);
+	at = FRAME_OF_TOC(toc,toc.FirstTrack);
+	if ( (ret=MCICDA_SkipDataTracks(wmcda, &at)) )
+	  return ret;
 	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;
+	if ( (ret=MCICDA_SkipDataTracks(wmcda, &at)) )
+	  return ret;
 	break;
     case MCI_TO:
 	TRACE("Seeking to %lu\n", lpParms->dwTo);
-	at = lpParms->dwTo;
+        at = MCICDA_CalcFrame(wmcda, lpParms->dwTo);
+	if ( (ret=MCICDA_SkipDataTracks(wmcda, &at)) )
+	  return ret;
 	break;
     default:
 	TRACE("Unknown seek action %08lX\n",

[Index of Archives]     [Gimp for Windows]     [Red Hat]     [Samba]     [Yosemite Camping]     [Graphics Cards]     [Wine Home]

  Powered by Linux