From d3e2585fafa8aa32319e2a981732bccc652ba620 Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Thu, 11 Oct 2012 19:57:53 -0700 Subject: [PATCH] Refactor AssParser so that AddLine is less of a monolithic monstrosity --- aegisub/src/ass_parser.cpp | 192 ++++++++++++++++++++----------------- aegisub/src/ass_parser.h | 9 ++ aegisub/src/mkv_wrap.cpp | 10 +- 3 files changed, 119 insertions(+), 92 deletions(-) diff --git a/aegisub/src/ass_parser.cpp b/aegisub/src/ass_parser.cpp index 32ee0381d..3dc63df81 100644 --- a/aegisub/src/ass_parser.cpp +++ b/aegisub/src/ass_parser.cpp @@ -25,46 +25,109 @@ AssParser::AssParser(AssFile *target, int version) : target(target) , version(version) , attach(0) +, state(&AssParser::ParseScriptInfoLine) { } AssParser::~AssParser() { } -void AssParser::AddLine(wxString const& data) { - // Is this line an attachment filename? - bool isFilename = data.StartsWith("fontname: ") || data.StartsWith("filename: "); +void AssParser::ParseAttachmentLine(wxString const& data) { + bool is_filename = data.StartsWith("fontname: ") || data.StartsWith("filename: "); - // If there's an attachment in progress, deal with it first as an - // attachment data line can appear to be other things - if (attach.get()) { - // Check if it's valid data - bool validData = data.size() > 0 && data.size() <= 80; - for (size_t i = 0; i < data.size(); ++i) { - if (data[i] < 33 || data[i] >= 97) { - validData = false; - break; - } + bool valid_data = data.size() > 0 && data.size() <= 80; + for (size_t i = 0; i < data.size(); ++i) { + if (data[i] < 33 || data[i] >= 97) { + valid_data = false; + break; } + } - // Data is over, add attachment to the file - if (!validData || isFilename) { + // Data is over, add attachment to the file + if (!valid_data || is_filename) { + attach->Finish(); + target->Line.push_back(attach.release()); + AddLine(data); + } + else { + attach->AddData(data); + + // Done building + if (data.Length() < 80) { attach->Finish(); target->Line.push_back(attach.release()); } - else { - // Insert data - attach->AddData(data); + } +} - // Done building - if (data.Length() < 80) { - attach->Finish(); - target->Line.push_back(attach.release()); - return; - } +void AssParser::ParseScriptInfoLine(wxString const& data) { + // 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]")); + + if (data.StartsWith(";")) { + // Skip stupid comments added by other programs + // Of course, we'll add our own in place later... ;) + return; + } + + 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 + throw "Unknown SSA file format version"; + if (trueVersion != version) { + wxLogMessage("Warning: File has the wrong extension."); + version = trueVersion; } } + target->Line.push_back(new AssEntry(data, "[Script Info]")); +} + +void AssParser::ParseEventLine(wxString const& data) { + if (data.StartsWith("Dialogue:") || data.StartsWith("Comment:")) + target->Line.push_back(new AssDialogue(data)); + else if (data.StartsWith("Format:")) + target->Line.push_back(new AssEntry("Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text", "[Events]")); +} + +void AssParser::ParseStyleLine(wxString const& data) { + if (data.StartsWith("Style:")) + target->Line.push_back(new AssStyle(data, version)); + else if (data.StartsWith("Format:")) + 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]")); +} + +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) { + target->Line.push_back(new AssEntry(data, target->Line.back()->group)); +} + +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; + } + if (data.empty()) return; // Section header @@ -75,77 +138,32 @@ void AssParser::AddLine(wxString const& data) { if (low == "[v4 styles]") { header = "[V4+ Styles]"; version = 0; + state = &AssParser::ParseStyleLine; } else if (low == "[v4+ styles]") { header = "[V4+ Styles]"; version = 1; + state = &AssParser::ParseStyleLine; + } + 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::AppendUnknownLine; } target->Line.push_back(new AssEntry(header, header)); return; } - // 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]")); - - wxString group = target->Line.back()->group; - wxString lowGroup = group.Lower(); - - // Attachment - if (lowGroup == "[fonts]" || lowGroup == "[graphics]") { - if (isFilename) { - attach.reset(new AssAttachment(data.Mid(10), group)); - } - return; - } - - // Dialogue - if (lowGroup == "[events]") { - if (data.StartsWith("Dialogue:") || data.StartsWith("Comment:")) - target->Line.push_back(new AssDialogue(data)); - else if (data.StartsWith("Format:")) - target->Line.push_back(new AssEntry("Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text", group)); - return; - } - - // Style - if (lowGroup == "[v4+ styles]") { - if (data.StartsWith("Style:")) - target->Line.push_back(new AssStyle(data, version)); - else if (data.StartsWith("Format:")) - 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", group)); - return; - } - - // Script info - if (lowGroup == "[script info]") { - // Comment - if (data.StartsWith(";")) { - // Skip stupid comments added by other programs - // Of course, we'll add our own in place later... ;) - return; - } - - 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 - throw "Unknown SSA file format version"; - if (trueVersion != version) { - wxLogMessage("Warning: File has the wrong extension."); - version = trueVersion; - } - } - - target->Line.push_back(new AssEntry(data, group)); - return; - } - - // Unrecognized group - target->Line.push_back(new AssEntry(data, group)); + (this->*state)(data); } diff --git a/aegisub/src/ass_parser.h b/aegisub/src/ass_parser.h index 7ae39c9ad..e11ec9ee6 100644 --- a/aegisub/src/ass_parser.h +++ b/aegisub/src/ass_parser.h @@ -25,6 +25,15 @@ class AssParser { AssFile *target; int version; std::auto_ptr attach; + void (AssParser::*state)(wxString const&); + + void ParseAttachmentLine(wxString const& data); + void ParseEventLine(wxString const& data); + void ParseStyleLine(wxString const& data); + void ParseScriptInfoLine(wxString const& data); + void ParseFontLine(wxString const& data); + void ParseGraphicsLine(wxString const& data); + void AppendUnknownLine(wxString const& data); public: AssParser(AssFile *target, int version); ~AssParser(); diff --git a/aegisub/src/mkv_wrap.cpp b/aegisub/src/mkv_wrap.cpp index bef2c116b..58da81792 100644 --- a/aegisub/src/mkv_wrap.cpp +++ b/aegisub/src/mkv_wrap.cpp @@ -77,7 +77,7 @@ public: #define std_ftell ftello #endif -static void read_subtitles(agi::ProgressSink *ps, MatroskaFile *file, MkvStdIO *input, bool srt, bool ssa, double totalTime, AssFile *target) { +static void read_subtitles(agi::ProgressSink *ps, MatroskaFile *file, MkvStdIO *input, bool srt, double totalTime, AssParser *parser) { std::map subList; char *readBuf = 0; size_t readBufSize = 0; @@ -135,9 +135,8 @@ static void read_subtitles(agi::ProgressSink *ps, MatroskaFile *file, MkvStdIO * delete[] readBuf; // Insert into file - AssParser parser(target, ssa); for (std::map::iterator it = subList.begin(); it != subList.end(); ++it) { - parser.AddLine(it->second); + parser->AddLine(it->second); } } @@ -197,6 +196,8 @@ void MatroskaWrapper::GetSubtitles(wxString const& filename, AssFile *target) { bool srt = CodecID == "S_TEXT/UTF8"; bool ssa = CodecID == "S_TEXT/SSA"; + AssParser parser(target, !ssa); + // Read private data if it's ASS/SSA if (!srt) { // Read raw data @@ -204,7 +205,6 @@ void MatroskaWrapper::GetSubtitles(wxString const& filename, AssFile *target) { wxString privString((const char *)trackInfo->CodecPrivate, wxConvUTF8, trackInfo->CodecPrivateSize); // Load into file - AssParser parser(target, !ssa); wxStringTokenizer token(privString, "\r\n", wxTOKEN_STRTOK); while (token.HasMoreTokens()) parser.AddLine(token.GetNextToken()); @@ -221,7 +221,7 @@ void MatroskaWrapper::GetSubtitles(wxString const& filename, AssFile *target) { // Progress bar double totalTime = double(segInfo->Duration) / timecodeScale; DialogProgress progress(NULL, _("Parsing Matroska"), _("Reading subtitles from Matroska file.")); - progress.Run(bind(read_subtitles, std::tr1::placeholders::_1, file, &input, srt, ssa, totalTime, target)); + progress.Run(bind(read_subtitles, std::tr1::placeholders::_1, file, &input, srt, totalTime, &parser)); } catch (...) { mkv_Close(file);