2 * Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
4 * This file is part of libass.
6 * libass is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * libass is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with libass; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
28 #include <sys/types.h>
38 #include "ass_utils.h"
39 #include "ass_library.h"
42 typedef enum { PST_UNKNOWN =
43 0, PST_INFO, PST_STYLES, PST_EVENTS, PST_FONTS } parser_state_t;
45 struct parser_priv_s {
53 #define ASS_STYLES_ALLOC 20
54 #define ASS_EVENTS_ALLOC 200
56 void ass_free_track(ass_track_t *track)
60 if (track->parser_priv) {
61 if (track->parser_priv->fontname)
62 free(track->parser_priv->fontname);
63 if (track->parser_priv->fontdata)
64 free(track->parser_priv->fontdata);
65 free(track->parser_priv);
67 if (track->style_format)
68 free(track->style_format);
69 if (track->event_format)
70 free(track->event_format);
72 for (i = 0; i < track->n_styles; ++i)
73 ass_free_style(track, i);
77 for (i = 0; i < track->n_events; ++i)
78 ass_free_event(track, i);
83 /// \brief Allocate a new style struct
84 /// \param track track
86 int ass_alloc_style(ass_track_t *track)
90 assert(track->n_styles <= track->max_styles);
92 if (track->n_styles == track->max_styles) {
93 track->max_styles += ASS_STYLES_ALLOC;
95 (ass_style_t *) realloc(track->styles,
100 sid = track->n_styles++;
101 memset(track->styles + sid, 0, sizeof(ass_style_t));
105 /// \brief Allocate a new event struct
106 /// \param track track
108 int ass_alloc_event(ass_track_t *track)
112 assert(track->n_events <= track->max_events);
114 if (track->n_events == track->max_events) {
115 track->max_events += ASS_EVENTS_ALLOC;
117 (ass_event_t *) realloc(track->events,
118 sizeof(ass_event_t) *
122 eid = track->n_events++;
123 memset(track->events + eid, 0, sizeof(ass_event_t));
127 void ass_free_event(ass_track_t *track, int eid)
129 ass_event_t *event = track->events + eid;
136 if (event->render_priv)
137 free(event->render_priv);
140 void ass_free_style(ass_track_t *track, int sid)
142 ass_style_t *style = track->styles + sid;
146 free(style->FontName);
149 // ==============================================================================================
151 static void skip_spaces(char **str)
154 while ((*p == ' ') || (*p == '\t'))
159 static void rskip_spaces(char **str, char *limit)
162 while ((p >= limit) && ((*p == ' ') || (*p == '\t')))
168 * \brief find style by name
170 * \param name style name
171 * \return index in track->styles
172 * Returnes 0 if no styles found => expects at least 1 style.
173 * Parsing code always adds "Default" style in the end.
175 static int lookup_style(ass_track_t *track, char *name)
179 ++name; // FIXME: what does '*' really mean ?
180 for (i = track->n_styles - 1; i >= 0; --i) {
181 // FIXME: mb strcasecmp ?
182 if (strcmp(track->styles[i].Name, name) == 0)
185 i = track->default_style;
186 mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_NoStyleNamedXFoundUsingY,
187 track, name, track->styles[i].Name);
188 return i; // use the first style
191 static uint32_t string2color(char *p)
194 (void) strtocolor(&p, &tmp);
198 static long long string2timecode(char *p)
200 unsigned h, m, s, ms;
202 int res = sscanf(p, "%1d:%2d:%2d.%2d", &h, &m, &s, &ms);
204 mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_BadTimestamp);
207 tm = ((h * 60 + m) * 60 + s) * 1000 + ms * 10;
212 * \brief converts numpad-style align to align.
214 static int numpad2align(int val)
217 v = (val - 1) / 3; // 0, 1 or 2 for vertical alignment
220 res = ((val - 1) % 3) + 1; // horizontal alignment
225 #define NEXT(str,token) \
226 token = next_token(&str); \
229 #define ANYVAL(name,func) \
230 } else if (strcasecmp(tname, #name) == 0) { \
231 target->name = func(token); \
232 mp_msg(MSGT_ASS, MSGL_DBG2, "%s = %s\n", #name, token);
234 #define STRVAL(name) \
235 } else if (strcasecmp(tname, #name) == 0) { \
236 if (target->name != NULL) free(target->name); \
237 target->name = strdup(token); \
238 mp_msg(MSGT_ASS, MSGL_DBG2, "%s = %s\n", #name, token);
240 #define COLORVAL(name) ANYVAL(name,string2color)
241 #define INTVAL(name) ANYVAL(name,atoi)
242 #define FPVAL(name) ANYVAL(name,atof)
243 #define TIMEVAL(name) ANYVAL(name,string2timecode)
244 #define STYLEVAL(name) \
245 } else if (strcasecmp(tname, #name) == 0) { \
246 target->name = lookup_style(track, token); \
247 mp_msg(MSGT_ASS, MSGL_DBG2, "%s = %s\n", #name, token);
249 #define ALIAS(alias,name) \
250 if (strcasecmp(tname, #alias) == 0) {tname = #name;}
252 static char *next_token(char **str)
261 start = p; // start of the token
262 for (; (*p != '\0') && (*p != ','); ++p) {
265 *str = p; // eos found, str will point to '\0' at exit
268 *str = p + 1; // ',' found, str will point to the next char (beginning of the next token)
270 --p; // end of current token
271 rskip_spaces(&p, start);
273 p = start; // empty token
275 ++p; // the first space character, or '\0'
281 * \brief Parse the tail of Dialogue line
283 * \param event parsed data goes here
284 * \param str string to parse, zero-terminated
285 * \param n_ignored number of format options to skip at the beginning
287 static int process_event_tail(ass_track_t *track, ass_event_t *event,
288 char *str, int n_ignored)
294 ass_event_t *target = event;
296 char *format = strdup(track->event_format);
297 char *q = format; // format scanning pointer
299 if (track->n_styles == 0) {
300 // add "Default" style to the end
301 // will be used if track does not contain a default style (or even does not contain styles at all)
302 int sid = ass_alloc_style(track);
303 track->styles[sid].Name = strdup("Default");
304 track->styles[sid].FontName = strdup("Arial");
307 for (i = 0; i < n_ignored; ++i) {
313 if (strcasecmp(tname, "Text") == 0) {
315 event->Text = strdup(p);
316 if (*event->Text != 0) {
317 last = event->Text + strlen(event->Text) - 1;
318 if (last >= event->Text && *last == '\r')
321 mp_msg(MSGT_ASS, MSGL_DBG2, "Text = %s\n", event->Text);
322 event->Duration -= event->Start;
324 return 0; // "Text" is always the last
328 ALIAS(End, Duration) // temporarily store end timecode in event->Duration
346 * \brief Parse command line style overrides (--ass-force-style option)
347 * \param track track to apply overrides to
348 * The format for overrides is [StyleName.]Field=Value
350 void process_force_style(ass_track_t *track)
352 char **fs, *eq, *dt, *style, *tname, *token;
355 char **list = track->library->style_overrides;
360 for (fs = list; *fs; ++fs) {
361 eq = strrchr(*fs, '=');
367 if (!strcasecmp(*fs, "PlayResX"))
368 track->PlayResX = atoi(token);
369 else if (!strcasecmp(*fs, "PlayResY"))
370 track->PlayResY = atoi(token);
371 else if (!strcasecmp(*fs, "Timer"))
372 track->Timer = atof(token);
373 else if (!strcasecmp(*fs, "WrapStyle"))
374 track->WrapStyle = atoi(token);
375 else if (!strcasecmp(*fs, "ScaledBorderAndShadow"))
376 track->ScaledBorderAndShadow = parse_bool(token);
378 dt = strrchr(*fs, '.');
387 for (sid = 0; sid < track->n_styles; ++sid) {
389 || strcasecmp(track->styles[sid].Name, style) == 0) {
390 target = track->styles + sid;
393 COLORVAL(PrimaryColour)
394 COLORVAL(SecondaryColour)
395 COLORVAL(OutlineColour)
424 * \brief Parse the Style line
426 * \param str string to parse, zero-terminated
427 * Allocates a new style struct.
429 static int process_style(ass_track_t *track, char *str)
436 char *q; // format scanning pointer
441 if (!track->style_format) {
442 // no style format header
443 // probably an ancient script version
444 if (track->track_type == TRACK_TYPE_SSA)
445 track->style_format =
447 ("Name, Fontname, Fontsize, PrimaryColour, SecondaryColour,"
448 "TertiaryColour, BackColour, Bold, Italic, BorderStyle, Outline,"
449 "Shadow, Alignment, MarginL, MarginR, MarginV, AlphaLevel, Encoding");
451 track->style_format =
453 ("Name, Fontname, Fontsize, PrimaryColour, SecondaryColour,"
454 "OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut,"
455 "ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow,"
456 "Alignment, MarginL, MarginR, MarginV, Encoding");
459 q = format = strdup(track->style_format);
461 mp_msg(MSGT_ASS, MSGL_V, "[%p] Style: %s\n", track, str);
463 sid = ass_alloc_style(track);
465 style = track->styles + sid;
467 // fill style with some default values
468 style->ScaleX = 100.;
469 style->ScaleY = 100.;
475 // ALIAS(TertiaryColour,OutlineColour) // ignore TertiaryColour; it appears only in SSA, and is overridden by BackColour
479 if ((strcmp(target->Name, "Default") == 0)
480 || (strcmp(target->Name, "*Default") == 0))
481 track->default_style = sid;
483 COLORVAL(PrimaryColour)
484 COLORVAL(SecondaryColour)
485 COLORVAL(OutlineColour) // TertiaryColor
487 // SSA uses BackColour for both outline and shadow
488 // this will destroy SSA's TertiaryColour, but i'm not going to use it anyway
489 if (track->track_type == TRACK_TYPE_SSA)
490 target->OutlineColour = target->BackColour;
500 if (track->track_type == TRACK_TYPE_ASS)
501 target->Alignment = numpad2align(target->Alignment);
512 style->ScaleX /= 100.;
513 style->ScaleY /= 100.;
514 style->Bold = !!style->Bold;
515 style->Italic = !!style->Italic;
516 style->Underline = !!style->Underline;
518 style->Name = strdup("Default");
519 if (!style->FontName)
520 style->FontName = strdup("Arial");
521 // skip '@' at the start of the font name
522 if (*style->FontName == '@') {
524 style->FontName = strdup(p + 1);
532 static int process_styles_line(ass_track_t *track, char *str)
534 if (!strncmp(str, "Format:", 7)) {
537 track->style_format = strdup(p);
538 mp_msg(MSGT_ASS, MSGL_DBG2, "Style format: %s\n",
539 track->style_format);
540 } else if (!strncmp(str, "Style:", 6)) {
543 process_style(track, p);
548 static int process_info_line(ass_track_t *track, char *str)
550 if (!strncmp(str, "PlayResX:", 9)) {
551 track->PlayResX = atoi(str + 9);
552 } else if (!strncmp(str, "PlayResY:", 9)) {
553 track->PlayResY = atoi(str + 9);
554 } else if (!strncmp(str, "Timer:", 6)) {
555 track->Timer = atof(str + 6);
556 } else if (!strncmp(str, "WrapStyle:", 10)) {
557 track->WrapStyle = atoi(str + 10);
558 } else if (!strncmp(str, "ScaledBorderAndShadow:", 22)) {
559 track->ScaledBorderAndShadow = parse_bool(str + 22);
564 static int process_events_line(ass_track_t *track, char *str)
566 if (!strncmp(str, "Format:", 7)) {
569 track->event_format = strdup(p);
570 mp_msg(MSGT_ASS, MSGL_DBG2, "Event format: %s\n",
571 track->event_format);
572 } else if (!strncmp(str, "Dialogue:", 9)) {
573 // This should never be reached for embedded subtitles.
574 // They have slightly different format and are parsed in ass_process_chunk,
575 // called directly from demuxer
582 eid = ass_alloc_event(track);
583 event = track->events + eid;
585 process_event_tail(track, event, str, 0);
587 mp_msg(MSGT_ASS, MSGL_V, "Not understood: %s \n", str);
592 // Copied from mkvtoolnix
593 static unsigned char *decode_chars(unsigned char c1, unsigned char c2,
594 unsigned char c3, unsigned char c4,
595 unsigned char *dst, int cnt)
598 unsigned char bytes[3];
602 ((c1 - 33) << 18) + ((c2 - 33) << 12) + ((c3 - 33) << 6) + (c4 -
604 bytes[2] = value & 0xff;
605 bytes[1] = (value & 0xff00) >> 8;
606 bytes[0] = (value & 0xff0000) >> 16;
608 for (i = 0; i < cnt; ++i)
613 static int decode_font(ass_track_t *track)
618 int size; // original size
619 int dsize; // decoded size
620 unsigned char *buf = 0;
622 mp_msg(MSGT_ASS, MSGL_V, "font: %d bytes encoded data \n",
623 track->parser_priv->fontdata_used);
624 size = track->parser_priv->fontdata_used;
626 mp_msg(MSGT_ASS, MSGL_ERR, MSGTR_LIBASS_BadEncodedDataSize);
627 goto error_decode_font;
629 buf = malloc(size / 4 * 3 + 2);
631 for (i = 0, p = (unsigned char *) track->parser_priv->fontdata;
632 i < size / 4; i++, p += 4) {
633 q = decode_chars(p[0], p[1], p[2], p[3], q, 3);
636 q = decode_chars(p[0], p[1], 0, 0, q, 1);
637 } else if (size % 4 == 3) {
638 q = decode_chars(p[0], p[1], p[2], 0, q, 2);
641 assert(dsize <= size / 4 * 3 + 2);
643 if (track->library->extract_fonts) {
644 ass_add_font(track->library, track->parser_priv->fontname,
645 (char *) buf, dsize);
652 free(track->parser_priv->fontname);
653 free(track->parser_priv->fontdata);
654 track->parser_priv->fontname = 0;
655 track->parser_priv->fontdata = 0;
656 track->parser_priv->fontdata_size = 0;
657 track->parser_priv->fontdata_used = 0;
661 static int process_fonts_line(ass_track_t *track, char *str)
665 if (!strncmp(str, "fontname:", 9)) {
668 if (track->parser_priv->fontname) {
671 track->parser_priv->fontname = strdup(p);
672 mp_msg(MSGT_ASS, MSGL_V, "fontname: %s\n",
673 track->parser_priv->fontname);
677 if (!track->parser_priv->fontname) {
678 mp_msg(MSGT_ASS, MSGL_V, "Not understood: %s \n", str);
684 mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FontLineTooLong, len, str);
687 if (track->parser_priv->fontdata_used + len >
688 track->parser_priv->fontdata_size) {
689 track->parser_priv->fontdata_size += 100 * 1024;
690 track->parser_priv->fontdata =
691 realloc(track->parser_priv->fontdata,
692 track->parser_priv->fontdata_size);
694 memcpy(track->parser_priv->fontdata + track->parser_priv->fontdata_used,
696 track->parser_priv->fontdata_used += len;
702 * \brief Parse a header line
704 * \param str string to parse, zero-terminated
706 static int process_line(ass_track_t *track, char *str)
708 if (!strncasecmp(str, "[Script Info]", 13)) {
709 track->parser_priv->state = PST_INFO;
710 } else if (!strncasecmp(str, "[V4 Styles]", 11)) {
711 track->parser_priv->state = PST_STYLES;
712 track->track_type = TRACK_TYPE_SSA;
713 } else if (!strncasecmp(str, "[V4+ Styles]", 12)) {
714 track->parser_priv->state = PST_STYLES;
715 track->track_type = TRACK_TYPE_ASS;
716 } else if (!strncasecmp(str, "[Events]", 8)) {
717 track->parser_priv->state = PST_EVENTS;
718 } else if (!strncasecmp(str, "[Fonts]", 7)) {
719 track->parser_priv->state = PST_FONTS;
721 switch (track->parser_priv->state) {
723 process_info_line(track, str);
726 process_styles_line(track, str);
729 process_events_line(track, str);
732 process_fonts_line(track, str);
739 // there is no explicit end-of-font marker in ssa/ass
740 if ((track->parser_priv->state != PST_FONTS)
741 && (track->parser_priv->fontname))
747 static int process_text(ass_track_t *track, char *str)
753 if ((*p == '\r') || (*p == '\n'))
755 else if (p[0] == '\xef' && p[1] == '\xbb' && p[2] == '\xbf')
756 p += 3; // U+FFFE (BOM)
760 for (q = p; ((*q != '\0') && (*q != '\r') && (*q != '\n')); ++q) {
766 process_line(track, p);
775 * \brief Process a chunk of subtitle stream data.
777 * \param data string to parse
778 * \param size length of data
780 void ass_process_data(ass_track_t *track, char *data, int size)
782 char *str = malloc(size + 1);
784 memcpy(str, data, size);
787 mp_msg(MSGT_ASS, MSGL_V, "event: %s\n", str);
788 process_text(track, str);
793 * \brief Process CodecPrivate section of subtitle stream
795 * \param data string to parse
796 * \param size length of data
797 CodecPrivate section contains [Stream Info] and [V4+ Styles] ([V4 Styles] for SSA) sections
799 void ass_process_codec_private(ass_track_t *track, char *data, int size)
801 ass_process_data(track, data, size);
803 if (!track->event_format) {
804 // probably an mkv produced by ancient mkvtoolnix
805 // such files don't have [Events] and Format: headers
806 track->parser_priv->state = PST_EVENTS;
807 if (track->track_type == TRACK_TYPE_SSA)
808 track->event_format =
810 ("Format: Marked, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text");
812 track->event_format =
814 ("Format: Layer, Start, End, Style, Actor, MarginL, MarginR, MarginV, Effect, Text");
817 process_force_style(track);
820 static int check_duplicate_event(ass_track_t *track, int ReadOrder)
823 for (i = 0; i < track->n_events - 1; ++i) // ignoring last event, it is the one we are comparing with
824 if (track->events[i].ReadOrder == ReadOrder)
830 * \brief Process a chunk of subtitle stream data. In Matroska, this contains exactly 1 event (or a commentary).
832 * \param data string to parse
833 * \param size length of data
834 * \param timecode starting time of the event (milliseconds)
835 * \param duration duration of the event (milliseconds)
837 void ass_process_chunk(ass_track_t *track, char *data, int size,
838 long long timecode, long long duration)
846 if (!track->event_format) {
847 mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_EventFormatHeaderMissing);
851 str = malloc(size + 1);
852 memcpy(str, data, size);
854 mp_msg(MSGT_ASS, MSGL_V, "event at %" PRId64 ", +%" PRId64 ": %s \n",
855 (int64_t) timecode, (int64_t) duration, str);
857 eid = ass_alloc_event(track);
858 event = track->events + eid;
864 event->ReadOrder = atoi(token);
865 if (check_duplicate_event(track, event->ReadOrder))
869 event->Layer = atoi(token);
871 process_event_tail(track, event, p, 3);
873 event->Start = timecode;
874 event->Duration = duration;
881 ass_free_event(track, eid);
887 /** \brief recode buffer to utf-8
888 * constraint: codepage != 0
889 * \param data pointer to text buffer
890 * \param size buffer size
891 * \return a pointer to recoded buffer, caller is responsible for freeing it
893 static char *sub_recode(char *data, size_t size, char *codepage)
896 char *tocp = "UTF-8";
901 const char *cp_tmp = codepage;
903 char enca_lang[3], enca_fallback[100];
904 if (sscanf(codepage, "enca:%2s:%99s", enca_lang, enca_fallback) == 2
905 || sscanf(codepage, "ENCA:%2s:%99s", enca_lang,
906 enca_fallback) == 2) {
908 guess_buffer_cp((unsigned char *) data, size, enca_lang,
912 if ((icdsc = iconv_open(tocp, cp_tmp)) != (iconv_t) (-1)) {
913 mp_msg(MSGT_ASS, MSGL_V, "LIBSUB: opened iconv descriptor.\n");
915 mp_msg(MSGT_ASS, MSGL_ERR,
916 MSGTR_LIBASS_ErrorOpeningIconvDescriptor);
922 size_t oleft = size - 1;
928 outbuf = malloc(osize);
934 rc = iconv(icdsc, &ip, &ileft, &op, &oleft);
935 else { // clear the conversion state and leave
937 rc = iconv(icdsc, NULL, NULL, &op, &oleft);
939 if (rc == (size_t) (-1)) {
940 if (errno == E2BIG) {
941 size_t offset = op - outbuf;
942 outbuf = (char *) realloc(outbuf, osize + size);
943 op = outbuf + offset;
947 mp_msg(MSGT_ASS, MSGL_WARN,
948 MSGTR_LIBASS_ErrorRecodingFile);
954 outbuf[osize - oleft - 1] = 0;
957 if (icdsc != (iconv_t) (-1)) {
958 (void) iconv_close(icdsc);
959 icdsc = (iconv_t) (-1);
960 mp_msg(MSGT_ASS, MSGL_V, "LIBSUB: closed iconv descriptor.\n");
968 * \brief read file contents into newly allocated buffer
969 * \param fname file name
970 * \param bufsize out: file size
971 * \return pointer to file contents. Caller is responsible for its deallocation.
973 static char *read_file(char *fname, size_t *bufsize)
980 FILE *fp = fopen(fname, "rb");
982 mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FopenFailed, fname);
985 res = fseek(fp, 0, SEEK_END);
987 mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FseekFailed, fname);
995 if (sz > 10 * 1024 * 1024) {
996 mp_msg(MSGT_ASS, MSGL_INFO,
997 MSGTR_LIBASS_RefusingToLoadSubtitlesLargerThan10M, fname);
1002 mp_msg(MSGT_ASS, MSGL_V, "file size: %ld\n", sz);
1004 buf = malloc(sz + 1);
1008 res = fread(buf + bytes_read, 1, sz - bytes_read, fp);
1010 mp_msg(MSGT_ASS, MSGL_INFO, MSGTR_LIBASS_ReadFailed, errno,
1017 } while (sz - bytes_read > 0);
1027 * \param buf pointer to subtitle text in utf-8
1029 static ass_track_t *parse_memory(ass_library_t *library, char *buf)
1034 track = ass_new_track(library);
1037 process_text(track, buf);
1039 // external SSA/ASS subs does not have ReadOrder field
1040 for (i = 0; i < track->n_events; ++i)
1041 track->events[i].ReadOrder = i;
1043 // there is no explicit end-of-font marker in ssa/ass
1044 if (track->parser_priv->fontname)
1047 if (track->track_type == TRACK_TYPE_UNKNOWN) {
1048 ass_free_track(track);
1052 process_force_style(track);
1058 * \brief Read subtitles from memory.
1059 * \param library libass library object
1060 * \param buf pointer to subtitles text
1061 * \param bufsize size of buffer
1062 * \param codepage recode buffer contents from given codepage
1063 * \return newly allocated track
1065 ass_track_t *ass_read_memory(ass_library_t *library, char *buf,
1066 size_t bufsize, char *codepage)
1076 buf = sub_recode(buf, bufsize, codepage);
1082 track = parse_memory(library, buf);
1088 mp_msg(MSGT_ASS, MSGL_INFO, MSGTR_LIBASS_AddedSubtitleFileMemory,
1089 track->n_styles, track->n_events);
1093 char *read_file_recode(char *fname, char *codepage, size_t *size)
1098 buf = read_file(fname, &bufsize);
1103 char *tmpbuf = sub_recode(buf, bufsize, codepage);
1115 * \brief Read subtitles from file.
1116 * \param library libass library object
1117 * \param fname file name
1118 * \param codepage recode buffer contents from given codepage
1119 * \return newly allocated track
1121 ass_track_t *ass_read_file(ass_library_t *library, char *fname,
1128 buf = read_file_recode(fname, codepage, &bufsize);
1131 track = parse_memory(library, buf);
1136 track->name = strdup(fname);
1138 mp_msg(MSGT_ASS, MSGL_INFO, MSGTR_LIBASS_AddedSubtitleFileFname, fname,
1139 track->n_styles, track->n_events);
1141 // dump_events(forced_tid);
1146 * \brief read styles from file into already initialized track
1148 int ass_read_styles(ass_track_t *track, char *fname, char *codepage)
1151 parser_state_t old_state;
1154 buf = read_file(fname, &sz);
1160 tmpbuf = sub_recode(buf, sz, codepage);
1168 old_state = track->parser_priv->state;
1169 track->parser_priv->state = PST_STYLES;
1170 process_text(track, buf);
1171 track->parser_priv->state = old_state;
1176 long long ass_step_sub(ass_track_t *track, long long now, int movement)
1182 if (track->n_events == 0)
1187 (i < track->n_events)
1189 ((long long) (track->events[i].Start +
1190 track->events[i].Duration) <= now); ++i) {
1192 for (i = track->n_events - 1;
1193 (i >= 0) && ((long long) (track->events[i].Start) > now);
1197 // -1 and n_events are ok
1199 assert(i <= track->n_events);
1203 if (i >= track->n_events)
1204 i = track->n_events - 1;
1205 return ((long long) track->events[i].Start) - now;
1208 ass_track_t *ass_new_track(ass_library_t *library)
1210 ass_track_t *track = calloc(1, sizeof(ass_track_t));
1211 track->library = library;
1212 track->ScaledBorderAndShadow = 1;
1213 track->parser_priv = calloc(1, sizeof(parser_priv_t));