File: | subreader.c |
Location: | line 2201, column 41 |
Description: | Call to 'malloc' has an allocation size of 0 bytes |
1 | /* | |||
2 | Reading of subtitle files for spumux | |||
3 | */ | |||
4 | /* Copyright (C) 2000 - 2003 various authors of the MPLAYER project | |||
5 | * This module uses various parts of the MPLAYER project (http://www.mplayerhq.hu) | |||
6 | * With many changes by Sjef van Gool (svangool@hotmail.com) November 2003 | |||
7 | * And many changes by Lawrence D'Oliveiro <ldo@geek-central.gen.nz> April 2010. | |||
8 | * | |||
9 | * This program is free software; you can redistribute it and/or modify | |||
10 | * it under the terms of the GNU General Public License as published by | |||
11 | * the Free Software Foundation; either version 2 of the License, or (at | |||
12 | * your option) any later version. | |||
13 | * | |||
14 | * This program is distributed in the hope that it will be useful, but | |||
15 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
17 | * General Public License for more details. | |||
18 | * | |||
19 | * You should have received a copy of the GNU General Public License | |||
20 | * along with this program; if not, write to the Free Software | |||
21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | |||
22 | * MA 02110-1301 USA. | |||
23 | */ | |||
24 | ||||
25 | /* | |||
26 | * Subtitle reader with format autodetection | |||
27 | * | |||
28 | * Written by laaz | |||
29 | * Some code cleanup & realloc() by A'rpi/ESP-team | |||
30 | * pjs sub format by szabi | |||
31 | * | |||
32 | */ | |||
33 | ||||
34 | #include "config.h" | |||
35 | ||||
36 | #include "compat.h" | |||
37 | ||||
38 | #include <ctype.h> | |||
39 | #include <fcntl.h> | |||
40 | #include <dirent.h> | |||
41 | #include <errno(*__errno_location ()).h> | |||
42 | #include <assert.h> | |||
43 | ||||
44 | #include "subglobals.h" | |||
45 | #include "subreader.h" | |||
46 | ||||
47 | #define ERR((void *) -1) ((void *) -1) | |||
48 | ||||
49 | #ifdef HAVE_FRIBIDI1 | |||
50 | #include <fribidi/fribidi.h> | |||
51 | #endif | |||
52 | ||||
53 | // subtitle formats | |||
54 | #define SUB_INVALID-1 -1 | |||
55 | #define SUB_MICRODVD0 0 /* see <http://en.wikipedia.org/wiki/MicroDVD> */ | |||
56 | #define SUB_SUBRIP1 1 /* see <http://wiki.multimedia.cx/index.php?title=SubRip> */ | |||
57 | #define SUB_SUBVIEWER2 2 /* see <http://en.wikipedia.org/wiki/SubViewer>, sample <http://wiki.videolan.org/SubViewer> */ | |||
58 | #define SUB_SAMI3 3 /* see <http://en.wikipedia.org/wiki/SAMI>, sample <http://www.titlefactory.com/TitleFactoryDocs/sami_format.htm> */ | |||
59 | #define SUB_VPLAYER4 4 | |||
60 | #define SUB_RT5 5 | |||
61 | #define SUB_SSA6 6 /* spec is at <http://moodub.free.fr/video/ass-specs.doc>, or see <http://www.matroska.org/technical/specs/subtitles/ssa.html> */ | |||
62 | #define SUB_PJS7 7 /* Phoenix Japanimation Society */ | |||
63 | #define SUB_MPSUB8 8 | |||
64 | #define SUB_AQTITLE9 9 | |||
65 | #define SUB_SUBVIEWER210 10 /* see <http://en.wikipedia.org/wiki/SubViewer>, sample <http://wiki.videolan.org/SubViewer> */ | |||
66 | #define SUB_SUBRIP0911 11 | |||
67 | #define SUB_JACOSUB12 12 /* spec is at <http://unicorn.us.com/jacosub/jscripts.html>. */ | |||
68 | ||||
69 | /* Maximal length of line of a subtitle */ | |||
70 | #define LINE_LEN1000 1000 | |||
71 | ||||
72 | static float | |||
73 | /* global state for mpsub subtitle format */ | |||
74 | mpsub_position = 0.0, | |||
75 | /* for determining next start time, because file format only gives incremental durations */ | |||
76 | mpsub_multiplier = 1.0; /* for scaling times */ | |||
77 | static int | |||
78 | /* global state for sami subtitle format */ | |||
79 | sami_sub_slacktime = 20000; //20 sec | |||
80 | ||||
81 | static int sub_no_text_pp=0; // 1 => do not apply text post-processing | |||
82 | // like {\...} elimination in SSA format. | |||
83 | /* never set to any other value */ | |||
84 | ||||
85 | #define USE_SORTSUB1 1 | |||
86 | /* whether to ensure that subtitle entries are sorted into time order */ | |||
87 | #ifdef USE_SORTSUB1 | |||
88 | /* | |||
89 | Some subtitling formats, namely AQT and Subrip09, define the end of a | |||
90 | subtitle as the beginning of the following. Since currently we read one | |||
91 | subtitle at time, for these format we keep two global *subtitle, | |||
92 | previous_aqt_sub and previous_subrip09_sub, pointing to previous subtitle, | |||
93 | so we can change its end when we read current subtitle starting time. | |||
94 | When USE_SORTSUB is defined, we use a single global unsigned long, | |||
95 | previous_sub_end, for both (and even future) formats, to store the end of | |||
96 | the previous sub: it is initialized to 0 in sub_read_file and eventually | |||
97 | modified by sub_read_aqt_line or sub_read_subrip09_line. | |||
98 | */ | |||
99 | static unsigned long previous_sub_end; | |||
100 | #endif | |||
101 | ||||
102 | /* | |||
103 | Caller-controllable settings | |||
104 | */ | |||
105 | ||||
106 | char* subtitle_charset = NULL((void*)0); | |||
107 | /* subtitle_charset (char) contains "from character-set" for ICONV like ISO8859-1 and UTF-8, */ | |||
108 | /* "to character-set" is set to UTF-8. If not specified, then defaults to locale */ | |||
109 | ||||
110 | float sub_delay=0.0; | |||
111 | /* sub_delay (float) contains delay for subtitles in 10 msec intervals (optional user parameter, 0.0 for no delay)*/ | |||
112 | float sub_fps=0.0; | |||
113 | /* sub_fps (float)contains subtitle frames per second, only applicable when we have no timed subs (detection from | |||
114 | video stream, 0.0 for setting taken over from fps otherwise subtitle fps)*/ | |||
115 | int suboverlap_enabled=1; | |||
116 | /* suboverlap_enabled (int) indicates overlap if the user forced it (suboverlap_enabled == 2) or | |||
117 | the user didn't forced no-overlapsub and the format is Jacosub or Ssa. | |||
118 | this is because usually overlapping subtitles are found in these formats, | |||
119 | while in others they are probably result of bad timing (set by subtile file type if | |||
120 | initialized on 1) */ | |||
121 | /* never set to any other value */ | |||
122 | ||||
123 | /* | |||
124 | Useful string-handling routines | |||
125 | */ | |||
126 | ||||
127 | static int eol(char p) | |||
128 | /* does p indicate the end of a line. */ | |||
129 | { | |||
130 | return p == '\r' || p == '\n' || p == '\0'; | |||
131 | } /*eol*/ | |||
132 | ||||
133 | /* Remove leading and trailing space */ | |||
134 | static void trail_space(char *s) | |||
135 | { | |||
136 | int i = 0; | |||
137 | while (isspace(s[i])((*__ctype_b_loc ())[(int) ((s[i]))] & (unsigned short int ) _ISspace)) | |||
138 | ++i; | |||
139 | if (i) | |||
140 | strcpy(s, s + i); | |||
141 | i = strlen(s) - 1; | |||
142 | while (i > 0 && isspace(s[i])((*__ctype_b_loc ())[(int) ((s[i]))] & (unsigned short int ) _ISspace)) | |||
143 | s[i--] = '\0'; | |||
144 | } /*trail_space*/ | |||
145 | ||||
146 | static const char *stristr(const char *haystack, const char *needle) | |||
147 | /* case-insensitive substring search. */ | |||
148 | { | |||
149 | int len = 0; | |||
150 | const char *p = haystack; | |||
151 | if (!(haystack && needle)) | |||
152 | return NULL((void*)0); | |||
153 | len = strlen(needle); | |||
154 | while (*p != '\0') | |||
155 | { | |||
156 | if (strncasecmp(p, needle, len) == 0) | |||
157 | return p; | |||
158 | p++; | |||
159 | } /*while*/ | |||
160 | return NULL((void*)0); | |||
161 | } /*stristr*/ | |||
162 | ||||
163 | /* | |||
164 | Input-file reading | |||
165 | */ | |||
166 | ||||
167 | enum | |||
168 | { | |||
169 | sub_buf_size = 2048, | |||
170 | }; | |||
171 | struct vfile | |||
172 | subfile; | |||
173 | static char * | |||
174 | sub_buf = NULL((void*)0); | |||
175 | static size_t | |||
176 | sub_out_size, | |||
177 | sub_next_out, | |||
178 | sub_end_out; | |||
179 | static bool_Bool | |||
180 | sub_buf_rewindable = false0; | |||
181 | static int | |||
182 | in_charno = 0, | |||
183 | in_lineno = 1; | |||
184 | ||||
185 | static void sub_open(const char * filename) | |||
186 | { | |||
187 | subfile = varied_open(filename, O_RDONLY00, "subtitle file"); | |||
188 | sub_out_size = sub_buf_size; | |||
189 | sub_buf = malloc(sub_out_size); | |||
190 | sub_next_out = 0; | |||
191 | sub_end_out = 0; | |||
192 | in_charno = 0; | |||
193 | in_lineno = 1; | |||
194 | } /*sub_open*/ | |||
195 | ||||
196 | static void sub_close(void) | |||
197 | { | |||
198 | varied_close(subfile); | |||
199 | free(sub_buf); | |||
200 | sub_buf = NULL((void*)0); | |||
201 | } /*sub_close*/ | |||
202 | ||||
203 | #ifdef HAVE_ICONV1 | |||
204 | ||||
205 | static iconv_t | |||
206 | icdsc = ICONV_NULL((iconv_t)-1); /* for converting subtitle text encoding to UTF-8 */ | |||
207 | static char | |||
208 | ic_inbuf[sub_buf_size]; /* size to minimize reads from disk */ | |||
209 | static size_t | |||
210 | ic_next_in, | |||
211 | ic_end_in; | |||
212 | static int | |||
213 | ic_needmore, | |||
214 | ic_eof; | |||
215 | ||||
216 | static void subcp_open(void) | |||
217 | /* opens an iconv context for converting subtitles from subtitle_charset to UTF-8 if appropriate. */ | |||
218 | { | |||
219 | const char * const fromcp = subtitle_charset != NULL((void*)0) ? subtitle_charset : default_charset; | |||
220 | const char * const tocp = "UTF-8"; | |||
221 | icdsc = iconv_open(tocp, fromcp); | |||
222 | if (icdsc != ICONV_NULL((iconv_t)-1)) | |||
223 | { | |||
224 | fprintf(stderrstderr, "INFO: Opened iconv descriptor. *%s* <= *%s*\n", tocp, fromcp); | |||
225 | ic_next_in = 0; | |||
226 | ic_end_in = 0; | |||
227 | ic_needmore = false0; | |||
228 | ic_eof = false0; | |||
229 | } | |||
230 | else | |||
231 | { | |||
232 | fprintf | |||
233 | ( | |||
234 | stderrstderr, | |||
235 | "ERR: Error %d -- %s opening iconv descriptor for charset \"%s\".\n", | |||
236 | errno(*__errno_location ()), strerror(errno(*__errno_location ())), fromcp | |||
237 | ); | |||
238 | exit(1); | |||
239 | } /*if*/ | |||
240 | } /*subcp_open*/ | |||
241 | ||||
242 | static void subcp_close(void) | |||
243 | /* closes the previously-opened iconv context, if any. */ | |||
244 | { | |||
245 | if (icdsc != ICONV_NULL((iconv_t)-1)) | |||
246 | { | |||
247 | (void)iconv_close(icdsc); | |||
248 | icdsc = ICONV_NULL((iconv_t)-1); | |||
249 | /* fprintf(stderr, "INFO: Closed iconv descriptor.\n"); */ | |||
250 | } /*if*/ | |||
251 | } /*subcp_close*/ | |||
252 | ||||
253 | #endif /*HAVE_ICONV*/ | |||
254 | ||||
255 | static int sub_getc() | |||
256 | /* gets the next decoded UTF-8 byte from the input file, or EOF if the | |||
257 | end of the file has been reached. */ | |||
258 | { | |||
259 | int result; | |||
260 | if (sub_next_out == sub_end_out) | |||
261 | { | |||
262 | #ifdef HAVE_ICONV1 | |||
263 | if (icdsc != ICONV_NULL((iconv_t)-1)) | |||
264 | { | |||
265 | if ((ic_next_in == ic_end_in || ic_needmore) && !ic_eof) | |||
266 | { | |||
267 | /* refill the input buffer */ | |||
268 | size_t bytesread; | |||
269 | if (ic_next_in < ic_end_in) | |||
270 | { | |||
271 | /* move down remaining unprocessed data from last read */ | |||
272 | ic_end_in -= ic_next_in; | |||
273 | memcpy(ic_inbuf, ic_inbuf + ic_next_in, ic_end_in); | |||
274 | } | |||
275 | else | |||
276 | { | |||
277 | ic_end_in = 0; | |||
278 | } /*if*/ | |||
279 | bytesread = fread(ic_inbuf + ic_end_in, 1, sizeof ic_inbuf - ic_end_in, subfile.h); | |||
280 | ic_end_in += bytesread; | |||
281 | if (ic_end_in < sizeof ic_inbuf) | |||
282 | { | |||
283 | ic_eof = true1; | |||
284 | } /*if*/ | |||
285 | ic_next_in = 0; | |||
286 | ic_needmore = false0; | |||
287 | } /*if*/ | |||
288 | if (ic_next_in < ic_end_in) | |||
289 | { | |||
290 | /* refill sub_buf with more decoded characters */ | |||
291 | const char * nextin; | |||
292 | char * nextout; | |||
293 | size_t inleft, outleft, prev_sub_end_out; | |||
294 | bool_Bool convok; | |||
295 | nextin = ic_inbuf + ic_next_in; | |||
296 | if (sub_buf_rewindable) | |||
297 | { | |||
298 | if (sub_out_size - sub_end_out < sub_buf_size) | |||
299 | { | |||
300 | /* make room for more */ | |||
301 | sub_out_size += sub_buf_size; | |||
302 | sub_buf = realloc(sub_buf, sub_out_size); | |||
303 | } /*if*/ | |||
304 | nextout = sub_buf + sub_end_out; /* keep what's in buffer */ | |||
305 | } | |||
306 | else | |||
307 | { | |||
308 | if (sub_out_size > sub_buf_size) /* don't need bigger buffer any more */ | |||
309 | { | |||
310 | sub_out_size = sub_buf_size; | |||
311 | sub_buf = realloc(sub_buf, sub_out_size); | |||
312 | } /*if*/ | |||
313 | nextout = sub_buf; | |||
314 | } /*if*/ | |||
315 | inleft = ic_end_in - ic_next_in; /* won't be zero */ | |||
316 | outleft = sub_out_size - (nextout - sub_buf); | |||
317 | prev_sub_end_out = outleft; | |||
318 | convok = iconv(icdsc, (char **)&nextin, &inleft, &nextout, &outleft) != (size_t)-1; | |||
319 | if (!convok && errno(*__errno_location ()) == E2BIG7 && outleft < prev_sub_end_out) | |||
320 | { /* no room to completely convert input, but got something so that's OK */ | |||
321 | convok = true1; | |||
322 | errno(*__errno_location ()) = 0; | |||
323 | } /*if*/ | |||
324 | if (!convok) | |||
325 | { | |||
326 | if (!ic_eof && errno(*__errno_location ()) == EINVAL22) | |||
327 | { | |||
328 | errno(*__errno_location ()) = 0; | |||
329 | ic_needmore = true1; | |||
330 | /* can't decode what's left in ic_inbuf without reading more */ | |||
331 | } | |||
332 | else /* E2BIG (shouldn't occur), EILSEQ, EINVAL at end of file */ | |||
333 | { | |||
334 | fprintf | |||
335 | ( | |||
336 | stderrstderr, | |||
337 | "ERR: Error %d -- %s -- decoding subtitle file at approx" | |||
338 | " line pos %d + char pos %d\n", | |||
339 | errno(*__errno_location ()), | |||
340 | strerror(errno(*__errno_location ())), | |||
341 | in_lineno, /* might be wrong */ | |||
342 | in_charno + (int)(nextin - ic_inbuf) /* hopefully this is better */ | |||
343 | ); | |||
344 | exit(1); | |||
345 | } /*if*/ | |||
346 | } /*if*/ | |||
347 | ic_next_in = nextin - ic_inbuf; | |||
348 | prev_sub_end_out = sub_buf_rewindable ? sub_end_out : 0; | |||
349 | sub_end_out = nextout - sub_buf; | |||
350 | assert(sub_end_out != prev_sub_end_out)((sub_end_out != prev_sub_end_out) ? (void) (0) : __assert_fail ("sub_end_out != prev_sub_end_out", "subreader.c", 350, __PRETTY_FUNCTION__ )); | |||
351 | /* because I gave it plenty of input data to work on */ | |||
352 | if (!sub_buf_rewindable) | |||
353 | { | |||
354 | sub_next_out = 0; | |||
355 | } /*if*/ | |||
356 | } /*if*/ | |||
357 | } | |||
358 | else | |||
359 | #endif /*HAVE_ICONV*/ | |||
360 | { | |||
361 | char * nextout; | |||
362 | size_t bytesread; | |||
363 | if (sub_buf_rewindable) | |||
364 | { | |||
365 | if (sub_out_size - sub_end_out < sub_buf_size) | |||
366 | { | |||
367 | /* make room for more */ | |||
368 | sub_out_size += sub_buf_size; | |||
369 | sub_buf = realloc(sub_buf, sub_out_size); | |||
370 | } /*if*/ | |||
371 | nextout = sub_buf + sub_end_out; /* keep what's in buffer */ | |||
372 | } | |||
373 | else | |||
374 | { | |||
375 | if (sub_out_size > sub_buf_size) /* don't need bigger buffer any more */ | |||
376 | { | |||
377 | sub_out_size = sub_buf_size; | |||
378 | sub_buf = realloc(sub_buf, sub_out_size); | |||
379 | } /*if*/ | |||
380 | nextout = sub_buf; | |||
381 | } /*if*/ | |||
382 | if (!sub_buf_rewindable) | |||
383 | { | |||
384 | sub_end_out = 0; | |||
385 | sub_next_out = 0; | |||
386 | } /*if*/ | |||
387 | bytesread = fread(nextout, 1, sub_out_size - sub_end_out, subfile.h); | |||
388 | sub_end_out += bytesread; | |||
389 | } /*if*/ | |||
390 | } /*if*/ | |||
391 | if (sub_next_out < sub_end_out) | |||
392 | { | |||
393 | result = sub_buf[sub_next_out]; | |||
394 | ++in_charno; | |||
395 | ++sub_next_out; | |||
396 | if (result == '\n') | |||
397 | { | |||
398 | ++in_lineno; | |||
399 | } /*if*/ | |||
400 | } | |||
401 | else | |||
402 | { | |||
403 | result = EOF(-1); | |||
404 | } /*if*/ | |||
405 | return result; | |||
406 | } /*sub_getc*/ | |||
407 | ||||
408 | static void sub_rewind() | |||
409 | /* rewinds to the beginning of the input subtitle file. */ | |||
410 | { | |||
411 | if (!sub_buf_rewindable) | |||
412 | { | |||
413 | fprintf(stderrstderr, "ERR: trying to rewind subtitle file when not in rewindable state\n"); | |||
414 | exit(1); | |||
415 | } /*if*/ | |||
416 | sub_next_out = 0; | |||
417 | in_charno = 0; | |||
418 | in_lineno = 1; | |||
419 | } /*sub_rewind*/ | |||
420 | ||||
421 | static char * sub_fgets | |||
422 | ( | |||
423 | char * dst, | |||
424 | size_t dstsize /* must be at least 1, probably should be at least 2 */ | |||
425 | ) | |||
426 | /* reads a whole line from the input subtitle file into dst, returning dst if | |||
427 | at least one character was read. */ | |||
428 | { | |||
429 | const char * dstend = dst + dstsize - 1; | |||
430 | char * dstnext = dst; | |||
431 | bool_Bool warned_truncated = false0; | |||
432 | for (;;) | |||
433 | { | |||
434 | const int nextch = sub_getc(); | |||
435 | if (nextch == EOF(-1)) | |||
436 | { | |||
437 | if (dstnext == dst) | |||
438 | { | |||
439 | dst = NULL((void*)0); /* indicate nothing read */ | |||
440 | } /*if*/ | |||
441 | break; | |||
442 | } /*if*/ | |||
443 | if (dstnext < dstend) | |||
444 | { | |||
445 | *dstnext = nextch; | |||
446 | ++dstnext; | |||
447 | } | |||
448 | else if (!warned_truncated) | |||
449 | { | |||
450 | fprintf(stderrstderr, "WARN: input subtitle line too long on line %d\n", in_lineno); | |||
451 | warned_truncated = true1; | |||
452 | /* and continue gobbling rest of line */ | |||
453 | } /*if*/ | |||
454 | if (nextch == '\n') | |||
455 | break; /* end of line */ | |||
456 | } /*for*/ | |||
457 | if (dst != NULL((void*)0)) | |||
458 | { | |||
459 | *dstnext = 0; /* terminating null */ | |||
460 | } /*if*/ | |||
461 | return dst; | |||
462 | } /*sub_fgets*/ | |||
463 | ||||
464 | #ifdef HAVE_FRIBIDI1 | |||
465 | #ifndef max | |||
466 | #define max(a,b)(((a)>(b))?(a):(b)) (((a)>(b))?(a):(b)) | |||
467 | #endif | |||
468 | subtitle_elt *sub_fribidi(subtitle_elt *sub) | |||
469 | /* reorders character codes as necessary so right-to-left text will come out | |||
470 | in the correct order when rendered left-to-right. */ | |||
471 | { | |||
472 | FriBidiChar logical[LINE_LEN1000 + 1], visual[LINE_LEN1000 + 1]; // Hopefully these two won't smash the stack | |||
473 | char *ip = NULL((void*)0), *op = NULL((void*)0); | |||
474 | FriBidiParType base; | |||
475 | size_t len, orig_len; | |||
476 | int l = sub->lines; | |||
477 | fribidi_boolean log2vis; | |||
478 | while (l) | |||
479 | { | |||
480 | ip = sub->text[--l]; | |||
481 | orig_len = len = strlen(ip); | |||
482 | // We assume that we don't use full unicode, only UTF-8 | |||
483 | if (len > LINE_LEN1000) | |||
484 | { | |||
485 | fprintf(stderrstderr, "WARN: Sub->text is longer than LINE_LEN.\n"); | |||
486 | l++; | |||
487 | break; | |||
488 | } /*if*/ | |||
489 | len = fribidi_charset_to_unicodefribidi_charset_to_unicode(FRIBIDI_CHAR_SET_UTF8, ip, len, logical); | |||
490 | /* fixme: how do I know it will fit? */ | |||
491 | base = FRIBIDI_TYPE_ON; /* request order-neutral */ | |||
492 | log2vis = fribidi_log2visfribidi_log2vis | |||
493 | ( | |||
494 | /*str =*/ logical, /* input logical string */ | |||
495 | /*len =*/ len, /* input logical string length */ | |||
496 | /*pbase_dir =*/ &base, /* requested and resolved paragraph base direction */ | |||
497 | /*visual_str =*/ visual, /* output visual string */ | |||
498 | /*positions_L_to_V =*/ NULL((void*)0), | |||
499 | /* output mapping from logical to visual string positions */ | |||
500 | /*positions_V_to_L =*/ NULL((void*)0), | |||
501 | /* output mapping from visual string back to the logical string positions */ | |||
502 | /*embedding_levels =*/ NULL((void*)0) /* output list of embedding levels */ | |||
503 | ); | |||
504 | /* result is maximum level found plus one, or zero if any error occurred */ | |||
505 | /* Note this function is deprecated because it only handles one-line paragraphs */ | |||
506 | if (log2vis) | |||
507 | { | |||
508 | len = fribidi_remove_bidi_marksfribidi_remove_bidi_marks | |||
509 | ( | |||
510 | /*str =*/ visual, | |||
511 | /*len =*/ len, | |||
512 | /*positions_to_this =*/ NULL((void*)0), | |||
513 | /*positions_from_this_list =*/ NULL((void*)0), | |||
514 | /*embedding_levels =*/ NULL((void*)0) | |||
515 | ); | |||
516 | op = (char*)malloc(sizeof(char) * (max(2 * orig_len, 2 * len)(((2 * orig_len)>(2 * len))?(2 * orig_len):(2 * len)) + 1)); | |||
517 | if (op == NULL((void*)0)) | |||
518 | { | |||
519 | fprintf(stderrstderr, "ERR: Error allocating mem.\n"); | |||
520 | l++; | |||
521 | break; | |||
522 | } /*if*/ | |||
523 | fribidi_unicode_to_charsetfribidi_unicode_to_charset(FRIBIDI_CHAR_SET_UTF8, visual, len, op); | |||
524 | /* fixme: how do I know it will fit? */ | |||
525 | free(ip); | |||
526 | sub->text[l] = op; | |||
527 | } /*if*/ | |||
528 | } /*while*/ | |||
529 | if (l) /* unprocessed lines remaining */ | |||
530 | { | |||
531 | for (l = sub->lines; l;) | |||
532 | free(sub->text[--l]); | |||
533 | return ERR((void *) -1); | |||
534 | } /*if*/ | |||
535 | return sub; | |||
536 | } /*sub_fribidi*/ | |||
537 | ||||
538 | #endif /*HAVE_FRIBIDI*/ | |||
539 | ||||
540 | /* | |||
541 | Decoders for various subtitle formats | |||
542 | */ | |||
543 | ||||
544 | subtitle_elt *sub_read_line_sami(subtitle_elt *current) | |||
545 | { | |||
546 | /* yuk--internal static state */ | |||
547 | static char line[LINE_LEN1000 + 1]; | |||
548 | static const char *s = NULL((void*)0); | |||
549 | /* to get rid of above, simply use sub_getc and process input one character | |||
550 | at a time, rather than a whole line at a time */ | |||
551 | char text[LINE_LEN1000 + 1], *p = text; | |||
552 | const char *q; | |||
553 | int state; | |||
554 | current->lines = current->start = current->end = 0; | |||
555 | state = 0; | |||
556 | /* read the first line */ | |||
557 | if (!s) | |||
558 | if (!(s = sub_fgets(line, LINE_LEN1000))) | |||
559 | return 0; | |||
560 | do | |||
561 | { | |||
562 | switch (state) | |||
563 | { | |||
564 | case 0: /* find "START=" or "Slacktime:" */ | |||
565 | { | |||
566 | const char * const slacktime_s = stristr(s, "Slacktime:"); | |||
567 | if (slacktime_s) | |||
568 | sami_sub_slacktime = strtol(slacktime_s + 10, NULL((void*)0), 0) / 10; | |||
569 | s = stristr(s, "Start="); | |||
570 | if (s) | |||
571 | { | |||
572 | current->start = strtol(s + 6, (char **)&s, 0) / 10; | |||
573 | state = 1; | |||
574 | continue; | |||
575 | } /*if*/ | |||
576 | } | |||
577 | break; | |||
578 | ||||
579 | case 1: /* find first "<P" */ | |||
580 | s = stristr(s, "<P"); | |||
581 | if (s != 0) | |||
582 | { | |||
583 | s += 2; | |||
584 | state = 2; | |||
585 | continue; | |||
586 | } /*if*/ | |||
587 | break; | |||
588 | ||||
589 | case 2: /* find ">" */ | |||
590 | s = strchr (s, '>')(__extension__ (__builtin_constant_p ('>') && !__builtin_constant_p (s) && ('>') == '\0' ? (char *) __rawmemchr (s, '>' ) : __builtin_strchr (s, '>'))); | |||
591 | if (s != 0) | |||
592 | { | |||
593 | s++; | |||
594 | state = 3; | |||
595 | p = text; /* start collecting text here */ | |||
596 | continue; | |||
597 | } /*if*/ | |||
598 | break; | |||
599 | ||||
600 | case 3: /* get all text until '<' appears */ | |||
601 | if (*s == '\0') | |||
602 | break; | |||
603 | else if (!strncasecmp (s, "<br>", 4)) | |||
604 | { | |||
605 | *p = '\0'; /* end of collected text */ | |||
606 | p = text; /* point to whole thing */ | |||
607 | trail_space(text); /* less whitespace */ | |||
608 | if (text[0] != '\0') /* what's left is nonempty */ | |||
609 | current->text[current->lines++] = strdup(text)(__extension__ (__builtin_constant_p (text) && ((size_t )(const void *)((text) + 1) - (size_t)(const void *)(text) == 1) ? (((const char *) (text))[0] == '\0' ? (char *) calloc ( (size_t) 1, (size_t) 1) : ({ size_t __len = strlen (text) + 1 ; char *__retval = (char *) malloc (__len); if (__retval != ( (void*)0)) __retval = (char *) memcpy (__retval, text, __len) ; __retval; })) : __strdup (text))); | |||
610 | s += 4; /* skip "<br>" tag and stay in this state */ | |||
611 | } | |||
612 | /* wot, no recognition of "<p>" here? */ | |||
613 | else if (*s == '<') | |||
614 | { | |||
615 | state = 4; | |||
616 | } | |||
617 | else if (!strncasecmp (s, " ", 6)) | |||
618 | { | |||
619 | *p++ = ' '; /* treat as space */ | |||
620 | s += 6; | |||
621 | } | |||
622 | else if (*s == '\t') | |||
623 | { | |||
624 | *p++ = ' '; /* treat as space */ | |||
625 | s++; | |||
626 | } | |||
627 | else if (*s == '\r' || *s == '\n') | |||
628 | { | |||
629 | s++; /* ignore line breaks */ | |||
630 | } | |||
631 | else | |||
632 | { | |||
633 | *p++ = *s++; /* preserve everything else */ | |||
634 | } /*if*/ | |||
635 | /* skip duplicated space */ | |||
636 | if (p > text + 2 && p[-1] == ' ' && p[-2] == ' ') | |||
637 | p--; | |||
638 | continue; | |||
639 | ||||
640 | case 4: /* get current->end or skip rest of <TAG> */ | |||
641 | q = stristr(s, "Start="); | |||
642 | if (q) | |||
643 | { | |||
644 | current->end = strtol(q + 6, (char **)&q, 0) / 10 - 1; | |||
645 | /* start time of new line is end time of previous line */ | |||
646 | *p = '\0'; | |||
647 | trail_space(text); | |||
648 | if (text[0] != '\0') | |||
649 | current->text[current->lines++] = strdup(text)(__extension__ (__builtin_constant_p (text) && ((size_t )(const void *)((text) + 1) - (size_t)(const void *)(text) == 1) ? (((const char *) (text))[0] == '\0' ? (char *) calloc ( (size_t) 1, (size_t) 1) : ({ size_t __len = strlen (text) + 1 ; char *__retval = (char *) malloc (__len); if (__retval != ( (void*)0)) __retval = (char *) memcpy (__retval, text, __len) ; __retval; })) : __strdup (text))); | |||
650 | if (current->lines > 0) /* got one line, leave new one for next call */ | |||
651 | { | |||
652 | state = 99; | |||
653 | break; | |||
654 | } /*if*/ | |||
655 | state = 0; | |||
656 | continue; | |||
657 | } /*if*/ | |||
658 | s = strchr (s, '>')(__extension__ (__builtin_constant_p ('>') && !__builtin_constant_p (s) && ('>') == '\0' ? (char *) __rawmemchr (s, '>' ) : __builtin_strchr (s, '>'))); | |||
659 | if (s) | |||
660 | { | |||
661 | s++; | |||
662 | state = 3; /* back to collecting text */ | |||
663 | continue; | |||
664 | } /*if*/ | |||
665 | break; | |||
666 | } /*switch*/ | |||
667 | /* read next line */ | |||
668 | if (state != 99 && !(s = sub_fgets(line, LINE_LEN1000))) | |||
669 | { | |||
670 | if (current->start > 0) | |||
671 | { | |||
672 | break; // if it is the last subtitle | |||
673 | } | |||
674 | else | |||
675 | { | |||
676 | return 0; | |||
677 | } /*if*/ | |||
678 | } /*if*/ | |||
679 | } | |||
680 | while (state != 99); | |||
681 | // For the last subtitle | |||
682 | if (current->end <= 0) | |||
683 | { | |||
684 | current->end = current->start + sami_sub_slacktime; | |||
685 | *p = '\0'; | |||
686 | trail_space(text); | |||
687 | if (text[0] != '\0') | |||
688 | current->text[current->lines++] = strdup(text)(__extension__ (__builtin_constant_p (text) && ((size_t )(const void *)((text) + 1) - (size_t)(const void *)(text) == 1) ? (((const char *) (text))[0] == '\0' ? (char *) calloc ( (size_t) 1, (size_t) 1) : ({ size_t __len = strlen (text) + 1 ; char *__retval = (char *) malloc (__len); if (__retval != ( (void*)0)) __retval = (char *) memcpy (__retval, text, __len) ; __retval; })) : __strdup (text))); | |||
689 | } /*if*/ | |||
690 | return current; | |||
691 | } /*sub_read_line_sami*/ | |||
692 | ||||
693 | static const char *sub_readtext(const char *source, char **dest) | |||
694 | /* extracts the next text item in source, and returns a copy of it in *dest | |||
695 | (could be the empty string). Returns a pointer into the unprocessed remainder | |||
696 | of source, or NULL if there is nothing left. */ | |||
697 | { | |||
698 | int len = 0; | |||
699 | const char *p = source; | |||
700 | // fprintf(stderr, "src=%p dest=%p \n", source, dest); | |||
701 | while (!eol(*p) && *p!= '|') | |||
702 | { | |||
703 | p++, len++; | |||
704 | } /*while*/ | |||
705 | *dest = (char *)malloc(len + 1); | |||
706 | if (!*dest) | |||
707 | { | |||
708 | return ERR((void *) -1); | |||
709 | } /*if*/ | |||
710 | strncpy(*dest, source, len)__builtin_strncpy (*dest, source, len); | |||
711 | (*dest)[len] = 0; | |||
712 | while (*p == '\r' || *p == '\n' || *p == '|') | |||
713 | p++; | |||
714 | if (*p) | |||
715 | return p; // not-last text field | |||
716 | else | |||
717 | return NULL((void*)0); // last text field | |||
718 | } /*sub_readtext*/ | |||
719 | ||||
720 | subtitle_elt *sub_read_line_microdvd(subtitle_elt *current) | |||
721 | { | |||
722 | char line[LINE_LEN1000 + 1]; | |||
723 | char line2[LINE_LEN1000 + 1]; | |||
724 | const char *p, *next; | |||
725 | int i; | |||
726 | do /* look for valid timing line */ | |||
727 | { | |||
728 | if (!sub_fgets(line, LINE_LEN1000)) | |||
729 | return NULL((void*)0); | |||
730 | } | |||
731 | while | |||
732 | ( | |||
733 | sscanf(line, "{%ld}{}%[^\r\n]", ¤t->start, line2) | |||
734 | < | |||
735 | 2 | |||
736 | ||||
737 | && | |||
738 | sscanf(line, "{%ld}{%ld}%[^\r\n]", ¤t->start, ¤t->end, line2) | |||
739 | < | |||
740 | 3 | |||
741 | ); | |||
742 | p = line2; | |||
743 | next = p, i = 0; | |||
744 | while ((next = sub_readtext(next, ¤t->text[i])) != 0) | |||
745 | { | |||
746 | if (current->text[i] == ERR((void *) -1)) | |||
747 | { | |||
748 | return ERR((void *) -1); | |||
749 | } /*if*/ | |||
750 | i++; | |||
751 | if (i >= SUB_MAX_TEXT16) | |||
752 | { | |||
753 | fprintf(stderrstderr, "WARN: Too many lines in a subtitle\n"); | |||
754 | current->lines = i; | |||
755 | return current; | |||
756 | } /*if*/ | |||
757 | } /*while*/ | |||
758 | current->lines = ++i; | |||
759 | return current; | |||
760 | } /*sub_read_line_microdvd*/ | |||
761 | ||||
762 | subtitle_elt *sub_read_line_subrip(subtitle_elt *current) | |||
763 | { | |||
764 | char line[LINE_LEN1000 + 1]; | |||
765 | int a1, a2, a3, a4, b1, b2, b3, b4; | |||
766 | const char *p = NULL((void*)0), *q = NULL((void*)0); | |||
767 | int len; | |||
768 | while (true1) | |||
769 | { | |||
770 | if (!sub_fgets(line, LINE_LEN1000)) | |||
771 | return NULL((void*)0); | |||
772 | if (sscanf(line, "%d:%d:%d.%d,%d:%d:%d.%d", &a1, &a2, &a3, &a4, &b1, &b2, &b3, &b4) < 8) | |||
773 | /* start and end times in hours:minutes:seconds.hundredths */ | |||
774 | continue; | |||
775 | current->start = a1 * 360000 + a2 * 6000 + a3 * 100 + a4; | |||
776 | current->end = b1 * 360000 + b2 * 6000 + b3 * 100 + b4; | |||
777 | if (!sub_fgets(line, LINE_LEN1000)) | |||
778 | return NULL((void*)0); | |||
779 | p = q = line; | |||
780 | for (current->lines = 1; current->lines < SUB_MAX_TEXT16; current->lines++) | |||
781 | { | |||
782 | for | |||
783 | ( | |||
784 | q = p, len = 0; | |||
785 | *p && *p != '\r' && *p != '\n' && *p != '|' && strncmp(p, "[br]", 4)(__extension__ (__builtin_constant_p (4) && ((__builtin_constant_p (p) && strlen (p) < ((size_t) (4))) || (__builtin_constant_p ("[br]") && strlen ("[br]") < ((size_t) (4)))) ? __extension__ ({ size_t __s1_len, __s2_len; (__builtin_constant_p (p) && __builtin_constant_p ("[br]") && (__s1_len = strlen ( p), __s2_len = strlen ("[br]"), (!((size_t)(const void *)((p) + 1) - (size_t)(const void *)(p) == 1) || __s1_len >= 4) && (!((size_t)(const void *)(("[br]") + 1) - (size_t)(const void *)("[br]") == 1) || __s2_len >= 4)) ? __builtin_strcmp (p , "[br]") : (__builtin_constant_p (p) && ((size_t)(const void *)((p) + 1) - (size_t)(const void *)(p) == 1) && (__s1_len = strlen (p), __s1_len < 4) ? (__builtin_constant_p ("[br]") && ((size_t)(const void *)(("[br]") + 1) - ( size_t)(const void *)("[br]") == 1) ? __builtin_strcmp (p, "[br]" ) : (__extension__ ({ const unsigned char *__s2 = (const unsigned char *) (const char *) ("[br]"); int __result = (((const unsigned char *) (const char *) (p))[0] - __s2[0]); if (__s1_len > 0 && __result == 0) { __result = (((const unsigned char *) (const char *) (p))[1] - __s2[1]); if (__s1_len > 1 && __result == 0) { __result = (((const unsigned char *) (const char *) (p))[2] - __s2[2]); if (__s1_len > 2 && __result == 0) __result = (((const unsigned char *) (const char *) (p ))[3] - __s2[3]); } } __result; }))) : (__builtin_constant_p ( "[br]") && ((size_t)(const void *)(("[br]") + 1) - (size_t )(const void *)("[br]") == 1) && (__s2_len = strlen ( "[br]"), __s2_len < 4) ? (__builtin_constant_p (p) && ((size_t)(const void *)((p) + 1) - (size_t)(const void *)(p) == 1) ? __builtin_strcmp (p, "[br]") : (- (__extension__ ({ const unsigned char *__s2 = (const unsigned char *) (const char *) (p); int __result = (((const unsigned char *) (const char *) ("[br]"))[0] - __s2[0]); if (__s2_len > 0 && __result == 0) { __result = (((const unsigned char *) (const char *) ( "[br]"))[1] - __s2[1]); if (__s2_len > 1 && __result == 0) { __result = (((const unsigned char *) (const char *) ( "[br]"))[2] - __s2[2]); if (__s2_len > 2 && __result == 0) __result = (((const unsigned char *) (const char *) ("[br]" ))[3] - __s2[3]); } } __result; })))) : __builtin_strcmp (p, "[br]" )))); }) : strncmp (p, "[br]", 4))); | |||
786 | p++, len++ | |||
787 | ) | |||
788 | /* include in current subtitle line until end of line or "|" or "[br]" markers */; | |||
789 | current->text[current->lines - 1] = (char *)malloc(len + 1); | |||
790 | if (!current->text[current->lines - 1]) | |||
791 | return ERR((void *) -1); | |||
792 | strncpy(current->text[current->lines - 1], q, len)__builtin_strncpy (current->text[current->lines - 1], q , len); | |||
793 | current->text[current->lines-1][len] = '\0'; | |||
794 | if (!*p || *p == '\r' || *p == '\n') | |||
795 | break; | |||
796 | if (*p == '|') | |||
797 | p++; | |||
798 | else | |||
799 | while (*p++ != ']') | |||
800 | /* skip "[br]" marker */; | |||
801 | } /*for*/ | |||
802 | break; | |||
803 | } /*while*/ | |||
804 | return current; | |||
805 | } /*sub_read_line_subrip*/ | |||
806 | ||||
807 | subtitle_elt *sub_read_line_subviewer(subtitle_elt *current) | |||
808 | { | |||
809 | char line[LINE_LEN1000 + 1]; | |||
810 | int a1, a2, a3, a4, b1, b2, b3, b4; | |||
811 | const char *p = NULL((void*)0); | |||
812 | int i, len; | |||
813 | while (!current->text[0]) | |||
814 | { | |||
815 | if (!sub_fgets(line, LINE_LEN1000)) | |||
816 | return NULL((void*)0); | |||
817 | if | |||
818 | ( | |||
819 | (len = sscanf | |||
820 | ( | |||
821 | line, | |||
822 | "%d:%d:%d%[,.:]%d --> %d:%d:%d%[,.:]%d", | |||
823 | &a1, &a2, &a3, (char *)&i, &a4, | |||
824 | &b1, &b2, &b3, (char *)&i, &b4 | |||
825 | )) | |||
826 | < | |||
827 | 10 | |||
828 | ) | |||
829 | continue; | |||
830 | current->start = a1 * 360000 + a2 * 6000 + a3 * 100 + a4 / 10; | |||
831 | current->end = b1 * 360000 + b2 * 6000 + b3 * 100 + b4 / 10; | |||
832 | for (i = 0; i < SUB_MAX_TEXT16;) | |||
833 | { | |||
834 | if (!sub_fgets(line, LINE_LEN1000)) | |||
835 | break; | |||
836 | len = 0; | |||
837 | for (p = line; *p != '\n' && *p != '\r' && *p; p++, len++) | |||
838 | /* find end of line */; | |||
839 | if (len) /* nonempty line */ | |||
840 | { | |||
841 | int j = 0; | |||
842 | bool_Bool skip = false0; | |||
843 | char *curptr = current->text[i] = (char *)malloc(len + 1); | |||
844 | if (!current->text[i]) | |||
845 | return ERR((void *) -1); | |||
846 | //strncpy(current->text[i], line, len); current->text[i][len] = '\0'; | |||
847 | for(; j < len; j++) | |||
848 | { | |||
849 | /* let's filter html tags ::atmos */ | |||
850 | /* fixme: if you're going to filter out "<" characters, | |||
851 | shouldn't you provide a way to escape them? For example, | |||
852 | using HTML-style "&"-escapes? */ | |||
853 | if (line[j] == '>') | |||
854 | { | |||
855 | skip = false0; | |||
856 | continue; | |||
857 | } /*if*/ | |||
858 | if (line[j] == '<') | |||
859 | { | |||
860 | skip = true1; | |||
861 | continue; | |||
862 | } /*if*/ | |||
863 | if (skip) | |||
864 | { | |||
865 | continue; | |||
866 | } /*if*/ | |||
867 | *curptr = line[j]; | |||
868 | curptr++; | |||
869 | } /*for*/ | |||
870 | *curptr = '\0'; | |||
871 | i++; | |||
872 | } | |||
873 | else | |||
874 | { | |||
875 | break; | |||
876 | } /*if*/ | |||
877 | } /*for*/ | |||
878 | current->lines = i; | |||
879 | } /*while*/ | |||
880 | return current; | |||
881 | } /*sub_read_line_subviewer*/ | |||
882 | ||||
883 | subtitle_elt *sub_read_line_subviewer2(subtitle_elt *current) | |||
884 | { | |||
885 | char line[LINE_LEN1000 + 1]; | |||
886 | int a1, a2, a3, a4; | |||
887 | const char *p = NULL((void*)0); | |||
888 | int i, len; | |||
889 | while (!current->text[0]) | |||
890 | { | |||
891 | if (!sub_fgets(line, LINE_LEN1000)) | |||
892 | return NULL((void*)0); | |||
893 | if (line[0] != '{') | |||
894 | continue; | |||
895 | if ((len = sscanf(line, "{T %d:%d:%d:%d", &a1, &a2, &a3, &a4)) < 4) | |||
896 | continue; | |||
897 | current->start = a1 * 360000 + a2 * 6000 + a3 * 100 + a4 / 10; | |||
898 | for (i = 0; i < SUB_MAX_TEXT16;) | |||
899 | { | |||
900 | if (!sub_fgets(line, LINE_LEN1000)) | |||
901 | break; | |||
902 | if (line[0] == '}') | |||
903 | break; | |||
904 | len = 0; | |||
905 | for (p = line; *p != '\n' && *p != '\r' && *p; ++p, ++len) | |||
906 | /* find end of line */; | |||
907 | if (len) /* nonempty line */ | |||
908 | { | |||
909 | current->text[i] = (char *)malloc(len + 1); | |||
910 | if (!current->text[i]) | |||
911 | return ERR((void *) -1); | |||
912 | strncpy(current->text[i], line, len)__builtin_strncpy (current->text[i], line, len); | |||
913 | current->text[i][len] = '\0'; | |||
914 | ++i; | |||
915 | } | |||
916 | else | |||
917 | { | |||
918 | break; | |||
919 | } /*if*/ | |||
920 | } /*for*/ | |||
921 | current->lines = i; | |||
922 | } /*while*/ | |||
923 | return current; | |||
924 | } /*sub_read_line_subviewer2*/ | |||
925 | ||||
926 | subtitle_elt *sub_read_line_vplayer(subtitle_elt *current) | |||
927 | { | |||
928 | char line[LINE_LEN1000 + 1]; | |||
929 | int a1, a2, a3; | |||
930 | const char *p = NULL((void*)0), *next; | |||
931 | char separator; | |||
932 | int i, len, plen; | |||
933 | while (!current->text[0]) | |||
934 | { | |||
935 | if (!sub_fgets(line, LINE_LEN1000)) | |||
936 | return NULL((void*)0); | |||
937 | if ((len = sscanf(line, "%d:%d:%d%c%n", &a1, &a2, &a3, &separator, &plen)) < 4) | |||
938 | continue; | |||
939 | if (!(current->start = a1 * 360000 + a2 * 6000 + a3 * 100)) | |||
940 | continue; | |||
941 | #if 0 /*removed by wodzu*/ | |||
942 | p = line; | |||
943 | // finds the body of the subtitle | |||
944 | for (i = 0; i < 3; i++) | |||
945 | { | |||
946 | p = strchr(p, ':')(__extension__ (__builtin_constant_p (':') && !__builtin_constant_p (p) && (':') == '\0' ? (char *) __rawmemchr (p, ':') : __builtin_strchr (p, ':'))); | |||
947 | if (p == NULL((void*)0)) | |||
948 | break; | |||
949 | ++p; | |||
950 | } /*for*/ | |||
951 | if (p == NULL((void*)0)) | |||
952 | { | |||
953 | fprintf(stderrstderr, "SUB: Skipping incorrect subtitle line!\n"); | |||
954 | continue; | |||
955 | } /*if*/ | |||
956 | #else | |||
957 | // by wodzu: hey! this time we know what length it has! what is | |||
958 | // that magic for? it can't deal with space instead of third | |||
959 | // colon! look, what simple it can be: | |||
960 | p = &line[plen]; | |||
961 | #endif | |||
962 | if (*p != '|') | |||
963 | { | |||
964 | // | |||
965 | next = p, i = 0; | |||
966 | while ((next = sub_readtext(next, ¤t->text[i])) != 0) | |||
967 | { | |||
968 | if (current->text[i] == ERR((void *) -1)) | |||
969 | { | |||
970 | return ERR((void *) -1); | |||
971 | } /*if*/ | |||
972 | i++; | |||
973 | if (i >= SUB_MAX_TEXT16) | |||
974 | { | |||
975 | fprintf(stderrstderr, "WARN: Too many lines in a subtitle\n"); | |||
976 | current->lines = i; | |||
977 | return current; | |||
978 | } /*if*/ | |||
979 | } /*while*/ | |||
980 | current->lines = i + 1; | |||
981 | } /*if*/ | |||
982 | } /*while*/ | |||
983 | return current; | |||
984 | } /*sub_read_line_vplayer*/ | |||
985 | ||||
986 | subtitle_elt *sub_read_line_rt(subtitle_elt *current) | |||
987 | { | |||
988 | //TODO: This format uses quite rich (sub/super)set of xhtml | |||
989 | // I couldn't check it since DTD is not included. | |||
990 | // WARNING: full XML parses can be required for proper parsing | |||
991 | char line[LINE_LEN1000 + 1]; | |||
992 | int a1, a2, a3, a4, b1, b2, b3, b4; | |||
993 | const char *next = NULL((void*)0); | |||
994 | int i, len, plen; | |||
995 | while (!current->text[0]) | |||
996 | { | |||
997 | if (!sub_fgets(line, LINE_LEN1000)) | |||
998 | return NULL((void*)0); | |||
999 | //TODO: it seems that format of time is not easily determined, it may be 1:12, 1:12.0 or 0:1:12.0 | |||
1000 | //to describe the same moment in time. Maybe there are even more formats in use. | |||
1001 | //if ((len = sscanf(line, "<Time Begin=\"%d:%d:%d.%d\" End=\"%d:%d:%d.%d\"",&a1,&a2,&a3,&a4,&b1,&b2,&b3,&b4)) < 8) | |||
1002 | plen = a1 = a2 = a3 = a4 = b1 = b2 = b3 = b4 = 0; | |||
1003 | if | |||
1004 | ( | |||
1005 | (len = sscanf | |||
1006 | ( | |||
1007 | line, | |||
1008 | "<%*[tT]ime %*[bB]egin=\"%d.%d\" %*[Ee]nd=\"%d.%d\"%*[^<]<clear/>%n", | |||
1009 | &a3, &a4, &b3, &b4, &plen | |||
1010 | )) | |||
1011 | < | |||
1012 | 4 | |||
1013 | && | |||
1014 | (len = sscanf | |||
1015 | ( | |||
1016 | line, | |||
1017 | "<%*[tT]ime %*[bB]egin=\"%d.%d\" %*[Ee]nd=\"%d:%d.%d\"%*[^<]<clear/>%n", | |||
1018 | &a3, &a4, &b2, &b3, &b4, &plen | |||
1019 | )) | |||
1020 | < | |||
1021 | 5 | |||
1022 | && | |||
1023 | (len = sscanf | |||
1024 | ( | |||
1025 | line, | |||
1026 | "<%*[tT]ime %*[bB]egin=\"%d:%d\" %*[Ee]nd=\"%d:%d\"%*[^<]<clear/>%n", | |||
1027 | &a2, &a3, &b2, &b3, &plen | |||
1028 | )) | |||
1029 | < | |||
1030 | 4 | |||
1031 | && | |||
1032 | (len = sscanf | |||
1033 | ( | |||
1034 | line, | |||
1035 | "<%*[tT]ime %*[bB]egin=\"%d:%d\" %*[Ee]nd=\"%d:%d.%d\"%*[^<]<clear/>%n", | |||
1036 | &a2, &a3, &b2, &b3, &b4, &plen | |||
1037 | )) | |||
1038 | < | |||
1039 | 5 | |||
1040 | #if 0 | |||
1041 | && | |||
1042 | (len = sscanf | |||
1043 | ( | |||
1044 | line, | |||
1045 | "<%*[tT]ime %*[bB]egin=\"%d:%d.%d\" %*[Ee]nd=\"%d:%d\"%*[^<]<clear/>%n", | |||
1046 | &a2, &a3, &a4, &b2, &b3, &plen | |||
1047 | )) | |||
1048 | < | |||
1049 | 5 | |||
1050 | #endif | |||
1051 | && | |||
1052 | (len = sscanf | |||
1053 | ( | |||
1054 | line, | |||
1055 | "<%*[tT]ime %*[bB]egin=\"%d:%d.%d\" %*[Ee]nd=\"%d:%d.%d\"%*[^<]<clear/>%n", | |||
1056 | &a2, &a3, &a4, &b2, &b3, &b4, &plen | |||
1057 | )) | |||
1058 | < | |||
1059 | 6 | |||
1060 | && | |||
1061 | (len = sscanf | |||
1062 | ( | |||
1063 | line, | |||
1064 | "<%*[tT]ime %*[bB]egin=\"%d:%d:%d.%d\" %*[Ee]nd=\"%d:%d:%d.%d\"%*[^<]<clear/>%n", | |||
1065 | &a1, &a2, &a3, &a4, &b1, &b2, &b3, &b4, &plen | |||
1066 | )) | |||
1067 | < | |||
1068 | 8 | |||
1069 | && | |||
1070 | //now try it without end time | |||
1071 | (len = sscanf | |||
1072 | ( | |||
1073 | line, | |||
1074 | "<%*[tT]ime %*[bB]egin=\"%d.%d\"%*[^<]<clear/>%n", | |||
1075 | &a3, &a4, &plen | |||
1076 | )) | |||
1077 | < | |||
1078 | 2 | |||
1079 | && | |||
1080 | (len = sscanf | |||
1081 | ( | |||
1082 | line, | |||
1083 | "<%*[tT]ime %*[bB]egin=\"%d:%d\"%*[^<]<clear/>%n", | |||
1084 | &a2, &a3, &plen | |||
1085 | )) | |||
1086 | < | |||
1087 | 2 | |||
1088 | && | |||
1089 | (len = sscanf | |||
1090 | ( | |||
1091 | line, | |||
1092 | "<%*[tT]ime %*[bB]egin=\"%d:%d.%d\"%*[^<]<clear/>%n", | |||
1093 | &a2, &a3, &a4, &plen | |||
1094 | )) | |||
1095 | < | |||
1096 | 3 | |||
1097 | && | |||
1098 | (len = sscanf | |||
1099 | ( | |||
1100 | line, | |||
1101 | "<%*[tT]ime %*[bB]egin=\"%d:%d:%d.%d\"%*[^<]<clear/>%n", | |||
1102 | &a1, &a2, &a3, &a4, &plen | |||
1103 | )) | |||
1104 | < | |||
1105 | 4 | |||
1106 | ) | |||
1107 | continue; /* couldn't match any of the above */ | |||
1108 | current->start = a1 * 360000 + a2 * 6000 + a3 * 100 + a4 / 10; | |||
1109 | current->end = b1 * 360000 + b2 * 6000 + b3 * 100 + b4 / 10; | |||
1110 | if (b1 == 0 && b2 == 0 && b3 == 0 && b4 == 0) | |||
1111 | current->end = current->start + 200; | |||
1112 | i = 0; | |||
1113 | // TODO: I don't know what kind of convention is here for marking | |||
1114 | // multiline subs, maybe <br/> like in xml? | |||
1115 | next = strstr(line, "<clear/>"); | |||
1116 | if (next && strlen(next) > 8) | |||
1117 | { | |||
1118 | next += 8; /* skip "<clear/>" tag */ | |||
1119 | while ((next = sub_readtext(next, ¤t->text[i])) != 0) | |||
1120 | { | |||
1121 | if (current->text[i] == ERR((void *) -1)) | |||
1122 | { | |||
1123 | return ERR((void *) -1); | |||
1124 | } /*if*/ | |||
1125 | i++; | |||
1126 | if (i >= SUB_MAX_TEXT16) | |||
1127 | { | |||
1128 | fprintf(stderrstderr, "WARN: Too many lines in a subtitle\n"); | |||
1129 | current->lines = i; | |||
1130 | return current; | |||
1131 | } /*if*/ | |||
1132 | } /*while*/ | |||
1133 | } /*if*/ | |||
1134 | current->lines = i + 1; | |||
1135 | } /*while*/ | |||
1136 | return current; | |||
1137 | } /*sub_read_line_rt*/ | |||
1138 | ||||
1139 | subtitle_elt *sub_read_line_ssa(subtitle_elt *current) | |||
1140 | { | |||
1141 | /* | |||
1142 | * Sub Station Alpha v4 (and v2?) scripts have 9 commas before subtitle | |||
1143 | * other Sub Station Alpha scripts have only 8 commas before subtitle | |||
1144 | * Reading the "ScriptType:" field is not reliable since many scripts appear | |||
1145 | * w/o it | |||
1146 | * | |||
1147 | * http://www.scriptclub.org is a good place to find more examples | |||
1148 | * http://www.eswat.demon.co.uk is where the SSA specs can be found | |||
1149 | */ | |||
1150 | int comma; | |||
1151 | static int max_comma = 32; /* let's use 32 for the case that the */ | |||
1152 | /* amount of commas increase with newer SSA versions */ | |||
1153 | ||||
1154 | int hour1, min1, sec1, hunsec1, | |||
1155 | hour2, min2, sec2, hunsec2, nothing; | |||
1156 | int num; | |||
1157 | char line[LINE_LEN1000 + 1], line3[LINE_LEN1000 + 1]; | |||
1158 | const char *line2; | |||
1159 | const char *tmp; | |||
1160 | do /* look for valid timing line */ | |||
1161 | { | |||
1162 | if (!sub_fgets(line, LINE_LEN1000)) | |||
1163 | return NULL((void*)0); | |||
1164 | } | |||
1165 | while | |||
1166 | ( | |||
1167 | sscanf | |||
1168 | ( | |||
1169 | line, | |||
1170 | "Dialogue: Marked=%d,%d:%d:%d.%d,%d:%d:%d.%d,%[^\n\r]", | |||
1171 | ¬hing, | |||
1172 | &hour1, &min1, &sec1, &hunsec1, | |||
1173 | &hour2, &min2, &sec2, &hunsec2, | |||
1174 | line3 | |||
1175 | ) | |||
1176 | < | |||
1177 | 9 | |||
1178 | && | |||
1179 | sscanf | |||
1180 | ( | |||
1181 | line, "Dialogue: %d,%d:%d:%d.%d,%d:%d:%d.%d,%[^\n\r]", | |||
1182 | ¬hing, | |||
1183 | &hour1, &min1, &sec1, &hunsec1, | |||
1184 | &hour2, &min2, &sec2, &hunsec2, | |||
1185 | line3 | |||
1186 | ) | |||
1187 | < | |||
1188 | 9 | |||
1189 | ); | |||
1190 | line2 = strchr(line3, ',')(__extension__ (__builtin_constant_p (',') && !__builtin_constant_p (line3) && (',') == '\0' ? (char *) __rawmemchr (line3 , ',') : __builtin_strchr (line3, ','))); | |||
1191 | for (comma = 4; comma < max_comma; comma ++) | |||
1192 | { | |||
1193 | tmp = line2; | |||
1194 | if (!(tmp = strchr(++tmp, ',')(__extension__ (__builtin_constant_p (',') && !__builtin_constant_p (++tmp) && (',') == '\0' ? (char *) __rawmemchr (++tmp , ',') : __builtin_strchr (++tmp, ','))))) | |||
1195 | break; | |||
1196 | if (*++tmp == ' ') | |||
1197 | break; | |||
1198 | /* a space after a comma means we're already in a sentence */ | |||
1199 | line2 = tmp; | |||
1200 | } /*for*/ | |||
1201 | if (comma < max_comma) | |||
1202 | max_comma = comma; | |||
1203 | /* eliminate the trailing comma */ | |||
1204 | if (*line2 == ',') | |||
1205 | line2++; | |||
1206 | current->lines = 0; | |||
1207 | num = 0; | |||
1208 | current->start = 360000 * hour1 + 6000 * min1 + 100 * sec1 + hunsec1; | |||
1209 | current->end = 360000 * hour2 + 6000 * min2 + 100 * sec2 + hunsec2; | |||
1210 | while ((tmp = strstr(line2, "\\n")) != NULL((void*)0) || (tmp = strstr(line2, "\\N")) != NULL((void*)0)) | |||
1211 | { | |||
1212 | current->text[num] = (char *)malloc(tmp - line2 + 1); | |||
1213 | strncpy(current->text[num], line2, tmp - line2)__builtin_strncpy (current->text[num], line2, tmp - line2); | |||
1214 | current->text[num][tmp - line2] = '\0'; | |||
1215 | line2 = tmp + 2; | |||
1216 | num++; | |||
1217 | current->lines++; | |||
1218 | if (current->lines >= SUB_MAX_TEXT16) | |||
1219 | return current; | |||
1220 | } /*while*/ | |||
1221 | current->text[num] = strdup(line2)(__extension__ (__builtin_constant_p (line2) && ((size_t )(const void *)((line2) + 1) - (size_t)(const void *)(line2) == 1) ? (((const char *) (line2))[0] == '\0' ? (char *) calloc ( (size_t) 1, (size_t) 1) : ({ size_t __len = strlen (line2) + 1 ; char *__retval = (char *) malloc (__len); if (__retval != ( (void*)0)) __retval = (char *) memcpy (__retval, line2, __len ); __retval; })) : __strdup (line2))); | |||
1222 | current->lines++; | |||
1223 | return current; | |||
1224 | } /*sub_read_line_ssa*/ | |||
1225 | ||||
1226 | void sub_pp_ssa(subtitle_elt *sub) | |||
1227 | { | |||
1228 | int l = sub->lines; | |||
1229 | const char *so, *start; | |||
1230 | char *de; | |||
1231 | while (l) | |||
1232 | { | |||
1233 | /* eliminate any text enclosed with {}, they are font and color settings */ | |||
1234 | so = de = sub->text[--l]; | |||
1235 | while (*so) | |||
1236 | { | |||
1237 | if (*so == '{' && so[1] == '\\') | |||
1238 | { | |||
1239 | for (start = so; *so && *so != '}'; so++); | |||
1240 | if (*so) | |||
1241 | so++; | |||
1242 | else | |||
1243 | so = start; | |||
1244 | } /*if*/ | |||
1245 | if (*so) | |||
1246 | { | |||
1247 | *de = *so; | |||
1248 | so++; | |||
1249 | de++; | |||
1250 | } /*if*/ | |||
1251 | } /*while*/ | |||
1252 | *de = *so; | |||
1253 | } /*while*/ | |||
1254 | } /*sub_pp_ssa*/ | |||
1255 | ||||
1256 | subtitle_elt *sub_read_line_pjs(subtitle_elt *current) | |||
1257 | { | |||
1258 | char line[LINE_LEN1000 + 1]; | |||
1259 | char text[LINE_LEN1000 + 1]; | |||
1260 | if (!sub_fgets(line, LINE_LEN1000)) | |||
1261 | return NULL((void*)0); | |||
1262 | if (sscanf(line, "%ld,%ld,\"%[^\"]", ¤t->start, ¤t->end, text) < 3) | |||
1263 | return ERR((void *) -1); | |||
1264 | current->text[0] = strdup(text)(__extension__ (__builtin_constant_p (text) && ((size_t )(const void *)((text) + 1) - (size_t)(const void *)(text) == 1) ? (((const char *) (text))[0] == '\0' ? (char *) calloc ( (size_t) 1, (size_t) 1) : ({ size_t __len = strlen (text) + 1 ; char *__retval = (char *) malloc (__len); if (__retval != ( (void*)0)) __retval = (char *) memcpy (__retval, text, __len) ; __retval; })) : __strdup (text))); | |||
1265 | current->lines = 1; | |||
1266 | return current; | |||
1267 | } /*sub_read_line_pjs*/ | |||
1268 | ||||
1269 | subtitle_elt *sub_read_line_mpsub(subtitle_elt *current) | |||
1270 | { | |||
1271 | char line[LINE_LEN1000 + 1]; | |||
1272 | float startdelay, duration; | |||
1273 | int num = 0; | |||
1274 | char *p, *q; | |||
1275 | do /* look for valid timing line */ | |||
1276 | { | |||
1277 | if (!sub_fgets(line, LINE_LEN1000)) | |||
1278 | return NULL((void*)0); | |||
1279 | } | |||
1280 | while (sscanf(line, "%f %f", &startdelay, &duration) != 2); | |||
1281 | mpsub_position += startdelay * mpsub_multiplier; | |||
1282 | current->start = (int)mpsub_position; | |||
1283 | mpsub_position += duration * mpsub_multiplier; | |||
1284 | current->end = (int)mpsub_position; | |||
1285 | while (num < SUB_MAX_TEXT16) | |||
1286 | { | |||
1287 | if (!sub_fgets(line, LINE_LEN1000)) | |||
1288 | { | |||
1289 | if (num == 0) | |||
1290 | return NULL((void*)0); | |||
1291 | else | |||
1292 | return current; | |||
1293 | } /*if*/ | |||
1294 | p = line; | |||
1295 | while (isspace(*p)((*__ctype_b_loc ())[(int) ((*p))] & (unsigned short int) _ISspace)) | |||
1296 | p++; | |||
1297 | if (eol(*p) && num > 0) | |||
1298 | return current; | |||
1299 | if (eol(*p)) | |||
1300 | return NULL((void*)0); | |||
1301 | for (q = p; !eol(*q); q++) | |||
1302 | /* look for end of line */; | |||
1303 | *q = '\0'; | |||
1304 | if (strlen(p)) /* nonempty line */ | |||
1305 | { | |||
1306 | current->text[num] = strdup(p)(__extension__ (__builtin_constant_p (p) && ((size_t) (const void *)((p) + 1) - (size_t)(const void *)(p) == 1) ? ( ((const char *) (p))[0] == '\0' ? (char *) calloc ((size_t) 1 , (size_t) 1) : ({ size_t __len = strlen (p) + 1; char *__retval = (char *) malloc (__len); if (__retval != ((void*)0)) __retval = (char *) memcpy (__retval, p, __len); __retval; })) : __strdup (p))); | |||
1307 | // fprintf(stderr, ">%s<\n", p); | |||
1308 | current->lines = ++num; | |||
1309 | } | |||
1310 | else | |||
1311 | { | |||
1312 | if (num) | |||
1313 | return current; | |||
1314 | else | |||
1315 | return NULL((void*)0); | |||
1316 | } /*if*/ | |||
1317 | } /*while*/ | |||
1318 | return NULL((void*)0); // we should have returned before if it's OK | |||
1319 | } /*sub_read_line_mpsub*/ | |||
1320 | ||||
1321 | #ifndef USE_SORTSUB1 | |||
1322 | //we don't need this if we use previous_sub_end | |||
1323 | static subtitle_elt *previous_aqt_sub = NULL((void*)0); | |||
1324 | #endif | |||
1325 | ||||
1326 | subtitle_elt *sub_read_line_aqt(subtitle_elt *current) | |||
1327 | { | |||
1328 | char line[LINE_LEN1000 + 1]; | |||
1329 | const char *next; | |||
1330 | int i; | |||
1331 | while (true1) | |||
1332 | { | |||
1333 | // try to locate next subtitle | |||
1334 | if (!sub_fgets(line, LINE_LEN1000)) | |||
1335 | return NULL((void*)0); | |||
1336 | if (sscanf(line, "-->> %ld", ¤t->start) == 1) | |||
1337 | break; | |||
1338 | } /*while*/ | |||
1339 | #ifdef USE_SORTSUB1 | |||
1340 | previous_sub_end = current->start ? current->start - 1 : 0; | |||
1341 | #else | |||
1342 | if (previous_aqt_sub != NULL((void*)0)) | |||
1343 | previous_aqt_sub->end = current->start - 1; | |||
1344 | previous_aqt_sub = current; | |||
1345 | #endif | |||
1346 | if (!sub_fgets(line, LINE_LEN1000)) | |||
1347 | return NULL((void*)0); | |||
1348 | (void)sub_readtext(line, ¤t->text[0]); | |||
1349 | current->lines = 1; | |||
1350 | current->end = current->start; // will be corrected by next subtitle | |||
1351 | if (!sub_fgets(line, LINE_LEN1000)) | |||
1352 | return current; | |||
1353 | next = line, i = 1; | |||
1354 | while ((next = sub_readtext (next, ¤t->text[i])) != 0) | |||
1355 | { | |||
1356 | if (current->text[i] == ERR((void *) -1)) | |||
1357 | { | |||
1358 | return ERR((void *) -1); | |||
1359 | } /*if*/ | |||
1360 | i++; | |||
1361 | if (i >= SUB_MAX_TEXT16) | |||
1362 | { | |||
1363 | fprintf(stderrstderr, "WARN: Too many lines in a subtitle\n"); | |||
1364 | current->lines = i; | |||
1365 | return current; | |||
1366 | } /*if*/ | |||
1367 | } /*while*/ | |||
1368 | current->lines = i + 1; | |||
1369 | if (current->text[0][0] == 0 && current->text[1][0] == 0) | |||
1370 | { | |||
1371 | #ifdef USE_SORTSUB1 | |||
1372 | previous_sub_end = 0; | |||
1373 | #else | |||
1374 | // void subtitle -> end of previous marked and exit | |||
1375 | previous_aqt_sub = NULL((void*)0); | |||
1376 | #endif | |||
1377 | return NULL((void*)0); | |||
1378 | } /*if*/ | |||
1379 | return current; | |||
1380 | } /*sub_read_line_aqt*/ | |||
1381 | ||||
1382 | #ifndef USE_SORTSUB1 | |||
1383 | static subtitle_elt *previous_subrip09_sub = NULL((void*)0); | |||
1384 | #endif | |||
1385 | ||||
1386 | subtitle_elt *sub_read_line_subrip09(subtitle_elt *current) | |||
1387 | { | |||
1388 | char line[LINE_LEN1000 + 1]; | |||
1389 | int a1, a2, a3; | |||
1390 | const char * next = NULL((void*)0); | |||
1391 | int i, len; | |||
1392 | while (true1) | |||
1393 | { | |||
1394 | // try to locate next subtitle | |||
1395 | if (!sub_fgets(line, LINE_LEN1000)) | |||
1396 | return NULL((void*)0); | |||
1397 | len = sscanf(line, "[%d:%d:%d]", &a1, &a2, &a3); | |||
1398 | if (len == 3) | |||
1399 | break; | |||
1400 | } /*while*/ | |||
1401 | current->start = a1 * 360000 + a2 * 6000 + a3 * 100; | |||
1402 | #ifdef USE_SORTSUB1 | |||
1403 | previous_sub_end = current->start ? current->start - 1 : 0; | |||
1404 | #else | |||
1405 | if (previous_subrip09_sub != NULL((void*)0)) | |||
1406 | previous_subrip09_sub->end = current->start - 1; | |||
1407 | previous_subrip09_sub = current; | |||
1408 | #endif | |||
1409 | if (!sub_fgets(line, LINE_LEN1000)) | |||
1410 | return NULL((void*)0); | |||
1411 | next = line, i = 0; | |||
1412 | while ((next = sub_readtext(next, ¤t->text[i])) != 0) | |||
1413 | { | |||
1414 | if (current->text[i] == ERR((void *) -1)) | |||
1415 | { | |||
1416 | return ERR((void *) -1); | |||
1417 | } /*if*/ | |||
1418 | i++; | |||
1419 | if (i >= SUB_MAX_TEXT16) | |||
1420 | { | |||
1421 | fprintf(stderrstderr, "WARN: Too many lines in a subtitle\n"); | |||
1422 | current->lines = i; | |||
1423 | return current; | |||
1424 | } /*if*/ | |||
1425 | } /*while*/ | |||
1426 | current->lines = i + 1; | |||
1427 | if (current->text[0][0] == 0 && i == 0) | |||
1428 | { | |||
1429 | #ifdef USE_SORTSUB1 | |||
1430 | previous_sub_end = 0; | |||
1431 | #else | |||
1432 | // void subtitle -> end of previous marked and exit | |||
1433 | previous_subrip09_sub = NULL((void*)0); | |||
1434 | #endif | |||
1435 | return NULL((void*)0); | |||
1436 | } /*if*/ | |||
1437 | return current; | |||
1438 | } /*sub_read_line_subrip09*/ | |||
1439 | ||||
1440 | subtitle_elt *sub_read_line_jacosub(subtitle_elt * current) | |||
1441 | { | |||
1442 | char line1[LINE_LEN1000], line2[LINE_LEN1000], directive[LINE_LEN1000]; | |||
1443 | unsigned a1, a2, a3, a4, b1, b2, b3, b4, comment = 0; | |||
1444 | static unsigned jacoTimeres = 30; | |||
1445 | static int jacoShift = 0; | |||
1446 | const char *p; | |||
1447 | char *q; | |||
1448 | ||||
1449 | bzero(current, sizeof(subtitle_elt)); | |||
1450 | bzero(line1, LINE_LEN1000); | |||
1451 | bzero(line2, LINE_LEN1000); | |||
1452 | bzero(directive, LINE_LEN1000); | |||
1453 | while (!current->text[0]) | |||
1454 | { | |||
1455 | if (!sub_fgets(line1, LINE_LEN1000)) | |||
1456 | { | |||
1457 | return NULL((void*)0); | |||
1458 | } /*if*/ | |||
1459 | if | |||
1460 | ( | |||
1461 | sscanf | |||
1462 | ( | |||
1463 | line1, | |||
1464 | "%u:%u:%u.%u %u:%u:%u.%u %[^\n\r]", | |||
1465 | &a1, &a2, &a3, &a4, &b1, &b2, &b3, &b4, line2 | |||
1466 | ) | |||
1467 | < | |||
1468 | 9 | |||
1469 | ) | |||
1470 | { | |||
1471 | if (sscanf(line1, "@%u @%u %[^\n\r]", &a4, &b4, line2) < 3) | |||
1472 | { | |||
1473 | if (line1[0] == '#') | |||
1474 | { | |||
1475 | int hours = 0, minutes = 0, seconds, delta, inverter = 1; | |||
1476 | unsigned units = jacoShift; | |||
1477 | switch (toupper(line1[1])(__extension__ ({ int __res; if (sizeof (line1[1]) > 1) { if (__builtin_constant_p (line1[1])) { int __c = (line1[1]); __res = __c < -128 || __c > 255 ? __c : (*__ctype_toupper_loc ())[__c]; } else __res = toupper (line1[1]); } else __res = ( *__ctype_toupper_loc ())[(int) (line1[1])]; __res; }))) | |||
1478 | { | |||
1479 | case 'S': | |||
1480 | if (isalpha(line1[2])((*__ctype_b_loc ())[(int) ((line1[2]))] & (unsigned short int) _ISalpha)) | |||
1481 | { | |||
1482 | delta = 6; | |||
1483 | } | |||
1484 | else | |||
1485 | { | |||
1486 | delta = 2; | |||
1487 | } /*if*/ | |||
1488 | if (sscanf(&line1[delta], "%d", &hours)) | |||
1489 | { | |||
1490 | if (hours < 0) | |||
1491 | { | |||
1492 | hours *= -1; | |||
1493 | inverter = -1; | |||
1494 | } /*if*/ | |||
1495 | if (sscanf(&line1[delta], "%*d:%d", &minutes)) | |||
1496 | { | |||
1497 | if | |||
1498 | ( | |||
1499 | sscanf | |||
1500 | ( | |||
1501 | &line1[delta], "%*d:%*d:%d", | |||
1502 | &seconds | |||
1503 | ) | |||
1504 | ) | |||
1505 | { | |||
1506 | sscanf(&line1[delta], "%*d:%*d:%*d.%d", &units); | |||
1507 | } | |||
1508 | else | |||
1509 | { | |||
1510 | hours = 0; | |||
1511 | sscanf(&line1[delta], "%d:%d.%d", &minutes, &seconds, &units); | |||
1512 | minutes *= inverter; | |||
1513 | } /*if*/ | |||
1514 | } | |||
1515 | else | |||
1516 | { | |||
1517 | hours = minutes = 0; | |||
1518 | sscanf(&line1[delta], "%d.%d", &seconds, &units); | |||
1519 | seconds *= inverter; | |||
1520 | } /*if*/ | |||
1521 | jacoShift = | |||
1522 | ( | |||
1523 | (hours * 3600 + minutes * 60 + seconds) * jacoTimeres | |||
1524 | + | |||
1525 | units | |||
1526 | ) | |||
1527 | * | |||
1528 | inverter; | |||
1529 | } /*if*/ | |||
1530 | break; | |||
1531 | case 'T': | |||
1532 | if (isalpha(line1[2])((*__ctype_b_loc ())[(int) ((line1[2]))] & (unsigned short int) _ISalpha)) | |||
1533 | { | |||
1534 | delta = 8; | |||
1535 | } | |||
1536 | else | |||
1537 | { | |||
1538 | delta = 2; | |||
1539 | } /*if*/ | |||
1540 | sscanf(&line1[delta], "%u", &jacoTimeres); | |||
1541 | break; | |||
1542 | } /*switch*/ | |||
1543 | } /*if*/ | |||
1544 | continue; | |||
1545 | } | |||
1546 | else /* timing line */ | |||
1547 | { | |||
1548 | current->start = | |||
1549 | (unsigned long)((a4 + jacoShift) * 100.0 / jacoTimeres); | |||
1550 | current->end = | |||
1551 | (unsigned long)((b4 + jacoShift) * 100.0 / jacoTimeres); | |||
1552 | } /*if*/ | |||
1553 | } | |||
1554 | else /* timing line */ | |||
1555 | { | |||
1556 | current->start = | |||
1557 | (unsigned long) | |||
1558 | ( | |||
1559 | ((a1 * 3600 + a2 * 60 + a3) * jacoTimeres + a4 + jacoShift) | |||
1560 | * | |||
1561 | 100.0 | |||
1562 | / | |||
1563 | jacoTimeres | |||
1564 | ); | |||
1565 | current->end = | |||
1566 | (unsigned long) | |||
1567 | ( | |||
1568 | ((b1 * 3600 + b2 * 60 + b3) * jacoTimeres + b4 + jacoShift) | |||
1569 | * | |||
1570 | 100.0 | |||
1571 | / | |||
1572 | jacoTimeres | |||
1573 | ); | |||
1574 | } /*if*/ | |||
1575 | current->lines = 0; | |||
1576 | p = line2; | |||
1577 | while (*p == ' ' || *p == '\t') | |||
1578 | { | |||
1579 | ++p; /* ignore leading whitespace */ | |||
1580 | } /*while*/ | |||
1581 | if (isalpha(*p)((*__ctype_b_loc ())[(int) ((*p))] & (unsigned short int) _ISalpha) || *p == '[') | |||
1582 | { | |||
1583 | int cont, jLength; | |||
1584 | if (sscanf(p, "%s %[^\n\r]", directive, line1) < 2) | |||
1585 | return (subtitle_elt *)ERR((void *) -1); | |||
1586 | jLength = strlen(directive); | |||
1587 | for (cont = 0; cont < jLength; ++cont) | |||
1588 | { | |||
1589 | if (isalpha(directive[cont])((*__ctype_b_loc ())[(int) ((directive[cont]))] & (unsigned short int) _ISalpha)) | |||
1590 | directive[cont] = toupper(directive[cont])(__extension__ ({ int __res; if (sizeof (directive[cont]) > 1) { if (__builtin_constant_p (directive[cont])) { int __c = (directive[cont]); __res = __c < -128 || __c > 255 ? __c : (*__ctype_toupper_loc ())[__c]; } else __res = toupper (directive [cont]); } else __res = (*__ctype_toupper_loc ())[(int) (directive [cont])]; __res; })); | |||
1591 | } /*for*/ | |||
1592 | if | |||
1593 | ( | |||
1594 | strstr(directive, "RDB") != NULL((void*)0) | |||
1595 | || | |||
1596 | strstr(directive, "RDC") != NULL((void*)0) | |||
1597 | || | |||
1598 | strstr(directive, "RLB") != NULL((void*)0) | |||
1599 | || | |||
1600 | strstr(directive, "RLG") != NULL((void*)0) | |||
1601 | ) | |||
1602 | { | |||
1603 | continue; | |||
1604 | } /*if*/ | |||
1605 | if (strstr(directive, "JL") != NULL((void*)0)) | |||
1606 | { | |||
1607 | current->alignment = H_SUB_ALIGNMENT_LEFT; | |||
1608 | } | |||
1609 | else if (strstr(directive, "JR") != NULL((void*)0)) | |||
1610 | { | |||
1611 | current->alignment = H_SUB_ALIGNMENT_RIGHT; | |||
1612 | } | |||
1613 | else | |||
1614 | { | |||
1615 | current->alignment = H_SUB_ALIGNMENT_CENTER; | |||
1616 | } /*if*/ | |||
1617 | strcpy(line2, line1); | |||
1618 | p = line2; | |||
1619 | } /*if*/ | |||
1620 | for (q = line1; !eol(*p) && current->lines < SUB_MAX_TEXT16; ++p) | |||
1621 | { /* collect text from p into q */ | |||
1622 | switch (*p) | |||
1623 | { | |||
1624 | case '{': | |||
1625 | comment++; | |||
1626 | break; | |||
1627 | case '}': | |||
1628 | if (comment) | |||
1629 | { | |||
1630 | --comment; | |||
1631 | //the next line to get rid of a blank after the comment | |||
1632 | if (p[1] == ' ') | |||
1633 | p++; | |||
1634 | } /*if*/ | |||
1635 | break; | |||
1636 | case '~': | |||
1637 | if (!comment) | |||
1638 | { | |||
1639 | *q = ' '; | |||
1640 | ++q; | |||
1641 | } /*if*/ | |||
1642 | break; | |||
1643 | case ' ': | |||
1644 | case '\t': | |||
1645 | if (p[1] == ' ' || p[1] == '\t') /* ignore duplicated space/tab */ | |||
1646 | break; | |||
1647 | if (!comment) | |||
1648 | { | |||
1649 | *q = ' '; /* whitespace => single space */ | |||
1650 | ++q; | |||
1651 | } /*if*/ | |||
1652 | break; | |||
1653 | case '\\': | |||
1654 | if (p[1] == 'n') /* non-literal line break */ | |||
1655 | { | |||
1656 | *q = '\0'; | |||
1657 | q = line1; | |||
1658 | current->text[current->lines++] = strdup(line1)(__extension__ (__builtin_constant_p (line1) && ((size_t )(const void *)((line1) + 1) - (size_t)(const void *)(line1) == 1) ? (((const char *) (line1))[0] == '\0' ? (char *) calloc ( (size_t) 1, (size_t) 1) : ({ size_t __len = strlen (line1) + 1 ; char *__retval = (char *) malloc (__len); if (__retval != ( (void*)0)) __retval = (char *) memcpy (__retval, line1, __len ); __retval; })) : __strdup (line1))); | |||
1659 | ++p; | |||
1660 | break; | |||
1661 | } /*if*/ | |||
1662 | if (toupper(p[1])(__extension__ ({ int __res; if (sizeof (p[1]) > 1) { if ( __builtin_constant_p (p[1])) { int __c = (p[1]); __res = __c < -128 || __c > 255 ? __c : (*__ctype_toupper_loc ())[__c]; } else __res = toupper (p[1]); } else __res = (*__ctype_toupper_loc ())[(int) (p[1])]; __res; })) == 'C' || toupper(p[1])(__extension__ ({ int __res; if (sizeof (p[1]) > 1) { if ( __builtin_constant_p (p[1])) { int __c = (p[1]); __res = __c < -128 || __c > 255 ? __c : (*__ctype_toupper_loc ())[__c]; } else __res = toupper (p[1]); } else __res = (*__ctype_toupper_loc ())[(int) (p[1])]; __res; })) == 'F') | |||
1663 | { | |||
1664 | ++p, ++p; /* ignore following character as well */ | |||
1665 | break; | |||
1666 | } /*if*/ | |||
1667 | if | |||
1668 | ( | |||
1669 | p[1] == 'B' | |||
1670 | || | |||
1671 | p[1] == 'b' | |||
1672 | || | |||
1673 | p[1] == 'D' | |||
1674 | || //actually this means "insert current date here" | |||
1675 | p[1] == 'I' | |||
1676 | || | |||
1677 | p[1] == 'i' | |||
1678 | || | |||
1679 | p[1] == 'N' | |||
1680 | || | |||
1681 | p[1] == 'T' | |||
1682 | || //actually this means "insert current time here" | |||
1683 | p[1] == 'U' | |||
1684 | || | |||
1685 | p[1] == 'u' | |||
1686 | ) | |||
1687 | { | |||
1688 | ++p; /* ignore */ | |||
1689 | break; | |||
1690 | } /*if*/ | |||
1691 | if | |||
1692 | ( | |||
1693 | p[1] == '\\' | |||
1694 | || | |||
1695 | p[1] == '~' | |||
1696 | || | |||
1697 | p[1] == '{' | |||
1698 | ) | |||
1699 | { | |||
1700 | ++p; /* fallthrough to insert char following "\" literally */ | |||
1701 | } | |||
1702 | else if (eol(p[1])) | |||
1703 | { | |||
1704 | if (!sub_fgets(directive, LINE_LEN1000)) | |||
1705 | return NULL((void*)0); | |||
1706 | trail_space(directive); | |||
1707 | strconcat(line2, LINE_LEN1000, directive); | |||
1708 | break; | |||
1709 | } /*if*/ | |||
1710 | /* fallthrough */ | |||
1711 | default: /* copy character *p literally */ | |||
1712 | if (!comment) | |||
1713 | { | |||
1714 | *q = *p; | |||
1715 | ++q; | |||
1716 | } /*if*/ | |||
1717 | } /*switch*/ | |||
1718 | } /*for*/ | |||
1719 | *q = '\0'; | |||
1720 | current->text[current->lines] = strdup(line1)(__extension__ (__builtin_constant_p (line1) && ((size_t )(const void *)((line1) + 1) - (size_t)(const void *)(line1) == 1) ? (((const char *) (line1))[0] == '\0' ? (char *) calloc ( (size_t) 1, (size_t) 1) : ({ size_t __len = strlen (line1) + 1 ; char *__retval = (char *) malloc (__len); if (__retval != ( (void*)0)) __retval = (char *) memcpy (__retval, line1, __len ); __retval; })) : __strdup (line1))); | |||
1721 | } /*while*/ | |||
1722 | current->lines++; | |||
1723 | return current; | |||
1724 | } /*sub_read_line_jacosub*/ | |||
1725 | ||||
1726 | static int sub_autodetect(bool_Bool * uses_time) | |||
1727 | /* scans the first few lines of the file to try to determine what format it is. */ | |||
1728 | { | |||
1729 | char line[LINE_LEN1000 + 1]; | |||
1730 | int i, j = 0; | |||
1731 | char p; | |||
1732 | while (j < 100) | |||
1733 | { | |||
1734 | j++; | |||
1735 | if (!sub_fgets(line, LINE_LEN1000)) | |||
1736 | return SUB_INVALID-1; | |||
1737 | if (sscanf(line, "{%d}{%d}", &i, &i) == 2) | |||
1738 | { | |||
1739 | *uses_time = false0; | |||
1740 | return SUB_MICRODVD0; | |||
1741 | } /*if*/ | |||
1742 | if (sscanf(line, "{%d}{}", &i) == 1) | |||
1743 | { | |||
1744 | *uses_time = false0; | |||
1745 | return SUB_MICRODVD0; | |||
1746 | } /*if*/ | |||
1747 | if (sscanf(line, "%d:%d:%d.%d,%d:%d:%d.%d", &i, &i, &i, &i, &i, &i, &i, &i) == 8) | |||
1748 | { | |||
1749 | *uses_time = true1; | |||
1750 | return SUB_SUBRIP1; | |||
1751 | } /*if*/ | |||
1752 | if | |||
1753 | ( | |||
1754 | sscanf | |||
1755 | ( | |||
1756 | line, | |||
1757 | "%d:%d:%d%[,.:]%d --> %d:%d:%d%[,.:]%d", | |||
1758 | &i, &i, &i, (char *)&i, &i, &i, &i, &i, (char *)&i, &i | |||
1759 | ) | |||
1760 | == | |||
1761 | 10 | |||
1762 | ) | |||
1763 | { | |||
1764 | *uses_time = true1; | |||
1765 | return SUB_SUBVIEWER2; | |||
1766 | } /*if*/ | |||
1767 | if (sscanf(line, "{T %d:%d:%d:%d", &i, &i, &i, &i)) | |||
1768 | { | |||
1769 | *uses_time = true1; | |||
1770 | return SUB_SUBVIEWER210; | |||
1771 | } /*if*/ | |||
1772 | if (strstr(line, "<SAMI>")) | |||
1773 | { | |||
1774 | *uses_time = true1; | |||
1775 | return SUB_SAMI3; | |||
1776 | } /*if*/ | |||
1777 | if (sscanf(line, "%d:%d:%d.%d %d:%d:%d.%d", &i, &i, &i, &i, &i, &i, &i, &i) == 8) | |||
1778 | { | |||
1779 | *uses_time = true1; | |||
1780 | return SUB_JACOSUB12; | |||
1781 | } /*if*/ | |||
1782 | if (sscanf(line, "@%d @%d", &i, &i) == 2) | |||
1783 | { | |||
1784 | *uses_time = true1; | |||
1785 | return SUB_JACOSUB12; | |||
1786 | } /*if*/ | |||
1787 | if (sscanf(line, "%d:%d:%d:", &i, &i, &i ) == 3) | |||
1788 | { | |||
1789 | *uses_time = true1; | |||
1790 | return SUB_VPLAYER4; | |||
1791 | } /*if*/ | |||
1792 | if (sscanf(line, "%d:%d:%d ", &i, &i, &i ) == 3) | |||
1793 | { | |||
1794 | *uses_time = true1; | |||
1795 | return SUB_VPLAYER4; | |||
1796 | } /*if*/ | |||
1797 | //TODO: just checking if first line of sub starts with "<" is WAY | |||
1798 | // too weak test for RT | |||
1799 | // Please someone who knows the format of RT... FIX IT!!! | |||
1800 | // It may conflict with other sub formats in the future (actually it doesn't) | |||
1801 | if (*line == '<') | |||
1802 | { | |||
1803 | *uses_time = true1; | |||
1804 | return SUB_RT5; | |||
1805 | } /*if*/ | |||
1806 | if (!memcmp(line, "Dialogue: Marked", 16)) | |||
1807 | { | |||
1808 | *uses_time = true1; | |||
1809 | return SUB_SSA6; | |||
1810 | } /*if*/ | |||
1811 | if (!memcmp(line, "Dialogue: ", 10)) | |||
1812 | { | |||
1813 | *uses_time = true1; | |||
1814 | return SUB_SSA6; | |||
1815 | } /*if*/ | |||
1816 | if (sscanf(line, "%d,%d,\"%c", &i, &i, (char *)&i) == 3) | |||
1817 | { | |||
1818 | *uses_time = false0; | |||
1819 | return SUB_PJS7; | |||
1820 | } /*if*/ | |||
1821 | if (sscanf(line, "FORMAT=%d", &i) == 1) | |||
1822 | { | |||
1823 | *uses_time = false0; /* actually means that durations are in seconds */ | |||
1824 | return SUB_MPSUB8; | |||
1825 | } /*if*/ | |||
1826 | if (sscanf(line, "FORMAT=TIM%c", &p) == 1 && p == 'E') | |||
1827 | { | |||
1828 | *uses_time = true1; /* actually means that durations are in hundredths of a second */ | |||
1829 | return SUB_MPSUB8; | |||
1830 | } /*if*/ | |||
1831 | if (strstr(line, "-->>")) | |||
1832 | { | |||
1833 | *uses_time = false0; | |||
1834 | return SUB_AQTITLE9; | |||
1835 | } /*if*/ | |||
1836 | if (sscanf(line, "[%d:%d:%d]", &i, &i, &i) == 3) | |||
1837 | { | |||
1838 | *uses_time = true1; | |||
1839 | return SUB_SUBRIP0911; | |||
1840 | } /*if*/ | |||
1841 | } /*while*/ | |||
1842 | return SUB_INVALID-1; // too many bad lines | |||
1843 | } /*sub_autodetect*/ | |||
1844 | ||||
1845 | /* | |||
1846 | Common subtitle-handling code | |||
1847 | */ | |||
1848 | ||||
1849 | static void adjust_subs_time | |||
1850 | ( | |||
1851 | subtitle_elt * sub, /* array of subtitle_elts */ | |||
1852 | float subtime, /* duration to truncate overlapping subtitle to--why? */ | |||
1853 | float fps, | |||
1854 | int block, /* whether to check for overlapping subtitles (false if caller will fix them up) */ | |||
1855 | int sub_num, /* nr entries in sub array */ | |||
1856 | bool_Bool sub_uses_time | |||
1857 | ) | |||
1858 | /* adjusts for overlapping subtitle durations, and also for sub_fps if specified. */ | |||
1859 | { | |||
1860 | int nradjusted, adjusted; | |||
1861 | subtitle_elt * nextsub; | |||
1862 | int i = sub_num; | |||
1863 | unsigned long const subfms = (sub_uses_time ? 100 : fps) * subtime; | |||
1864 | /* subtime converted to subtitle duration units */ | |||
1865 | unsigned long const short_overlap = (sub_uses_time ? 100 : fps) / 5; // 0.2s | |||
1866 | nradjusted = 0; | |||
1867 | if (i) | |||
1868 | for (;;) | |||
1869 | { | |||
1870 | adjusted = 0; /* to begin with */ | |||
1871 | if (sub->end <= sub->start) | |||
1872 | { | |||
1873 | sub->end = sub->start + subfms; | |||
1874 | adjusted++; | |||
1875 | nradjusted++; | |||
1876 | } /*if*/ | |||
1877 | if (!--i) | |||
1878 | break; /* no nextsub */ | |||
1879 | nextsub = sub + 1; | |||
1880 | if (block) | |||
1881 | { | |||
1882 | if (sub->end > nextsub->start && sub->end <= nextsub->start + short_overlap) | |||
1883 | { | |||
1884 | // these subtitles overlap for less than 0.2 seconds | |||
1885 | // and would result in very short overlapping subtitle | |||
1886 | // so let's fix the problem here, before overlapping code | |||
1887 | // get its hands on them | |||
1888 | const unsigned delta = sub->end - nextsub->start, half = delta / 2; | |||
1889 | /* remove the overlap by splitting the difference */ | |||
1890 | sub->end -= half + 1; | |||
1891 | nextsub->start += delta - half; | |||
1892 | } /*if*/ | |||
1893 | if (sub->end >= nextsub->start) | |||
1894 | { | |||
1895 | /* either exact abut, or overlap by more than short_overlap */ | |||
1896 | sub->end = nextsub->start - 1; | |||
1897 | if (sub->end - sub->start > subfms) | |||
1898 | sub->end = sub->start + subfms; | |||
1899 | /* maximum subtitle duration -- why? */ | |||
1900 | if (!adjusted) | |||
1901 | nradjusted++; | |||
1902 | } /*if*/ | |||
1903 | } /*if*/ | |||
1904 | /* Theory: | |||
1905 | * Movies are often converted from FILM (24 fps) | |||
1906 | * to PAL (25) by simply speeding it up, so we | |||
1907 | * to multiply the original timestmaps by | |||
1908 | * (Movie's FPS / Subtitle's (guessed) FPS) | |||
1909 | * so eg. for 23.98 fps movie and PAL time based | |||
1910 | * subtitles we say -subfps 25 and we're fine! | |||
1911 | */ | |||
1912 | /* timed sub fps correction ::atmos */ | |||
1913 | if (sub_uses_time && sub_fps) | |||
1914 | { | |||
1915 | sub->start *= sub_fps / fps; | |||
1916 | sub->end *= sub_fps / fps; | |||
1917 | } /*if*/ | |||
1918 | sub = nextsub; | |||
1919 | } /*for; if*/ | |||
1920 | if (nradjusted != 0) | |||
1921 | fprintf(stderrstderr, "INFO: Adjusted %d subtitle(s).\n", nradjusted); | |||
1922 | } /*adjust_subs_time*/ | |||
1923 | ||||
1924 | struct subreader { /* describes a subtitle format */ | |||
1925 | subtitle_elt * (*read)(subtitle_elt *dest); /* file reader routine */ | |||
1926 | void (*post)(subtitle_elt *dest); /* optional post-processor routine */ | |||
1927 | const char *name; /* descriptive name */ | |||
1928 | }; | |||
1929 | ||||
1930 | sub_data *sub_read_file(const char *filename, float movie_fps) | |||
1931 | /* parses the contents of filename, auto-recognizing the subtitle file format, | |||
1932 | and returns the result. The subtitles will be translated from the subtitle_charset | |||
1933 | character set to Unicode if specified, unless the file name ends in ".utf", | |||
1934 | ".utf8" or ".utf-8" (case-insensitive), in which case they will be assumed | |||
1935 | to already be in UTF-8. movie_fps is the movie frame rate, needed for subtitle | |||
1936 | formats which specify fractional-second durations in frames. */ | |||
1937 | { | |||
1938 | //filename is assumed to be malloc'ed, free() is used in sub_free() | |||
1939 | int sub_format; | |||
1940 | int n_max; | |||
1941 | subtitle_elt *first, *second, *new_sub, *return_sub; | |||
1942 | #ifdef USE_SORTSUB1 | |||
1943 | subtitle_elt temp_sub; | |||
1944 | #endif | |||
1945 | sub_data *subt_data; | |||
1946 | bool_Bool uses_time = false0; | |||
1947 | int sub_num = 0, sub_errs = 0; | |||
1948 | struct subreader const sr[] = | |||
1949 | /* all the subtitle formats I know about, indexed by the codes defined in subreader.h */ | |||
1950 | { | |||
1951 | {sub_read_line_microdvd, NULL((void*)0), "microdvd"}, | |||
1952 | {sub_read_line_subrip, NULL((void*)0), "subrip"}, | |||
1953 | {sub_read_line_subviewer, NULL((void*)0), "subviewer"}, | |||
1954 | {sub_read_line_sami, NULL((void*)0), "sami"}, | |||
1955 | {sub_read_line_vplayer, NULL((void*)0), "vplayer"}, | |||
1956 | {sub_read_line_rt, NULL((void*)0), "rt"}, | |||
1957 | {sub_read_line_ssa, sub_pp_ssa, "ssa"}, | |||
1958 | {sub_read_line_pjs, NULL((void*)0), "pjs"}, | |||
1959 | {sub_read_line_mpsub, NULL((void*)0), "mpsub"}, | |||
1960 | {sub_read_line_aqt, NULL((void*)0), "aqt"}, | |||
1961 | {sub_read_line_subviewer2, NULL((void*)0), "subviewer 2.0"}, | |||
1962 | {sub_read_line_subrip09, NULL((void*)0), "subrip 0.9"}, | |||
1963 | {sub_read_line_jacosub, NULL((void*)0), "jacosub"}, | |||
1964 | }; | |||
1965 | const struct subreader *srp; /* the autodetected format */ | |||
1966 | ||||
1967 | if (filename == NULL((void*)0)) | |||
| ||||
1968 | return NULL((void*)0); //qnx segfault | |||
1969 | sub_open(filename); | |||
1970 | #ifdef HAVE_ICONV1 | |||
1971 | { | |||
1972 | int l, k; | |||
1973 | k = -1; | |||
1974 | l = strlen(filename); | |||
1975 | if (l > 4) /* long enough to have an extension */ | |||
1976 | { | |||
1977 | const char * const exts[] = {".utf", ".utf8", ".utf-8"}; | |||
1978 | /* if filename ends with one of these extensions, then its contents | |||
1979 | are assumed to be in UTF-8 encoding, and subtitle_charset is ignored */ | |||
1980 | for (k = 3; --k >= 0;) | |||
1981 | if (l > strlen(exts[k]) && !strcasecmp(filename + (l - strlen(exts[k])), exts[k])) | |||
1982 | { | |||
1983 | break; | |||
1984 | } /*if; for*/ | |||
1985 | } /*if*/ | |||
1986 | if (k < 0) /* assume it's not UTF-8 */ | |||
1987 | subcp_open(); /* to convert the text to UTF-8 */ | |||
1988 | } | |||
1989 | #endif | |||
1990 | sub_buf_rewindable = true1; | |||
1991 | sub_format = sub_autodetect(&uses_time); | |||
1992 | mpsub_multiplier = (uses_time ? 100.0 : 1.0); | |||
1993 | if (sub_format == SUB_INVALID-1) | |||
1994 | { | |||
1995 | fprintf(stderrstderr, "ERR: Could not determine format of subtitle file \"%s\"\n", filename); | |||
1996 | exit(1); | |||
1997 | } /*if*/ | |||
1998 | srp = sr + sub_format; | |||
1999 | fprintf(stderrstderr, "INFO: Detected subtitle file format: %s\n", srp->name); | |||
2000 | sub_rewind(); | |||
2001 | sub_buf_rewindable = false0; | |||
2002 | sub_num = 0; | |||
2003 | n_max = 32; /* initial size of "first" array */ | |||
2004 | first = (subtitle_elt *)malloc(n_max * sizeof(subtitle_elt)); | |||
2005 | /* fixme: don't bother recovering from any of the following allocation etc failures, just die */ | |||
2006 | if(!first) | |||
2007 | { | |||
2008 | #ifdef HAVE_ICONV1 | |||
2009 | subcp_close(); | |||
2010 | #endif | |||
2011 | return NULL((void*)0); | |||
2012 | } /*if*/ | |||
2013 | #ifdef USE_SORTSUB1 | |||
2014 | //This is to deal with those formats (AQT & Subrip) which define the end of a subtitle | |||
2015 | //as the beginning of the following | |||
2016 | previous_sub_end = 0; | |||
2017 | #endif | |||
2018 | while (true1) | |||
2019 | { | |||
2020 | /* read subtitle entries from input file */ | |||
2021 | if (sub_num == n_max) /* need more room in "first" array */ | |||
2022 | { | |||
2023 | n_max += 16; | |||
2024 | first = realloc(first, n_max * sizeof(subtitle_elt)); | |||
2025 | } /*if*/ | |||
2026 | #ifdef USE_SORTSUB1 | |||
2027 | new_sub = &temp_sub; | |||
2028 | /* temporary holding area before copying entry into right place in first array */ | |||
2029 | #else | |||
2030 | new_sub = &first[sub_num]; | |||
2031 | /* just put it directly on the end */ | |||
2032 | #endif | |||
2033 | memset(new_sub, '\0', sizeof(subtitle_elt)); | |||
2034 | new_sub = srp->read(new_sub); | |||
2035 | if (!new_sub) | |||
2036 | break; // EOF | |||
2037 | if (h_sub_alignment != H_SUB_ALIGNMENT_DEFAULT) | |||
2038 | { | |||
2039 | new_sub->alignment = h_sub_alignment; /* override settings from subtitle file */ | |||
2040 | } /*if*/ | |||
2041 | #ifdef HAVE_FRIBIDI1 | |||
2042 | if (new_sub != ERR((void *) -1)) | |||
2043 | new_sub = sub_fribidi(new_sub); | |||
2044 | #endif | |||
2045 | if (new_sub == ERR((void *) -1)) | |||
2046 | { | |||
2047 | #ifdef HAVE_ICONV1 | |||
2048 | subcp_close(); | |||
2049 | #endif | |||
2050 | if (first) | |||
2051 | free(first); | |||
2052 | return NULL((void*)0); | |||
2053 | } /*if*/ | |||
2054 | // Apply any post processing that needs recoding first | |||
2055 | if (new_sub != ERR((void *) -1) && !sub_no_text_pp && srp->post) | |||
2056 | srp->post(new_sub); | |||
2057 | #ifdef USE_SORTSUB1 | |||
2058 | /* fixme: this will all crash if new_sub == ERR */ | |||
2059 | if (!sub_num || first[sub_num - 1].start <= new_sub->start) | |||
2060 | { | |||
2061 | /* append contents of new_sub to first */ | |||
2062 | int i; | |||
2063 | first[sub_num].start = new_sub->start; | |||
2064 | first[sub_num].end = new_sub->end; | |||
2065 | first[sub_num].lines = new_sub->lines; | |||
2066 | first[sub_num].alignment = new_sub->alignment; | |||
2067 | for (i = 0; i < new_sub->lines; ++i) | |||
2068 | { | |||
2069 | first[sub_num].text[i] = new_sub->text[i]; | |||
2070 | }/*for*/ | |||
2071 | if (previous_sub_end) | |||
2072 | { | |||
2073 | first[sub_num - 1].end = previous_sub_end; | |||
2074 | previous_sub_end = 0; | |||
2075 | } /*if*/ | |||
2076 | } | |||
2077 | else | |||
2078 | { | |||
2079 | /* insert new_sub into first to keep it sorted by start time */ | |||
2080 | int i, j; | |||
2081 | for (j = sub_num - 1; j >= 0; --j) | |||
2082 | { | |||
2083 | first[j + 1].start = first[j].start; | |||
2084 | first[j + 1].end = first[j].end; | |||
2085 | first[j + 1].lines = first[j].lines; | |||
2086 | first[j + 1].alignment = first[j].alignment; | |||
2087 | for (i = 0; i < first[j].lines; ++i) | |||
2088 | { | |||
2089 | first[j + 1].text[i] = first[j].text[i]; | |||
2090 | } /*for*/ | |||
2091 | if (!j || first[j - 1].start <= new_sub->start) | |||
2092 | { | |||
2093 | first[j].start = new_sub->start; | |||
2094 | first[j].end = new_sub->end; | |||
2095 | first[j].lines = new_sub->lines; | |||
2096 | first[j].alignment = new_sub->alignment; | |||
2097 | for (i = 0; i < SUB_MAX_TEXT16; ++i) | |||
2098 | { | |||
2099 | first[j].text[i] = new_sub->text[i]; | |||
2100 | } /*for*/ | |||
2101 | if (previous_sub_end) | |||
2102 | { | |||
2103 | first[j].end = first[j - 1].end; | |||
2104 | first[j - 1].end = previous_sub_end; | |||
2105 | previous_sub_end = 0; | |||
2106 | } /*if*/ | |||
2107 | break; | |||
2108 | } /*if*/ | |||
2109 | } /*for*/ | |||
2110 | } /*if*/ | |||
2111 | #endif | |||
2112 | if (new_sub == ERR((void *) -1)) | |||
2113 | ++sub_errs; | |||
2114 | else | |||
2115 | ++sub_num; // Error vs. Valid | |||
2116 | } /*while*/ | |||
2117 | #ifdef HAVE_ICONV1 | |||
2118 | subcp_close(); | |||
2119 | #endif | |||
2120 | sub_close(); | |||
2121 | // fprintf(stderr, "SUB: Subtitle format %s time.\n", uses_time ? "uses" : "doesn't use"); | |||
2122 | fprintf(stderrstderr, "INFO: Read %i subtitles\n", sub_num); | |||
2123 | if (sub_errs) | |||
2124 | fprintf(stderrstderr, "INFO: %i bad line(s).\n", sub_errs); | |||
2125 | if (sub_num <= 0) | |||
2126 | { | |||
2127 | free(first); | |||
2128 | return NULL((void*)0); | |||
2129 | } /*if*/ | |||
2130 | // we do overlap if the user forced it (suboverlap_enable == 2) or | |||
2131 | // the user didn't forced no-overlapsub and the format is Jacosub or Ssa. | |||
2132 | // this is because usually overlapping subtitles are found in these formats, | |||
2133 | // while in others they are probably result of bad timing | |||
2134 | if | |||
2135 | ( | |||
2136 | suboverlap_enabled == 2 | |||
2137 | || | |||
2138 | suboverlap_enabled | |||
2139 | && | |||
2140 | (sub_format == SUB_JACOSUB12 || sub_format == SUB_SSA6) | |||
2141 | ) | |||
2142 | { | |||
2143 | // here we manage overlapping subtitles | |||
2144 | int n_first, sub_first, sub_orig, i, j; | |||
2145 | adjust_subs_time(first, 6.0, movie_fps, 0, sub_num, uses_time); /*~6 secs AST*/ | |||
2146 | sub_orig = sub_num; | |||
2147 | n_first = sub_num; | |||
2148 | sub_num = 0; | |||
2149 | second = NULL((void*)0); | |||
2150 | // for each subtitle in first[] we deal with its 'block' of | |||
2151 | // bonded subtitles | |||
2152 | for (sub_first = 0; sub_first < n_first; ++sub_first) | |||
2153 | { | |||
2154 | unsigned long | |||
2155 | global_start = first[sub_first].start, | |||
2156 | global_end = first[sub_first].end, | |||
2157 | local_start, | |||
2158 | local_end; | |||
2159 | int | |||
2160 | lines_to_add = first[sub_first].lines, /* total nr lines in block */ | |||
2161 | sub_to_add = 0, | |||
2162 | **placeholder = NULL((void*)0), | |||
2163 | highest_line = 0, | |||
2164 | nr_placeholder_entries, | |||
2165 | subs_done; | |||
2166 | const int | |||
2167 | start_block_sub = sub_num; | |||
2168 | bool_Bool real_block = true1; | |||
2169 | // here we find the number of subtitles inside the 'block' | |||
2170 | // and its span interval. this works well only with sorted | |||
2171 | // subtitles | |||
2172 | while | |||
2173 | ( | |||
2174 | sub_first + sub_to_add + 1 < n_first | |||
2175 | && | |||
2176 | first[sub_first + sub_to_add + 1].start < global_end | |||
2177 | ) | |||
2178 | { | |||
2179 | /* another subtitle overlapping sub_first--include in block */ | |||
2180 | ++sub_to_add; | |||
2181 | lines_to_add += first[sub_first + sub_to_add].lines; | |||
2182 | /* extend block duration to include this subtitle: */ | |||
2183 | if (first[sub_first + sub_to_add].start < global_start) | |||
2184 | { | |||
2185 | global_start = first[sub_first + sub_to_add].start; | |||
2186 | } /*if*/ | |||
2187 | if (first[sub_first + sub_to_add].end > global_end) | |||
2188 | { | |||
2189 | global_end = first[sub_first + sub_to_add].end; | |||
2190 | } /*if*/ | |||
2191 | } /*while*/ | |||
2192 | // we need a structure to keep trace of the screen lines | |||
2193 | // used by the subs, a 'placeholder' | |||
2194 | nr_placeholder_entries = 2 * sub_to_add + 1; | |||
2195 | // the maximum number of subs derived from a block of sub_to_add + 1 subs | |||
2196 | /* fixme: but only two entries are ever accessed in the following loop: | |||
2197 | the previous one and the current one */ | |||
2198 | placeholder = (int **)malloc(sizeof(int *) * nr_placeholder_entries); | |||
2199 | for (i = 0; i < nr_placeholder_entries; ++i) | |||
2200 | { | |||
2201 | placeholder[i] = (int *)malloc(sizeof(int) * lines_to_add); | |||
| ||||
2202 | for (j = 0; j < lines_to_add; ++j) | |||
2203 | { | |||
2204 | placeholder[i][j] = -1; | |||
2205 | } /*for*/ | |||
2206 | } /*for*/ | |||
2207 | subs_done = 0; | |||
2208 | local_end = global_start - 1; | |||
2209 | do | |||
2210 | { | |||
2211 | // here we find the beginning and the end of a new | |||
2212 | // subtitle in the block | |||
2213 | local_start = local_end + 1; /* start after previous duration done */ | |||
2214 | local_end = global_end; | |||
2215 | for (j = 0; j <= sub_to_add; ++j) | |||
2216 | { | |||
2217 | if | |||
2218 | ( | |||
2219 | first[sub_first + j].start - 1 > local_start | |||
2220 | && | |||
2221 | first[sub_first + j].start - 1 < local_end | |||
2222 | ) | |||
2223 | { | |||
2224 | local_end = first[sub_first + j].start - 1; | |||
2225 | /* local_end becomes earliest start if after local_start? */ | |||
2226 | } | |||
2227 | else if | |||
2228 | ( | |||
2229 | first[sub_first + j].end > local_start | |||
2230 | && | |||
2231 | first[sub_first + j].end < local_end | |||
2232 | ) | |||
2233 | { | |||
2234 | local_end = first[sub_first + j].end; | |||
2235 | /* local_end becomes earliest end if after local_start? */ | |||
2236 | } /*if*/ | |||
2237 | } /*for*/ | |||
2238 | // here we allocate the screen lines to subs we must | |||
2239 | // display in current local_start-local_end interval. | |||
2240 | // if the subs were yet presents in the previous interval | |||
2241 | // they keep the same lines, otherside they get unused lines | |||
2242 | for (j = 0; j <= sub_to_add; ++j) | |||
2243 | { | |||
2244 | if | |||
2245 | ( | |||
2246 | first[sub_first + j].start <= local_end | |||
2247 | && | |||
2248 | first[sub_first + j].end > local_start | |||
2249 | ) | |||
2250 | { | |||
2251 | /* this one overlaps (local_start .. local_end] */ | |||
2252 | const unsigned long sub_lines = first[sub_first + j].lines; | |||
2253 | unsigned long | |||
2254 | fragment_length = lines_to_add + 1, | |||
2255 | blank_lines_avail = 0; | |||
2256 | bool_Bool wasinprev = false0; | |||
2257 | int fragment_position = -1; | |||
2258 | // if this is not the first new sub of the block | |||
2259 | // we find if this sub was present in the previous | |||
2260 | // new sub | |||
2261 | if (subs_done) | |||
2262 | for (i = 0; i < lines_to_add; ++i) | |||
2263 | { | |||
2264 | if (placeholder[subs_done - 1][i] == sub_first + j) | |||
2265 | { | |||
2266 | placeholder[subs_done][i] = sub_first + j; | |||
2267 | wasinprev = true1; | |||
2268 | } /*if*/ | |||
2269 | } /*for; if*/ | |||
2270 | if (wasinprev) | |||
2271 | continue; /* already processed this subtitle */ | |||
2272 | // we are looking for the shortest among all groups of | |||
2273 | // sequential blank lines whose length is greater than or | |||
2274 | // equal to sub_lines. we store in fragment_position the | |||
2275 | // position of the shortest group, in fragment_length its | |||
2276 | // length, and in blank_lines_avail the length of the group currently | |||
2277 | // examined | |||
2278 | for (i = 0; i < lines_to_add; ++i) | |||
2279 | { | |||
2280 | if (placeholder[subs_done][i] == -1) | |||
2281 | { | |||
2282 | // placeholder[subs_done][i] is part of the current group | |||
2283 | // of blank lines | |||
2284 | ++blank_lines_avail; | |||
2285 | } | |||
2286 | else /* end of a run of empty lines */ | |||
2287 | { | |||
2288 | if (blank_lines_avail == sub_lines) | |||
2289 | { | |||
2290 | // current group's size fits exactly the one we | |||
2291 | // need, so we stop looking | |||
2292 | fragment_position = i - blank_lines_avail; | |||
2293 | /* starting line position */ | |||
2294 | blank_lines_avail = 0; | |||
2295 | break; | |||
2296 | } /*if*/ | |||
2297 | if | |||
2298 | ( | |||
2299 | blank_lines_avail | |||
2300 | && | |||
2301 | blank_lines_avail > sub_lines | |||
2302 | && | |||
2303 | blank_lines_avail < fragment_length | |||
2304 | ) | |||
2305 | { | |||
2306 | // current group is the best we found till here, | |||
2307 | // but is still bigger than the one we are looking | |||
2308 | // for, so we keep on looking | |||
2309 | fragment_length = blank_lines_avail; | |||
2310 | fragment_position = i - blank_lines_avail; | |||
2311 | blank_lines_avail = 0; | |||
2312 | } | |||
2313 | else | |||
2314 | { | |||
2315 | // current group doesn't fit at all, so we forget it | |||
2316 | blank_lines_avail = 0; | |||
2317 | } /*if*/ | |||
2318 | } /*if*/ | |||
2319 | } /*for*/ | |||
2320 | if (blank_lines_avail) | |||
2321 | { | |||
2322 | // last screen line is blank, a group ends with it | |||
2323 | if | |||
2324 | ( | |||
2325 | blank_lines_avail >= sub_lines | |||
2326 | && | |||
2327 | blank_lines_avail < fragment_length | |||
2328 | ) | |||
2329 | { | |||
2330 | fragment_position = i - blank_lines_avail; | |||
2331 | } /*if*/ | |||
2332 | } /*if*/ | |||
2333 | if (fragment_position == -1) | |||
2334 | { | |||
2335 | // it was not possible to find free screen line(s) for a subtitle, | |||
2336 | // usually this means a bug in the code; however we do not overlap | |||
2337 | fprintf | |||
2338 | ( | |||
2339 | stderrstderr, | |||
2340 | "WARN: We could not find a suitable position for an" | |||
2341 | " overlapping subtitle\n" | |||
2342 | ); | |||
2343 | highest_line = SUB_MAX_TEXT16 + 1; | |||
2344 | break; | |||
2345 | } | |||
2346 | else /* found a place to put it */ | |||
2347 | { | |||
2348 | int k; | |||
2349 | for (k = 0; k < sub_lines; ++k) | |||
2350 | { | |||
2351 | placeholder[subs_done][fragment_position + k] = sub_first + j; | |||
2352 | } /*for*/ | |||
2353 | } /*if*/ | |||
2354 | } /*if*/ | |||
2355 | } /*for*/ | |||
2356 | for (j = highest_line + 1; j < lines_to_add; ++j) | |||
2357 | { | |||
2358 | if (placeholder[subs_done][j] != -1) | |||
2359 | highest_line = j; /* highest nonblank line within block */ | |||
2360 | else | |||
2361 | break; | |||
2362 | } /*for*/ | |||
2363 | if (highest_line >= SUB_MAX_TEXT16) | |||
2364 | { | |||
2365 | // the 'block' has too much lines, so we don't overlap the | |||
2366 | // subtitles | |||
2367 | second = (subtitle_elt *)realloc | |||
2368 | ( | |||
2369 | second, | |||
2370 | (sub_num + sub_to_add + 1) * sizeof(subtitle_elt) | |||
2371 | ); | |||
2372 | for (j = 0; j <= sub_to_add; ++j) | |||
2373 | { | |||
2374 | int ls; | |||
2375 | memset(&second[sub_num + j], '\0', sizeof(subtitle_elt)); | |||
2376 | second[sub_num + j].start = first[sub_first + j].start; | |||
2377 | second[sub_num + j].end = first[sub_first + j].end; | |||
2378 | second[sub_num + j].lines = first[sub_first + j].lines; | |||
2379 | second[sub_num + j].alignment = first[sub_first + j].alignment; | |||
2380 | for (ls = 0; ls < second[sub_num + j].lines; ls++) | |||
2381 | { | |||
2382 | second[sub_num + j].text[ls] = strdup(first[sub_first + j].text[ls])(__extension__ (__builtin_constant_p (first[sub_first + j].text [ls]) && ((size_t)(const void *)((first[sub_first + j ].text[ls]) + 1) - (size_t)(const void *)(first[sub_first + j ].text[ls]) == 1) ? (((const char *) (first[sub_first + j].text [ls]))[0] == '\0' ? (char *) calloc ((size_t) 1, (size_t) 1) : ({ size_t __len = strlen (first[sub_first + j].text[ls]) + 1 ; char *__retval = (char *) malloc (__len); if (__retval != ( (void*)0)) __retval = (char *) memcpy (__retval, first[sub_first + j].text[ls], __len); __retval; })) : __strdup (first[sub_first + j].text[ls]))); | |||
2383 | } /*for*/ | |||
2384 | } /*for*/ | |||
2385 | sub_num += sub_to_add + 1; | |||
2386 | sub_first += sub_to_add; | |||
2387 | real_block = false0; | |||
2388 | break; | |||
2389 | } /*if*/ | |||
2390 | // we read the placeholder structure and create the new subs. | |||
2391 | second = (subtitle_elt *)realloc(second, (sub_num + 1) * sizeof(subtitle_elt)); | |||
2392 | memset(&second[sub_num], '\0', sizeof(subtitle_elt)); | |||
2393 | second[sub_num].start = local_start; | |||
2394 | second[sub_num].end = local_end; | |||
2395 | second[sub_num].alignment = H_SUB_ALIGNMENT_CENTER; | |||
2396 | n_max = (lines_to_add < SUB_MAX_TEXT16) ? lines_to_add : SUB_MAX_TEXT16; | |||
2397 | for (i = 0, j = 0; j < n_max; ++j) | |||
2398 | { | |||
2399 | if (placeholder[subs_done][j] != -1) | |||
2400 | { | |||
2401 | /* copy all the lines from first[placeholder[subs_done][j]] into | |||
2402 | i and following positions in second */ | |||
2403 | const int lines = first[placeholder[subs_done][j]].lines; | |||
2404 | int ls; | |||
2405 | for (ls = 0; ls < lines; ++ls) | |||
2406 | { | |||
2407 | second[sub_num].text[i++] = | |||
2408 | strdup(first[placeholder[subs_done][j]].text[ls])(__extension__ (__builtin_constant_p (first[placeholder[subs_done ][j]].text[ls]) && ((size_t)(const void *)((first[placeholder [subs_done][j]].text[ls]) + 1) - (size_t)(const void *)(first [placeholder[subs_done][j]].text[ls]) == 1) ? (((const char * ) (first[placeholder[subs_done][j]].text[ls]))[0] == '\0' ? ( char *) calloc ((size_t) 1, (size_t) 1) : ({ size_t __len = strlen (first[placeholder[subs_done][j]].text[ls]) + 1; char *__retval = (char *) malloc (__len); if (__retval != ((void*)0)) __retval = (char *) memcpy (__retval, first[placeholder[subs_done][j] ].text[ls], __len); __retval; })) : __strdup (first[placeholder [subs_done][j]].text[ls]))); | |||
2409 | } /*for*/ | |||
2410 | j += lines - 1; /* skip over lines just copied */ | |||
2411 | } | |||
2412 | else | |||
2413 | { | |||
2414 | /* no subtitle goes here -- put in blank line */ | |||
2415 | second[sub_num].text[i++] = strdup(" ")(__extension__ (__builtin_constant_p (" ") && ((size_t )(const void *)((" ") + 1) - (size_t)(const void *)(" ") == 1 ) ? (((const char *) (" "))[0] == '\0' ? (char *) calloc ((size_t ) 1, (size_t) 1) : ({ size_t __len = strlen (" ") + 1; char * __retval = (char *) malloc (__len); if (__retval != ((void*)0 )) __retval = (char *) memcpy (__retval, " ", __len); __retval ; })) : __strdup (" "))); | |||
2416 | } /*if*/ | |||
2417 | } /*for*/ | |||
2418 | ++sub_num; | |||
2419 | ++subs_done; | |||
2420 | } | |||
2421 | while (local_end < global_end); | |||
2422 | if (real_block) | |||
2423 | for (i = 0; i < subs_done; ++i) | |||
2424 | second[start_block_sub + i].lines = highest_line + 1; | |||
2425 | for (i = 0; i < nr_placeholder_entries; ++i) | |||
2426 | { | |||
2427 | free(placeholder[i]); | |||
2428 | } /*for*/ | |||
2429 | free(placeholder); | |||
2430 | sub_first += sub_to_add; /* skip over the ones I just added */ | |||
2431 | } /*for*/ | |||
2432 | for (j = sub_orig - 1; j >= 0; --j) | |||
2433 | { | |||
2434 | for (i = first[j].lines - 1; i >= 0; --i) | |||
2435 | { | |||
2436 | free(first[j].text[i]); | |||
2437 | } /*for*/ | |||
2438 | } /*for*/ | |||
2439 | free(first); | |||
2440 | return_sub = second; | |||
2441 | } | |||
2442 | else /* not suboverlap_enabled */ | |||
2443 | { | |||
2444 | adjust_subs_time(first, 6.0, movie_fps, 1, sub_num, uses_time);/*~6 secs AST*/ | |||
2445 | return_sub = first; | |||
2446 | } /*if*/ | |||
2447 | if (return_sub == NULL((void*)0)) | |||
2448 | return NULL((void*)0); | |||
2449 | subt_data = (sub_data *)malloc(sizeof(sub_data)); | |||
2450 | subt_data->filename = filename; | |||
2451 | subt_data->sub_uses_time = uses_time; | |||
2452 | subt_data->sub_num = sub_num; | |||
2453 | subt_data->sub_errs = sub_errs; | |||
2454 | subt_data->subtitles = return_sub; | |||
2455 | return subt_data; | |||
2456 | } /*sub_read_file*/ | |||
2457 | ||||
2458 | void sub_free(sub_data * subd) | |||
2459 | /* frees all storage allocated for subd. */ | |||
2460 | { | |||
2461 | int i; | |||
2462 | if (!subd) | |||
2463 | return; | |||
2464 | if (subd->subtitles) | |||
2465 | { | |||
2466 | for (i = 0; i < subd->subtitles->lines; i++) | |||
2467 | free(subd->subtitles->text[i]); | |||
2468 | } /*if*/ | |||
2469 | free(subd->subtitles); | |||
2470 | free((void *)subd->filename); | |||
2471 | free(subd); | |||
2472 | } /*sub_free*/ | |||
2473 | ||||
2474 | /* | |||
2475 | Additional unused stuff (remove some time) | |||
2476 | */ | |||
2477 | ||||
2478 | void list_sub_file(const sub_data * subd) | |||
2479 | { | |||
2480 | int i, j; | |||
2481 | const subtitle_elt * const subs = subd->subtitles; | |||
2482 | for (j = 0; j < subd->sub_num; j++) | |||
2483 | { | |||
2484 | const subtitle_elt * const egysub = &subs[j]; | |||
2485 | fprintf(stdoutstdout, "%i line%c (%li-%li)\n", | |||
2486 | egysub->lines, | |||
2487 | 1 == egysub->lines ? ' ' : 's', | |||
2488 | egysub->start, | |||
2489 | egysub->end); | |||
2490 | for (i = 0; i < egysub->lines; i++) | |||
2491 | { | |||
2492 | fprintf(stdoutstdout, "\t\t%d: %s%s", i, egysub->text[i], i == egysub->lines-1 ? "" : " \n "); | |||
2493 | } /*for*/ | |||
2494 | fprintf(stdoutstdout, "\n"); | |||
2495 | } /*for*/ | |||
2496 | fprintf(stdoutstdout, "Subtitle format %s time.\n", subd->sub_uses_time ? "uses" : "doesn't use"); | |||
2497 | fprintf(stdoutstdout, "Read %i subtitles, %i errors.\n", subd->sub_num, subd->sub_errs); | |||
2498 | } /*list_sub_file*/ |