| 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*/ |