1 //=================== VideoCD BinCue ==========================
22 #define byte unsigned char
24 #define SIZEISO_MODE1 2048
25 #define SIZEISO_MODE2_RAW 2352
26 #define SIZEISO_MODE2_FORM1 2048
27 #define SIZEISO_MODE2_FORM2 2336
37 static struct stream_priv_s
{
39 } stream_priv_dflts
= {
43 #define ST_OFF(f) M_ST_OFF(struct stream_priv_s,f)
45 static m_option_t stream_opts_fields
[] = {
46 { "string", ST_OFF(filename
), CONF_TYPE_STRING
, 0, 0 ,0, NULL
},
47 { NULL
, NULL
, 0, 0, 0, 0, NULL
}
49 static struct m_struct_st stream_opts
= {
51 sizeof(struct stream_priv_s
),
57 static int fd_bin
= 0;
59 static char bin_filename
[256];
61 static char cue_filename
[256];
62 static char bincue_path
[256];
68 unsigned short minute
;
69 unsigned short second
;
72 /* (min*60 + sec) * 75 + fps */
74 unsigned long start_sector
;
76 /* = the sizes in bytes off all tracks bevor this one */
77 /* its needed if there are mode1 tracks befor the mpeg tracks */
78 unsigned long start_offset
;
80 /* unsigned char num[3]; */
83 /* max 99 tracks on a cd */
84 static tTrack tracks
[100];
86 static struct cue_track_pos
{
89 unsigned short minute
;
90 unsigned short second
;
94 /* number of tracks on the cd */
95 static int nTracks
= 0;
97 /* presumes Line is preloaded with the "current" line of the file */
98 static int cue_getTrackinfo(char *Line
, tTrack
*track
)
107 if (strncmp(&Line
[2], "TRACK ", 6)==0)
109 /* strncpy(track->num, &Line[8], 2); track->num[2] = '\0'; */
111 track
->mode
= UNKNOWN
;
112 if(strncmp(&Line
[11], "AUDIO", 5)==0) track
->mode
= AUDIO
;
113 if(strncmp(&Line
[11], "MODE1/2352", 10)==0) track
->mode
= MODE1_2352
;
114 if(strncmp(&Line
[11], "MODE1/2048", 10)==0) track
->mode
= MODE1_2048
;
115 if(strncmp(&Line
[11], "MODE2/2352", 10)==0) track
->mode
= MODE2_2352
;
116 if(strncmp(&Line
[11], "MODE2/2336", 10)==0) track
->mode
= MODE2_2336
;
120 /* Get the track indexes */
122 if(! fgets( Line
, 256, fd_cue
) ) { break;}
124 if (strncmp(&Line
[2], "TRACK ", 6)==0)
126 /* next track starting */
130 /* Track 0 or 1, take the first an get fill the values*/
131 if (strncmp(&Line
[4], "INDEX ", 6)==0)
133 /* check stuff here so if the answer is false the else stuff below won't be executed */
134 strncpy(inum
, &Line
[10], 2); inum
[2] = '\0';
135 if ((already_set
== 0) &&
136 ((strcmp(inum
, "00")==0) || (strcmp(inum
, "01")==0)))
140 min
= ((Line
[13]-'0')<<4) | (Line
[14]-'0');
141 sec
= ((Line
[16]-'0')<<4) | (Line
[17]-'0');
142 fps
= ((Line
[19]-'0')<<4) | (Line
[20]-'0');
144 track
->minute
= (((min
>>4)*10) + (min
&0xf));
145 track
->second
= (((sec
>>4)*10) + (sec
&0xf));
146 track
->frame
= (((fps
>>4)*10) + (fps
&0xf));
149 else if (strncmp(&Line
[4], "PREGAP ", 7)==0) { ; /* ignore */ }
150 else if (strncmp(&Line
[4], "FLAGS ", 6)==0) { ; /* ignore */ }
151 else mp_msg (MSGT_OPEN
,MSGL_INFO
,
152 MSGTR_MPDEMUX_CUEREAD_UnexpectedCuefileLine
, Line
);
159 /* FIXME: the string operations ( strcpy,strcat ) below depend
160 * on the arrays to have the same size, thus we need to make
161 * sure the sizes are in sync.
163 static int cue_find_bin (char *firstline
) {
168 /* get the filename out of that */
170 mp_msg (MSGT_OPEN
,MSGL_INFO
, "[bincue] cue_find_bin(%s)\n", firstline
);
171 if (strncmp(firstline
, "FILE \"",6)==0)
175 while ( firstline
[6 + i
] != '"')
177 bin_filename
[j
] = firstline
[6 + i
];
179 /* if I found a path info, than delete all bevor it */
180 switch (bin_filename
[j
])
195 bin_filename
[j
+1] = '\0';
199 /* now try to open that file, without path */
200 fd_bin
= open (bin_filename
, O_RDONLY
);
203 mp_msg(MSGT_OPEN
,MSGL_STATUS
, MSGTR_MPDEMUX_CUEREAD_BinFilenameTested
,
206 /* now try to find it with the path of the cue file */
207 snprintf(s
,sizeof( s
),"%s/%s",bincue_path
,bin_filename
);
208 fd_bin
= open (s
, O_RDONLY
);
211 mp_msg(MSGT_OPEN
,MSGL_STATUS
,
212 MSGTR_MPDEMUX_CUEREAD_BinFilenameTested
, s
);
213 /* now I would say the whole filename is shit, build our own */
214 strncpy(s
, cue_filename
, strlen(cue_filename
) - 3 );
215 s
[strlen(cue_filename
) - 3] = '\0';
217 fd_bin
= open (s
, O_RDONLY
);
220 mp_msg(MSGT_OPEN
,MSGL_STATUS
,
221 MSGTR_MPDEMUX_CUEREAD_BinFilenameTested
, s
);
223 /* ok try it with path */
224 snprintf(t
, sizeof( t
), "%s/%s", bincue_path
, s
);
225 fd_bin
= open (t
, O_RDONLY
);
228 mp_msg(MSGT_OPEN
,MSGL_STATUS
,
229 MSGTR_MPDEMUX_CUEREAD_BinFilenameTested
,t
);
230 /* now I would say the whole filename is shit, build our own */
231 strncpy(s
, cue_filename
, strlen(cue_filename
) - 3 );
232 s
[strlen(cue_filename
) - 3] = '\0';
234 fd_bin
= open (s
, O_RDONLY
);
237 mp_msg(MSGT_OPEN
,MSGL_STATUS
,
238 MSGTR_MPDEMUX_CUEREAD_BinFilenameTested
, s
);
239 /* ok try it with path */
240 snprintf(t
, sizeof( t
), "%s/%s", bincue_path
, s
);
241 fd_bin
= open (t
, O_RDONLY
);
244 mp_msg(MSGT_OPEN
,MSGL_STATUS
,
245 MSGTR_MPDEMUX_CUEREAD_BinFilenameTested
, s
);
248 mp_msg(MSGT_OPEN
,MSGL_ERR
,
249 MSGTR_MPDEMUX_CUEREAD_CannotFindBinFile
);
253 } else strcpy(bin_filename
, t
);
255 } else strcpy(bin_filename
, s
);
257 } else strcpy(bin_filename
, s
);
261 mp_msg(MSGT_OPEN
,MSGL_INFO
,
262 MSGTR_MPDEMUX_CUEREAD_UsingBinFile
, bin_filename
);
266 static inline int cue_msf_2_sector(int minute
, int second
, int frame
) {
267 return frame
+ (second
+ minute
* 60 ) * 75;
270 static inline int cue_get_msf(void) {
271 return cue_msf_2_sector (cue_current_pos
.minute
,
272 cue_current_pos
.second
,
273 cue_current_pos
.frame
);
276 static inline void cue_set_msf(unsigned int sect
){
277 cue_current_pos
.frame
=sect
%75;
279 cue_current_pos
.second
=sect
%60;
281 cue_current_pos
.minute
=sect
;
284 static inline int cue_mode_2_sector_size(int mode
)
288 case AUDIO
: return AUDIO
;
289 case MODE1_2352
: return SIZERAW
;
290 case MODE1_2048
: return SIZEISO_MODE1
;
291 case MODE2_2352
: return SIZEISO_MODE2_RAW
;
292 case MODE2_2336
: return SIZEISO_MODE2_FORM2
;
295 mp_msg(MSGT_OPEN
,MSGL_FATAL
,
296 MSGTR_MPDEMUX_CUEREAD_UnknownModeForBinfile
);
303 static int cue_read_cue (char *in_cue_filename
)
305 struct stat filestat
;
311 /* we have no tracks at the beginning */
316 /* split the filename into a path and filename part */
317 s
= strdup(in_cue_filename
);
319 if (t
== (char *)NULL
)
328 strlcpy(bincue_path
,t
,sizeof( bincue_path
));
329 mp_msg(MSGT_OPEN
,MSGL_V
,"dirname: %s, cuepath: %s\n", t
, bincue_path
);
331 /* no path at all? */
332 if (strcmp(bincue_path
, ".") == 0) {
333 mp_msg(MSGT_OPEN
,MSGL_V
,"bincue_path: %s\n", bincue_path
);
334 strlcpy(cue_filename
,in_cue_filename
,sizeof( cue_filename
));
336 strlcpy(cue_filename
,in_cue_filename
+ strlen(bincue_path
) + 1,
337 sizeof( cue_filename
));
342 /* open the cue file */
343 fd_cue
= fopen (in_cue_filename
, "r");
346 mp_msg(MSGT_OPEN
,MSGL_ERR
,
347 MSGTR_MPDEMUX_CUEREAD_CannotOpenCueFile
, in_cue_filename
);
351 /* read the first line and hand it to find_bin, which will
352 test more than one possible name of the file */
354 if(! fgets( sLine
, 256, fd_cue
) )
356 mp_msg(MSGT_OPEN
,MSGL_ERR
,
357 MSGTR_MPDEMUX_CUEREAD_ErrReadingFromCueFile
, in_cue_filename
);
362 if (cue_find_bin(sLine
)) {
368 /* now build the track list */
369 /* red the next line and call our track finder */
370 if(! fgets( sLine
, 256, fd_cue
) )
372 mp_msg(MSGT_OPEN
,MSGL_ERR
,
373 MSGTR_MPDEMUX_CUEREAD_ErrReadingFromCueFile
, in_cue_filename
);
380 if (cue_getTrackinfo(sLine
, &tracks
[nTracks
++]) != 0)
382 mp_msg(MSGT_OPEN
,MSGL_ERR
,
383 MSGTR_MPDEMUX_CUEREAD_ErrReadingFromCueFile
, in_cue_filename
);
389 /* make a fake track with stands for the Lead out */
390 if (fstat (fd_bin
, &filestat
) == -1) {
391 mp_msg(MSGT_OPEN
,MSGL_ERR
,
392 MSGTR_MPDEMUX_CUEREAD_ErrGettingBinFileSize
);
397 sect
= filestat
.st_size
/ 2352;
399 tracks
[nTracks
].frame
= sect
%75;
401 tracks
[nTracks
].second
= sect
%60;
403 tracks
[nTracks
].minute
= sect
;
406 /* let's calculate the start sectors and offsets */
407 for(i
= 0; i
<= nTracks
; i
++)
409 tracks
[i
].start_sector
= cue_msf_2_sector(tracks
[i
].minute
,
410 tracks
[nTracks
].second
,
411 tracks
[nTracks
].frame
);
413 /* if we're the first track we don't need to offset of the one befor */
416 /* was always 0 on my svcds, but who knows */
417 tracks
[0].start_offset
= tracks
[0].start_sector
*
418 cue_mode_2_sector_size(tracks
[0].mode
);
421 tracks
[i
].start_offset
= tracks
[i
-1].start_offset
+
422 (tracks
[i
].start_sector
- tracks
[i
-1].start_sector
) *
423 cue_mode_2_sector_size(tracks
[i
-1].mode
);
435 static int cue_read_toc_entry(void) {
437 int track
= cue_current_pos
.track
- 1;
439 /* check if its a valid track, if not return -1 */
440 if (track
>= nTracks
)
444 switch (tracks
[track
].mode
)
447 cue_current_pos
.mode
= AUDIO
;
450 cue_current_pos
.mode
= MODE1
;
453 cue_current_pos
.mode
= MODE1
;
455 default: /* MODE2_2352 and MODE2_2336 */
456 cue_current_pos
.mode
= MODE2
;
458 cue_current_pos
.minute
= tracks
[track
].minute
;
459 cue_current_pos
.second
= tracks
[track
].second
;
460 cue_current_pos
.frame
= tracks
[track
].frame
;
465 static int cue_vcd_seek_to_track (int track
){
466 cue_current_pos
.track
= track
;
468 if (cue_read_toc_entry ())
471 return VCD_SECTOR_DATA
* cue_get_msf();
474 static int cue_vcd_get_track_end (int track
){
475 cue_current_pos
.frame
= tracks
[track
].frame
;
476 cue_current_pos
.second
= tracks
[track
].second
;
477 cue_current_pos
.minute
= tracks
[track
].minute
;
479 return VCD_SECTOR_DATA
* cue_get_msf();
482 static void cue_vcd_read_toc(void){
484 for (i
= 0; i
< nTracks
; ++i
) {
486 mp_msg(MSGT_OPEN
,MSGL_INFO
,
487 MSGTR_MPDEMUX_CUEREAD_InfoTrackFormat
,
497 static int cue_vcd_read(stream_t
*stream
, char *mem
, int size
) {
498 unsigned long position
;
499 int track
= cue_current_pos
.track
- 1;
501 position
= tracks
[track
].start_offset
+
502 (cue_msf_2_sector(cue_current_pos
.minute
,
503 cue_current_pos
.second
,
504 cue_current_pos
.frame
) -
505 tracks
[track
].start_sector
)
506 * cue_mode_2_sector_size(tracks
[track
].mode
);
509 if(position
>= tracks
[track
+1].start_offset
)
512 if(lseek(fd_bin
, position
+VCD_SECTOR_OFFS
, SEEK_SET
) == -1) {
513 mp_msg(MSGT_OPEN
,MSGL_ERR
, MSGTR_MPDEMUX_CUEREAD_UnexpectedBinFileEOF
);
517 if(read(fd_bin
, mem
, VCD_SECTOR_DATA
) != VCD_SECTOR_DATA
) {
518 mp_msg(MSGT_OPEN
,MSGL_ERR
, MSGTR_MPDEMUX_CUEREAD_CannotReadNBytesOfPayload
, VCD_SECTOR_DATA
);
522 cue_current_pos
.frame
++;
523 if (cue_current_pos
.frame
==75){
524 cue_current_pos
.frame
=0;
525 cue_current_pos
.second
++;
526 if (cue_current_pos
.second
==60){
527 cue_current_pos
.second
=0;
528 cue_current_pos
.minute
++;
532 return VCD_SECTOR_DATA
;
535 static int seek(stream_t
*s
,off_t newpos
) {
537 cue_set_msf(s
->pos
/VCD_SECTOR_DATA
);
542 static int open_s(stream_t
*stream
,int mode
, void* opts
, int* file_format
) {
543 struct stream_priv_s
* p
= (struct stream_priv_s
*)opts
;
544 int ret
,ret2
,f
,track
= 0;
545 char *filename
= NULL
, *colon
= NULL
;
547 if(mode
!= STREAM_READ
|| !p
->filename
) {
548 m_struct_free(&stream_opts
,opts
);
549 return STREAM_UNSUPORTED
;
551 filename
= strdup(p
->filename
);
553 m_struct_free(&stream_opts
,opts
);
554 return STREAM_UNSUPORTED
;
556 colon
= strstr(filename
, ":");
559 track
= atoi(colon
+1);
565 f
= cue_read_cue(filename
);
567 m_struct_free(&stream_opts
,opts
);
568 return STREAM_UNSUPORTED
;
571 ret2
=cue_vcd_get_track_end(track
);
572 ret
=cue_vcd_seek_to_track(track
);
574 mp_msg(MSGT_OPEN
,MSGL_ERR
,MSGTR_ErrTrackSelect
" (seek)\n");
575 return STREAM_UNSUPORTED
;
577 mp_msg(MSGT_OPEN
,MSGL_INFO
,MSGTR_MPDEMUX_CUEREAD_CueStreamInfo_FilenameTrackTracksavail
, filename
, track
, ret
, ret2
);
580 stream
->type
= STREAMTYPE_VCDBINCUE
;
581 stream
->sector_size
= VCD_SECTOR_DATA
;
582 stream
->flags
= STREAM_READ
| STREAM_SEEK_FW
;
583 stream
->start_pos
= ret
;
584 stream
->end_pos
= ret2
;
585 stream
->fill_buffer
= cue_vcd_read
;
589 m_struct_free(&stream_opts
,opts
);
593 stream_info_t stream_info_cue
= {
597 "based on the code from ???",
601 1 // Urls are an option string