Add explanatory comments to the #endif part of multiple inclusion guards.
[mplayer/greg.git] / stream / stream_cue.c
blob238ae183a8285e6b224dfae16a2cddcf6131d2c9
1 //=================== VideoCD BinCue ==========================
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
7 #include <sys/types.h>
8 #include <sys/stat.h>
9 #include <unistd.h>
10 #include <fcntl.h>
12 #include "config.h"
13 #include "mp_msg.h"
14 #include "help_mp.h"
16 #include "stream.h"
18 #include "help_mp.h"
19 #include "m_option.h"
20 #include "m_struct.h"
21 #include "libavutil/avstring.h"
23 #define byte unsigned char
24 #define SIZERAW 2352
25 #define SIZEISO_MODE1 2048
26 #define SIZEISO_MODE2_RAW 2352
27 #define SIZEISO_MODE2_FORM1 2048
28 #define SIZEISO_MODE2_FORM2 2336
29 #define AUDIO 0
30 #define MODE1 1
31 #define MODE2 2
32 #define MODE1_2352 10
33 #define MODE2_2352 20
34 #define MODE1_2048 30
35 #define MODE2_2336 40
36 #define UNKNOWN -1
38 static struct stream_priv_s {
39 char* filename;
40 } stream_priv_dflts = {
41 NULL
44 #define ST_OFF(f) M_ST_OFF(struct stream_priv_s,f)
45 /// URL definition
46 static const m_option_t stream_opts_fields[] = {
47 { "string", ST_OFF(filename), CONF_TYPE_STRING, 0, 0 ,0, NULL},
48 { NULL, NULL, 0, 0, 0, 0, NULL }
50 static struct m_struct_st stream_opts = {
51 "cue",
52 sizeof(struct stream_priv_s),
53 &stream_priv_dflts,
54 stream_opts_fields
57 static FILE* fd_cue;
58 static int fd_bin = 0;
60 static char bin_filename[256];
62 static char cue_filename[256];
63 static char bincue_path[256];
66 typedef struct track
68 unsigned short mode;
69 unsigned short minute;
70 unsigned short second;
71 unsigned short frame;
73 /* (min*60 + sec) * 75 + fps */
75 unsigned long start_sector;
77 /* = the sizes in bytes off all tracks bevor this one */
78 /* its needed if there are mode1 tracks befor the mpeg tracks */
79 unsigned long start_offset;
81 /* unsigned char num[3]; */
82 } tTrack;
84 /* max 99 tracks on a cd */
85 static tTrack tracks[100];
87 static struct cue_track_pos {
88 int track;
89 unsigned short mode;
90 unsigned short minute;
91 unsigned short second;
92 unsigned short frame;
93 } cue_current_pos;
95 /* number of tracks on the cd */
96 static int nTracks = 0;
98 /* presumes Line is preloaded with the "current" line of the file */
99 static int cue_getTrackinfo(char *Line, tTrack *track)
101 char inum[3];
102 char min;
103 char sec;
104 char fps;
105 int already_set = 0;
107 /* Get the 'mode' */
108 if (strncmp(&Line[2], "TRACK ", 6)==0)
110 /* strncpy(track->num, &Line[8], 2); track->num[2] = '\0'; */
112 track->mode = UNKNOWN;
113 if(strncmp(&Line[11], "AUDIO", 5)==0) track->mode = AUDIO;
114 if(strncmp(&Line[11], "MODE1/2352", 10)==0) track->mode = MODE1_2352;
115 if(strncmp(&Line[11], "MODE1/2048", 10)==0) track->mode = MODE1_2048;
116 if(strncmp(&Line[11], "MODE2/2352", 10)==0) track->mode = MODE2_2352;
117 if(strncmp(&Line[11], "MODE2/2336", 10)==0) track->mode = MODE2_2336;
119 else return(1);
121 /* Get the track indexes */
122 while(1) {
123 if(! fgets( Line, 256, fd_cue ) ) { break;}
125 if (strncmp(&Line[2], "TRACK ", 6)==0)
127 /* next track starting */
128 break;
131 /* Track 0 or 1, take the first an get fill the values*/
132 if (strncmp(&Line[4], "INDEX ", 6)==0)
134 /* check stuff here so if the answer is false the else stuff below won't be executed */
135 strncpy(inum, &Line[10], 2); inum[2] = '\0';
136 if ((already_set == 0) &&
137 ((strcmp(inum, "00")==0) || (strcmp(inum, "01")==0)))
139 already_set = 1;
141 min = ((Line[13]-'0')<<4) | (Line[14]-'0');
142 sec = ((Line[16]-'0')<<4) | (Line[17]-'0');
143 fps = ((Line[19]-'0')<<4) | (Line[20]-'0');
145 track->minute = (((min>>4)*10) + (min&0xf));
146 track->second = (((sec>>4)*10) + (sec&0xf));
147 track->frame = (((fps>>4)*10) + (fps&0xf));
150 else if (strncmp(&Line[4], "PREGAP ", 7)==0) { ; /* ignore */ }
151 else if (strncmp(&Line[4], "FLAGS ", 6)==0) { ; /* ignore */ }
152 else mp_msg (MSGT_OPEN,MSGL_INFO,
153 MSGTR_MPDEMUX_CUEREAD_UnexpectedCuefileLine, Line);
155 return(0);
160 /* FIXME: the string operations ( strcpy,strcat ) below depend
161 * on the arrays to have the same size, thus we need to make
162 * sure the sizes are in sync.
164 static int cue_find_bin (char *firstline) {
165 int i,j;
166 char s[256];
167 char t[256];
169 /* get the filename out of that */
170 /* 12345 6 */
171 mp_msg (MSGT_OPEN,MSGL_INFO, "[bincue] cue_find_bin(%s)\n", firstline);
172 if (strncmp(firstline, "FILE \"",6)==0)
174 i = 0;
175 j = 0;
176 while ( firstline[6 + i] != '"')
178 bin_filename[j] = firstline[6 + i];
180 /* if I found a path info, than delete all bevor it */
181 switch (bin_filename[j])
183 case '\\':
184 j = 0;
185 break;
187 case '/':
188 j = 0;
189 break;
191 default:
192 j++;
194 i++;
196 bin_filename[j+1] = '\0';
200 /* now try to open that file, without path */
201 fd_bin = open (bin_filename, O_RDONLY);
202 if (fd_bin == -1)
204 mp_msg(MSGT_OPEN,MSGL_STATUS, MSGTR_MPDEMUX_CUEREAD_BinFilenameTested,
205 bin_filename);
207 /* now try to find it with the path of the cue file */
208 snprintf(s,sizeof( s ),"%s/%s",bincue_path,bin_filename);
209 fd_bin = open (s, O_RDONLY);
210 if (fd_bin == -1)
212 mp_msg(MSGT_OPEN,MSGL_STATUS,
213 MSGTR_MPDEMUX_CUEREAD_BinFilenameTested, s);
214 /* now I would say the whole filename is shit, build our own */
215 strncpy(s, cue_filename, strlen(cue_filename) - 3 );
216 s[strlen(cue_filename) - 3] = '\0';
217 strcat(s, "bin");
218 fd_bin = open (s, O_RDONLY);
219 if (fd_bin == -1)
221 mp_msg(MSGT_OPEN,MSGL_STATUS,
222 MSGTR_MPDEMUX_CUEREAD_BinFilenameTested, s);
224 /* ok try it with path */
225 snprintf(t, sizeof( t ), "%s/%s", bincue_path, s);
226 fd_bin = open (t, O_RDONLY);
227 if (fd_bin == -1)
229 mp_msg(MSGT_OPEN,MSGL_STATUS,
230 MSGTR_MPDEMUX_CUEREAD_BinFilenameTested,t);
231 /* now I would say the whole filename is shit, build our own */
232 strncpy(s, cue_filename, strlen(cue_filename) - 3 );
233 s[strlen(cue_filename) - 3] = '\0';
234 strcat(s, "img");
235 fd_bin = open (s, O_RDONLY);
236 if (fd_bin == -1)
238 mp_msg(MSGT_OPEN,MSGL_STATUS,
239 MSGTR_MPDEMUX_CUEREAD_BinFilenameTested, s);
240 /* ok try it with path */
241 snprintf(t, sizeof( t ), "%s/%s", bincue_path, s);
242 fd_bin = open (t, O_RDONLY);
243 if (fd_bin == -1)
245 mp_msg(MSGT_OPEN,MSGL_STATUS,
246 MSGTR_MPDEMUX_CUEREAD_BinFilenameTested, s);
248 /* I'll give up */
249 mp_msg(MSGT_OPEN,MSGL_ERR,
250 MSGTR_MPDEMUX_CUEREAD_CannotFindBinFile);
251 return -1;
254 } else strcpy(bin_filename, t);
256 } else strcpy(bin_filename, s);
258 } else strcpy(bin_filename, s);
262 mp_msg(MSGT_OPEN,MSGL_INFO,
263 MSGTR_MPDEMUX_CUEREAD_UsingBinFile, bin_filename);
264 return 0;
267 static inline int cue_msf_2_sector(int minute, int second, int frame) {
268 return frame + (second + minute * 60 ) * 75;
271 static inline int cue_get_msf(void) {
272 return cue_msf_2_sector (cue_current_pos.minute,
273 cue_current_pos.second,
274 cue_current_pos.frame);
277 static inline void cue_set_msf(unsigned int sect){
278 cue_current_pos.frame=sect%75;
279 sect=sect/75;
280 cue_current_pos.second=sect%60;
281 sect=sect/60;
282 cue_current_pos.minute=sect;
285 static inline int cue_mode_2_sector_size(int mode)
287 switch (mode)
289 case AUDIO: return AUDIO;
290 case MODE1_2352: return SIZERAW;
291 case MODE1_2048: return SIZEISO_MODE1;
292 case MODE2_2352: return SIZEISO_MODE2_RAW;
293 case MODE2_2336: return SIZEISO_MODE2_FORM2;
295 default:
296 mp_msg(MSGT_OPEN,MSGL_FATAL,
297 MSGTR_MPDEMUX_CUEREAD_UnknownModeForBinfile);
298 abort();
304 static int cue_read_cue (char *in_cue_filename)
306 struct stat filestat;
307 char sLine[256];
308 unsigned int sect;
309 char *s,*t;
310 int i;
312 /* we have no tracks at the beginning */
313 nTracks = 0;
315 fd_bin = 0;
317 /* split the filename into a path and filename part */
318 s = strdup(in_cue_filename);
319 t = strrchr(s, '/');
320 if (t == (char *)NULL)
321 t = ".";
322 else {
323 *t = '\0';
324 t = s;
325 if (*t == '\0')
326 strcpy(t, "/");
329 av_strlcpy(bincue_path,t,sizeof( bincue_path ));
330 mp_msg(MSGT_OPEN,MSGL_V,"dirname: %s, cuepath: %s\n", t, bincue_path);
332 /* no path at all? */
333 if (strcmp(bincue_path, ".") == 0) {
334 mp_msg(MSGT_OPEN,MSGL_V,"bincue_path: %s\n", bincue_path);
335 av_strlcpy(cue_filename,in_cue_filename,sizeof( cue_filename ));
336 } else {
337 av_strlcpy(cue_filename,in_cue_filename + strlen(bincue_path) + 1,
338 sizeof( cue_filename ));
343 /* open the cue file */
344 fd_cue = fopen (in_cue_filename, "r");
345 if (fd_cue == NULL)
347 mp_msg(MSGT_OPEN,MSGL_ERR,
348 MSGTR_MPDEMUX_CUEREAD_CannotOpenCueFile, in_cue_filename);
349 return -1;
352 /* read the first line and hand it to find_bin, which will
353 test more than one possible name of the file */
355 if(! fgets( sLine, 256, fd_cue ) )
357 mp_msg(MSGT_OPEN,MSGL_ERR,
358 MSGTR_MPDEMUX_CUEREAD_ErrReadingFromCueFile, in_cue_filename);
359 fclose (fd_cue);
360 return -1;
363 if (cue_find_bin(sLine)) {
364 fclose (fd_cue);
365 return -1;
369 /* now build the track list */
370 /* red the next line and call our track finder */
371 if(! fgets( sLine, 256, fd_cue ) )
373 mp_msg(MSGT_OPEN,MSGL_ERR,
374 MSGTR_MPDEMUX_CUEREAD_ErrReadingFromCueFile, in_cue_filename);
375 fclose (fd_cue);
376 return -1;
379 while(!feof(fd_cue))
381 if (cue_getTrackinfo(sLine, &tracks[nTracks++]) != 0)
383 mp_msg(MSGT_OPEN,MSGL_ERR,
384 MSGTR_MPDEMUX_CUEREAD_ErrReadingFromCueFile, in_cue_filename);
385 fclose (fd_cue);
386 return -1;
390 /* make a fake track with stands for the Lead out */
391 if (fstat (fd_bin, &filestat) == -1) {
392 mp_msg(MSGT_OPEN,MSGL_ERR,
393 MSGTR_MPDEMUX_CUEREAD_ErrGettingBinFileSize);
394 fclose (fd_cue);
395 return -1;
398 sect = filestat.st_size / 2352;
400 tracks[nTracks].frame = sect%75;
401 sect=sect/75;
402 tracks[nTracks].second = sect%60;
403 sect=sect/60;
404 tracks[nTracks].minute = sect;
407 /* let's calculate the start sectors and offsets */
408 for(i = 0; i <= nTracks; i++)
410 tracks[i].start_sector = cue_msf_2_sector(tracks[i].minute,
411 tracks[nTracks].second,
412 tracks[nTracks].frame);
414 /* if we're the first track we don't need to offset of the one befor */
415 if (i == 0)
417 /* was always 0 on my svcds, but who knows */
418 tracks[0].start_offset = tracks[0].start_sector *
419 cue_mode_2_sector_size(tracks[0].mode);
420 } else
422 tracks[i].start_offset = tracks[i-1].start_offset +
423 (tracks[i].start_sector - tracks[i-1].start_sector) *
424 cue_mode_2_sector_size(tracks[i-1].mode);
428 fclose (fd_cue);
430 return fd_bin;
436 static int cue_read_toc_entry(void) {
438 int track = cue_current_pos.track - 1;
440 /* check if its a valid track, if not return -1 */
441 if (track >= nTracks)
442 return -1;
445 switch (tracks[track].mode)
447 case AUDIO:
448 cue_current_pos.mode = AUDIO;
449 break;
450 case MODE1_2352:
451 cue_current_pos.mode = MODE1;
452 break;
453 case MODE1_2048:
454 cue_current_pos.mode = MODE1;
455 break;
456 default: /* MODE2_2352 and MODE2_2336 */
457 cue_current_pos.mode = MODE2;
459 cue_current_pos.minute = tracks[track].minute;
460 cue_current_pos.second = tracks[track].second;
461 cue_current_pos.frame = tracks[track].frame;
463 return 0;
466 static int cue_vcd_seek_to_track (int track){
467 cue_current_pos.track = track;
469 if (cue_read_toc_entry ())
470 return -1;
472 return VCD_SECTOR_DATA * cue_get_msf();
475 static int cue_vcd_get_track_end (int track){
476 cue_current_pos.frame = tracks[track].frame;
477 cue_current_pos.second = tracks[track].second;
478 cue_current_pos.minute = tracks[track].minute;
480 return VCD_SECTOR_DATA * cue_get_msf();
483 static void cue_vcd_read_toc(void){
484 int i;
485 for (i = 0; i < nTracks; ++i) {
487 mp_msg(MSGT_OPEN,MSGL_INFO,
488 MSGTR_MPDEMUX_CUEREAD_InfoTrackFormat,
489 i+1,
490 tracks[i].mode,
491 tracks[i].minute,
492 tracks[i].second,
493 tracks[i].frame
498 static int cue_vcd_read(stream_t *stream, char *mem, int size) {
499 unsigned long position;
500 int track = cue_current_pos.track - 1;
502 position = tracks[track].start_offset +
503 (cue_msf_2_sector(cue_current_pos.minute,
504 cue_current_pos.second,
505 cue_current_pos.frame) -
506 tracks[track].start_sector)
507 * cue_mode_2_sector_size(tracks[track].mode);
510 if(position >= tracks[track+1].start_offset)
511 return 0;
513 if(lseek(fd_bin, position+VCD_SECTOR_OFFS, SEEK_SET) == -1) {
514 mp_msg(MSGT_OPEN,MSGL_ERR, MSGTR_MPDEMUX_CUEREAD_UnexpectedBinFileEOF);
515 return 0;
518 if(read(fd_bin, mem, VCD_SECTOR_DATA) != VCD_SECTOR_DATA) {
519 mp_msg(MSGT_OPEN,MSGL_ERR, MSGTR_MPDEMUX_CUEREAD_CannotReadNBytesOfPayload, VCD_SECTOR_DATA);
520 return 0;
523 cue_current_pos.frame++;
524 if (cue_current_pos.frame==75){
525 cue_current_pos.frame=0;
526 cue_current_pos.second++;
527 if (cue_current_pos.second==60){
528 cue_current_pos.second=0;
529 cue_current_pos.minute++;
533 return VCD_SECTOR_DATA;
536 static int seek(stream_t *s,off_t newpos) {
537 s->pos=newpos;
538 cue_set_msf(s->pos/VCD_SECTOR_DATA);
539 return 1;
543 static int open_s(stream_t *stream,int mode, void* opts, int* file_format) {
544 struct stream_priv_s* p = (struct stream_priv_s*)opts;
545 int ret,ret2,f,track = 0;
546 char *filename = NULL, *colon = NULL;
548 if(mode != STREAM_READ || !p->filename) {
549 m_struct_free(&stream_opts,opts);
550 return STREAM_UNSUPPORTED;
552 filename = strdup(p->filename);
553 if(!filename) {
554 m_struct_free(&stream_opts,opts);
555 return STREAM_UNSUPPORTED;
557 colon = strstr(filename, ":");
558 if(colon) {
559 if(strlen(colon)>1)
560 track = atoi(colon+1);
561 *colon = 0;
563 if(!track)
564 track = 1;
566 f = cue_read_cue(filename);
567 if(f < 0) {
568 m_struct_free(&stream_opts,opts);
569 return STREAM_UNSUPPORTED;
571 cue_vcd_read_toc();
572 ret2=cue_vcd_get_track_end(track);
573 ret=cue_vcd_seek_to_track(track);
574 if(ret<0){
575 mp_msg(MSGT_OPEN,MSGL_ERR,MSGTR_ErrTrackSelect " (seek)\n");
576 return STREAM_UNSUPPORTED;
578 mp_msg(MSGT_OPEN,MSGL_INFO,MSGTR_MPDEMUX_CUEREAD_CueStreamInfo_FilenameTrackTracksavail, filename, track, ret, ret2);
580 stream->fd = f;
581 stream->type = STREAMTYPE_VCDBINCUE;
582 stream->sector_size = VCD_SECTOR_DATA;
583 stream->flags = STREAM_READ | STREAM_SEEK_FW;
584 stream->start_pos = ret;
585 stream->end_pos = ret2;
586 stream->fill_buffer = cue_vcd_read;
587 stream->seek = seek;
589 free(filename);
590 m_struct_free(&stream_opts,opts);
591 return STREAM_OK;
594 const stream_info_t stream_info_cue = {
595 "CUE track",
596 "cue",
597 "Albeu",
598 "based on the code from ???",
599 open_s,
600 { "cue", NULL },
601 &stream_opts,
602 1 // Urls are an option string