Aegisub/src/ass_parser.cpp

164 lines
4.8 KiB
C++
Raw Normal View History

2012-10-12 04:12:42 +02:00
// Copyright (c) 2012, Thomas Goyne <plorkyeran@aegisub.org>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include "config.h"
#include "ass_parser.h"
#include "ass_attachment.h"
#include "ass_dialogue.h"
#include "ass_file.h"
#include "ass_info.h"
2012-10-12 04:12:42 +02:00
#include "ass_style.h"
#include "subtitle_format.h"
2012-10-12 04:12:42 +02:00
2012-11-25 01:08:29 +01:00
#include <algorithm>
#include <boost/algorithm/string/case_conv.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/trim.hpp>
2012-10-19 17:57:56 +02:00
2012-10-12 04:12:42 +02:00
AssParser::AssParser(AssFile *target, int version)
: target(target)
, version(version)
, state(&AssParser::ParseScriptInfoLine)
2012-10-12 04:12:42 +02:00
{
2012-11-25 01:08:29 +01:00
std::fill(begin(insertion_positions), end(insertion_positions), nullptr);
2012-10-12 04:12:42 +02:00
}
AssParser::~AssParser() {
}
void AssParser::ParseAttachmentLine(std::string const& data) {
bool is_filename = boost::starts_with(data, "fontname: ") || boost::starts_with(data, "filename: ");
2012-10-12 04:12:42 +02:00
bool valid_data = data.size() > 0 && data.size() <= 80;
for (auto byte : data) {
if (byte < 33 || byte >= 97) {
valid_data = false;
break;
2012-10-12 04:12:42 +02:00
}
}
// Data is over, add attachment to the file
if (!valid_data || is_filename) {
InsertLine(attach.release());
AddLine(data);
}
else {
attach->AddData(data);
2012-10-12 04:12:42 +02:00
// Done building
if (data.size() < 80)
InsertLine(attach.release());
}
}
void AssParser::ParseScriptInfoLine(std::string const& data) {
if (boost::starts_with(data, ";")) {
// Skip stupid comments added by other programs
// Of course, we'll add our own in place later... ;)
return;
}
if (boost::starts_with(data, "ScriptType:")) {
std::string version_str = data.substr(11);
boost::trim(version_str);
boost::to_lower(version_str);
if (version_str == "v4.00")
version = 0;
else if (version_str == "v4.00+")
version = 1;
else
2013-11-21 18:13:36 +01:00
throw SubtitleFormatParseError("Unknown SSA file format version", nullptr);
2012-10-12 04:12:42 +02:00
}
// Nothing actually supports the Collisions property and malformed values
// crash VSFilter, so just remove it entirely
if (boost::starts_with(data, "Collisions:"))
return;
size_t pos = data.find(':');
if (pos == data.npos) return;
InsertLine(new AssInfo(data.substr(0, pos), boost::trim_left_copy(data.substr(pos + 1))));
}
void AssParser::ParseEventLine(std::string const& data) {
if (boost::starts_with(data, "Dialogue:") || boost::starts_with(data, "Comment:"))
InsertLine(new AssDialogue(data));
}
void AssParser::ParseStyleLine(std::string const& data) {
if (boost::starts_with(data, "Style:"))
InsertLine(new AssStyle(data, version));
}
void AssParser::ParseFontLine(std::string const& data) {
if (boost::starts_with(data, "fontname: "))
attach.reset(new AssAttachment(data, AssEntryGroup::FONT));
}
void AssParser::ParseGraphicsLine(std::string const& data) {
if (boost::starts_with(data, "filename: "))
attach.reset(new AssAttachment(data, AssEntryGroup::GRAPHIC));
}
void AssParser::AddLine(std::string const& data) {
// Special-case for attachments since a line could theoretically be both a
// valid attachment data line and a valid section header, and if an
// attachment is in progress it needs to be treated as that
if (attach.get()) {
ParseAttachmentLine(data);
return;
}
2012-10-12 04:12:42 +02:00
if (data.empty()) return;
// Section header
if (data[0] == '[' && data.back() == ']') {
2012-10-12 04:12:42 +02:00
// Ugly hacks to allow intermixed v4 and v4+ style sections
const std::string low = boost::to_lower_copy(data);
2012-10-12 04:12:42 +02:00
if (low == "[v4 styles]") {
version = 0;
state = &AssParser::ParseStyleLine;
2012-10-12 04:12:42 +02:00
}
else if (low == "[v4+ styles]") {
version = 1;
state = &AssParser::ParseStyleLine;
2012-10-12 04:12:42 +02:00
}
else if (low == "[events]")
state = &AssParser::ParseEventLine;
else if (low == "[script info]")
state = &AssParser::ParseScriptInfoLine;
else if (low == "[graphics]")
state = &AssParser::ParseGraphicsLine;
else if (low == "[fonts]")
state = &AssParser::ParseFontLine;
else
state = &AssParser::UnknownLine;
2012-10-12 04:12:42 +02:00
return;
}
(this->*state)(data);
2012-10-12 04:12:42 +02:00
}
void AssParser::InsertLine(AssEntry *entry) {
2013-06-13 00:54:19 +02:00
AssEntry *position = insertion_positions[(size_t)entry->Group()];
2012-11-25 01:08:29 +01:00
if (position)
target->Line.insert(++target->Line.iterator_to(*position), *entry);
else
2012-11-25 01:08:29 +01:00
target->Line.push_back(*entry);
2013-06-13 00:54:19 +02:00
insertion_positions[(size_t)entry->Group()] = entry;
}