1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: 8; c-basic-offset: 8 -*- */
7 Based on code from libcdaudio 0.5.0 (Copyright (C)1998 Tony Arcieri)
9 All changes copyright (c) 1998 by Mike Oliphant - oliphant@ling.ed.ac.uk
11 http://www.ling.ed.ac.uk/~oliphant/grip
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
24 #include "cdda-cddb.h"
30 #include <sys/ioctl.h>
31 #include <sys/types.h>
32 #include <sys/socket.h>
34 #include <netinet/in.h>
35 #include <arpa/inet.h>
42 #include <glib/gmessages.h>
43 #include <glib/gstrfuncs.h>
44 #include <glib/gutils.h>
48 #include <cdda_interface.h>
50 #include "cdda-cdrom-extensions.h"
52 /* This is here to work around a broken header file.
53 * cdda_interface.h has a statically defined array of
54 * chars that is unused. This will break our build
55 * due to our strict error checking.
57 char **broken_header_fix2 = strerror_tr;
59 static int CDDBSum(int val);
60 static int CDDBConnect(CDDBServer *server);
61 static void CDDBDisconnect(int sock);
62 static void CDDBSkipHTTP(int sock);
63 static int CDDBReadLine(int sock,char *inbuffer,int len);
64 static void CDDBMakeHello(CDDBHello *hello,char *hellobuf);
65 static void CDDBMakeRequest(CDDBServer *server, CDDBHello *hello, char *cmd,char *outbuf,int outlen);
66 static void CDDBProcessLine(char *inbuffer,DiscData *data, int numtracks);
68 static void CDDBWriteLine(char *header,int num,char *data,FILE *outfile);
71 static char *cddb_genres[] = {"unknown","blues","classical","country",
72 "data","folk","jazz","misc","newage",
73 "reggae","rock","soundtrack"};
76 /*Check whether the node is IPv6 enabled.*/
82 s = socket (AF_INET6, SOCK_STREAM, 0);
92 /* CDDB sum function */
96 char *bufptr, buf[16];
99 g_snprintf(buf,16,"%lu",(unsigned long int)val);
101 for(bufptr = buf; *bufptr != '\0'; bufptr++) {
102 ret += (*bufptr - '0');
107 /* Produce CDDB ID for CD currently in CD-ROM */
109 CDDBDiscid (cdrom_drive *drive)
111 int index, tracksum = 0, discid, retval;
114 retval = CDStat (drive->ioctl_fd, &disc, TRUE);
116 for(index = 0; index < disc.disc_totaltracks; index++) {
117 tracksum += CDDBSum(disc.track[index].track_pos.minutes * 60 +
118 disc.track[index].track_pos.seconds);
121 discid = (disc.disc_length.minutes * 60 + disc.disc_length.seconds) -
122 (disc.track[0].track_pos.minutes * 60 + disc.track[0].track_pos.seconds);
124 return (tracksum % 0xFF) << 24 | discid << 8 | disc.disc_totaltracks;
127 /* Convert numerical genre to text */
128 char *CDDBGenre(int genre)
134 return cddb_genres[genre];
137 /* Convert genre from text form into an integer value */
138 int CDDBGenreValue(char *genre)
142 for (pos = 0; pos < 12; pos++) {
143 if (!strcmp (genre,cddb_genres[pos])) {
150 /* Connect to a CDDB server */
152 CDDBConnect (CDDBServer *server)
156 struct sockaddr_in6 sin6;
157 struct addrinfo hints, *result, *res; /*info abt the IP of node*/
159 struct sockaddr_in sin;
160 struct hostent *host;
167 memset (&sin6, 0 , sizeof (sin6));
168 sin6.sin6_family = AF_INET6;
170 if (server->use_proxy) {
171 sin6.sin6_port = htons (server->proxy->port);
173 sin6.sin6_port = htons (server->port);
178 memset (&sin, 0, sizeof (sin));
179 sin.sin_family = AF_INET;
181 if (server->use_proxy)
182 sin.sin_port=htons(server->proxy->port);
184 sin.sin_port = htons(server->port);
186 if (server->use_proxy)
187 sname=server->proxy->name;
193 memset (&hints, 0, sizeof (hints));
194 hints.ai_socktype = SOCK_STREAM;
196 if ((getaddrinfo (sname, NULL, &hints, &result)) != 0) {
200 for (res = result; res; res = res->ai_next) {
202 if (res->ai_family != AF_INET && res->ai_family != AF_INET6) {
206 sock = socket (res->ai_family, SOCK_STREAM, 0);
211 if (res->ai_family == AF_INET) {
212 memcpy (&sin.sin_addr, &((struct sockaddr_in *)res->ai_addr)->sin_addr, sizeof (struct in_addr));
214 if (connect (sock, (struct sockaddr *)&sin, sizeof (sin)) != -1) {
219 if (res->ai_family == AF_INET6) {
220 memcpy (&sin6.sin6_addr, &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr, sizeof (struct in6_addr));
222 if (connect (sock, (struct sockaddr *)&sin6, sizeof (sin6)) != -1) {
230 freeaddrinfo (result);
233 /* No valid address found. */
239 sin.sin_addr.s_addr = inet_addr (sname);
241 if (sin.sin_addr.s_addr == (unsigned long)-1)
243 if (sin.sin_addr.s_addr == INADDR_NONE)
246 host = gethostbyname (sname);
251 bcopy (host->h_addr, &sin.sin_addr, host->h_length);
254 sock = socket (AF_INET, SOCK_STREAM, 0);
259 if (connect (sock, (struct sockaddr *)&sin, sizeof (sin)) < 0) {
269 /* Disconnect from CDDB server */
271 static void CDDBDisconnect(int sock)
277 /* Skip http header */
279 static void CDDBSkipHTTP(int sock)
287 read(sock,&inchar,1);
289 //g_message ("%c",inchar);
296 /* Read a single line from the CDDB server*/
298 static int CDDBReadLine(int sock,char *inbuffer,int len)
306 for(index = 0; index < len; index++) {
307 read(sock, &inchar, 1);
310 inbuffer[index] = '\0';
311 //g_message ("[%s]\n",pos);
314 if(inbuffer[0] == '.')
320 inbuffer[index] = inchar;
326 /* Make a 'hello' string from a cddb_hello structure */
328 static void CDDBMakeHello(CDDBHello *hello,char *hellobuf)
330 g_snprintf(hellobuf,256,"&hello=private+free.the.cddb+%s+%s",
331 hello->hello_program,hello->hello_version);
334 /* Make a CDDB http request string */
336 static void CDDBMakeRequest(CDDBServer *server,
338 char *cmd,char *outbuf,int outlen)
342 CDDBMakeHello(hello,hellobuf);
344 if(server->use_proxy)
345 g_snprintf(outbuf,outlen,
346 "GET http://%s/%s?cmd=%s%s&proto=%s HTTP/1.1\r\nHost: %s\r\nUser-Agent: %s/%s\r\nAccept: text/plain\n\n",
347 server->name,server->cgi_prog,cmd,hellobuf,
348 //CDDA_CDDB_LEVEL, server->name, Program, Version);
349 CDDA_CDDB_LEVEL, server->name, "Loser", "1.0");
351 g_snprintf(outbuf,outlen,"GET /%s?cmd=%s%s&proto=%s HTTP/1.1\r\nHost: %s\r\nUser-Agent: %s/%s\r\nAccept: text/plain\n\n",
352 server->cgi_prog,cmd,hellobuf,CDDA_CDDB_LEVEL,server->name,
357 /* Query the CDDB for the CD currently in the CD-ROM */
359 CDDBDoQuery (cdrom_drive *cd_desc, CDDBServer *server, CDDBHello *hello, CDDBQuery *query)
363 char *offset_buffer, *query_buffer, *http_buffer, inbuffer[256];
366 socket = CDDBConnect (server);
368 //g_message ("CDDBConnect failure");
372 query->query_matches = 0;
374 CDStat (cd_desc->ioctl_fd, &disc, TRUE);
376 // Figure out a good buffer size -- 7 chars per track, plus 256 for the rest of the query
377 tot_len = (disc.disc_totaltracks * 7) + 256;
379 offset_buffer = malloc(tot_len);
382 len += g_snprintf (offset_buffer + len, tot_len - len, "%d", disc.disc_totaltracks);
384 for (index = 0; index < disc.disc_totaltracks; index++) {
385 len += g_snprintf (offset_buffer + len, tot_len - len, "+%d", disc.track[index].track_start);
388 query_buffer = malloc(tot_len);
390 g_snprintf (query_buffer, tot_len, "cddb+query+%08x+%s+%d",
393 disc.disc_length.minutes * 60 +
394 disc.disc_length.seconds);
396 http_buffer = malloc(tot_len);
398 CDDBMakeRequest (server, hello, query_buffer, http_buffer, tot_len);
400 //g_message ("Query is [%s]\n",http_buffer);
402 write (socket, http_buffer, strlen (http_buffer));
404 free (offset_buffer);
408 CDDBSkipHTTP (socket);
412 CDDBReadLine(socket,inbuffer,256);
414 /* Skip the keep-alive */
415 if((strlen(inbuffer)<5)||!strncmp(inbuffer,"Keep",4)) {
416 //g_message("Skipping keepalive\n");
417 CDDBReadLine (socket,inbuffer,256);
420 //g_message ("Reply is [%s]\n",inbuffer);
422 switch(strtol(strtok(inbuffer," "),NULL,10)) {
423 /* 200 - exact match */
425 query->query_match=MATCH_EXACT;
426 query->query_matches=1;
428 query->query_list[0].list_genre=
429 CDDBGenreValue(ChopWhite(strtok(NULL," ")));
431 sscanf(ChopWhite(strtok(NULL," ")),"%xd",
432 &query->query_list[0].list_id);
434 CDDBParseTitle(ChopWhite(strtok(NULL,"")),query->query_list[0].list_title,
435 query->query_list[0].list_artist,"/");
438 /* 211 - inexact match */
440 query->query_match=MATCH_INEXACT;
441 query->query_matches=0;
443 while(!CDDBReadLine(socket,inbuffer,256)) {
444 query->query_list[query->query_matches].list_genre=
445 CDDBGenreValue(ChopWhite(strtok(inbuffer," ")));
447 sscanf(ChopWhite(strtok(NULL," ")),"%xd",
448 &query->query_list[query->query_matches].list_id);
450 CDDBParseTitle(ChopWhite(strtok(NULL,"")),
451 query->query_list[query->query_matches].list_title,
452 query->query_list[query->query_matches].list_artist,"/");
454 query->query_matches++;
461 query->query_match=MATCH_NOMATCH;
463 CDDBDisconnect(socket);
468 CDDBDisconnect(socket);
473 /* Get rid of whitespace at the beginning or end of a string */
475 char *ChopWhite(char *buf)
479 for(pos=strlen(buf)-1;(pos>=0)&&g_ascii_isspace(buf[pos]);pos--);
483 for(;g_ascii_isspace(*buf);buf++);
488 /* Split string into title/artist */
490 void CDDBParseTitle(char *buf,char *title,char *artist,char *sep)
498 strncpy(artist,ChopWhite(tmp),64);
503 strncpy(title,ChopWhite(tmp),64);
504 else strcpy(title,artist);
507 /* Process a line of input data */
509 static void CDDBProcessLine(char *inbuffer,DiscData *data,
516 if(!g_ascii_strncasecmp(inbuffer,"DTITLE",6)) {
517 len = strlen(data->data_title);
519 strncpy(data->data_title+len,ChopWhite(inbuffer+7),256-len);
521 else if(!g_ascii_strncasecmp(inbuffer,"DYEAR",5)) {
522 strtok(inbuffer,"=");
524 st = strtok(NULL, "");
528 data->data_year=atoi(ChopWhite(st));
530 else if(!g_ascii_strncasecmp(inbuffer,"DGENRE",6)) {
531 strtok(inbuffer,"=");
533 st = strtok(NULL, "");
537 data->data_genre=CDDBGenreValue(ChopWhite(st));
539 else if(!g_ascii_strncasecmp(inbuffer,"TTITLE",6)) {
540 track=atoi(strtok(inbuffer+6,"="));
543 len=strlen(data->data_track[track].track_name);
545 strncpy(data->data_track[track].track_name+len,
546 ChopWhite(strtok(NULL,"")),256-len);
548 else if(!g_ascii_strncasecmp(inbuffer,"TARTIST",7)) {
549 data->data_multi_artist=TRUE;
551 track=atoi(strtok(inbuffer+7,"="));
554 len=strlen(data->data_track[track].track_artist);
556 st = strtok(NULL, "");
560 strncpy(data->data_track[track].track_artist+len,
561 ChopWhite(st),256-len);
563 else if(!g_ascii_strncasecmp(inbuffer,"EXTD",4)) {
564 len=strlen(data->data_extended);
566 strncpy(data->data_extended+len,ChopWhite(inbuffer+5),4096-len);
568 else if(!g_ascii_strncasecmp(inbuffer,"EXTT",4)) {
569 track=atoi(strtok(inbuffer+4,"="));
572 len=strlen(data->data_track[track].track_extended);
574 st = strtok(NULL, "");
578 strncpy(data->data_track[track].track_extended+len,
579 ChopWhite(st),4096-len);
581 else if(!g_ascii_strncasecmp(inbuffer,"PLAYORDER",5)) {
582 len=strlen(data->data_playlist);
584 strncpy(data->data_playlist+len,ChopWhite(inbuffer+10),256-len);
589 /* Read the actual CDDB entry */
590 gboolean CDDBRead(cdrom_drive *cd_desc, CDDBServer *server,
591 CDDBHello *hello,CDDBEntry *entry,
596 char outbuffer[256], inbuffer[512],cmdbuffer[256];
599 socket=CDDBConnect(server);
600 if(socket==-1) return FALSE;
602 //CDStat(cd_desc,&disc,TRUE);
604 data->data_genre=entry->entry_genre;
605 data->data_id=CDDBDiscid(cd_desc);
606 *(data->data_extended)='\0';
607 *(data->data_title)='\0';
608 *(data->data_artist)='\0';
609 *(data->data_playlist)='\0';
610 data->data_multi_artist=FALSE;
613 for(index=0;index<MAX_TRACKS;index++) {
614 *(data->data_track[index].track_name)='\0';
615 *(data->data_track[index].track_artist)='\0';
616 *(data->data_track[index].track_extended)='\0';
619 g_snprintf(cmdbuffer,256,"cddb+read+%s+%08x",CDDBGenre(entry->entry_genre),
622 CDDBMakeRequest(server,hello,cmdbuffer,outbuffer,256);
624 write(socket,outbuffer,strlen(outbuffer));
626 CDDBSkipHTTP(socket);
628 CDDBReadLine(socket,inbuffer,256);
630 /* Skip the keep-alive */
631 if((strlen(inbuffer)<5)||!strncmp(inbuffer,"Keep",4)) {
632 //g_message ("Skipping keepalive\n");
633 CDDBReadLine(socket,inbuffer,256);
636 while(!CDDBReadLine(socket,inbuffer,512))
637 CDDBProcessLine(inbuffer,data,disc.disc_totaltracks);
639 /* Both disc title and artist have been stuffed in the title field, so the
640 need to be separated */
642 CDDBParseTitle(data->data_title,data->data_title,data->data_artist,"/");
644 CDDBDisconnect(socket);
649 /* See if a disc is in the local database */
651 gboolean CDDBStatDiscData(cdrom_drive *cd_desc)
656 char root_dir[256], file[256];
658 //CDStat(cd_desc,&disc,TRUE);
660 id=CDDBDiscid(cd_desc);
662 g_snprintf(root_dir,256,"%s/.cddb",getenv("HOME"));
664 if(stat(root_dir, &st) < 0)
667 if(!S_ISDIR(st.st_mode))
671 g_snprintf(file,256,"%s/%08x",root_dir,id);
672 if(stat(file,&st)==0) return TRUE;
674 for(index=0;index<12;index++) {
675 g_snprintf(file,256,"%s/%s/%08x",root_dir,CDDBGenre(index),id);
677 if(stat(file,&st) == 0)
684 /* Read from the local database */
686 CDDBReadDiscData(cdrom_drive *cd_desc,DiscData *ddata)
688 FILE *cddb_data = NULL;
690 char root_dir[256], file[256], inbuf[512];
694 g_snprintf(root_dir,256,"%s/.cddb",getenv("HOME"));
696 if(stat(root_dir, &st) < 0) {
699 if(!S_ISDIR(st.st_mode)) {
705 CDStat (cd_desc->ioctl_fd, &disc, TRUE);
707 ddata->data_id=CDDBDiscid(cd_desc);
708 *(ddata->data_extended)='\0';
709 *(ddata->data_title)='\0';
710 *(ddata->data_artist)='\0';
711 *(ddata->data_playlist)='\0';
712 ddata->data_multi_artist=FALSE;
715 for(index=0;index<MAX_TRACKS;index++) {
716 *(ddata->data_track[index].track_name)='\0';
717 *(ddata->data_track[index].track_artist)='\0';
718 *(ddata->data_track[index].track_extended)='\0';
721 g_snprintf(file,256,"%s/%08x",root_dir,ddata->data_id);
722 if(stat(file,&st)==0) {
723 cddb_data=fopen(file, "r");
726 for(genre=0;genre<12;genre++) {
727 g_snprintf(file,256,"%s/%s/%08x",root_dir,CDDBGenre(genre),
730 if(stat(file,&st)==0) {
731 cddb_data=fopen(file, "r");
733 ddata->data_genre=genre;
738 if(genre==12) return -1;
741 while(fgets(inbuf,512,cddb_data))
742 CDDBProcessLine(inbuf,ddata,disc.disc_totaltracks);
744 /* Both disc title and artist have been stuffed in the title field, so the
745 need to be separated */
747 CDDBParseTitle(ddata->data_title,ddata->data_title,ddata->data_artist,"/");
755 static void CDDBWriteLine(char *header,int num,char *data,FILE *outfile)
766 fprintf(outfile,"%s=%.70s\n",header,offset);
767 else fprintf(outfile,"%s%d=%.70s\n",header,num,offset);
773 if(num==-1) fprintf(outfile,"%s=%s\n",header,offset);
774 else fprintf(outfile,"%s%d=%s\n",header,num,offset);
781 /* Write to the local cache */
782 int CDDBWriteDiscData(cdrom_drive *drive, DiscData *ddata,FILE *outfile,
788 char root_dir[256],file[256];
792 //CDStat(cd_desc,&disc,TRUE);
795 g_snprintf(root_dir,256,"%s/.cddb",getenv("HOME"));
796 g_snprintf(file,256,"%s/%08x",root_dir,ddata->data_id);
798 if(stat(root_dir,&st)<0) {
799 if(errno != ENOENT) {
800 //g_message("Stat error %d on %s\n",errno,root_dir);
804 //g_message("Creating directory %s\n",root_dir);
805 mkdir(root_dir, 0755);
808 if(!S_ISDIR(st.st_mode)) {
809 //g_message("Error: %s exists, but is a file\n",root_dir);
815 if((cddb_data=fopen(file,"w"))==NULL) {
816 //g_message("Error: Unable to open %s for writing\n",file);
820 else cddb_data=outfile;
822 //fprintf(cddb_data,"# xmcd CD database file generated by %s %s\n", Program,Version);
823 fprintf(cddb_data,"# xmcd CD database file generated by Loser 1.0\n");
824 fputs("# \n",cddb_data);
825 fputs("# Track frame offsets:\n",cddb_data);
827 for (track = 0; track < disc.disc_totaltracks; track++)
828 fprintf(cddb_data, "# %d\n",disc.track[track].track_start);
830 fputs("# \n",cddb_data);
831 fprintf(cddb_data,"# Disc length: %d seconds\n",disc.disc_length.minutes *
832 60 + disc.disc_length.seconds);
833 fputs("# \n",cddb_data);
834 fprintf(cddb_data,"# Revision: %s\n",CDDA_CDDB_LEVEL);
835 //fprintf(cddb_data,"# Submitted via: %s %s\n",Program,Version);
836 fprintf(cddb_data,"# Submitted via: Loser 1.0\n");
837 fputs("# \n",cddb_data);
838 fprintf(cddb_data,"DISCID=%08x\n",ddata->data_id);
840 fprintf(cddb_data,"DTITLE=%s / %s\n",
841 ddata->data_artist,ddata->data_title);
843 if(gripext&&ddata->data_year)
844 fprintf(cddb_data,"DYEAR=%d\n",ddata->data_year);
847 fprintf(cddb_data,"DGENRE=%s\n",CDDBGenre(ddata->data_genre));
849 for(track=0;track<disc.disc_totaltracks;track++) {
850 if(gripext||!*(ddata->data_track[track].track_artist)) {
851 fprintf(cddb_data,"TTITLE%d=%s\n",track,
852 ddata->data_track[track].track_name);
855 fprintf(cddb_data,"TTITLE%d=%s / %s\n",track,
856 ddata->data_track[track].track_name,
857 ddata->data_track[track].track_artist);
860 if(gripext&&*(ddata->data_track[track].track_artist))
861 fprintf(cddb_data,"TARTIST%d=%s\n",track,
862 ddata->data_track[track].track_artist);
866 CDDBWriteLine("EXTD",-1,ddata->data_extended,cddb_data);
868 for(track=0;track<disc.disc_totaltracks;track++)
869 CDDBWriteLine("EXTT",track,
870 ddata->data_track[track].track_extended,cddb_data);
873 fprintf(cddb_data,"PLAYORDER=\n");
875 fprintf(cddb_data,"PLAYORDER=%s\n",ddata->data_playlist);
884 CDDBLookupDisc (CDDBServer *server, cdrom_drive *drive, DiscData *disc_data)
889 gboolean success = FALSE;
891 if(server->use_proxy) {
892 //g_message ("Querying %s (through %s:%d) for disc %02x.\n", server->name,
893 //server->proxy->name, server->proxy->port, CDDBDiscid (drive));
895 //g_message ("Querying %s for disc %02x.\n",server->name, CDDBDiscid (drive));
898 strncpy (hello.hello_program, "Loser", 256);
899 strncpy (hello.hello_version, "1.0", 256);
901 if (!CDDBDoQuery (drive, server, &hello, &query)) {
902 g_message ("Query failed");
904 switch(query.query_match) {
907 //g_message ("Match for \"%s / %s\"\nDownloading data...\n",
908 // query.query_list[0].list_artist,
909 // query.query_list[0].list_title);
910 entry.entry_genre = query.query_list[0].list_genre;
911 entry.entry_id = query.query_list[0].list_id;
912 CDDBRead (drive, server, &hello, &entry, disc_data);
914 //g_message ("Done\n");
917 //if (CDDBWriteDiscData (drive, disc_data, NULL, TRUE) < 0) {
918 // printf ("Error saving disc data\n");
923 g_message ("No match\n");
930 /* Update a CD status structure... because operating system interfaces vary
931 so does this function. */
934 CDStat (int cd_desc, disc_info *disc, gboolean read_toc)
936 struct cdrom_tochdr cdth;
937 struct cdrom_tocentry cdte;
938 int readtracks,frame[MAX_TRACKS],pos;
941 retcode = ioctl(cd_desc, CDROM_DRIVE_STATUS, CDSL_CURRENT);
942 //g_message("Drive status is %d\n", retcode);
944 //g_message("Drive doesn't support drive status check (assume CDS_NO_INFO)\n");
945 } else if (retcode != CDS_DISC_OK && retcode != CDS_NO_INFO) {
949 disc->disc_present = 1;
952 //g_message ("Reading TOC");
953 /* Read the Table Of Contents header */
954 if(ioctl(cd_desc, CDROMREADTOCHDR, &cdth) < 0) {
955 printf("Error: Failed to read disc contents\n");
958 disc->disc_totaltracks = cdth.cdth_trk1;
960 /* Read the table of contents */
961 for(readtracks = 0; readtracks <= disc->disc_totaltracks; readtracks++) {
962 if(readtracks == disc->disc_totaltracks)
963 cdte.cdte_track = CDROM_LEADOUT;
965 cdte.cdte_track = readtracks + 1;
967 cdte.cdte_format = CDROM_MSF;
968 if(ioctl(cd_desc, CDROMREADTOCENTRY, &cdte) < 0) {
969 printf("Error: Failed to read disc contents\n");
973 disc->track[readtracks].track_pos.minutes = cdte.cdte_addr.msf.minute;
974 disc->track[readtracks].track_pos.seconds = cdte.cdte_addr.msf.second;
975 frame[readtracks] = cdte.cdte_addr.msf.frame;
978 for(readtracks = 0; readtracks <= disc->disc_totaltracks; readtracks++) {
979 disc->track[readtracks].track_start=
980 (disc->track[readtracks].track_pos.minutes * 60 +
981 disc->track[readtracks].track_pos.seconds) * 75 + frame[readtracks];
984 pos = (disc->track[readtracks].track_pos.minutes * 60 +
985 disc->track[readtracks].track_pos.seconds) -
986 (disc->track[readtracks - 1].track_pos.minutes * 60 +
987 disc->track[readtracks -1].track_pos.seconds);
988 disc->track[readtracks - 1].track_length.minutes = pos / 60;
989 disc->track[readtracks - 1].track_length.seconds = pos % 60;
993 disc->disc_length.minutes=
994 disc->track[disc->disc_totaltracks].track_pos.minutes;
996 disc->disc_length.seconds=
997 disc->track[disc->disc_totaltracks].track_pos.seconds;
1000 disc->disc_track = 0;
1002 while(disc->disc_track < disc->disc_totaltracks &&
1003 disc->disc_frame >= disc->track[disc->disc_track].track_start)
1006 pos=(disc->disc_frame - disc->track[disc->disc_track - 1].track_start) / 75;
1008 disc->track_time.minutes = pos / 60;
1009 disc->track_time.seconds = pos % 60;