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