21
19
#include "ass_file.h"
22
20
#include "ass_info.h"
23
21
#include "ass_style.h"
22
#include "string_codec.h"
24
23
#include "subtitle_format.h"
25
#include <libaegisub/ass/uuencode.h>
26
#include <libaegisub/make_unique.h>
27
#include <libaegisub/util.h>
26
29
#include <algorithm>
27
30
#include <boost/algorithm/string/case_conv.hpp>
28
31
#include <boost/algorithm/string/predicate.hpp>
29
32
#include <boost/algorithm/string/trim.hpp>
33
#include <boost/lexical_cast.hpp>
34
#include <boost/regex.hpp>
35
#include <boost/variant.hpp>
36
#include <unordered_map>
38
class AssParser::HeaderToProperty {
39
using field = boost::variant<
40
std::string ProjectProperties::*,
41
int ProjectProperties::*,
42
double ProjectProperties::*
44
std::unordered_map<std::string, field> fields;
49
{"Automation Scripts", &ProjectProperties::automation_scripts},
50
{"Export Filters", &ProjectProperties::export_filters},
51
{"Export Encoding", &ProjectProperties::export_encoding},
52
{"Last Style Storage", &ProjectProperties::style_storage},
53
{"Audio URI", &ProjectProperties::audio_file},
54
{"Audio File", &ProjectProperties::audio_file},
55
{"Video File", &ProjectProperties::video_file},
56
{"Timecodes File", &ProjectProperties::timecodes_file},
57
{"Keyframes File", &ProjectProperties::keyframes_file},
58
{"Video Zoom Percent", &ProjectProperties::video_zoom},
59
{"Scroll Position", &ProjectProperties::scroll_position},
60
{"Active Line", &ProjectProperties::active_row},
61
{"Video Position", &ProjectProperties::video_position},
62
{"Video AR Mode", &ProjectProperties::ar_mode},
63
{"Video AR Value", &ProjectProperties::ar_value},
64
{"Aegisub Video Zoom Percent", &ProjectProperties::video_zoom},
65
{"Aegisub Scroll Position", &ProjectProperties::scroll_position},
66
{"Aegisub Active Line", &ProjectProperties::active_row},
67
{"Aegisub Video Position", &ProjectProperties::video_position}
72
bool ProcessProperty(AssFile *target, std::string const& key, std::string const& value) {
73
auto it = fields.find(key);
74
if (it != end(fields)) {
75
using namespace agi::util;
77
using result_type = void;
78
ProjectProperties &obj;
79
std::string const& value;
80
void operator()(std::string ProjectProperties::*f) const { obj.*f = value; }
81
void operator()(int ProjectProperties::*f) const { try_parse(value, &(obj.*f)); }
82
void operator()(double ProjectProperties::*f) const { try_parse(value, &(obj.*f)); }
83
} visitor {target->Properties, value};
84
boost::apply_visitor(visitor, it->second);
88
if (boost::starts_with(key, "Automation Settings ")) {
89
target->Properties.automation_settings[key.substr(strlen("Automation Settings"))] = value;
31
97
AssParser::AssParser(AssFile *target, int version)
98
: property_handler(new HeaderToProperty)
33
100
, version(version)
34
101
, state(&AssParser::ParseScriptInfoLine)
36
std::fill(begin(insertion_positions), end(insertion_positions), nullptr);
39
105
AssParser::~AssParser() {
91
157
size_t pos = data.find(':');
92
158
if (pos == data.npos) return;
94
InsertLine(new AssInfo(data.substr(0, pos), boost::trim_left_copy(data.substr(pos + 1))));
160
auto key = data.substr(0, pos);
161
auto value = data.substr(pos + 1);
162
boost::trim_left(value);
164
if (!property_handler->ProcessProperty(target, key, value))
165
target->Info.push_back(*new AssInfo(std::move(key), std::move(value)));
168
void AssParser::ParseMetadataLine(std::string const& data) {
169
size_t pos = data.find(':');
170
if (pos == data.npos) return;
172
auto key = data.substr(0, pos);
173
auto value = data.substr(pos + 1);
174
boost::trim_left(value);
176
property_handler->ProcessProperty(target, key, value);
97
179
void AssParser::ParseEventLine(std::string const& data) {
98
180
if (boost::starts_with(data, "Dialogue:") || boost::starts_with(data, "Comment:"))
99
InsertLine(new AssDialogue(data));
181
target->Events.push_back(*new AssDialogue(data));
102
184
void AssParser::ParseStyleLine(std::string const& data) {
103
185
if (boost::starts_with(data, "Style:"))
104
InsertLine(new AssStyle(data, version));
186
target->Styles.push_back(*new AssStyle(data, version));
107
189
void AssParser::ParseFontLine(std::string const& data) {
108
190
if (boost::starts_with(data, "fontname: "))
109
attach.reset(new AssAttachment(data, AssEntryGroup::FONT));
191
attach = agi::make_unique<AssAttachment>(data, AssEntryGroup::FONT);
112
194
void AssParser::ParseGraphicsLine(std::string const& data) {
113
195
if (boost::starts_with(data, "filename: "))
114
attach.reset(new AssAttachment(data, AssEntryGroup::GRAPHIC));
196
attach = agi::make_unique<AssAttachment>(data, AssEntryGroup::GRAPHIC);
199
void AssParser::ParseExtradataLine(std::string const &data) {
200
static const boost::regex matcher("Data:[[:space:]]*(\\d+),([^,]+),(.)(.*)");
201
boost::match_results<std::string::const_iterator> mr;
203
if (boost::regex_match(data, mr, matcher)) {
204
auto id = boost::lexical_cast<uint32_t>(mr.str(1));
205
auto key = inline_string_decode(mr.str(2));
206
auto valuetype = mr.str(3);
207
auto value = mr.str(4);
208
if (valuetype == "e") {
209
// escaped/inline_string encoded
210
value = inline_string_decode(value);
211
} else if (valuetype == "u") {
213
auto valuedata = agi::ass::UUDecode(value);
214
value = std::string(valuedata.begin(), valuedata.end());
220
// ensure next_extradata_id is always at least 1 more than the largest existing id
221
target->next_extradata_id = std::max(id+1, target->next_extradata_id);
222
target->Extradata.push_back(ExtradataEntry{id, std::move(key), std::move(value)});
117
226
void AssParser::AddLine(std::string const& data) {