subassconvert: do not escape likely ASS override tags
[mplayer.git] / stream / stream_cue.c
blobecc5e5b43ade0a442f9647be5d82cee2ea061a32
1 /*
2 * VideoCD BinCue
4 * This file is part of MPlayer.
6 * MPlayer is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * MPlayer is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <unistd.h>
28 #include <fcntl.h>
30 #include "config.h"
31 #include "mp_msg.h"
33 #include "stream.h"
35 #include "m_option.h"
36 #include "m_struct.h"
37 #include "libavutil/avstring.h"
39 #define SIZERAW 2352
40 #define SIZEISO_MODE1 2048
41 #define SIZEISO_MODE2_RAW 2352
42 #define SIZEISO_MODE2_FORM1 2048
43 #define SIZEISO_MODE2_FORM2 2336
44 #define AUDIO 0
45 #define MODE1 1
46 #define MODE2 2
47 #define MODE1_2352 10
48 #define MODE2_2352 20
49 #define MODE1_2048 30
50 #define MODE2_2336 40
51 #define UNKNOWN -1
53 static struct stream_priv_s {
54 char* filename;
55 } stream_priv_dflts = {
56 NULL
59 #define ST_OFF(f) M_ST_OFF(struct stream_priv_s,f)
60 /// URL definition
61 static const m_option_t stream_opts_fields[] = {
62 { "string", ST_OFF(filename), CONF_TYPE_STRING, 0, 0 ,0, NULL},
63 { NULL, NULL, 0, 0, 0, 0, NULL }
65 static const struct m_struct_st stream_opts = {
66 "cue",
67 sizeof(struct stream_priv_s),
68 &stream_priv_dflts,
69 stream_opts_fields
72 static char cue_filename[256];
73 static char bincue_path[256];
76 typedef struct track
78 unsigned short mode;
79 unsigned short minute;
80 unsigned short second;
81 unsigned short frame;
83 /* (min*60 + sec) * 75 + fps */
85 unsigned long start_sector;
87 /* = the sizes in bytes off all tracks bevor this one */
88 /* its needed if there are mode1 tracks befor the mpeg tracks */
89 unsigned long start_offset;
91 /* unsigned char num[3]; */
92 } tTrack;
94 /* max 99 tracks on a cd */
95 static tTrack tracks[100];
97 static struct cue_track_pos {
98 int track;
99 unsigned short mode;
100 unsigned short minute;
101 unsigned short second;
102 unsigned short frame;
103 } cue_current_pos;
105 /* number of tracks on the cd */
106 static int nTracks = 0;
108 static int digits2int(const char s[2], int errval) {
109 uint8_t a = s[0] - '0';
110 uint8_t b = s[1] - '0';
111 if (a > 9 || b > 9)
112 return errval;
113 return a * 10 + b;
116 /* presumes Line is preloaded with the "current" line of the file */
117 static int cue_getTrackinfo(FILE *fd_cue, char *Line, tTrack *track)
119 int already_set = 0;
121 /* Get the 'mode' */
122 if (strncmp(&Line[2], "TRACK ", 6)==0)
124 /* strncpy(track->num, &Line[8], 2); track->num[2] = '\0'; */
126 track->mode = UNKNOWN;
127 if(strncmp(&Line[11], "AUDIO", 5)==0) track->mode = AUDIO;
128 if(strncmp(&Line[11], "MODE1/2352", 10)==0) track->mode = MODE1_2352;
129 if(strncmp(&Line[11], "MODE1/2048", 10)==0) track->mode = MODE1_2048;
130 if(strncmp(&Line[11], "MODE2/2352", 10)==0) track->mode = MODE2_2352;
131 if(strncmp(&Line[11], "MODE2/2336", 10)==0) track->mode = MODE2_2336;
133 else return 1;
135 /* Get the track indexes */
136 while(1) {
137 if(! fgets( Line, 256, fd_cue ) ) { break;}
139 if (strncmp(&Line[2], "TRACK ", 6)==0)
141 /* next track starting */
142 break;
145 /* Track 0 or 1, take the first an get fill the values*/
146 if (strncmp(&Line[4], "INDEX ", 6)==0)
148 /* check stuff here so if the answer is false the else stuff below won't be executed */
149 if ((already_set == 0) && digits2int(Line + 10, 100) <= 1)
151 already_set = 1;
153 track->minute = digits2int(Line + 13, 0);
154 track->second = digits2int(Line + 16, 0);
155 track->frame = digits2int(Line + 19, 0);
158 else if (strncmp(&Line[4], "PREGAP ", 7)==0) { ; /* ignore */ }
159 else if (strncmp(&Line[4], "FLAGS ", 6)==0) { ; /* ignore */ }
160 else mp_tmsg (MSGT_OPEN,MSGL_INFO,
161 "[bincue] Unexpected cuefile line: %s\n", Line);
163 return 0;
168 /* FIXME: the string operations ( strcpy,strcat ) below depend
169 * on the arrays to have the same size, thus we need to make
170 * sure the sizes are in sync.
172 static int cue_find_bin (const char *firstline) {
173 struct stat filestat;
174 const char *cur_name;
175 char bin_filename[256];
176 char s[256];
177 char t[256];
178 int fd_bin;
179 int i = 0;
181 /* get the filename out of that */
182 /* 12345 6 */
183 mp_msg (MSGT_OPEN,MSGL_INFO, "[bincue] cue_find_bin(%s)\n", firstline);
184 if (strncmp(firstline, "FILE \"",6)==0)
186 firstline += 6;
187 while ( *firstline && *firstline != '"')
189 bin_filename[i] = *firstline++;
191 /* if I found a path info, then delete all before it */
192 switch (bin_filename[i])
194 case '\\':
195 i = 0;
196 break;
198 case '/':
199 i = 0;
200 break;
202 default:
203 i++;
207 bin_filename[i] = '\0';
209 fd_bin = -1;
210 for (i = 0; fd_bin == -1 && i < 6; i++) {
211 if (i <=1 && bin_filename[0] == '\0')
212 continue;
213 if (i > 1 && strlen(cue_filename) < 3)
214 break;
216 switch (i) {
217 case 0:
218 /* now try to open that file, without path */
219 cur_name = bin_filename;
220 break;
221 case 1:
222 /* now try to find it with the path of the cue file */
223 snprintf(s,sizeof( s ),"%s/%s",bincue_path,bin_filename);
224 cur_name = s;
225 break;
226 case 2:
227 /* now I would say the whole filename is shit, build our own */
228 av_strlcpy(s, cue_filename, strlen(cue_filename) - 3 );
229 strcat(s, ".bin");
230 cur_name = s;
231 break;
232 case 3:
233 /* ok try it with path */
234 snprintf(t, sizeof( t ), "%s/%s", bincue_path, s);
235 cur_name = t;
236 break;
237 case 4:
238 /* now I would say the whole filename is shit, build our own */
239 av_strlcpy(s, cue_filename, strlen(cue_filename) - 3 );
240 strcat(s, ".img");
241 cur_name = s;
242 break;
243 case 5:
244 /* ok try it with path */
245 snprintf(t, sizeof( t ), "%s/%s", bincue_path, s);
246 cur_name = t;
247 break;
249 fd_bin = open(cur_name, O_RDONLY);
250 if (fstat(fd_bin, &filestat) == -1 || !S_ISREG(filestat.st_mode)) {
251 close(fd_bin);
252 fd_bin = -1;
254 if (fd_bin == -1) {
255 mp_tmsg(MSGT_OPEN,MSGL_STATUS, "[bincue] bin filename tested: %s\n",
256 cur_name);
260 if (fd_bin == -1)
262 /* I'll give up */
263 mp_tmsg(MSGT_OPEN,MSGL_ERR,
264 "[bincue] Couldn't find the bin file - giving up.\n");
265 return -1;
268 mp_tmsg(MSGT_OPEN,MSGL_INFO,
269 "[bincue] Using bin file %s.\n", cur_name);
270 return fd_bin;
273 static inline int cue_msf_2_sector(int minute, int second, int frame) {
274 return frame + (second + minute * 60 ) * 75;
277 static inline int cue_get_msf(void) {
278 return cue_msf_2_sector (cue_current_pos.minute,
279 cue_current_pos.second,
280 cue_current_pos.frame);
283 static inline void cue_set_msf(unsigned int sect){
284 cue_current_pos.frame=sect%75;
285 sect=sect/75;
286 cue_current_pos.second=sect%60;
287 sect=sect/60;
288 cue_current_pos.minute=sect;
291 static inline int cue_mode_2_sector_size(int mode)
293 switch (mode)
295 case AUDIO: return AUDIO;
296 case MODE1_2352: return SIZERAW;
297 case MODE1_2048: return SIZEISO_MODE1;
298 case MODE2_2352: return SIZEISO_MODE2_RAW;
299 case MODE2_2336: return SIZEISO_MODE2_FORM2;
301 default:
302 mp_tmsg(MSGT_OPEN,MSGL_FATAL,
303 "[bincue] unknown mode for binfile. Should not happen. Aborting.\n");
304 abort();
310 static int cue_read_cue (const char *in_cue_filename)
312 struct stat filestat;
313 char sLine[256];
314 unsigned int sect;
315 char *s,*t;
316 int i;
317 int fd_bin;
318 FILE *fd_cue;
320 /* we have no tracks at the beginning */
321 nTracks = 0;
323 /* split the filename into a path and filename part */
324 s = strdup(in_cue_filename);
325 t = strrchr(s, '/');
326 if (!t)
327 t = ".";
328 else {
329 *t = '\0';
330 t = s;
331 if (*t == '\0')
332 strcpy(t, "/");
335 av_strlcpy(bincue_path,t,sizeof( bincue_path ));
336 mp_msg(MSGT_OPEN,MSGL_V,"dirname: %s, cuepath: %s\n", t, bincue_path);
337 free(s);
338 s = t = NULL;
340 /* no path at all? */
341 if (strcmp(bincue_path, ".") == 0) {
342 mp_msg(MSGT_OPEN,MSGL_V,"bincue_path: %s\n", bincue_path);
343 av_strlcpy(cue_filename,in_cue_filename,sizeof( cue_filename ));
344 } else {
345 av_strlcpy(cue_filename,in_cue_filename + strlen(bincue_path) + 1,
346 sizeof( cue_filename ));
351 /* open the cue file */
352 fd_cue = fopen (in_cue_filename, "r");
353 if (fd_cue == NULL)
355 mp_tmsg(MSGT_OPEN,MSGL_ERR,
356 "[bincue] Cannot open %s.\n", in_cue_filename);
357 return -1;
360 /* read the first line and hand it to find_bin, which will
361 test more than one possible name of the file */
363 if(! fgets( sLine, sizeof(sLine), fd_cue ) )
365 mp_tmsg(MSGT_OPEN,MSGL_ERR,
366 "[bincue] Error reading from %s\n", in_cue_filename);
367 fclose (fd_cue);
368 return -1;
371 fd_bin = cue_find_bin(sLine);
372 if (fd_bin == -1) {
373 fclose (fd_cue);
374 return -1;
378 /* now build the track list */
379 /* red the next line and call our track finder */
380 if(! fgets( sLine, sizeof(sLine), fd_cue ) )
382 mp_tmsg(MSGT_OPEN,MSGL_ERR,
383 "[bincue] Error reading from %s\n", in_cue_filename);
384 fclose (fd_cue);
385 return -1;
388 while(!feof(fd_cue))
390 if (cue_getTrackinfo(fd_cue, sLine, &tracks[nTracks++]) != 0)
392 mp_tmsg(MSGT_OPEN,MSGL_ERR,
393 "[bincue] Error reading from %s\n", in_cue_filename);
394 fclose (fd_cue);
395 return -1;
399 /* make a fake track with stands for the Lead out */
400 if (fstat (fd_bin, &filestat) == -1) {
401 mp_tmsg(MSGT_OPEN,MSGL_ERR,
402 "[bincue] Error getting size of bin file.\n");
403 fclose (fd_cue);
404 return -1;
407 sect = filestat.st_size / 2352;
409 tracks[nTracks].frame = sect%75;
410 sect=sect/75;
411 tracks[nTracks].second = sect%60;
412 sect=sect/60;
413 tracks[nTracks].minute = sect;
416 /* let's calculate the start sectors and offsets */
417 for(i = 0; i <= nTracks; i++)
419 tracks[i].start_sector = cue_msf_2_sector(tracks[i].minute,
420 tracks[nTracks].second,
421 tracks[nTracks].frame);
423 /* if we're the first track we don't need to offset of the one befor */
424 if (i == 0)
426 /* was always 0 on my svcds, but who knows */
427 tracks[0].start_offset = tracks[0].start_sector *
428 cue_mode_2_sector_size(tracks[0].mode);
429 } else
431 tracks[i].start_offset = tracks[i-1].start_offset +
432 (tracks[i].start_sector - tracks[i-1].start_sector) *
433 cue_mode_2_sector_size(tracks[i-1].mode);
437 fclose (fd_cue);
439 return fd_bin;
445 static int cue_read_toc_entry(int track) {
446 /* check if its a valid track, if not return -1 */
447 if (track <= 0 || track > nTracks)
448 return -1;
451 cue_current_pos.track = track;
452 track--;
453 switch (tracks[track].mode)
455 case AUDIO:
456 cue_current_pos.mode = AUDIO;
457 break;
458 case MODE1_2352:
459 cue_current_pos.mode = MODE1;
460 break;
461 case MODE1_2048:
462 cue_current_pos.mode = MODE1;
463 break;
464 default: /* MODE2_2352 and MODE2_2336 */
465 cue_current_pos.mode = MODE2;
467 cue_current_pos.minute = tracks[track].minute;
468 cue_current_pos.second = tracks[track].second;
469 cue_current_pos.frame = tracks[track].frame;
471 return 0;
474 static int cue_vcd_get_track_end (int track){
475 int sector = cue_msf_2_sector(tracks[track].minute, tracks[track].second,
476 tracks[track].frame);
478 return VCD_SECTOR_DATA * sector;
481 static int seek(stream_t *s,off_t newpos) {
482 s->pos=newpos;
483 cue_set_msf(s->pos/VCD_SECTOR_DATA);
484 return 1;
487 static int cue_vcd_seek_to_track (stream_t *stream, int track){
488 int pos;
489 if (cue_read_toc_entry (track))
490 return -1;
492 pos = VCD_SECTOR_DATA * cue_get_msf();
493 stream->start_pos = pos;
494 stream->end_pos = cue_vcd_get_track_end(track);
495 seek(stream, pos);
496 return pos;
499 static void cue_vcd_read_toc(void){
500 int i;
501 for (i = 0; i < nTracks; ++i) {
503 mp_tmsg(MSGT_OPEN,MSGL_INFO,
504 "track %02d: format=%d %02d:%02d:%02d\n",
505 i+1,
506 tracks[i].mode,
507 tracks[i].minute,
508 tracks[i].second,
509 tracks[i].frame
514 static int cue_vcd_read(stream_t *stream, char *mem, int size) {
515 unsigned long position;
516 int fd_bin = stream->fd;
517 int track = cue_current_pos.track - 1;
519 position = tracks[track].start_offset +
520 (cue_msf_2_sector(cue_current_pos.minute,
521 cue_current_pos.second,
522 cue_current_pos.frame) -
523 tracks[track].start_sector)
524 * cue_mode_2_sector_size(tracks[track].mode);
527 if(position >= tracks[track+1].start_offset)
528 return 0;
530 if(lseek(fd_bin, position+VCD_SECTOR_OFFS, SEEK_SET) == -1) {
531 mp_tmsg(MSGT_OPEN,MSGL_ERR, "[bincue] unexpected end of bin file\n");
532 return 0;
535 if(read(fd_bin, mem, VCD_SECTOR_DATA) != VCD_SECTOR_DATA) {
536 mp_tmsg(MSGT_OPEN,MSGL_ERR, "[bincue] Couldn't read %d bytes of payload.\n", VCD_SECTOR_DATA);
537 return 0;
540 cue_current_pos.frame++;
541 if (cue_current_pos.frame==75){
542 cue_current_pos.frame=0;
543 cue_current_pos.second++;
544 if (cue_current_pos.second==60){
545 cue_current_pos.second=0;
546 cue_current_pos.minute++;
550 return VCD_SECTOR_DATA;
553 static int control(stream_t *stream, int cmd, void *arg) {
554 switch(cmd) {
555 case STREAM_CTRL_GET_NUM_CHAPTERS:
557 *(unsigned int *)arg = nTracks;
558 return STREAM_OK;
560 case STREAM_CTRL_SEEK_TO_CHAPTER:
562 int r;
563 unsigned int track = *(unsigned int *)arg + 1;
564 r = cue_vcd_seek_to_track(stream, track);
565 if (r >= 0) {
566 return STREAM_OK;
568 break;
570 case STREAM_CTRL_GET_CURRENT_CHAPTER:
572 *(unsigned int *)arg = cue_current_pos.track - 1;
573 return STREAM_OK;
576 return STREAM_UNSUPPORTED;
579 static int open_s(stream_t *stream,int mode, void* opts, int* file_format) {
580 struct stream_priv_s* p = (struct stream_priv_s*)opts;
581 int ret,f,track = 0;
582 char *filename = NULL, *colon = NULL;
584 if(mode != STREAM_READ || !p->filename) {
585 m_struct_free(&stream_opts,opts);
586 return STREAM_UNSUPPORTED;
588 filename = strdup(p->filename);
589 if(!filename) {
590 m_struct_free(&stream_opts,opts);
591 return STREAM_UNSUPPORTED;
593 colon = strstr(filename, ":");
594 if(colon) {
595 if(strlen(colon)>1)
596 track = atoi(colon+1);
597 *colon = 0;
599 if(!track)
600 track = 1;
602 f = cue_read_cue(filename);
603 if(f < 0) {
604 m_struct_free(&stream_opts,opts);
605 return STREAM_UNSUPPORTED;
607 cue_vcd_read_toc();
608 ret=cue_vcd_seek_to_track(stream, track);
609 if(ret<0){
610 mp_msg(MSGT_OPEN, MSGL_ERR, "%s (seek)\n",
611 mp_gtext("Error selecting VCD track."));
612 return STREAM_UNSUPPORTED;
614 mp_tmsg(MSGT_OPEN, MSGL_INFO,
615 "CUE stream_open, filename=%s, track=%d, "
616 "available tracks: %d -> %d\n",
617 filename, track, ret, (int)stream->end_pos);
619 stream->fd = f;
620 stream->type = STREAMTYPE_VCDBINCUE;
621 stream->sector_size = VCD_SECTOR_DATA;
622 stream->flags = STREAM_READ | MP_STREAM_SEEK_FW;
623 stream->fill_buffer = cue_vcd_read;
624 stream->seek = seek;
625 stream->control = control;
627 free(filename);
628 m_struct_free(&stream_opts,opts);
629 return STREAM_OK;
632 const stream_info_t stream_info_cue = {
633 "CUE track",
634 "cue",
635 "Albeu",
636 "based on the code from ???",
637 open_s,
638 { "cue", NULL },
639 &stream_opts,
640 1 // Urls are an option string