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_style.h"
|
2012-10-25 15:45:06 +02:00
|
|
|
#include "subtitle_format.h"
|
2012-10-12 04:12:42 +02:00
|
|
|
|
2012-10-19 17:57:56 +02:00
|
|
|
#ifndef AGI_PRE
|
|
|
|
#include <wx/log.h>
|
|
|
|
#endif
|
|
|
|
|
2012-10-12 04:12:42 +02:00
|
|
|
AssParser::AssParser(AssFile *target, int version)
|
|
|
|
: target(target)
|
|
|
|
, version(version)
|
2012-11-16 00:46:56 +01:00
|
|
|
, attach(nullptr)
|
2012-10-12 04:57:53 +02:00
|
|
|
, state(&AssParser::ParseScriptInfoLine)
|
2012-10-12 04:12:42 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
AssParser::~AssParser() {
|
|
|
|
}
|
|
|
|
|
2012-10-12 04:57:53 +02:00
|
|
|
void AssParser::ParseAttachmentLine(wxString const& data) {
|
|
|
|
bool is_filename = data.StartsWith("fontname: ") || data.StartsWith("filename: ");
|
2012-10-12 04:12:42 +02:00
|
|
|
|
2012-10-12 04:57:53 +02:00
|
|
|
bool valid_data = data.size() > 0 && data.size() <= 80;
|
2012-11-04 04:53:03 +01:00
|
|
|
for (auto byte : data) {
|
|
|
|
if (byte < 33 || byte >= 97) {
|
2012-10-12 04:57:53 +02:00
|
|
|
valid_data = false;
|
|
|
|
break;
|
2012-10-12 04:12:42 +02:00
|
|
|
}
|
2012-10-12 04:57:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Data is over, add attachment to the file
|
|
|
|
if (!valid_data || is_filename) {
|
|
|
|
attach->Finish();
|
2012-10-12 19:16:39 +02:00
|
|
|
target->Line.push_back(*attach.release());
|
2012-10-12 04:57:53 +02:00
|
|
|
AddLine(data);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
attach->AddData(data);
|
2012-10-12 04:12:42 +02:00
|
|
|
|
2012-10-12 04:57:53 +02:00
|
|
|
// Done building
|
|
|
|
if (data.Length() < 80) {
|
2012-10-12 04:12:42 +02:00
|
|
|
attach->Finish();
|
2012-10-12 19:16:39 +02:00
|
|
|
target->Line.push_back(*attach.release());
|
2012-10-12 04:12:42 +02:00
|
|
|
}
|
2012-10-12 04:57:53 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void AssParser::ParseScriptInfoLine(wxString const& data) {
|
|
|
|
if (data.StartsWith(";")) {
|
|
|
|
// Skip stupid comments added by other programs
|
|
|
|
// Of course, we'll add our own in place later... ;)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-11-22 03:25:41 +01:00
|
|
|
// If the first nonblank line isn't a header pretend it starts with [Script Info]
|
|
|
|
if (target->Line.empty())
|
|
|
|
target->Line.push_back(*new AssEntry("[Script Info]", "[Script Info]"));
|
|
|
|
|
2012-10-12 04:57:53 +02:00
|
|
|
if (data.StartsWith("ScriptType:")) {
|
|
|
|
wxString versionString = data.Mid(11).Trim(true).Trim(false).Lower();
|
|
|
|
int trueVersion;
|
|
|
|
if (versionString == "v4.00")
|
|
|
|
trueVersion = 0;
|
|
|
|
else if (versionString == "v4.00+")
|
|
|
|
trueVersion = 1;
|
|
|
|
else
|
2012-10-25 15:45:06 +02:00
|
|
|
throw SubtitleFormatParseError("Unknown SSA file format version", 0);
|
2012-10-12 04:57:53 +02:00
|
|
|
if (trueVersion != version) {
|
|
|
|
wxLogMessage("Warning: File has the wrong extension.");
|
|
|
|
version = trueVersion;
|
2012-10-12 04:12:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-12 19:16:39 +02:00
|
|
|
target->Line.push_back(*new AssEntry(data, "[Script Info]"));
|
2012-10-12 04:57:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void AssParser::ParseEventLine(wxString const& data) {
|
|
|
|
if (data.StartsWith("Dialogue:") || data.StartsWith("Comment:"))
|
2012-10-12 19:16:39 +02:00
|
|
|
target->Line.push_back(*new AssDialogue(data));
|
2012-10-12 04:57:53 +02:00
|
|
|
else if (data.StartsWith("Format:"))
|
2012-10-12 19:16:39 +02:00
|
|
|
target->Line.push_back(*new AssEntry("Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text", "[Events]"));
|
2012-10-12 04:57:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void AssParser::ParseStyleLine(wxString const& data) {
|
|
|
|
if (data.StartsWith("Style:"))
|
2012-10-12 19:16:39 +02:00
|
|
|
target->Line.push_back(*new AssStyle(data, version));
|
2012-10-12 04:57:53 +02:00
|
|
|
else if (data.StartsWith("Format:"))
|
2012-10-12 19:16:39 +02:00
|
|
|
target->Line.push_back(*new AssEntry("Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding", "[V4+ Styles]"));
|
2012-10-12 04:57:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void AssParser::ParseFontLine(wxString const& data) {
|
|
|
|
if (data.StartsWith("fontname: ")) {
|
|
|
|
attach.reset(new AssAttachment(data.Mid(10), "[Fonts]"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void AssParser::ParseGraphicsLine(wxString const& data) {
|
|
|
|
if (data.StartsWith("filename: ")) {
|
|
|
|
attach.reset(new AssAttachment(data.Mid(10), "[Graphics]"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void AssParser::AppendUnknownLine(wxString const& data) {
|
2012-10-12 19:16:39 +02:00
|
|
|
target->Line.push_back(*new AssEntry(data, target->Line.back().group));
|
2012-10-12 04:57:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void AssParser::AddLine(wxString 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.Last() == ']') {
|
|
|
|
// Ugly hacks to allow intermixed v4 and v4+ style sections
|
|
|
|
const wxString low = data.Lower();
|
|
|
|
wxString header = data;
|
|
|
|
if (low == "[v4 styles]") {
|
|
|
|
header = "[V4+ Styles]";
|
|
|
|
version = 0;
|
2012-10-12 04:57:53 +02:00
|
|
|
state = &AssParser::ParseStyleLine;
|
2012-10-12 04:12:42 +02:00
|
|
|
}
|
|
|
|
else if (low == "[v4+ styles]") {
|
|
|
|
header = "[V4+ Styles]";
|
|
|
|
version = 1;
|
2012-10-12 04:57:53 +02:00
|
|
|
state = &AssParser::ParseStyleLine;
|
2012-10-12 04:12:42 +02:00
|
|
|
}
|
2012-10-12 04:57:53 +02:00
|
|
|
else if (low == "[events]") {
|
|
|
|
state = &AssParser::ParseEventLine;
|
2012-10-12 04:12:42 +02:00
|
|
|
}
|
2012-10-12 04:57:53 +02:00
|
|
|
else if (low == "[script info]") {
|
|
|
|
state = &AssParser::ParseScriptInfoLine;
|
2012-10-12 04:12:42 +02:00
|
|
|
}
|
2012-10-12 04:57:53 +02:00
|
|
|
else if (low == "[graphics]") {
|
|
|
|
state = &AssParser::ParseGraphicsLine;
|
|
|
|
}
|
|
|
|
else if (low == "[fonts]") {
|
|
|
|
state = &AssParser::ParseFontLine;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
state = &AssParser::AppendUnknownLine;
|
2012-10-12 04:12:42 +02:00
|
|
|
}
|
|
|
|
|
2012-10-12 19:16:39 +02:00
|
|
|
target->Line.push_back(*new AssEntry(header, header));
|
2012-10-12 04:12:42 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-10-12 04:57:53 +02:00
|
|
|
(this->*state)(data);
|
2012-10-12 04:12:42 +02:00
|
|
|
}
|