• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * SSA/ASS spliting functions
3  * Copyright (c) 2010  Aurelien Jacobs <aurel@gnuage.org>
4  *
5  * This file is part of FFmpeg.
6  *
7  * FFmpeg is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * FFmpeg is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with FFmpeg; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20  */
21 
22 #include <limits.h>
23 #include <stddef.h>
24 #include <stdint.h>
25 #include <stdio.h>
26 #include <string.h>
27 
28 #include "libavutil/error.h"
29 #include "libavutil/macros.h"
30 #include "libavutil/mem.h"
31 #include "ass_split.h"
32 
33 typedef enum {
34     ASS_STR,
35     ASS_INT,
36     ASS_FLT,
37     ASS_COLOR,
38     ASS_TIMESTAMP,
39     ASS_ALGN,
40 } ASSFieldType;
41 
42 typedef struct {
43     const char *name;
44     int type;
45     int offset;
46 } ASSFields;
47 
48 typedef struct {
49     const char *section;
50     const char *format_header;
51     const char *fields_header;
52     int         size;
53     int         offset;
54     int         offset_count;
55     ASSFields   fields[24];
56 } ASSSection;
57 
58 static const ASSSection ass_sections[] = {
59     { .section       = "Script Info",
60       .offset        = offsetof(ASS, script_info),
61       .fields = {{"ScriptType", ASS_STR, offsetof(ASSScriptInfo, script_type)},
62                  {"Collisions", ASS_STR, offsetof(ASSScriptInfo, collisions) },
63                  {"PlayResX",   ASS_INT, offsetof(ASSScriptInfo, play_res_x) },
64                  {"PlayResY",   ASS_INT, offsetof(ASSScriptInfo, play_res_y) },
65                  {"Timer",      ASS_FLT, offsetof(ASSScriptInfo, timer)      },
66                  {0},
67         }
68     },
69     { .section       = "V4+ Styles",
70       .format_header = "Format",
71       .fields_header = "Style",
72       .size          = sizeof(ASSStyle),
73       .offset        = offsetof(ASS, styles),
74       .offset_count  = offsetof(ASS, styles_count),
75       .fields = {{"Name",            ASS_STR,   offsetof(ASSStyle, name)           },
76                  {"Fontname",        ASS_STR,   offsetof(ASSStyle, font_name)      },
77                  {"Fontsize",        ASS_INT,   offsetof(ASSStyle, font_size)      },
78                  {"PrimaryColour",   ASS_COLOR, offsetof(ASSStyle, primary_color)  },
79                  {"SecondaryColour", ASS_COLOR, offsetof(ASSStyle, secondary_color)},
80                  {"OutlineColour",   ASS_COLOR, offsetof(ASSStyle, outline_color)  },
81                  {"BackColour",      ASS_COLOR, offsetof(ASSStyle, back_color)     },
82                  {"Bold",            ASS_INT,   offsetof(ASSStyle, bold)           },
83                  {"Italic",          ASS_INT,   offsetof(ASSStyle, italic)         },
84                  {"Underline",       ASS_INT,   offsetof(ASSStyle, underline)      },
85                  {"StrikeOut",       ASS_INT,   offsetof(ASSStyle, strikeout)      },
86                  {"ScaleX",          ASS_FLT,   offsetof(ASSStyle, scalex)         },
87                  {"ScaleY",          ASS_FLT,   offsetof(ASSStyle, scaley)         },
88                  {"Spacing",         ASS_FLT,   offsetof(ASSStyle, spacing)        },
89                  {"Angle",           ASS_FLT,   offsetof(ASSStyle, angle)          },
90                  {"BorderStyle",     ASS_INT,   offsetof(ASSStyle, border_style)   },
91                  {"Outline",         ASS_FLT,   offsetof(ASSStyle, outline)        },
92                  {"Shadow",          ASS_FLT,   offsetof(ASSStyle, shadow)         },
93                  {"Alignment",       ASS_INT,   offsetof(ASSStyle, alignment)      },
94                  {"MarginL",         ASS_INT,   offsetof(ASSStyle, margin_l)       },
95                  {"MarginR",         ASS_INT,   offsetof(ASSStyle, margin_r)       },
96                  {"MarginV",         ASS_INT,   offsetof(ASSStyle, margin_v)       },
97                  {"Encoding",        ASS_INT,   offsetof(ASSStyle, encoding)       },
98                  {0},
99         }
100     },
101     { .section       = "V4 Styles",
102       .format_header = "Format",
103       .fields_header = "Style",
104       .size          = sizeof(ASSStyle),
105       .offset        = offsetof(ASS, styles),
106       .offset_count  = offsetof(ASS, styles_count),
107       .fields = {{"Name",            ASS_STR,   offsetof(ASSStyle, name)           },
108                  {"Fontname",        ASS_STR,   offsetof(ASSStyle, font_name)      },
109                  {"Fontsize",        ASS_INT,   offsetof(ASSStyle, font_size)      },
110                  {"PrimaryColour",   ASS_COLOR, offsetof(ASSStyle, primary_color)  },
111                  {"SecondaryColour", ASS_COLOR, offsetof(ASSStyle, secondary_color)},
112                  {"TertiaryColour",  ASS_COLOR, offsetof(ASSStyle, outline_color)  },
113                  {"BackColour",      ASS_COLOR, offsetof(ASSStyle, back_color)     },
114                  {"Bold",            ASS_INT,   offsetof(ASSStyle, bold)           },
115                  {"Italic",          ASS_INT,   offsetof(ASSStyle, italic)         },
116                  {"BorderStyle",     ASS_INT,   offsetof(ASSStyle, border_style)   },
117                  {"Outline",         ASS_FLT,   offsetof(ASSStyle, outline)        },
118                  {"Shadow",          ASS_FLT,   offsetof(ASSStyle, shadow)         },
119                  {"Alignment",       ASS_ALGN,  offsetof(ASSStyle, alignment)      },
120                  {"MarginL",         ASS_INT,   offsetof(ASSStyle, margin_l)       },
121                  {"MarginR",         ASS_INT,   offsetof(ASSStyle, margin_r)       },
122                  {"MarginV",         ASS_INT,   offsetof(ASSStyle, margin_v)       },
123                  {"AlphaLevel",      ASS_INT,   offsetof(ASSStyle, alpha_level)    },
124                  {"Encoding",        ASS_INT,   offsetof(ASSStyle, encoding)       },
125                  {0},
126         }
127     },
128     { .section       = "Events",
129       .format_header = "Format",
130       .fields_header = "Dialogue",
131       .size          = sizeof(ASSDialog),
132       .offset        = offsetof(ASS, dialogs),
133       .offset_count  = offsetof(ASS, dialogs_count),
134       .fields = {{"Layer",   ASS_INT,        offsetof(ASSDialog, layer)   },
135                  {"Start",   ASS_TIMESTAMP,  offsetof(ASSDialog, start)   },
136                  {"End",     ASS_TIMESTAMP,  offsetof(ASSDialog, end)     },
137                  {"Style",   ASS_STR,        offsetof(ASSDialog, style)   },
138                  {"Name",    ASS_STR,        offsetof(ASSDialog, name)    },
139                  {"MarginL", ASS_INT,        offsetof(ASSDialog, margin_l)},
140                  {"MarginR", ASS_INT,        offsetof(ASSDialog, margin_r)},
141                  {"MarginV", ASS_INT,        offsetof(ASSDialog, margin_v)},
142                  {"Effect",  ASS_STR,        offsetof(ASSDialog, effect)  },
143                  {"Text",    ASS_STR,        offsetof(ASSDialog, text)    },
144                  {0},
145         }
146     },
147 };
148 
149 
150 typedef int (*ASSConvertFunc)(void *dest, const char *buf, int len);
151 
convert_str(void * dest,const char * buf,int len)152 static int convert_str(void *dest, const char *buf, int len)
153 {
154     char *str = av_malloc(len + 1);
155     if (str) {
156         memcpy(str, buf, len);
157         str[len] = 0;
158         if (*(void **)dest)
159             av_free(*(void **)dest);
160         *(char **)dest = str;
161     }
162     return !str;
163 }
convert_int(void * dest,const char * buf,int len)164 static int convert_int(void *dest, const char *buf, int len)
165 {
166     return sscanf(buf, "%d", (int *)dest) == 1;
167 }
convert_flt(void * dest,const char * buf,int len)168 static int convert_flt(void *dest, const char *buf, int len)
169 {
170     return sscanf(buf, "%f", (float *)dest) == 1;
171 }
convert_color(void * dest,const char * buf,int len)172 static int convert_color(void *dest, const char *buf, int len)
173 {
174     return sscanf(buf, "&H%8x", (int *)dest) == 1 ||
175            sscanf(buf, "%d",    (int *)dest) == 1;
176 }
convert_timestamp(void * dest,const char * buf,int len)177 static int convert_timestamp(void *dest, const char *buf, int len)
178 {
179     int c, h, m, s, cs;
180     if ((c = sscanf(buf, "%d:%02d:%02d.%02d", &h, &m, &s, &cs)) == 4)
181         *(int *)dest = 360000*h + 6000*m + 100*s + cs;
182     return c == 4;
183 }
convert_alignment(void * dest,const char * buf,int len)184 static int convert_alignment(void *dest, const char *buf, int len)
185 {
186     int a;
187     if (sscanf(buf, "%d", &a) == 1) {
188         /* convert V4 Style alignment to V4+ Style */
189         *(int *)dest = a + ((a&4) >> 1) - 5*!!(a&8);
190         return 1;
191     }
192     return 0;
193 }
194 
195 static const ASSConvertFunc convert_func[] = {
196     [ASS_STR]       = convert_str,
197     [ASS_INT]       = convert_int,
198     [ASS_FLT]       = convert_flt,
199     [ASS_COLOR]     = convert_color,
200     [ASS_TIMESTAMP] = convert_timestamp,
201     [ASS_ALGN]      = convert_alignment,
202 };
203 
204 
205 struct ASSSplitContext {
206     ASS ass;
207     int current_section;
208     int field_number[FF_ARRAY_ELEMS(ass_sections)];
209     int *field_order[FF_ARRAY_ELEMS(ass_sections)];
210 };
211 
212 
realloc_section_array(ASSSplitContext * ctx)213 static uint8_t *realloc_section_array(ASSSplitContext *ctx)
214 {
215     const ASSSection *section = &ass_sections[ctx->current_section];
216     int *count = (int *)((uint8_t *)&ctx->ass + section->offset_count);
217     void **section_ptr = (void **)((uint8_t *)&ctx->ass + section->offset);
218     uint8_t *tmp = av_realloc_array(*section_ptr, (*count+1), section->size);
219     if (!tmp)
220         return NULL;
221     *section_ptr = tmp;
222     tmp += *count * section->size;
223     memset(tmp, 0, section->size);
224     (*count)++;
225     return tmp;
226 }
227 
is_eol(char buf)228 static inline int is_eol(char buf)
229 {
230     return buf == '\r' || buf == '\n' || buf == 0;
231 }
232 
skip_space(const char * buf)233 static inline const char *skip_space(const char *buf)
234 {
235     while (*buf == ' ')
236         buf++;
237     return buf;
238 }
239 
get_default_field_orders(const ASSSection * section,int * number)240 static int *get_default_field_orders(const ASSSection *section, int *number)
241 {
242     int i;
243     int *order = av_malloc_array(FF_ARRAY_ELEMS(section->fields), sizeof(*order));
244 
245     if (!order)
246         return NULL;
247     for (i = 0; section->fields[i].name; i++)
248         order[i] = i;
249     *number = i;
250     while (i < FF_ARRAY_ELEMS(section->fields))
251         order[i++] = -1;
252     return order;
253 }
254 
ass_split_section(ASSSplitContext * ctx,const char * buf)255 static const char *ass_split_section(ASSSplitContext *ctx, const char *buf)
256 {
257     const ASSSection *section = &ass_sections[ctx->current_section];
258     int *number = &ctx->field_number[ctx->current_section];
259     int *order = ctx->field_order[ctx->current_section];
260     int i, len;
261 
262     while (buf && *buf) {
263         if (buf[0] == '[') {
264             ctx->current_section = -1;
265             break;
266         }
267         if (buf[0] == ';' || (buf[0] == '!' && buf[1] == ':'))
268             goto next_line; // skip comments
269 
270         len = strcspn(buf, ":\r\n");
271         if (buf[len] == ':' &&
272             (!section->fields_header || strncmp(buf, section->fields_header, len))) {
273             for (i = 0; i < FF_ARRAY_ELEMS(ass_sections); i++) {
274                 if (ass_sections[i].fields_header &&
275                     !strncmp(buf, ass_sections[i].fields_header, len)) {
276                     ctx->current_section = i;
277                     section = &ass_sections[ctx->current_section];
278                     number = &ctx->field_number[ctx->current_section];
279                     order = ctx->field_order[ctx->current_section];
280                     break;
281                 }
282             }
283         }
284         if (section->format_header && !order) {
285             len = strlen(section->format_header);
286             if (!strncmp(buf, section->format_header, len) && buf[len] == ':') {
287                 buf += len + 1;
288                 while (!is_eol(*buf)) {
289                     buf = skip_space(buf);
290                     len = strcspn(buf, ", \r\n");
291                     if (av_reallocp_array(&order, (*number + 1), sizeof(*order)) != 0)
292                         return NULL;
293 
294                     order[*number] = -1;
295                     for (i=0; section->fields[i].name; i++)
296                         if (!strncmp(buf, section->fields[i].name, len)) {
297                             order[*number] = i;
298                             break;
299                         }
300                     (*number)++;
301                     buf = skip_space(buf + len + (buf[len] == ','));
302                 }
303                 ctx->field_order[ctx->current_section] = order;
304                 goto next_line;
305             }
306         }
307         if (section->fields_header) {
308             len = strlen(section->fields_header);
309             if (!strncmp(buf, section->fields_header, len) && buf[len] == ':') {
310                 uint8_t *ptr, *struct_ptr = realloc_section_array(ctx);
311                 if (!struct_ptr)  return NULL;
312 
313                 /* No format header line found so far, assume default */
314                 if (!order) {
315                     order = get_default_field_orders(section, number);
316                     if (!order)
317                         return NULL;
318                     ctx->field_order[ctx->current_section] = order;
319                 }
320 
321                 buf += len + 1;
322                 for (i=0; !is_eol(*buf) && i < *number; i++) {
323                     int last = i == *number - 1;
324                     buf = skip_space(buf);
325                     len = strcspn(buf, last ? "\r\n" : ",\r\n");
326                     if (order[i] >= 0) {
327                         ASSFieldType type = section->fields[order[i]].type;
328                         ptr = struct_ptr + section->fields[order[i]].offset;
329                         convert_func[type](ptr, buf, len);
330                     }
331                     buf += len;
332                     if (!last && *buf) buf++;
333                     buf = skip_space(buf);
334                 }
335             }
336         } else {
337             len = strcspn(buf, ":\r\n");
338             if (buf[len] == ':') {
339                 for (i=0; section->fields[i].name; i++)
340                     if (!strncmp(buf, section->fields[i].name, len)) {
341                         ASSFieldType type = section->fields[i].type;
342                         uint8_t *ptr = (uint8_t *)&ctx->ass + section->offset;
343                         ptr += section->fields[i].offset;
344                         buf = skip_space(buf + len + 1);
345                         convert_func[type](ptr, buf, strcspn(buf, "\r\n"));
346                         break;
347                     }
348             }
349         }
350 next_line:
351         buf += strcspn(buf, "\n");
352         buf += !!*buf;
353     }
354     return buf;
355 }
356 
ass_split(ASSSplitContext * ctx,const char * buf)357 static int ass_split(ASSSplitContext *ctx, const char *buf)
358 {
359     char c, section[16];
360     int i;
361 
362     if (ctx->current_section >= 0)
363         buf = ass_split_section(ctx, buf);
364 
365     while (buf && *buf) {
366         if (sscanf(buf, "[%15[0-9A-Za-z+ ]]%c", section, &c) == 2) {
367             buf += strcspn(buf, "\n");
368             buf += !!*buf;
369             for (i=0; i<FF_ARRAY_ELEMS(ass_sections); i++)
370                 if (!strcmp(section, ass_sections[i].section)) {
371                     ctx->current_section = i;
372                     buf = ass_split_section(ctx, buf);
373                 }
374         } else {
375             buf += strcspn(buf, "\n");
376             buf += !!*buf;
377         }
378     }
379     return buf ? 0 : AVERROR_INVALIDDATA;
380 }
381 
ff_ass_split(const char * buf)382 ASSSplitContext *ff_ass_split(const char *buf)
383 {
384     ASSSplitContext *ctx = av_mallocz(sizeof(*ctx));
385     if (!ctx)
386         return NULL;
387     if (buf && !strncmp(buf, "\xef\xbb\xbf", 3)) // Skip UTF-8 BOM header
388         buf += 3;
389     ctx->current_section = -1;
390     if (ass_split(ctx, buf) < 0) {
391         ff_ass_split_free(ctx);
392         return NULL;
393     }
394     return ctx;
395 }
396 
free_section(ASSSplitContext * ctx,const ASSSection * section)397 static void free_section(ASSSplitContext *ctx, const ASSSection *section)
398 {
399     uint8_t *ptr = (uint8_t *)&ctx->ass + section->offset;
400     int i, j, *count, c = 1;
401 
402     if (section->format_header) {
403         ptr   = *(void **)ptr;
404         count = (int *)((uint8_t *)&ctx->ass + section->offset_count);
405     } else
406         count = &c;
407 
408     if (ptr)
409         for (i=0; i<*count; i++, ptr += section->size)
410             for (j=0; section->fields[j].name; j++) {
411                 const ASSFields *field = &section->fields[j];
412                 if (field->type == ASS_STR)
413                     av_freep(ptr + field->offset);
414             }
415     *count = 0;
416 
417     if (section->format_header)
418         av_freep((uint8_t *)&ctx->ass + section->offset);
419 }
420 
ff_ass_free_dialog(ASSDialog ** dialogp)421 void ff_ass_free_dialog(ASSDialog **dialogp)
422 {
423     ASSDialog *dialog = *dialogp;
424     if (!dialog)
425         return;
426     av_freep(&dialog->style);
427     av_freep(&dialog->name);
428     av_freep(&dialog->effect);
429     av_freep(&dialog->text);
430     av_freep(dialogp);
431 }
432 
ff_ass_split_dialog(ASSSplitContext * ctx,const char * buf)433 ASSDialog *ff_ass_split_dialog(ASSSplitContext *ctx, const char *buf)
434 {
435     int i;
436     static const ASSFields fields[] = {
437         {"ReadOrder", ASS_INT, offsetof(ASSDialog, readorder)},
438         {"Layer",     ASS_INT, offsetof(ASSDialog, layer)    },
439         {"Style",     ASS_STR, offsetof(ASSDialog, style)    },
440         {"Name",      ASS_STR, offsetof(ASSDialog, name)     },
441         {"MarginL",   ASS_INT, offsetof(ASSDialog, margin_l) },
442         {"MarginR",   ASS_INT, offsetof(ASSDialog, margin_r) },
443         {"MarginV",   ASS_INT, offsetof(ASSDialog, margin_v) },
444         {"Effect",    ASS_STR, offsetof(ASSDialog, effect)   },
445         {"Text",      ASS_STR, offsetof(ASSDialog, text)     },
446     };
447 
448     ASSDialog *dialog = av_mallocz(sizeof(*dialog));
449     if (!dialog)
450         return NULL;
451 
452     for (i = 0; i < FF_ARRAY_ELEMS(fields); i++) {
453         size_t len;
454         const int last = i == FF_ARRAY_ELEMS(fields) - 1;
455         const ASSFieldType type = fields[i].type;
456         uint8_t *ptr = (uint8_t *)dialog + fields[i].offset;
457         buf = skip_space(buf);
458         len = last ? strlen(buf) : strcspn(buf, ",");
459         if (len >= INT_MAX) {
460             ff_ass_free_dialog(&dialog);
461             return NULL;
462         }
463         convert_func[type](ptr, buf, len);
464         buf += len;
465         if (*buf) buf++;
466     }
467     return dialog;
468 }
469 
ff_ass_split_free(ASSSplitContext * ctx)470 void ff_ass_split_free(ASSSplitContext *ctx)
471 {
472     if (ctx) {
473         int i;
474         for (i=0; i<FF_ARRAY_ELEMS(ass_sections); i++) {
475             free_section(ctx, &ass_sections[i]);
476             av_freep(&(ctx->field_order[i]));
477         }
478         av_free(ctx);
479     }
480 }
481 
482 
ff_ass_split_override_codes(const ASSCodesCallbacks * callbacks,void * priv,const char * buf)483 int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv,
484                                 const char *buf)
485 {
486     const char *text = NULL;
487     char new_line[2];
488     int text_len = 0;
489 
490     while (buf && *buf) {
491         if (text && callbacks->text &&
492             (sscanf(buf, "\\%1[nN]", new_line) == 1 ||
493              !strncmp(buf, "{\\", 2))) {
494             callbacks->text(priv, text, text_len);
495             text = NULL;
496         }
497         if (sscanf(buf, "\\%1[nN]", new_line) == 1) {
498             if (callbacks->new_line)
499                 callbacks->new_line(priv, new_line[0] == 'N');
500             buf += 2;
501         } else if (!strncmp(buf, "{\\", 2)) {
502             buf++;
503             while (*buf == '\\') {
504                 char style[2], c[2], sep[2], c_num[2] = "0", tmp[128] = {0};
505                 unsigned int color = 0xFFFFFFFF;
506                 int len, size = -1, an = -1, alpha = -1;
507                 int x1, y1, x2, y2, t1 = -1, t2 = -1;
508                 if (sscanf(buf, "\\%1[bisu]%1[01\\}]%n", style, c, &len) > 1) {
509                     int close = c[0] == '0' ? 1 : c[0] == '1' ? 0 : -1;
510                     len += close != -1;
511                     if (callbacks->style)
512                         callbacks->style(priv, style[0], close);
513                 } else if (sscanf(buf, "\\c%1[\\}]%n", sep, &len) > 0 ||
514                            sscanf(buf, "\\c&H%X&%1[\\}]%n", &color, sep, &len) > 1 ||
515                            sscanf(buf, "\\%1[1234]c%1[\\}]%n", c_num, sep, &len) > 1 ||
516                            sscanf(buf, "\\%1[1234]c&H%X&%1[\\}]%n", c_num, &color, sep, &len) > 2) {
517                     if (callbacks->color)
518                         callbacks->color(priv, color, c_num[0] - '0');
519                 } else if (sscanf(buf, "\\alpha%1[\\}]%n", sep, &len) > 0 ||
520                            sscanf(buf, "\\alpha&H%2X&%1[\\}]%n", &alpha, sep, &len) > 1 ||
521                            sscanf(buf, "\\%1[1234]a%1[\\}]%n", c_num, sep, &len) > 1 ||
522                            sscanf(buf, "\\%1[1234]a&H%2X&%1[\\}]%n", c_num, &alpha, sep, &len) > 2) {
523                     if (callbacks->alpha)
524                         callbacks->alpha(priv, alpha, c_num[0] - '0');
525                 } else if (sscanf(buf, "\\fn%1[\\}]%n", sep, &len) > 0 ||
526                            sscanf(buf, "\\fn%127[^\\}]%1[\\}]%n", tmp, sep, &len) > 1) {
527                     if (callbacks->font_name)
528                         callbacks->font_name(priv, tmp[0] ? tmp : NULL);
529                 } else if (sscanf(buf, "\\fs%1[\\}]%n", sep, &len) > 0 ||
530                            sscanf(buf, "\\fs%u%1[\\}]%n", &size, sep, &len) > 1) {
531                     if (callbacks->font_size)
532                         callbacks->font_size(priv, size);
533                 } else if (sscanf(buf, "\\a%1[\\}]%n", sep, &len) > 0 ||
534                            sscanf(buf, "\\a%2u%1[\\}]%n", &an, sep, &len) > 1 ||
535                            sscanf(buf, "\\an%1[\\}]%n", sep, &len) > 0 ||
536                            sscanf(buf, "\\an%1u%1[\\}]%n", &an, sep, &len) > 1) {
537                     if (an != -1 && buf[2] != 'n')
538                         an = (an&3) + (an&4 ? 6 : an&8 ? 3 : 0);
539                     if (callbacks->alignment)
540                         callbacks->alignment(priv, an);
541                 } else if (sscanf(buf, "\\r%1[\\}]%n", sep, &len) > 0 ||
542                            sscanf(buf, "\\r%127[^\\}]%1[\\}]%n", tmp, sep, &len) > 1) {
543                     if (callbacks->cancel_overrides)
544                         callbacks->cancel_overrides(priv, tmp);
545                 } else if (sscanf(buf, "\\move(%d,%d,%d,%d)%1[\\}]%n", &x1, &y1, &x2, &y2, sep, &len) > 4 ||
546                            sscanf(buf, "\\move(%d,%d,%d,%d,%d,%d)%1[\\}]%n", &x1, &y1, &x2, &y2, &t1, &t2, sep, &len) > 6) {
547                     if (callbacks->move)
548                         callbacks->move(priv, x1, y1, x2, y2, t1, t2);
549                 } else if (sscanf(buf, "\\pos(%d,%d)%1[\\}]%n", &x1, &y1, sep, &len) > 2) {
550                     if (callbacks->move)
551                         callbacks->move(priv, x1, y1, x1, y1, -1, -1);
552                 } else if (sscanf(buf, "\\org(%d,%d)%1[\\}]%n", &x1, &y1, sep, &len) > 2) {
553                     if (callbacks->origin)
554                         callbacks->origin(priv, x1, y1);
555                 } else {
556                     len = strcspn(buf+1, "\\}") + 2;  /* skip unknown code */
557                 }
558                 buf += len - 1;
559             }
560             if (*buf++ != '}')
561                 return AVERROR_INVALIDDATA;
562         } else {
563             if (!text) {
564                 text = buf;
565                 text_len = 1;
566             } else
567                 text_len++;
568             buf++;
569         }
570     }
571     if (text && callbacks->text)
572         callbacks->text(priv, text, text_len);
573     if (callbacks->end)
574         callbacks->end(priv);
575     return 0;
576 }
577 
ff_ass_style_get(ASSSplitContext * ctx,const char * style)578 ASSStyle *ff_ass_style_get(ASSSplitContext *ctx, const char *style)
579 {
580     ASS *ass = &ctx->ass;
581     int i;
582 
583     if (!style || !*style)
584         style = "Default";
585     for (i=0; i<ass->styles_count; i++)
586         if (ass->styles[i].name && !strcmp(ass->styles[i].name, style))
587             return ass->styles + i;
588     return NULL;
589 }
590