workaround wrong detected memory sizes on G400 cards.
[mplayer/glamo.git] / subreader.c
blob28fea35218c29c2b4f8034536cefdc665e321281
1 /*
2 * Subtitle reader with format autodetection
4 * Written by laaz
5 * Some code cleanup & realloc() by A'rpi/ESP-team
6 * dunnowhat sub format by szabi
7 */
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <ctype.h>
15 #include "config.h"
16 #include "mp_msg.h"
17 #include "subreader.h"
19 #define ERR ((void *) -1)
21 #ifdef USE_ICONV
22 #include <iconv.h>
23 char *sub_cp=NULL;
24 #endif
26 /* Maximal length of line of a subtitle */
27 #define LINE_LEN 1000
29 static float mpsub_position=0;
31 int sub_uses_time=0;
32 int sub_errs=0;
33 int sub_num=0; // number of subtitle structs
34 int sub_slacktime=2000; // 20 seconds
35 int sub_no_text_pp=0; // 1 => do not apply text post-processing
36 // like {\...} elimination in SSA format.
38 /* Use the SUB_* constant defined in the header file */
39 int sub_format=SUB_INVALID;
40 #ifdef USE_SORTSUB
41 /*
42 Some subtitling formats, namely AQT and Subrip09, define the end of a
43 subtitle as the beginning of the following. Since currently we read one
44 subtitle at time, for these format we keep two global *subtitle,
45 previous_aqt_sub and previous_subrip09_sub, pointing to previous subtitle,
46 so we can change its end when we read current subtitle starting time.
47 When USE_SORTSUB is defined, we use a single global unsigned long,
48 previous_sub_end, for both (and even future) formats, to store the end of
49 the previous sub: it is initialized to 0 in sub_read_file and eventually
50 modified by sub_read_aqt_line or sub_read_subrip09_line.
52 unsigned long previous_sub_end;
53 #endif
55 static int eol(char p) {
56 return (p=='\r' || p=='\n' || p=='\0');
59 /* Remove leading and trailing space */
60 static void trail_space(char *s) {
61 int i = 0;
62 while (isspace(s[i])) ++i;
63 if (i) strcpy(s, s + i);
64 i = strlen(s) - 1;
65 while (i > 0 && isspace(s[i])) s[i--] = '\0';
69 subtitle *sub_read_line_sami(FILE *fd, subtitle *current) {
70 static char line[LINE_LEN+1];
71 static char *s = NULL, *slacktime_s;
72 char text[LINE_LEN+1], *p=NULL, *q;
73 int state;
75 current->lines = current->start = current->end = 0;
76 state = 0;
78 /* read the first line */
79 if (!s)
80 if (!(s = fgets(line, LINE_LEN, fd))) return 0;
82 do {
83 switch (state) {
85 case 0: /* find "START=" or "Slacktime:" */
86 slacktime_s = strstr (s, "Slacktime:");
87 if (slacktime_s) sub_slacktime = strtol (slacktime_s + 10, NULL, 0) / 10;
89 s = strstr (s, "Start=");
90 if (s) {
91 current->start = strtol (s + 6, &s, 0) / 10;
92 state = 1; continue;
94 break;
96 case 1: /* find "<P" */
97 if ((s = strstr (s, "<P"))) { s += 2; state = 2; continue; }
98 break;
100 case 2: /* find ">" */
101 if ((s = strchr (s, '>'))) { s++; state = 3; p = text; continue; }
102 break;
104 case 3: /* get all text until '<' appears */
105 if (*s == '\0') break;
106 else if (!strncasecmp (s, "<br>", 4)) {
107 *p = '\0'; p = text; trail_space (text);
108 if (text[0] != '\0')
109 current->text[current->lines++] = strdup (text);
110 s += 4;
112 else if (*s == '<') { state = 4; }
113 else if (!strncasecmp (s, "&nbsp;", 6)) { *p++ = ' '; s += 6; }
114 else if (*s == '\t') { *p++ = ' '; s++; }
115 else if (*s == '\r' || *s == '\n') { s++; }
116 else *p++ = *s++;
118 /* skip duplicated space */
119 if (p > text + 2) if (*(p-1) == ' ' && *(p-2) == ' ') p--;
121 continue;
123 case 4: /* get current->end or skip <TAG> */
124 q = strstr (s, "Start=");
125 if (q) {
126 current->end = strtol (q + 6, &q, 0) / 10 - 1;
127 *p = '\0'; trail_space (text);
128 if (text[0] != '\0')
129 current->text[current->lines++] = strdup (text);
130 if (current->lines > 0) { state = 99; break; }
131 state = 0; continue;
133 s = strchr (s, '>');
134 if (s) { s++; state = 3; continue; }
135 break;
138 /* read next line */
139 if (state != 99 && !(s = fgets (line, LINE_LEN, fd))) {
140 if (current->start > 0) {
141 break; // if it is the last subtitle
142 } else {
143 return 0;
147 } while (state != 99);
149 // For the last subtitle
150 if (current->end <= 0) {
151 current->end = current->start + sub_slacktime;
152 *p = '\0'; trail_space (text);
153 if (text[0] != '\0')
154 current->text[current->lines++] = strdup (text);
157 return current;
161 char *sub_readtext(char *source, char **dest) {
162 int len=0;
163 char *p=source;
165 // printf("src=%p dest=%p \n",source,dest);
167 while ( !eol(*p) && *p!= '|' ) {
168 p++,len++;
171 *dest= (char *)malloc (len+1);
172 if (!dest) {return ERR;}
174 strncpy(*dest, source, len);
175 (*dest)[len]=0;
177 while (*p=='\r' || *p=='\n' || *p=='|') p++;
179 if (*p) return p; // not-last text field
180 else return NULL; // last text field
183 subtitle *sub_read_line_microdvd(FILE *fd,subtitle *current) {
184 char line[LINE_LEN+1];
185 char line2[LINE_LEN+1];
186 char *p, *next;
187 int i;
189 do {
190 if (!fgets (line, LINE_LEN, fd)) return NULL;
191 } while ((sscanf (line,
192 "{%ld}{}%[^\r\n]",
193 &(current->start), line2) < 2) &&
194 (sscanf (line,
195 "{%ld}{%ld}%[^\r\n]",
196 &(current->start), &(current->end), line2) < 3));
198 p=line2;
200 next=p, i=0;
201 while ((next =sub_readtext (next, &(current->text[i])))) {
202 if (current->text[i]==ERR) {return ERR;}
203 i++;
204 if (i>=SUB_MAX_TEXT) { mp_msg(MSGT_SUBREADER,MSGL_WARN,"Too many lines in a subtitle\n");current->lines=i;return current;}
206 current->lines= ++i;
208 return current;
211 subtitle *sub_read_line_subrip(FILE *fd, subtitle *current) {
212 char line[LINE_LEN+1];
213 int a1,a2,a3,a4,b1,b2,b3,b4;
214 char *p=NULL, *q=NULL;
215 int len;
217 while (1) {
218 if (!fgets (line, LINE_LEN, fd)) return NULL;
219 if (sscanf (line, "%d:%d:%d.%d,%d:%d:%d.%d",&a1,&a2,&a3,&a4,&b1,&b2,&b3,&b4) < 8) continue;
220 current->start = a1*360000+a2*6000+a3*100+a4;
221 current->end = b1*360000+b2*6000+b3*100+b4;
223 if (!fgets (line, LINE_LEN, fd)) return NULL;
225 p=q=line;
226 for (current->lines=1; current->lines < SUB_MAX_TEXT; current->lines++) {
227 for (q=p,len=0; *p && *p!='\r' && *p!='\n' && *p!='|' && strncmp(p,"[br]",4); p++,len++);
228 current->text[current->lines-1]=(char *)malloc (len+1);
229 if (!current->text[current->lines-1]) return ERR;
230 strncpy (current->text[current->lines-1], q, len);
231 current->text[current->lines-1][len]='\0';
232 if (!*p || *p=='\r' || *p=='\n') break;
233 if (*p=='|') p++;
234 else while (*p++!=']');
236 break;
238 return current;
241 subtitle *sub_read_line_subviewer(FILE *fd,subtitle *current) {
242 char line[LINE_LEN+1];
243 int a1,a2,a3,a4,b1,b2,b3,b4;
244 char *p=NULL;
245 int i,len;
247 while (!current->text[0]) {
248 if (!fgets (line, LINE_LEN, fd)) return NULL;
249 if ((len=sscanf (line, "%d:%d:%d%[,.:]%d --> %d:%d:%d%[,.:]%d",&a1,&a2,&a3,(char *)&i,&a4,&b1,&b2,&b3,(char *)&i,&b4)) < 10)
250 continue;
251 current->start = a1*360000+a2*6000+a3*100+a4/10;
252 current->end = b1*360000+b2*6000+b3*100+b4/10;
253 for (i=0; i<SUB_MAX_TEXT;) {
254 if (!fgets (line, LINE_LEN, fd)) break;
255 len=0;
256 for (p=line; *p!='\n' && *p!='\r' && *p; p++,len++);
257 if (len) {
258 int j=0,skip=0;
259 char *curptr=current->text[i]=(char *)malloc (len+1);
260 if (!current->text[i]) return ERR;
261 //strncpy (current->text[i], line, len); current->text[i][len]='\0';
262 for(; j<len; j++) {
263 /* let's filter html tags ::atmos */
264 if(line[j]=='>') {
265 skip=0;
266 continue;
268 if(line[j]=='<') {
269 skip=1;
270 continue;
272 if(skip) {
273 continue;
275 *curptr=line[j];
276 curptr++;
278 *curptr='\0';
280 i++;
281 } else {
282 break;
285 current->lines=i;
287 return current;
290 subtitle *sub_read_line_subviewer2(FILE *fd,subtitle *current) {
291 char line[LINE_LEN+1];
292 int a1,a2,a3,a4;
293 char *p=NULL;
294 int i,len;
296 while (!current->text[0]) {
297 if (!fgets (line, LINE_LEN, fd)) return NULL;
298 if (line[0]!='{')
299 continue;
300 if ((len=sscanf (line, "{T %d:%d:%d:%d",&a1,&a2,&a3,&a4)) < 4)
301 continue;
302 current->start = a1*360000+a2*6000+a3*100+a4/10;
303 for (i=0; i<SUB_MAX_TEXT;) {
304 if (!fgets (line, LINE_LEN, fd)) break;
305 if (line[0]=='}') break;
306 len=0;
307 for (p=line; *p!='\n' && *p!='\r' && *p; ++p,++len);
308 if (len) {
309 current->text[i]=(char *)malloc (len+1);
310 if (!current->text[i]) return ERR;
311 strncpy (current->text[i], line, len); current->text[i][len]='\0';
312 ++i;
313 } else {
314 break;
317 current->lines=i;
319 return current;
323 subtitle *sub_read_line_vplayer(FILE *fd,subtitle *current) {
324 char line[LINE_LEN+1];
325 int a1,a2,a3;
326 char *p=NULL, *next,separator;
327 int i,len,plen;
329 while (!current->text[0]) {
330 if (!fgets (line, LINE_LEN, fd)) return NULL;
331 if ((len=sscanf (line, "%d:%d:%d%c%n",&a1,&a2,&a3,&separator,&plen)) < 4)
332 continue;
334 if (!(current->start = a1*360000+a2*6000+a3*100))
335 continue;
336 /* removed by wodzu
337 p=line;
338 // finds the body of the subtitle
339 for (i=0; i<3; i++){
340 p=strchr(p,':');
341 if (p==NULL) break;
342 ++p;
344 if (p==NULL) {
345 printf("SUB: Skipping incorrect subtitle line!\n");
346 continue;
349 // by wodzu: hey! this time we know what length it has! what is
350 // that magic for? it can't deal with space instead of third
351 // colon! look, what simple it can be:
352 p = &line[ plen ];
354 i=0;
355 if (*p!='|') {
357 next = p,i=0;
358 while ((next =sub_readtext (next, &(current->text[i])))) {
359 if (current->text[i]==ERR) {return ERR;}
360 i++;
361 if (i>=SUB_MAX_TEXT) { mp_msg(MSGT_SUBREADER,MSGL_WARN,"Too many lines in a subtitle\n");current->lines=i;return current;}
363 current->lines=i+1;
366 return current;
369 subtitle *sub_read_line_rt(FILE *fd,subtitle *current) {
370 //TODO: This format uses quite rich (sub/super)set of xhtml
371 // I couldn't check it since DTD is not included.
372 // WARNING: full XML parses can be required for proper parsing
373 char line[LINE_LEN+1];
374 int a1,a2,a3,a4,b1,b2,b3,b4;
375 char *p=NULL,*next=NULL;
376 int i,len,plen;
378 while (!current->text[0]) {
379 if (!fgets (line, LINE_LEN, fd)) return NULL;
380 //TODO: it seems that format of time is not easily determined, it may be 1:12, 1:12.0 or 0:1:12.0
381 //to describe the same moment in time. Maybe there are even more formats in use.
382 //if ((len=sscanf (line, "<Time Begin=\"%d:%d:%d.%d\" End=\"%d:%d:%d.%d\"",&a1,&a2,&a3,&a4,&b1,&b2,&b3,&b4)) < 8)
383 plen=a1=a2=a3=a4=b1=b2=b3=b4=0;
384 if (
385 ((len=sscanf (line, "<%*[tT]ime %*[bB]egin=\"%d.%d\" %*[Ee]nd=\"%d.%d\"%*[^<]<clear/>%n",&a3,&a4,&b3,&b4,&plen)) < 4) &&
386 ((len=sscanf (line, "<%*[tT]ime %*[bB]egin=\"%d.%d\" %*[Ee]nd=\"%d:%d.%d\"%*[^<]<clear/>%n",&a3,&a4,&b2,&b3,&b4,&plen)) < 5) &&
387 ((len=sscanf (line, "<%*[tT]ime %*[bB]egin=\"%d:%d\" %*[Ee]nd=\"%d:%d\"%*[^<]<clear/>%n",&a2,&a3,&b2,&b3,&plen)) < 4) &&
388 ((len=sscanf (line, "<%*[tT]ime %*[bB]egin=\"%d:%d\" %*[Ee]nd=\"%d:%d.%d\"%*[^<]<clear/>%n",&a2,&a3,&b2,&b3,&b4,&plen)) < 5) &&
389 // ((len=sscanf (line, "<%*[tT]ime %*[bB]egin=\"%d:%d.%d\" %*[Ee]nd=\"%d:%d\"%*[^<]<clear/>%n",&a2,&a3,&a4,&b2,&b3,&plen)) < 5) &&
390 ((len=sscanf (line, "<%*[tT]ime %*[bB]egin=\"%d:%d.%d\" %*[Ee]nd=\"%d:%d.%d\"%*[^<]<clear/>%n",&a2,&a3,&a4,&b2,&b3,&b4,&plen)) < 6) &&
391 ((len=sscanf (line, "<%*[tT]ime %*[bB]egin=\"%d:%d:%d.%d\" %*[Ee]nd=\"%d:%d:%d.%d\"%*[^<]<clear/>%n",&a1,&a2,&a3,&a4,&b1,&b2,&b3,&b4,&plen)) < 8) &&
392 //now try it without end time
393 ((len=sscanf (line, "<%*[tT]ime %*[bB]egin=\"%d.%d\"%*[^<]<clear/>%n",&a3,&a4,&plen)) < 2) &&
394 ((len=sscanf (line, "<%*[tT]ime %*[bB]egin=\"%d:%d\"%*[^<]<clear/>%n",&a2,&a3,&plen)) < 2) &&
395 ((len=sscanf (line, "<%*[tT]ime %*[bB]egin=\"%d:%d.%d\"%*[^<]<clear/>%n",&a2,&a3,&a4,&plen)) < 3) &&
396 ((len=sscanf (line, "<%*[tT]ime %*[bB]egin=\"%d:%d:%d.%d\"%*[^<]<clear/>%n",&a1,&a2,&a3,&a4,&plen)) < 4)
398 continue;
399 current->start = a1*360000+a2*6000+a3*100+a4/10;
400 current->end = b1*360000+b2*6000+b3*100+b4/10;
401 if (b1 == 0 && b2 == 0 && b3 == 0 && b4 == 0)
402 current->end = current->start+200;
403 p=line; p+=plen;i=0;
404 // TODO: I don't know what kind of convention is here for marking multiline subs, maybe <br/> like in xml?
405 next = strstr(line,"<clear/>");
406 if(next && strlen(next)>8){
407 next+=8;i=0;
408 while ((next =sub_readtext (next, &(current->text[i])))) {
409 if (current->text[i]==ERR) {return ERR;}
410 i++;
411 if (i>=SUB_MAX_TEXT) { mp_msg(MSGT_SUBREADER,MSGL_WARN,"Too many lines in a subtitle\n");current->lines=i;return current;}
414 current->lines=i+1;
416 return current;
419 subtitle *sub_read_line_ssa(FILE *fd,subtitle *current) {
421 * Sub Station Alpha v4 (and v2?) scripts have 9 commas before subtitle
422 * other Sub Station Alpha scripts have only 8 commas before subtitle
423 * Reading the "ScriptType:" field is not reliable since many scripts appear
424 * w/o it
426 * http://www.scriptclub.org is a good place to find more examples
427 * http://www.eswat.demon.co.uk is where the SSA specs can be found
429 int comma;
430 static int max_comma = 32; /* let's use 32 for the case that the */
431 /* amount of commas increase with newer SSA versions */
433 int hour1, min1, sec1, hunsec1,
434 hour2, min2, sec2, hunsec2, nothing;
435 int num;
437 char line[LINE_LEN+1],
438 line3[LINE_LEN+1],
439 *line2;
440 char *tmp;
442 do {
443 if (!fgets (line, LINE_LEN, fd)) return NULL;
444 } while (sscanf (line, "Dialogue: Marked=%d,%d:%d:%d.%d,%d:%d:%d.%d,"
445 "%[^\n\r]", &nothing,
446 &hour1, &min1, &sec1, &hunsec1,
447 &hour2, &min2, &sec2, &hunsec2,
448 line3) < 9);
450 line2=strchr(line3, ',');
452 for (comma = 4; comma < max_comma; comma ++)
454 tmp = line2;
455 if(!(tmp=strchr(++tmp, ','))) break;
456 if(*(++tmp) == ' ') break;
457 /* a space after a comma means we're already in a sentence */
458 line2 = tmp;
461 if(comma < max_comma)max_comma = comma;
462 /* eliminate the trailing comma */
463 if(*line2 == ',') line2++;
465 current->lines=0;num=0;
466 current->start = 360000*hour1 + 6000*min1 + 100*sec1 + hunsec1;
467 current->end = 360000*hour2 + 6000*min2 + 100*sec2 + hunsec2;
469 while (((tmp=strstr(line2, "\\n")) != NULL) || ((tmp=strstr(line2, "\\N")) != NULL) ){
470 current->text[num]=(char *)malloc(tmp-line2+1);
471 strncpy (current->text[num], line2, tmp-line2);
472 current->text[num][tmp-line2]='\0';
473 line2=tmp+2;
474 num++;
475 current->lines++;
476 if (current->lines >= SUB_MAX_TEXT) return current;
479 current->text[num]=strdup(line2);
480 current->lines++;
482 return current;
485 void sub_pp_ssa(subtitle *sub) {
486 int l=sub->lines;
487 char *so,*de,*start;
489 while (l){
490 /* eliminate any text enclosed with {}, they are font and color settings */
491 so=de=sub->text[--l];
492 while (*so) {
493 if(*so == '{' && so[1]=='\\') {
494 for (start=so; *so && *so!='}'; so++);
495 if(*so) so++; else so=start;
497 if(*so) {
498 *de=*so;
499 so++; de++;
502 *de=*so;
506 subtitle *sub_read_line_dunnowhat(FILE *fd,subtitle *current) {
507 char line[LINE_LEN+1];
508 char text[LINE_LEN+1];
510 if (!fgets (line, LINE_LEN, fd))
511 return NULL;
512 if (sscanf (line, "%ld,%ld,\"%[^\"]", &(current->start),
513 &(current->end), text) <3)
514 return ERR;
515 current->text[0] = strdup(text);
516 current->lines = 1;
518 return current;
521 subtitle *sub_read_line_mpsub(FILE *fd, subtitle *current) {
522 char line[LINE_LEN+1];
523 float a,b;
524 int num=0;
525 char *p, *q;
529 if (!fgets(line, LINE_LEN, fd)) return NULL;
530 } while (sscanf (line, "%f %f", &a, &b) !=2);
532 mpsub_position += a*(sub_uses_time ? 100.0 : 1.0);
533 current->start=(int) mpsub_position;
534 mpsub_position += b*(sub_uses_time ? 100.0 : 1.0);
535 current->end=(int) mpsub_position;
537 while (num < SUB_MAX_TEXT) {
538 if (!fgets (line, LINE_LEN, fd)) {
539 if (num == 0) return NULL;
540 else return current;
542 p=line;
543 while (isspace(*p)) p++;
544 if (eol(*p) && num > 0) return current;
545 if (eol(*p)) return NULL;
547 for (q=p; !eol(*q); q++);
548 *q='\0';
549 if (strlen(p)) {
550 current->text[num]=strdup(p);
551 // printf (">%s<\n",p);
552 current->lines = ++num;
553 } else {
554 if (num) return current;
555 else return NULL;
558 return NULL; // we should have returned before if it's OK
561 #ifndef USE_SORTSUB
562 //we don't need this if we use previous_sub_end
563 subtitle *previous_aqt_sub = NULL;
564 #endif
566 subtitle *sub_read_line_aqt(FILE *fd,subtitle *current) {
567 char line[LINE_LEN+1];
568 char *next;
569 int i;
571 while (1) {
572 // try to locate next subtitle
573 if (!fgets (line, LINE_LEN, fd))
574 return NULL;
575 if (!(sscanf (line, "-->> %ld", &(current->start)) <1))
576 break;
579 #ifdef USE_SORTSUB
580 previous_sub_end = (current->start) ? current->start - 1 : 0;
581 #else
582 if (previous_aqt_sub != NULL)
583 previous_aqt_sub->end = current->start-1;
585 previous_aqt_sub = current;
586 #endif
588 if (!fgets (line, LINE_LEN, fd))
589 return NULL;
591 sub_readtext((char *) &line,&current->text[0]);
592 current->lines = 1;
593 current->end = current->start; // will be corrected by next subtitle
595 if (!fgets (line, LINE_LEN, fd))
596 return current;
598 next = line,i=1;
599 while ((next =sub_readtext (next, &(current->text[i])))) {
600 if (current->text[i]==ERR) {return ERR;}
601 i++;
602 if (i>=SUB_MAX_TEXT) { mp_msg(MSGT_SUBREADER,MSGL_WARN,"Too many lines in a subtitle\n");current->lines=i;return current;}
604 current->lines=i+1;
606 if ((current->text[0]=="") && (current->text[1]=="")) {
607 #ifdef USE_SORTSUB
608 previous_sub_end = 0;
609 #else
610 // void subtitle -> end of previous marked and exit
611 previous_aqt_sub = NULL;
612 #endif
613 return NULL;
616 return current;
619 #ifndef USE_SORTSUB
620 subtitle *previous_subrip09_sub = NULL;
621 #endif
623 subtitle *sub_read_line_subrip09(FILE *fd,subtitle *current) {
624 char line[LINE_LEN+1];
625 int a1,a2,a3;
626 char * next=NULL;
627 int i,len;
629 while (1) {
630 // try to locate next subtitle
631 if (!fgets (line, LINE_LEN, fd))
632 return NULL;
633 if (!((len=sscanf (line, "[%d:%d:%d]",&a1,&a2,&a3)) < 3))
634 break;
637 current->start = a1*360000+a2*6000+a3*100;
639 #ifdef USE_SORTSUB
640 previous_sub_end = (current->start) ? current->start - 1 : 0;
641 #else
642 if (previous_subrip09_sub != NULL)
643 previous_subrip09_sub->end = current->start-1;
645 previous_subrip09_sub = current;
646 #endif
648 if (!fgets (line, LINE_LEN, fd))
649 return NULL;
651 next = line,i=0;
653 current->text[0]=""; // just to be sure that string is clear
655 while ((next =sub_readtext (next, &(current->text[i])))) {
656 if (current->text[i]==ERR) {return ERR;}
657 i++;
658 if (i>=SUB_MAX_TEXT) { mp_msg(MSGT_SUBREADER,MSGL_WARN,"Too many lines in a subtitle\n");current->lines=i;return current;}
660 current->lines=i+1;
662 if ((current->text[0]=="") && (i==0)) {
663 #ifdef USE_SORTSUB
664 previous_sub_end = 0;
665 #else
666 // void subtitle -> end of previous marked and exit
667 previous_subrip09_sub = NULL;
668 #endif
669 return NULL;
672 return current;
675 subtitle *sub_read_line_jacosub(FILE * fd, subtitle * current)
677 char line1[LINE_LEN], line2[LINE_LEN], directive[LINE_LEN], *p, *q;
678 unsigned a1, a2, a3, a4, b1, b2, b3, b4, comment = 0;
679 static unsigned jacoTimeres = 30;
680 static int jacoShift = 0;
682 bzero(current, sizeof(subtitle));
683 bzero(line1, LINE_LEN);
684 bzero(line2, LINE_LEN);
685 bzero(directive, LINE_LEN);
686 while (!current->text[0]) {
687 if (!fgets(line1, LINE_LEN, fd)) {
688 return NULL;
690 if (sscanf
691 (line1, "%u:%u:%u.%u %u:%u:%u.%u %[^\n\r]", &a1, &a2, &a3, &a4,
692 &b1, &b2, &b3, &b4, line2) < 9) {
693 if (sscanf(line1, "@%u @%u %[^\n\r]", &a4, &b4, line2) < 3) {
694 if (line1[0] == '#') {
695 int hours = 0, minutes = 0, seconds, delta, inverter =
697 unsigned units = jacoShift;
698 switch (toupper(line1[1])) {
699 case 'S':
700 if (isalpha(line1[2])) {
701 delta = 6;
702 } else {
703 delta = 2;
705 if (sscanf(&line1[delta], "%d", &hours)) {
706 if (hours < 0) {
707 hours *= -1;
708 inverter = -1;
710 if (sscanf(&line1[delta], "%*d:%d", &minutes)) {
711 if (sscanf
712 (&line1[delta], "%*d:%*d:%d",
713 &seconds)) {
714 sscanf(&line1[delta], "%*d:%*d:%*d.%d",
715 &units);
716 } else {
717 hours = 0;
718 sscanf(&line1[delta], "%d:%d.%d",
719 &minutes, &seconds, &units);
720 minutes *= inverter;
722 } else {
723 hours = minutes = 0;
724 sscanf(&line1[delta], "%d.%d", &seconds,
725 &units);
726 seconds *= inverter;
728 jacoShift =
729 ((hours * 3600 + minutes * 60 +
730 seconds) * jacoTimeres +
731 units) * inverter;
733 break;
734 case 'T':
735 if (isalpha(line1[2])) {
736 delta = 8;
737 } else {
738 delta = 2;
740 sscanf(&line1[delta], "%u", &jacoTimeres);
741 break;
744 continue;
745 } else {
746 current->start =
747 (unsigned long) ((a4 + jacoShift) * 100.0 /
748 jacoTimeres);
749 current->end =
750 (unsigned long) ((b4 + jacoShift) * 100.0 /
751 jacoTimeres);
753 } else {
754 current->start =
755 (unsigned
756 long) (((a1 * 3600 + a2 * 60 + a3) * jacoTimeres + a4 +
757 jacoShift) * 100.0 / jacoTimeres);
758 current->end =
759 (unsigned
760 long) (((b1 * 3600 + b2 * 60 + b3) * jacoTimeres + b4 +
761 jacoShift) * 100.0 / jacoTimeres);
763 current->lines = 0;
764 p = line2;
765 while ((*p == ' ') || (*p == '\t')) {
766 ++p;
768 if (isalpha(*p)||*p == '[') {
769 int cont, jLength;
771 if (sscanf(p, "%s %[^\n\r]", directive, line1) < 2)
772 return (subtitle *) ERR;
773 jLength = strlen(directive);
774 for (cont = 0; cont < jLength; ++cont) {
775 if (isalpha(*(directive + cont)))
776 *(directive + cont) = toupper(*(directive + cont));
778 if ((strstr(directive, "RDB") != NULL)
779 || (strstr(directive, "RDC") != NULL)
780 || (strstr(directive, "RLB") != NULL)
781 || (strstr(directive, "RLG") != NULL)) {
782 continue;
784 strcpy(line2, line1);
785 p = line2;
787 for (q = line1; (!eol(*p)) && (current->lines < SUB_MAX_TEXT);
788 ++p) {
789 switch (*p) {
790 case '{':
791 comment++;
792 break;
793 case '}':
794 if (comment) {
795 --comment;
796 //the next line to get rid of a blank after the comment
797 if ((*(p + 1)) == ' ')
798 p++;
800 break;
801 case '~':
802 if (!comment) {
803 *q = ' ';
804 ++q;
806 break;
807 case ' ':
808 case '\t':
809 if ((*(p + 1) == ' ') || (*(p + 1) == '\t'))
810 break;
811 if (!comment) {
812 *q = ' ';
813 ++q;
815 break;
816 case '\\':
817 if (*(p + 1) == 'n') {
818 *q = '\0';
819 q = line1;
820 current->text[current->lines++] = strdup(line1);
821 ++p;
822 break;
824 if ((toupper(*(p + 1)) == 'C')
825 || (toupper(*(p + 1)) == 'F')) {
826 ++p,++p;
827 break;
829 if ((*(p + 1) == 'B') || (*(p + 1) == 'b') || (*(p + 1) == 'D') || //actually this means "insert current date here"
830 (*(p + 1) == 'I') || (*(p + 1) == 'i') || (*(p + 1) == 'N') || (*(p + 1) == 'T') || //actually this means "insert current time here"
831 (*(p + 1) == 'U') || (*(p + 1) == 'u')) {
832 ++p;
833 break;
835 if ((*(p + 1) == '\\') ||
836 (*(p + 1) == '~') || (*(p + 1) == '{')) {
837 ++p;
838 } else if (eol(*(p + 1))) {
839 if (!fgets(directive, LINE_LEN, fd))
840 return NULL;
841 trail_space(directive);
842 strncat(line2, directive,
843 (LINE_LEN > 511) ? LINE_LEN : 511);
844 break;
846 default:
847 if (!comment) {
848 *q = *p;
849 ++q;
851 } //-- switch
852 } //-- for
853 *q = '\0';
854 current->text[current->lines] = strdup(line1);
855 } //-- while
856 current->lines++;
857 return current;
860 int sub_autodetect (FILE *fd) {
861 char line[LINE_LEN+1];
862 int i,j=0;
863 char p;
865 while (j < 100) {
866 j++;
867 if (!fgets (line, LINE_LEN, fd))
868 return SUB_INVALID;
870 if (sscanf (line, "{%d}{%d}", &i, &i)==2)
871 {sub_uses_time=0;return SUB_MICRODVD;}
872 if (sscanf (line, "{%d}{}", &i)==1)
873 {sub_uses_time=0;return SUB_MICRODVD;}
874 if (sscanf (line, "%d:%d:%d.%d,%d:%d:%d.%d", &i, &i, &i, &i, &i, &i, &i, &i)==8)
875 {sub_uses_time=1;return SUB_SUBRIP;}
876 if (sscanf (line, "%d:%d:%d%[,.:]%d --> %d:%d:%d%[,.:]%d", &i, &i, &i, (char *)&i, &i, &i, &i, &i, (char *)&i, &i)==10)
877 {sub_uses_time=1;return SUB_SUBVIEWER;}
878 if (sscanf (line, "{T %d:%d:%d:%d",&i, &i, &i, &i))
879 {sub_uses_time=1;return SUB_SUBVIEWER2;}
880 if (strstr (line, "<SAMI>"))
881 {sub_uses_time=1; return SUB_SAMI;}
882 if (sscanf(line, "%d:%d:%d.%d %d:%d:%d.%d", &i, &i, &i, &i, &i, &i, &i, &i) == 8)
883 {sub_uses_time = 1; return SUB_JACOSUB;}
884 if (sscanf(line, "@%d @%d", &i, &i) == 2)
885 {sub_uses_time = 1; return SUB_JACOSUB;}
886 if (sscanf (line, "%d:%d:%d:", &i, &i, &i )==3)
887 {sub_uses_time=1;return SUB_VPLAYER;}
888 if (sscanf (line, "%d:%d:%d ", &i, &i, &i )==3)
889 {sub_uses_time=1;return SUB_VPLAYER;}
890 //TODO: just checking if first line of sub starts with "<" is WAY
891 // too weak test for RT
892 // Please someone who knows the format of RT... FIX IT!!!
893 // It may conflict with other sub formats in the future (actually it doesn't)
894 if ( *line == '<' )
895 {sub_uses_time=1;return SUB_RT;}
897 if (!memcmp(line, "Dialogue: Marked", 16))
898 {sub_uses_time=1; return SUB_SSA;}
899 if (sscanf (line, "%d,%d,\"%c", &i, &i, (char *) &i) == 3)
900 {sub_uses_time=0;return SUB_DUNNOWHAT;}
901 if (sscanf (line, "FORMAT=%d", &i) == 1)
902 {sub_uses_time=0; return SUB_MPSUB;}
903 if (sscanf (line, "FORMAT=TIM%c", &p)==1 && p=='E')
904 {sub_uses_time=1; return SUB_MPSUB;}
905 if (strstr (line, "-->>"))
906 {sub_uses_time=0; return SUB_AQTITLE;}
907 if (sscanf (line, "[%d:%d:%d]", &i, &i, &i)==3)
908 {sub_uses_time=1;return SUB_SUBRIP09;}
911 return SUB_INVALID; // too many bad lines
914 #ifdef DUMPSUBS
915 int sub_utf8=0;
916 #else
917 extern int sub_utf8;
918 int sub_utf8_prev=0;
919 #endif
921 extern float sub_delay;
922 extern float sub_fps;
924 #ifdef USE_ICONV
925 static iconv_t icdsc;
927 void subcp_open (void)
929 char *tocp = "UTF-8";
930 icdsc = (iconv_t)(-1);
932 if (sub_cp){
933 if ((icdsc = iconv_open (tocp, sub_cp)) != (iconv_t)(-1)){
934 mp_msg(MSGT_SUBREADER,MSGL_V,"SUB: opened iconv descriptor.\n");
935 sub_utf8 = 2;
936 } else
937 mp_msg(MSGT_SUBREADER,MSGL_ERR,"SUB: error opening iconv descriptor.\n");
941 void subcp_close (void)
943 if (icdsc != (iconv_t)(-1)){
944 (void) iconv_close (icdsc);
945 icdsc = (iconv_t)(-1);
946 mp_msg(MSGT_SUBREADER,MSGL_V,"SUB: closed iconv descriptor.\n");
950 #define ICBUFFSIZE 512
951 static char icbuffer[ICBUFFSIZE];
953 subtitle* subcp_recode (subtitle *sub)
955 int l=sub->lines;
956 size_t ileft, oleft;
957 char *op, *ip, *ot;
959 while (l){
960 op = icbuffer;
961 ip = sub->text[--l];
962 ileft = strlen(ip);
963 oleft = ICBUFFSIZE - 1;
965 if (iconv(icdsc, &ip, &ileft,
966 &op, &oleft) == (size_t)(-1)) {
967 mp_msg(MSGT_SUBREADER,MSGL_WARN,"SUB: error recoding line (1).\n");
968 l++;
969 break;
971 if (!(ot = (char *)malloc(op - icbuffer + 1))){
972 mp_msg(MSGT_SUBREADER,MSGL_WARN,"SUB: error allocating mem.\n");
973 l++;
974 break;
976 *op='\0' ;
977 strcpy (ot, icbuffer);
978 free (sub->text[l]);
979 sub->text[l] = ot;
981 if (l){
982 for (l = sub->lines; l;)
983 free (sub->text[--l]);
984 return ERR;
986 return sub;
989 // for demux_ogg.c:
990 subtitle* subcp_recode1 (subtitle *sub)
992 int l=sub->lines;
993 size_t ileft, oleft;
995 if(icdsc == (iconv_t)(-1)) return sub;
997 while (l){
998 char *ip = icbuffer;
999 char *op = sub->text[--l];
1000 strcpy(ip, op);
1001 ileft = strlen(ip);
1002 oleft = ICBUFFSIZE - 1;
1004 if (iconv(icdsc, &ip, &ileft,
1005 &op, &oleft) == (size_t)(-1)) {
1006 mp_msg(MSGT_SUBREADER,MSGL_V,"SUB: error recoding line (2).\n");
1007 return sub;
1009 *op='\0' ;
1011 return sub;
1013 #endif
1015 static void adjust_subs_time(subtitle* sub, float subtime, float fps, int block){
1016 int n,m;
1017 subtitle* nextsub;
1018 int i = sub_num;
1019 unsigned long subfms = (sub_uses_time ? 100 : fps) * subtime;
1020 unsigned long overlap = (sub_uses_time ? 100 : fps) / 5; // 0.2s
1022 n=m=0;
1023 if (i) for (;;){
1024 if (sub->end <= sub->start){
1025 sub->end = sub->start + subfms;
1026 m++;
1027 n++;
1029 if (!--i) break;
1030 nextsub = sub + 1;
1031 if(block){
1032 if ((sub->end > nextsub->start) && (sub->end <= nextsub->start + overlap)) {
1033 // these subtitles overlap for less than 0.2 seconds
1034 // and would result in very short overlapping subtitle
1035 // so let's fix the problem here, before overlapping code
1036 // get its hands on them
1037 unsigned delta = sub->end - nextsub->start, half = delta / 2;
1038 sub->end -= half + 1;
1039 nextsub->start += delta - half;
1041 if (sub->end >= nextsub->start){
1042 sub->end = nextsub->start - 1;
1043 if (sub->end - sub->start > subfms)
1044 sub->end = sub->start + subfms;
1045 if (!m)
1046 n++;
1050 /* Theory:
1051 * Movies are often converted from FILM (24 fps)
1052 * to PAL (25) by simply speeding it up, so we
1053 * to multiply the original timestmaps by
1054 * (Movie's FPS / Subtitle's (guessed) FPS)
1055 * so eg. for 23.98 fps movie and PAL time based
1056 * subtitles we say -subfps 25 and we're fine!
1059 /* timed sub fps correction ::atmos */
1060 if(sub_uses_time && sub_fps) {
1061 sub->start *= sub_fps/fps;
1062 sub->end *= sub_fps/fps;
1065 sub = nextsub;
1066 m = 0;
1068 if (n) mp_msg(MSGT_SUBREADER,MSGL_INFO,"SUB: Adjusted %d subtitle(s).\n", n);
1071 struct subreader {
1072 subtitle * (*read)(FILE *fd,subtitle *dest);
1073 void (*post)(subtitle *dest);
1074 const char *name;
1077 subtitle* sub_read_file (char *filename, float fps) {
1078 FILE *fd;
1079 int n_max, n_first, i, j, sub_first, sub_orig;
1080 subtitle *first, *second, *sub;
1081 struct subreader sr[]=
1083 { sub_read_line_microdvd, NULL, "microdvd" },
1084 { sub_read_line_subrip, NULL, "subrip" },
1085 { sub_read_line_subviewer, NULL, "subviewer" },
1086 { sub_read_line_sami, NULL, "sami" },
1087 { sub_read_line_vplayer, NULL, "vplayer" },
1088 { sub_read_line_rt, NULL, "rt" },
1089 { sub_read_line_ssa, sub_pp_ssa, "ssa" },
1090 { sub_read_line_dunnowhat, NULL, "dunnowhat" },
1091 { sub_read_line_mpsub, NULL, "mpsub" },
1092 { sub_read_line_aqt, NULL, "aqt" },
1093 { sub_read_line_subviewer2, NULL, "subviewer 2.0" },
1094 { sub_read_line_subrip09, NULL, "subrip 0.9" },
1095 { sub_read_line_jacosub, NULL, "jacosub" }
1097 struct subreader *srp;
1099 if(filename==NULL) return NULL; //qnx segfault
1100 fd=fopen (filename, "r"); if (!fd) return NULL;
1102 sub_format=sub_autodetect (fd);
1103 if (sub_format==SUB_INVALID) {mp_msg(MSGT_SUBREADER,MSGL_WARN,"SUB: Could not determine file format\n");return NULL;}
1104 srp=sr+sub_format;
1105 mp_msg(MSGT_SUBREADER,MSGL_INFO,"SUB: Detected subtitle file format: %s\n", srp->name);
1107 rewind (fd);
1109 #ifdef USE_ICONV
1110 sub_utf8_prev=sub_utf8;
1111 subcp_open();
1112 #endif
1114 sub_num=0;n_max=32;
1115 first=(subtitle *)malloc(n_max*sizeof(subtitle));
1116 if(!first) return NULL;
1118 #ifdef USE_SORTSUB
1119 sub = (subtitle *)malloc(sizeof(subtitle));
1120 //This is to deal with those formats (AQT & Subrip) which define the end of a subtitle
1121 //as the beginning of the following
1122 previous_sub_end = 0;
1123 #endif
1124 while(1){
1125 if(sub_num>=n_max){
1126 n_max+=16;
1127 first=realloc(first,n_max*sizeof(subtitle));
1129 #ifndef USE_SORTSUB
1130 sub = &first[sub_num];
1131 #endif
1132 memset(sub, '\0', sizeof(subtitle));
1133 sub=srp->read(fd,sub);
1134 if(!sub) break; // EOF
1135 #ifdef USE_ICONV
1136 if ((sub!=ERR) && (sub_utf8 & 2)) sub=subcp_recode(sub);
1137 #endif
1138 if ( sub == ERR )
1140 #ifdef USE_ICONV
1141 subcp_close();
1142 sub_utf8=sub_utf8_prev;
1143 #endif
1144 if ( first ) free(first);
1145 return NULL;
1147 // Apply any post processing that needs recoding first
1148 if ((sub!=ERR) && !sub_no_text_pp && srp->post) srp->post(sub);
1149 #ifdef USE_SORTSUB
1150 if(!sub_num || (first[sub_num - 1].start <= sub->start)){
1151 first[sub_num].start = sub->start;
1152 first[sub_num].end = sub->end;
1153 first[sub_num].lines = sub->lines;
1154 for(i = 0; i < sub->lines; ++i){
1155 first[sub_num].text[i] = sub->text[i];
1157 if (previous_sub_end){
1158 first[sub_num - 1].end = previous_sub_end;
1159 previous_sub_end = 0;
1161 } else {
1162 for(j = sub_num - 1; j >= 0; --j){
1163 first[j + 1].start = first[j].start;
1164 first[j + 1].end = first[j].end;
1165 first[j + 1].lines = first[j].lines;
1166 for(i = 0; i < first[j].lines; ++i){
1167 first[j + 1].text[i] = first[j].text[i];
1169 if(!j || (first[j - 1].start <= sub->start)){
1170 first[j].start = sub->start;
1171 first[j].end = sub->end;
1172 first[j].lines = sub->lines;
1173 for(i = 0; i < SUB_MAX_TEXT; ++i){
1174 first[j].text[i] = sub->text[i];
1176 if (previous_sub_end){
1177 first[j].end = first[j - 1].end;
1178 first[j - 1].end = previous_sub_end;
1179 previous_sub_end = 0;
1181 break;
1185 #endif
1186 if(sub==ERR) ++sub_errs; else ++sub_num; // Error vs. Valid
1189 fclose(fd);
1191 #ifdef USE_ICONV
1192 subcp_close();
1193 #endif
1195 // printf ("SUB: Subtitle format %s time.\n", sub_uses_time?"uses":"doesn't use");
1196 mp_msg(MSGT_SUBREADER,MSGL_INFO,"SUB: Read %i subtitles", sub_num);
1197 if (sub_errs) mp_msg(MSGT_SUBREADER,MSGL_INFO,", %i bad line(s).\n", sub_errs);
1198 else mp_msg(MSGT_SUBREADER,MSGL_INFO,".\n");
1200 if(sub_num<=0){
1201 free(first);
1202 return NULL;
1205 // we do overlap if the user forced it (suboverlap_enable == 2) or
1206 // the user didn't forced no-overlapsub and the format is Jacosub or Ssa.
1207 // this is because usually overlapping subtitles are found in these formats,
1208 // while in others they are probably result of bad timing
1209 if ((suboverlap_enabled == 2) ||
1210 ((suboverlap_enabled) && ((sub_format == SUB_JACOSUB) || (sub_format == SUB_SSA)))) {
1211 adjust_subs_time(first, 6.0, fps, 0); /* ~6 secs AST */
1212 // here we manage overlapping subtitles
1213 sub_orig = sub_num;
1214 n_first = sub_num;
1215 sub_num = 0;
1216 second = NULL;
1217 // for each subtitle in first[] we deal with its 'block' of
1218 // bonded subtitles
1219 for (sub_first = 0; sub_first < n_first; ++sub_first) {
1220 unsigned long global_start = first[sub_first].start,
1221 global_end = first[sub_first].end, local_start, local_end;
1222 int lines_to_add = first[sub_first].lines, sub_to_add = 0,
1223 **placeholder = NULL, higher_line = 0, counter, start_block_sub = sub_num;
1224 char real_block = 1;
1226 // here we find the number of subtitles inside the 'block'
1227 // and its span interval. this works well only with sorted
1228 // subtitles
1229 while ((sub_first + sub_to_add + 1 < n_first) && (first[sub_first + sub_to_add + 1].start < global_end)) {
1230 ++sub_to_add;
1231 lines_to_add += first[sub_first + sub_to_add].lines;
1232 if (first[sub_first + sub_to_add].start < global_start) {
1233 global_start = first[sub_first + sub_to_add].start;
1235 if (first[sub_first + sub_to_add].end > global_end) {
1236 global_end = first[sub_first + sub_to_add].end;
1240 // we need a structure to keep trace of the screen lines
1241 // used by the subs, a 'placeholder'
1242 counter = 2 * sub_to_add + 1; // the maximum number of subs derived
1243 // from a block of sub_to_add+1 subs
1244 placeholder = (int **) malloc(sizeof(int *) * counter);
1245 for (i = 0; i < counter; ++i) {
1246 placeholder[i] = (int *) malloc(sizeof(int) * lines_to_add);
1247 for (j = 0; j < lines_to_add; ++j) {
1248 placeholder[i][j] = -1;
1252 counter = 0;
1253 local_end = global_start - 1;
1254 do {
1255 int ls;
1257 // here we find the beginning and the end of a new
1258 // subtitle in the block
1259 local_start = local_end + 1;
1260 local_end = global_end;
1261 for (j = 0; j <= sub_to_add; ++j) {
1262 if ((first[sub_first + j].start - 1 > local_start) && (first[sub_first + j].start - 1 < local_end)) {
1263 local_end = first[sub_first + j].start - 1;
1264 } else if ((first[sub_first + j].end > local_start) && (first[sub_first + j].end < local_end)) {
1265 local_end = first[sub_first + j].end;
1268 // here we allocate the screen lines to subs we must
1269 // display in current local_start-local_end interval.
1270 // if the subs were yet presents in the previous interval
1271 // they keep the same lines, otherside they get unused lines
1272 for (j = 0; j <= sub_to_add; ++j) {
1273 if ((first[sub_first + j].start <= local_end) && (first[sub_first + j].end > local_start)) {
1274 unsigned long sub_lines = first[sub_first + j].lines, fragment_length = lines_to_add + 1,
1275 tmp = 0;
1276 char boolean = 0;
1277 int fragment_position = -1;
1279 // if this is not the first new sub of the block
1280 // we find if this sub was present in the previous
1281 // new sub
1282 if (counter)
1283 for (i = 0; i < lines_to_add; ++i) {
1284 if (placeholder[counter - 1][i] == sub_first + j) {
1285 placeholder[counter][i] = sub_first + j;
1286 boolean = 1;
1289 if (boolean)
1290 continue;
1292 // we are looking for the shortest among all groups of
1293 // sequential blank lines whose length is greater than or
1294 // equal to sub_lines. we store in fragment_position the
1295 // position of the shortest group, in fragment_length its
1296 // length, and in tmp the length of the group currently
1297 // examinated
1298 for (i = 0; i < lines_to_add; ++i) {
1299 if (placeholder[counter][i] == -1) {
1300 // placeholder[counter][i] is part of the current group
1301 // of blank lines
1302 ++tmp;
1303 } else {
1304 if (tmp == sub_lines) {
1305 // current group's size fits exactly the one we
1306 // need, so we stop looking
1307 fragment_position = i - tmp;
1308 tmp = 0;
1309 break;
1311 if ((tmp) && (tmp > sub_lines) && (tmp < fragment_length)) {
1312 // current group is the best we found till here,
1313 // but is still bigger than the one we are looking
1314 // for, so we keep on looking
1315 fragment_length = tmp;
1316 fragment_position = i - tmp;
1317 tmp = 0;
1318 } else {
1319 // current group doesn't fit at all, so we forget it
1320 tmp = 0;
1324 if (tmp) {
1325 // last screen line is blank, a group ends with it
1326 if ((tmp >= sub_lines) && (tmp < fragment_length)) {
1327 fragment_position = i - tmp;
1330 if (fragment_position == -1) {
1331 // it was not possible to find free screen line(s) for a subtitle,
1332 // usually this means a bug in the code; however we do not overlap
1333 mp_msg(MSGT_SUBREADER, MSGL_WARN, "SUB: we could not find a suitable position for an overlapping subtitle\n");
1334 higher_line = SUB_MAX_TEXT + 1;
1335 break;
1336 } else {
1337 for (tmp = 0; tmp < sub_lines; ++tmp) {
1338 placeholder[counter][fragment_position + tmp] = sub_first + j;
1343 for (j = higher_line + 1; j < lines_to_add; ++j) {
1344 if (placeholder[counter][j] != -1)
1345 higher_line = j;
1346 else
1347 break;
1349 if (higher_line >= SUB_MAX_TEXT) {
1350 // the 'block' has too much lines, so we don't overlap the
1351 // subtitles
1352 second = (subtitle *) realloc(second, (sub_num + sub_to_add + 1) * sizeof(subtitle));
1353 for (j = 0; j <= sub_to_add; ++j) {
1354 int ls;
1355 memset(&second[sub_num + j], '\0', sizeof(subtitle));
1356 second[sub_num + j].start = first[sub_first + j].start;
1357 second[sub_num + j].end = first[sub_first + j].end;
1358 second[sub_num + j].lines = first[sub_first + j].lines;
1359 for (ls = 0; ls < second[sub_num + j].lines; ls++) {
1360 second[sub_num + j].text[ls] = strdup(first[sub_first + j].text[ls]);
1363 sub_num += sub_to_add + 1;
1364 sub_first += sub_to_add;
1365 real_block = 0;
1366 break;
1369 // we read the placeholder structure and create the new
1370 // subs.
1371 second = (subtitle *) realloc(second, (sub_num + 1) * sizeof(subtitle));
1372 memset(&second[sub_num], '\0', sizeof(subtitle));
1373 second[sub_num].start = local_start;
1374 second[sub_num].end = local_end;
1375 n_max = (lines_to_add < SUB_MAX_TEXT) ? lines_to_add : SUB_MAX_TEXT;
1376 for (i = 0, j = 0; j < n_max; ++j) {
1377 if (placeholder[counter][j] != -1) {
1378 int lines = first[placeholder[counter][j]].lines;
1379 for (ls = 0; ls < lines; ++ls) {
1380 second[sub_num].text[i++] = strdup(first[placeholder[counter][j]].text[ls]);
1382 j += lines - 1;
1383 } else {
1384 second[sub_num].text[i++] = strdup(" ");
1387 ++sub_num;
1388 ++counter;
1389 } while (local_end < global_end);
1390 if (real_block)
1391 for (i = 0; i < counter; ++i)
1392 second[start_block_sub + i].lines = higher_line + 1;
1394 counter = 2 * sub_to_add + 1;
1395 for (i = 0; i < counter; ++i) {
1396 free(placeholder[i]);
1398 free(placeholder);
1399 sub_first += sub_to_add;
1402 for (j = sub_orig - 1; j >= 0; --j) {
1403 for (i = first[j].lines - 1; i >= 0; --i) {
1404 free(first[j].text[i]);
1407 free(first);
1409 return second;
1410 } else { //if(suboverlap_enabled)
1411 adjust_subs_time(first, 6.0, fps, 1); /* ~6 secs AST */
1413 return first;
1417 #if 0
1418 char * strreplace( char * in,char * what,char * whereof )
1420 int i;
1421 char * tmp;
1423 if ( ( in == NULL )||( what == NULL )||( whereof == NULL )||( ( tmp=strstr( in,what ) ) == NULL ) ) return NULL;
1424 for( i=0;i<strlen( whereof );i++ ) tmp[i]=whereof[i];
1425 if ( strlen( what ) > strlen( whereof ) ) tmp[i]=0;
1426 return in;
1428 #endif
1430 char * sub_filename(char* path, char * fname )
1432 char * sub_name1;
1433 char * sub_name2;
1434 char * aviptr1, * aviptr2, * tmp;
1435 int i,j;
1436 FILE * f;
1437 int pos=0;
1438 char * sub_exts[] =
1439 { ".utf",
1440 ".UTF",
1441 ".sub",
1442 ".SUB",
1443 ".srt",
1444 ".SRT",
1445 ".smi",
1446 ".SMI",
1447 ".rt",
1448 ".RT",
1449 ".txt",
1450 ".TXT",
1451 ".ssa",
1452 ".SSA",
1453 ".aqt",
1454 ".AQT",
1455 ".jss",
1456 ".JSS" };
1459 if ( fname == NULL ) return NULL;
1461 sub_name1=strrchr(fname,'.');
1462 if (!sub_name1) return NULL;
1463 pos=sub_name1-fname;
1465 sub_name1=malloc(strlen(fname)+8);
1466 strcpy(sub_name1,fname);
1468 sub_name2=malloc (strlen(path) + strlen(fname) + 8);
1469 if ((tmp=strrchr(fname,'/')))
1470 sprintf (sub_name2, "%s%s", path, tmp+1);
1471 else
1472 sprintf (sub_name2, "%s%s", path, fname);
1474 aviptr1=strrchr(sub_name1,'.');
1475 aviptr2=strrchr(sub_name2,'.');
1477 for(j=0;j<=1;j++){
1478 char* sub_name=j?sub_name1:sub_name2;
1479 #ifdef USE_ICONV
1480 for ( i=(sub_cp?2:0);i<(sizeof(sub_exts)/sizeof(char*));i++ ) {
1481 #else
1482 for ( i=0;i<(sizeof(sub_exts)/sizeof(char*));i++ ) {
1483 #endif
1484 strcpy(j?aviptr1:aviptr2,sub_exts[i]);
1485 // printf("trying: '%s'\n",sub_name);
1486 if((f=fopen( sub_name,"rt" ))) {
1487 fclose( f );
1488 mp_msg(MSGT_SUBREADER,MSGL_INFO,"SUB: Detected sub file: %s\n",sub_name );
1489 if (i<2) sub_utf8=1;
1490 return sub_name;
1495 free(sub_name2);
1496 free(sub_name1);
1497 return NULL;
1500 void list_sub_file(subtitle* subs){
1501 int i,j;
1503 for(j=0;j<sub_num;j++){
1504 subtitle* egysub=&subs[j];
1505 printf ("%i line%c (%li-%li)\n",
1506 egysub->lines,
1507 (1==egysub->lines)?' ':'s',
1508 egysub->start,
1509 egysub->end);
1510 for (i=0; i<egysub->lines; i++) {
1511 printf ("\t\t%d: %s%s", i,egysub->text[i], i==egysub->lines-1?"":" \n ");
1513 printf ("\n");
1516 printf ("Subtitle format %s time.\n", sub_uses_time?"uses":"doesn't use");
1517 printf ("Read %i subtitles, %i errors.\n", sub_num, sub_errs);
1520 void dump_srt(subtitle* subs, float fps){
1521 int i,j;
1522 int h,m,s,ms;
1523 FILE * fd;
1524 subtitle * onesub;
1525 unsigned long temp;
1527 if (!sub_uses_time && sub_fps == 0)
1528 sub_fps = fps;
1529 fd=fopen("dumpsub.srt","w");
1530 if(!fd)
1532 perror("dump_srt: fopen");
1533 return;
1535 for(i=0;i<sub_num;i++)
1537 onesub=subs+i; //=&subs[i];
1538 fprintf(fd,"%d\n",i+1);//line number
1540 temp=onesub->start;
1541 if (!sub_uses_time)
1542 temp = temp * 100 / sub_fps;
1543 temp -= sub_delay * 100;
1544 h=temp/360000;temp%=360000; //h =1*100*60*60
1545 m=temp/6000; temp%=6000; //m =1*100*60
1546 s=temp/100; temp%=100; //s =1*100
1547 ms=temp*10; //ms=1*10
1548 fprintf(fd,"%02d:%02d:%02d,%03d --> ",h,m,s,ms);
1550 temp=onesub->end;
1551 if (!sub_uses_time)
1552 temp = temp * 100 / sub_fps;
1553 temp -= sub_delay * 100;
1554 h=temp/360000;temp%=360000;
1555 m=temp/6000; temp%=6000;
1556 s=temp/100; temp%=100;
1557 ms=temp*10;
1558 fprintf(fd,"%02d:%02d:%02d,%03d\n",h,m,s,ms);
1560 for(j=0;j<onesub->lines;j++)
1561 fprintf(fd,"%s\n",onesub->text[j]);
1563 fprintf(fd,"\n");
1565 fclose(fd);
1566 mp_msg(MSGT_SUBREADER,MSGL_INFO,"SUB: Subtitles dumped in \'dumpsub.srt\'.\n");
1569 void dump_mpsub(subtitle* subs, float fps){
1570 int i,j;
1571 FILE *fd;
1572 float a,b;
1574 mpsub_position=sub_uses_time?(sub_delay*100):(sub_delay*fps);
1575 if (sub_fps==0) sub_fps=fps;
1577 fd=fopen ("dump.mpsub", "w");
1578 if (!fd) {
1579 perror ("dump_mpsub: fopen");
1580 return;
1584 if (sub_uses_time) fprintf (fd,"FORMAT=TIME\n\n");
1585 else fprintf (fd, "FORMAT=%5.2f\n\n", fps);
1587 for(j=0;j<sub_num;j++){
1588 subtitle* egysub=&subs[j];
1589 if (sub_uses_time) {
1590 a=((egysub->start-mpsub_position)/100.0);
1591 b=((egysub->end-egysub->start)/100.0);
1592 if ( (float)((int)a) == a)
1593 fprintf (fd, "%.0f",a);
1594 else
1595 fprintf (fd, "%.2f",a);
1597 if ( (float)((int)b) == b)
1598 fprintf (fd, " %.0f\n",b);
1599 else
1600 fprintf (fd, " %.2f\n",b);
1601 } else {
1602 fprintf (fd, "%ld %ld\n", (long)((egysub->start*(fps/sub_fps))-((mpsub_position*(fps/sub_fps)))),
1603 (long)(((egysub->end)-(egysub->start))*(fps/sub_fps)));
1606 mpsub_position = egysub->end;
1607 for (i=0; i<egysub->lines; i++) {
1608 fprintf (fd, "%s\n",egysub->text[i]);
1610 fprintf (fd, "\n");
1612 fclose (fd);
1613 mp_msg(MSGT_SUBREADER,MSGL_INFO,"SUB: Subtitles dumped in \'dump.mpsub\'.\n");
1616 void dump_microdvd(subtitle* subs, float fps) {
1617 int i, delay;
1618 FILE *fd;
1619 if (sub_fps == 0)
1620 sub_fps = fps;
1621 fd = fopen("dumpsub.txt", "w");
1622 if (!fd) {
1623 perror("dumpsub.txt: fopen");
1624 return;
1626 delay = sub_delay * sub_fps;
1627 for (i = 0; i < sub_num; ++i) {
1628 int j, start, end;
1629 start = subs[i].start;
1630 end = subs[i].end;
1631 if (sub_uses_time) {
1632 start = start * sub_fps / 100 ;
1633 end = end * sub_fps / 100;
1635 else {
1636 start = start * sub_fps / fps;
1637 end = end * sub_fps / fps;
1639 start -= delay;
1640 end -= delay;
1641 fprintf(fd, "{%d}{%d}", start, end);
1642 for (j = 0; j < subs[i].lines; ++j)
1643 fprintf(fd, "%s%s", j ? "|" : "", subs[i].text[j]);
1644 fprintf(fd, "\n");
1646 fclose(fd);
1647 mp_msg(MSGT_SUBREADER,MSGL_INFO,"SUB: Subtitles dumped in \'dumpsub.txt\'.\n");
1650 void dump_jacosub(subtitle* subs, float fps) {
1651 int i,j;
1652 int h,m,s,cs;
1653 FILE * fd;
1654 subtitle * onesub;
1655 unsigned long temp;
1657 if (!sub_uses_time && sub_fps == 0)
1658 sub_fps = fps;
1659 fd=fopen("dumpsub.jss","w");
1660 if(!fd)
1662 perror("dump_jacosub: fopen");
1663 return;
1665 fprintf(fd, "#TIMERES %d\n", (sub_uses_time) ? 100 : (int)sub_fps);
1666 for(i=0;i<sub_num;i++)
1668 onesub=subs+i; //=&subs[i];
1670 temp=onesub->start;
1671 if (!sub_uses_time)
1672 temp = temp * 100 / sub_fps;
1673 temp -= sub_delay * 100;
1674 h=temp/360000;temp%=360000; //h =1*100*60*60
1675 m=temp/6000; temp%=6000; //m =1*100*60
1676 s=temp/100; temp%=100; //s =1*100
1677 cs=temp; //cs=1*10
1678 fprintf(fd,"%02d:%02d:%02d.%02d ",h,m,s,cs);
1680 temp=onesub->end;
1681 if (!sub_uses_time)
1682 temp = temp * 100 / sub_fps;
1683 temp -= sub_delay * 100;
1684 h=temp/360000;temp%=360000;
1685 m=temp/6000; temp%=6000;
1686 s=temp/100; temp%=100;
1687 cs=temp;
1688 fprintf(fd,"%02d:%02d:%02d.%02d {~} ",h,m,s,cs);
1690 for(j=0;j<onesub->lines;j++)
1691 fprintf(fd,"%s%s",j ? "\\n" : "", onesub->text[j]);
1693 fprintf(fd,"\n");
1695 fclose(fd);
1696 mp_msg(MSGT_SUBREADER,MSGL_INFO,"SUB: Subtitles dumped in \'dumpsub.js\'.\n");
1699 void dump_sami(subtitle* subs, float fps) {
1700 int i,j;
1701 FILE * fd;
1702 subtitle * onesub;
1703 unsigned long temp;
1705 if (!sub_uses_time && sub_fps == 0)
1706 sub_fps = fps;
1707 fd=fopen("dumpsub.smi","w");
1708 if(!fd)
1710 perror("dump_jacosub: fopen");
1711 return;
1713 fprintf(fd, "<SAMI>\n"
1714 "<HEAD>\n"
1715 " <STYLE TYPE=\"Text/css\">\n"
1716 " <!--\n"
1717 " P {margin-left: 29pt; margin-right: 29pt; font-size: 24pt; text-align: center; font-family: Tahoma; font-weight: bold; color: #FCDD03; background-color: #000000;}\n"
1718 " .SUBTTL {Name: 'Subtitles'; Lang: en-US; SAMIType: CC;}\n"
1719 " -->\n"
1720 " </STYLE>\n"
1721 "</HEAD>\n"
1722 "<BODY>\n");
1723 for(i=0;i<sub_num;i++)
1725 onesub=subs+i; //=&subs[i];
1727 temp=onesub->start;
1728 if (!sub_uses_time)
1729 temp = temp * 100 / sub_fps;
1730 temp -= sub_delay * 100;
1731 fprintf(fd,"\t<SYNC Start=%lu>\n"
1732 "\t <P>", temp * 10);
1734 for(j=0;j<onesub->lines;j++)
1735 fprintf(fd,"%s%s",j ? "<br>" : "", onesub->text[j]);
1737 fprintf(fd,"\n");
1739 temp=onesub->end;
1740 if (!sub_uses_time)
1741 temp = temp * 100 / sub_fps;
1742 temp -= sub_delay * 100;
1743 fprintf(fd,"\t<SYNC Start=%lu>\n"
1744 "\t <P>&nbsp;\n", temp * 10);
1746 fprintf(fd, "</BODY>\n"
1747 "</SAMI>\n");
1748 fclose(fd);
1749 mp_msg(MSGT_SUBREADER,MSGL_INFO,"SUB: Subtitles dumped in \'dumpsub.smi\'.\n");
1752 void sub_free( subtitle * subs )
1754 int i;
1756 if ( !subs ) return;
1758 sub_num=0;
1759 sub_errs=0;
1760 for ( i=0;i<subs->lines;i++ ) free( subs->text[i] );
1761 free( subs );
1762 subs=NULL;
1765 #ifdef DUMPSUBS
1766 int main(int argc, char **argv) { // for testing
1768 int i,j;
1769 subtitle *subs;
1770 subtitle *egysub;
1772 if(argc<2){
1773 printf("\nUsage: subreader filename.sub\n\n");
1774 exit(1);
1776 sub_cp = argv[2];
1777 subs=sub_read_file(argv[1]);
1778 if(!subs){
1779 printf("Couldn't load file.\n");
1780 exit(1);
1783 list_sub_file(subs);
1785 return 0;
1787 #endif