From b94547aa71a3e319c75e8378fc006159e7ff7740 Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Thu, 22 Nov 2012 08:14:34 -0800 Subject: [PATCH] Drop format and header lines from the in-memory file representation They're just pointless cruft, so drop them from the file when parsing and re-add them when saving as ASS or SSA. --- aegisub/src/ass_entry.cpp | 10 +--- aegisub/src/ass_file.cpp | 73 ++++++++++------------------ aegisub/src/ass_file.h | 8 +-- aegisub/src/ass_parser.cpp | 25 ++-------- aegisub/src/auto4_lua_assfile.cpp | 33 +------------ aegisub/src/command/edit.cpp | 2 +- aegisub/src/dialog_attachments.cpp | 26 +--------- aegisub/src/dialog_style_editor.cpp | 2 +- aegisub/src/dialog_style_manager.cpp | 6 +-- aegisub/src/subs_preview.cpp | 2 +- aegisub/src/subtitle_format_ass.cpp | 43 ++++++++++++++-- 11 files changed, 83 insertions(+), 147 deletions(-) diff --git a/aegisub/src/ass_entry.cpp b/aegisub/src/ass_entry.cpp index 728173d01..2d37d2bdc 100644 --- a/aegisub/src/ass_entry.cpp +++ b/aegisub/src/ass_entry.cpp @@ -37,15 +37,7 @@ #include "ass_entry.h" wxString AssEntry::GetSSAText() const { - wxString lower = data.Lower(); - - // Special cases - if (lower == "[v4+ styles]") return "[V4 Styles]"; - if (lower == "scripttype: v4.00+") return "ScriptType: v4.00"; - if (lower.Left(7) == "format:") { - if (group.Lower() == "[events]") return "Format: Marked, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text"; - if (group.Lower() == "[v4+ styles]") return "Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, TertiaryColour, BackColour, Bold, Italic, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, AlphaLevel, Encoding"; - } + if (data.Lower() == "scripttype: v4.00+") return "ScriptType: v4.00"; return GetEntryData(); } diff --git a/aegisub/src/ass_file.cpp b/aegisub/src/ass_file.cpp index e69c57424..c7acd61d9 100644 --- a/aegisub/src/ass_file.cpp +++ b/aegisub/src/ass_file.cpp @@ -98,9 +98,9 @@ void AssFile::Load(const wxString &_filename, wxString const& charset) { // And if it doesn't add defaults for each if (!found_style) - temp.InsertStyle(new AssStyle); + temp.InsertLine(new AssStyle); if (!found_dialogue) - temp.InsertDialogue(new AssDialogue); + temp.InsertLine(new AssDialogue); swap(temp); } @@ -171,20 +171,29 @@ wxString AssFile::AutoSave() { return dstpath.GetFullPath(); } +static void write_line(wxString const& line, std::vector& dst) { + wxCharBuffer buffer = (line + "\r\n").utf8_str(); + copy(buffer.data(), buffer.data() + buffer.length(), back_inserter(dst)); +} + void AssFile::SaveMemory(std::vector &dst) { // Check if subs contain at least one style // Add a default style if they don't for compatibility with libass/asa - if (GetStyles().Count() == 0) - InsertStyle(new AssStyle); + if (GetStyles().empty()) + InsertLine(new AssStyle); // Prepare vector dst.clear(); dst.reserve(0x4000); // Write file + wxString group; for (auto const& line : Line) { - wxCharBuffer buffer = (line.GetEntryData() + "\r\n").utf8_str(); - copy(buffer.data(), buffer.data() + buffer.length(), back_inserter(dst)); + if (group != line.group) { + group = line.group; + write_line(group, dst); + } + write_line(line.GetEntryData(), dst); } } @@ -211,7 +220,6 @@ void AssFile::LoadDefault(bool defline) { Clear(); // Write headers - Line.push_back(*new AssEntry("[Script Info]", "[Script Info]")); Line.push_back(*new AssEntry("Title: Default Aegisub file", "[Script Info]")); Line.push_back(*new AssEntry("ScriptType: v4.00+", "[Script Info]")); Line.push_back(*new AssEntry("WrapStyle: 0", "[Script Info]")); @@ -223,10 +231,7 @@ void AssFile::LoadDefault(bool defline) { } Line.push_back(*new AssEntry("YCbCr Matrix: None", "[Script Info]")); - InsertStyle(new AssStyle); - - Line.push_back(*new AssEntry("[Events]", "[Events]")); - Line.push_back(*new AssEntry("Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text", "[Events]")); + Line.push_back(*new AssStyle); if (defline) Line.push_back(*new AssDialogue); @@ -260,37 +265,23 @@ AssFile& AssFile::operator=(AssFile from) { return *this; } -static bool try_insert(EntryList &lines, AssEntry *entry) { - if (lines.empty()) return false; +void AssFile::InsertLine( AssEntry *entry) { + if (Line.empty()) { + Line.push_back(*entry); + return; + } // Search for insertion point - entryIter it = lines.end(); + entryIter it = Line.end(); do { --it; if (it->group == entry->group) { - lines.insert(++it, *entry); - return true; + Line.insert(++it, *entry); + return; } - } while (it != lines.begin()); + } while (it != Line.begin()); - return false; -} - -void AssFile::InsertStyle(AssStyle *style) { - if (try_insert(Line, style)) return; - - // No styles found, add them - Line.push_back(*new AssEntry("[V4+ Styles]", "[V4+ Styles]")); - 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]")); - Line.push_back(*style); -} - -void AssFile::InsertAttachment(AssAttachment *attach) { - if (try_insert(Line, attach)) return; - - // Didn't find a group of the appropriate type so create it - Line.push_back(*new AssEntry(attach->group, attach->group)); - Line.push_back(*attach); + Line.push_back(*entry); } void AssFile::InsertAttachment(wxString filename) { @@ -303,16 +294,7 @@ void AssFile::InsertAttachment(wxString filename) { std::unique_ptr newAttach(new AssAttachment(wxFileName(filename).GetFullName(), group)); newAttach->Import(filename); - InsertAttachment(newAttach.release()); -} - -void AssFile::InsertDialogue(AssDialogue *diag) { - if (try_insert(Line, diag)) return; - - // Didn't find a group of the appropriate type so create it - Line.push_back(*new AssEntry("[Events]", "[Events]")); - Line.push_back(*new AssEntry("Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text", "[Events]")); - Line.push_back(*diag); + InsertLine(newAttach.release()); } wxString AssFile::GetScriptInfo(wxString key) const { @@ -373,7 +355,6 @@ void AssFile::SetScriptInfo(wxString const& key, wxString const& value) { // Script info section not found, so add it at the beginning of the file else { Line.push_front(*new AssEntry(key + ": " + value, "[Script Info]")); - Line.push_front(*new AssEntry("[Script Info]", "[Script Info]")); } } diff --git a/aegisub/src/ass_file.h b/aegisub/src/ass_file.h index a7e4f800d..73275dc57 100644 --- a/aegisub/src/ass_file.h +++ b/aegisub/src/ass_file.h @@ -102,14 +102,10 @@ public: /// @brief Load default file /// @param defline Add a blank line to the file void LoadDefault(bool defline=true); - /// Add a style to the file - void InsertStyle(AssStyle *style); - /// Add an attachment to the file - void InsertAttachment(AssAttachment *attach); + /// Add a line to the file at the end of the appropriate section + void InsertLine(AssEntry *line); /// Attach a file to the ass file void InsertAttachment(wxString filename); - /// Add a dialogue line to the file - void InsertDialogue(AssDialogue *diag); /// Get the names of all of the styles available wxArrayString GetStyles() const; /// @brief Get a style by name diff --git a/aegisub/src/ass_parser.cpp b/aegisub/src/ass_parser.cpp index aee97d94d..5a27a2027 100644 --- a/aegisub/src/ass_parser.cpp +++ b/aegisub/src/ass_parser.cpp @@ -72,10 +72,6 @@ void AssParser::ParseScriptInfoLine(wxString const& data) { 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]")); - if (data.StartsWith("ScriptType:")) { wxString versionString = data.Mid(11).Trim(true).Trim(false).Lower(); int trueVersion; @@ -97,15 +93,11 @@ void AssParser::ParseScriptInfoLine(wxString const& data) { 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) { @@ -150,23 +142,16 @@ void AssParser::AddLine(wxString const& data) { version = 1; state = &AssParser::ParseStyleLine; } - else if (low == "[events]") { + else if (low == "[events]") state = &AssParser::ParseEventLine; - } - else if (low == "[script info]") { + else if (low == "[script info]") state = &AssParser::ParseScriptInfoLine; - } - else if (low == "[graphics]") { + else if (low == "[graphics]") state = &AssParser::ParseGraphicsLine; - } - else if (low == "[fonts]") { + else if (low == "[fonts]") state = &AssParser::ParseFontLine; - } - else { + else state = &AssParser::AppendUnknownLine; - } - - target->Line.push_back(*new AssEntry(header, header)); return; } diff --git a/aegisub/src/auto4_lua_assfile.cpp b/aegisub/src/auto4_lua_assfile.cpp index c97702933..e3ee87787 100644 --- a/aegisub/src/auto4_lua_assfile.cpp +++ b/aegisub/src/auto4_lua_assfile.cpp @@ -213,23 +213,11 @@ namespace Automation4 { if (StringEmptyOrWhitespace(raw)) { set_field(L, "class", "clear"); } - else if (raw[0] == ';') { - // "text" field, same as "raw" but with semicolon stripped - set_field(L, "text", raw.Mid(1)); - set_field(L, "class", "comment"); - } - else if (raw[0] == '[') { - set_field(L, "class", "head"); - } else if (e->group.Lower() == "[script info]") { set_field(L, "key", raw.BeforeFirst(':')); set_field(L, "value", raw.AfterFirst(':')); set_field(L, "class", "info"); } - else if (raw.Left(7).Lower() == "format:") { - // TODO: parse the format line; just use a tokenizer - set_field(L, "class", "format"); - } else if (AssDialogue *dia = dynamic_cast(e)) { set_field(L, "comment", dia->Comment); @@ -317,20 +305,9 @@ namespace Automation4 { try { wxString section = get_wxstring_field(L, "section", "common"); - if (lclass == "clear") - result = new AssEntry("", ""); - else if (lclass == "comment") - result = new AssEntry(";" + get_wxstring_field(L, "text", "comment"), section); - else if (lclass == "head") - result = new AssEntry(section, section); - else if (lclass == "info") { + if (lclass == "info") { result = new AssEntry(wxString::Format("%s: %s", get_wxstring_field(L, "key", "info"), get_wxstring_field(L, "value", "info")), "[Script Info]"); } - else if (lclass == "format") { - // ohshi- ... - // *FIXME* maybe ignore the actual data and just put some default stuff based on section? - result = new AssEntry("Format: Auto4,Is,Broken", section); - } else if (lclass == "style") { AssStyle *sty = new AssStyle; result = sty; @@ -559,17 +536,11 @@ namespace Automation4 { if (it == lines.end() || (*it)->group != e->group) { // The new entry belongs to a group that doesn't exist yet, so // create it at the end of the file - if (e->GetEntryData() != e->group) { - // Add the header if the entry being added isn't a header - lines.push_back(new AssEntry(e->group, e->group)); - } - lines.push_back(e); } else { // Append the entry to the end of the existing group - ++it; - lines.insert(it, e); + lines.insert(++it, e); } } } diff --git a/aegisub/src/command/edit.cpp b/aegisub/src/command/edit.cpp index 96d5cb303..3470abfbe 100644 --- a/aegisub/src/command/edit.cpp +++ b/aegisub/src/command/edit.cpp @@ -528,7 +528,7 @@ static void delete_lines(agi::Context *c, wxString const& commit_message) { // lines, so make a new one if (!new_active) { new_active = new AssDialogue; - c->ass->InsertDialogue(new_active); + c->ass->InsertLine(new_active); } c->ass->Commit(commit_message, AssFile::COMMIT_DIAG_ADDREM); diff --git a/aegisub/src/dialog_attachments.cpp b/aegisub/src/dialog_attachments.cpp index e9fe5b5b5..80cf86b20 100644 --- a/aegisub/src/dialog_attachments.cpp +++ b/aegisub/src/dialog_attachments.cpp @@ -142,7 +142,7 @@ void DialogAttachments::AttachFile(wxFileDialog &diag, wxString const& group, wx delete newAttach; return; } - ass->InsertAttachment(newAttach); + ass->InsertLine(newAttach); } ass->Commit(commit_msg, AssFile::COMMIT_ATTACHMENT); @@ -211,30 +211,6 @@ void DialogAttachments::OnDelete(wxCommandEvent &) { i = listView->GetNextSelected(i); } - // Remove empty attachment sections in the file - for (entryIter it = ass->Line.begin(); it != ass->Line.end(); ) { - if (it->GetType() == ENTRY_BASE && (it->group == "[Fonts]" || it->group == "[Graphics]")) { - wxString group = it->group; - entryIter header = it; - - bool has_attachments = false; - for (++it; it != ass->Line.end() && it->group == group; ++it) { - if (it->GetType() == ENTRY_ATTACHMENT) { - has_attachments = true; - break; - } - } - - // Empty group found, delete it - if (!has_attachments) { - while (header != it) - delete &*header++; - } - } - else - ++it; - } - ass->Commit(_("remove attachment"), AssFile::COMMIT_ATTACHMENT); UpdateList(); diff --git a/aegisub/src/dialog_style_editor.cpp b/aegisub/src/dialog_style_editor.cpp index 7ed155a86..509a0a8e6 100644 --- a/aegisub/src/dialog_style_editor.cpp +++ b/aegisub/src/dialog_style_editor.cpp @@ -465,7 +465,7 @@ void DialogStyleEditor::Apply(bool apply, bool close) { if (store) store->push_back(style); else - c->ass->InsertStyle(style); + c->ass->InsertLine(style); is_new = false; } if (!store) diff --git a/aegisub/src/dialog_style_manager.cpp b/aegisub/src/dialog_style_manager.cpp index ba0f2442b..25dd559f7 100644 --- a/aegisub/src/dialog_style_manager.cpp +++ b/aegisub/src/dialog_style_manager.cpp @@ -445,7 +445,7 @@ void DialogStyleManager::OnCopyToCurrent() { } } if (addStyle) { - c->ass->InsertStyle(new AssStyle(*Store[selections[i]])); + c->ass->InsertLine(new AssStyle(*Store[selections[i]])); copied.push_back(styleName); } } @@ -477,7 +477,7 @@ void DialogStyleManager::CopyToClipboard(wxListBox *list, T const& v) { void DialogStyleManager::PasteToCurrent() { add_styles( std::bind(&AssFile::GetStyle, c->ass, _1), - std::bind(&AssFile::InsertStyle, c->ass, _1)); + std::bind(&AssFile::InsertLine, c->ass, _1)); c->ass->Commit(_("style paste"), AssFile::COMMIT_STYLES); } @@ -626,7 +626,7 @@ void DialogStyleManager::OnCurrentImport() { modified = true; AssStyle *tempStyle = new AssStyle; *tempStyle = *temp.GetStyle(styles[sel]); - c->ass->InsertStyle(tempStyle); + c->ass->InsertLine(tempStyle); } // Update diff --git a/aegisub/src/subs_preview.cpp b/aegisub/src/subs_preview.cpp index e846b10dc..a3633f370 100644 --- a/aegisub/src/subs_preview.cpp +++ b/aegisub/src/subs_preview.cpp @@ -60,7 +60,7 @@ SubtitlesPreview::SubtitlesPreview(wxWindow *parent, wxSize size, int winStyle, SetStyle(*style); subFile->LoadDefault(); - subFile->InsertStyle(style); + subFile->InsertLine(style); subFile->Line.push_back(*line); SetSizeHints(size.GetWidth(), size.GetHeight(), -1, -1); diff --git a/aegisub/src/subtitle_format_ass.cpp b/aegisub/src/subtitle_format_ass.cpp index 72a7b5cce..ea28016e7 100644 --- a/aegisub/src/subtitle_format_ass.cpp +++ b/aegisub/src/subtitle_format_ass.cpp @@ -83,6 +83,36 @@ void AssSubtitleFormat::ReadFile(AssFile *target, wxString const& filename, wxSt } } +static inline wxString header(wxString const& group, bool ssa) { + if (ssa && group == "[V4+ Styles]") + return "[V4 Styles]"; + return group; +} + +#ifdef _WIN32 +#define LINEBREAK "\r\n" +#else +#define LINEBREAK "\n" +#endif + +static inline wxString format(wxString const& group, bool ssa) { + if (group == "[Events]") { + if (ssa) + return "Format: Marked, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text" LINEBREAK; + else + return "Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text" LINEBREAK; + } + + if (group == "[v4+ styles]") { + if (ssa) + return "Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, TertiaryColour, BackColour, Bold, Italic, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, AlphaLevel, Encoding" LINEBREAK; + else + return "Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding" LINEBREAK; + } + + return wxS(""); +} + void AssSubtitleFormat::WriteFile(const AssFile *src, wxString const& filename, wxString const& encoding) const { TextFileWriter file(filename, encoding); @@ -90,15 +120,20 @@ void AssSubtitleFormat::WriteFile(const AssFile *src, wxString const& filename, file.WriteLineToFile("; http://www.aegisub.org/"); bool ssa = filename.Right(4).Lower() == ".ssa"; + wxString group; - wxString group = src->Line.front().group; for (auto const& line : src->Line) { - // Add a blank line between each group if (line.group != group) { - file.WriteLineToFile(""); + // Add a blank line between each group + if (!group.empty()) + file.WriteLineToFile(""); + + file.WriteLineToFile(header(line.group, ssa)); + file.WriteLineToFile(format(line.group, ssa), false); + group = line.group; } - file.WriteLineToFile(ssa ? line.GetSSAText() : line.GetEntryData(), true); + file.WriteLineToFile(ssa ? line.GetSSAText() : line.GetEntryData()); } }