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.
This commit is contained in:
Thomas Goyne 2012-11-22 08:14:34 -08:00
parent 30ceced39f
commit b94547aa71
11 changed files with 83 additions and 147 deletions

View File

@ -37,15 +37,7 @@
#include "ass_entry.h" #include "ass_entry.h"
wxString AssEntry::GetSSAText() const { wxString AssEntry::GetSSAText() const {
wxString lower = data.Lower(); if (data.Lower() == "scripttype: v4.00+") return "ScriptType: v4.00";
// 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";
}
return GetEntryData(); return GetEntryData();
} }

View File

@ -98,9 +98,9 @@ void AssFile::Load(const wxString &_filename, wxString const& charset) {
// And if it doesn't add defaults for each // And if it doesn't add defaults for each
if (!found_style) if (!found_style)
temp.InsertStyle(new AssStyle); temp.InsertLine(new AssStyle);
if (!found_dialogue) if (!found_dialogue)
temp.InsertDialogue(new AssDialogue); temp.InsertLine(new AssDialogue);
swap(temp); swap(temp);
} }
@ -171,20 +171,29 @@ wxString AssFile::AutoSave() {
return dstpath.GetFullPath(); return dstpath.GetFullPath();
} }
static void write_line(wxString const& line, std::vector<char>& dst) {
wxCharBuffer buffer = (line + "\r\n").utf8_str();
copy(buffer.data(), buffer.data() + buffer.length(), back_inserter(dst));
}
void AssFile::SaveMemory(std::vector<char> &dst) { void AssFile::SaveMemory(std::vector<char> &dst) {
// Check if subs contain at least one style // Check if subs contain at least one style
// Add a default style if they don't for compatibility with libass/asa // Add a default style if they don't for compatibility with libass/asa
if (GetStyles().Count() == 0) if (GetStyles().empty())
InsertStyle(new AssStyle); InsertLine(new AssStyle);
// Prepare vector // Prepare vector
dst.clear(); dst.clear();
dst.reserve(0x4000); dst.reserve(0x4000);
// Write file // Write file
wxString group;
for (auto const& line : Line) { for (auto const& line : Line) {
wxCharBuffer buffer = (line.GetEntryData() + "\r\n").utf8_str(); if (group != line.group) {
copy(buffer.data(), buffer.data() + buffer.length(), back_inserter(dst)); group = line.group;
write_line(group, dst);
}
write_line(line.GetEntryData(), dst);
} }
} }
@ -211,7 +220,6 @@ void AssFile::LoadDefault(bool defline) {
Clear(); Clear();
// Write headers // 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("Title: Default Aegisub file", "[Script Info]"));
Line.push_back(*new AssEntry("ScriptType: v4.00+", "[Script Info]")); Line.push_back(*new AssEntry("ScriptType: v4.00+", "[Script Info]"));
Line.push_back(*new AssEntry("WrapStyle: 0", "[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]")); Line.push_back(*new AssEntry("YCbCr Matrix: None", "[Script Info]"));
InsertStyle(new AssStyle); Line.push_back(*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]"));
if (defline) if (defline)
Line.push_back(*new AssDialogue); Line.push_back(*new AssDialogue);
@ -260,37 +265,23 @@ AssFile& AssFile::operator=(AssFile from) {
return *this; return *this;
} }
static bool try_insert(EntryList &lines, AssEntry *entry) { void AssFile::InsertLine( AssEntry *entry) {
if (lines.empty()) return false; if (Line.empty()) {
Line.push_back(*entry);
return;
}
// Search for insertion point // Search for insertion point
entryIter it = lines.end(); entryIter it = Line.end();
do { do {
--it; --it;
if (it->group == entry->group) { if (it->group == entry->group) {
lines.insert(++it, *entry); Line.insert(++it, *entry);
return true; return;
} }
} while (it != lines.begin()); } while (it != Line.begin());
return false; Line.push_back(*entry);
}
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);
} }
void AssFile::InsertAttachment(wxString filename) { void AssFile::InsertAttachment(wxString filename) {
@ -303,16 +294,7 @@ void AssFile::InsertAttachment(wxString filename) {
std::unique_ptr<AssAttachment> newAttach(new AssAttachment(wxFileName(filename).GetFullName(), group)); std::unique_ptr<AssAttachment> newAttach(new AssAttachment(wxFileName(filename).GetFullName(), group));
newAttach->Import(filename); newAttach->Import(filename);
InsertAttachment(newAttach.release()); InsertLine(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);
} }
wxString AssFile::GetScriptInfo(wxString key) const { 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 // Script info section not found, so add it at the beginning of the file
else { else {
Line.push_front(*new AssEntry(key + ": " + value, "[Script Info]")); Line.push_front(*new AssEntry(key + ": " + value, "[Script Info]"));
Line.push_front(*new AssEntry("[Script Info]", "[Script Info]"));
} }
} }

View File

@ -102,14 +102,10 @@ public:
/// @brief Load default file /// @brief Load default file
/// @param defline Add a blank line to the file /// @param defline Add a blank line to the file
void LoadDefault(bool defline=true); void LoadDefault(bool defline=true);
/// Add a style to the file /// Add a line to the file at the end of the appropriate section
void InsertStyle(AssStyle *style); void InsertLine(AssEntry *line);
/// Add an attachment to the file
void InsertAttachment(AssAttachment *attach);
/// Attach a file to the ass file /// Attach a file to the ass file
void InsertAttachment(wxString filename); void InsertAttachment(wxString filename);
/// Add a dialogue line to the file
void InsertDialogue(AssDialogue *diag);
/// Get the names of all of the styles available /// Get the names of all of the styles available
wxArrayString GetStyles() const; wxArrayString GetStyles() const;
/// @brief Get a style by name /// @brief Get a style by name

View File

@ -72,10 +72,6 @@ void AssParser::ParseScriptInfoLine(wxString const& data) {
return; 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:")) { if (data.StartsWith("ScriptType:")) {
wxString versionString = data.Mid(11).Trim(true).Trim(false).Lower(); wxString versionString = data.Mid(11).Trim(true).Trim(false).Lower();
int trueVersion; int trueVersion;
@ -97,15 +93,11 @@ void AssParser::ParseScriptInfoLine(wxString const& data) {
void AssParser::ParseEventLine(wxString const& data) { void AssParser::ParseEventLine(wxString const& data) {
if (data.StartsWith("Dialogue:") || data.StartsWith("Comment:")) if (data.StartsWith("Dialogue:") || data.StartsWith("Comment:"))
target->Line.push_back(*new AssDialogue(data)); 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) { void AssParser::ParseStyleLine(wxString const& data) {
if (data.StartsWith("Style:")) if (data.StartsWith("Style:"))
target->Line.push_back(*new AssStyle(data, version)); 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) { void AssParser::ParseFontLine(wxString const& data) {
@ -150,23 +142,16 @@ void AssParser::AddLine(wxString const& data) {
version = 1; version = 1;
state = &AssParser::ParseStyleLine; state = &AssParser::ParseStyleLine;
} }
else if (low == "[events]") { else if (low == "[events]")
state = &AssParser::ParseEventLine; state = &AssParser::ParseEventLine;
} else if (low == "[script info]")
else if (low == "[script info]") {
state = &AssParser::ParseScriptInfoLine; state = &AssParser::ParseScriptInfoLine;
} else if (low == "[graphics]")
else if (low == "[graphics]") {
state = &AssParser::ParseGraphicsLine; state = &AssParser::ParseGraphicsLine;
} else if (low == "[fonts]")
else if (low == "[fonts]") {
state = &AssParser::ParseFontLine; state = &AssParser::ParseFontLine;
} else
else {
state = &AssParser::AppendUnknownLine; state = &AssParser::AppendUnknownLine;
}
target->Line.push_back(*new AssEntry(header, header));
return; return;
} }

View File

@ -213,23 +213,11 @@ namespace Automation4 {
if (StringEmptyOrWhitespace(raw)) { if (StringEmptyOrWhitespace(raw)) {
set_field(L, "class", "clear"); 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]") { else if (e->group.Lower() == "[script info]") {
set_field(L, "key", raw.BeforeFirst(':')); set_field(L, "key", raw.BeforeFirst(':'));
set_field(L, "value", raw.AfterFirst(':')); set_field(L, "value", raw.AfterFirst(':'));
set_field(L, "class", "info"); 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<AssDialogue*>(e)) { else if (AssDialogue *dia = dynamic_cast<AssDialogue*>(e)) {
set_field(L, "comment", dia->Comment); set_field(L, "comment", dia->Comment);
@ -317,20 +305,9 @@ namespace Automation4 {
try { try {
wxString section = get_wxstring_field(L, "section", "common"); wxString section = get_wxstring_field(L, "section", "common");
if (lclass == "clear") if (lclass == "info") {
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") {
result = new AssEntry(wxString::Format("%s: %s", get_wxstring_field(L, "key", "info"), get_wxstring_field(L, "value", "info")), "[Script 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") { else if (lclass == "style") {
AssStyle *sty = new AssStyle; AssStyle *sty = new AssStyle;
result = sty; result = sty;
@ -559,17 +536,11 @@ namespace Automation4 {
if (it == lines.end() || (*it)->group != e->group) { if (it == lines.end() || (*it)->group != e->group) {
// The new entry belongs to a group that doesn't exist yet, so // The new entry belongs to a group that doesn't exist yet, so
// create it at the end of the file // 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); lines.push_back(e);
} }
else { else {
// Append the entry to the end of the existing group // Append the entry to the end of the existing group
++it; lines.insert(++it, e);
lines.insert(it, e);
} }
} }
} }

View File

@ -528,7 +528,7 @@ static void delete_lines(agi::Context *c, wxString const& commit_message) {
// lines, so make a new one // lines, so make a new one
if (!new_active) { if (!new_active) {
new_active = new AssDialogue; new_active = new AssDialogue;
c->ass->InsertDialogue(new_active); c->ass->InsertLine(new_active);
} }
c->ass->Commit(commit_message, AssFile::COMMIT_DIAG_ADDREM); c->ass->Commit(commit_message, AssFile::COMMIT_DIAG_ADDREM);

View File

@ -142,7 +142,7 @@ void DialogAttachments::AttachFile(wxFileDialog &diag, wxString const& group, wx
delete newAttach; delete newAttach;
return; return;
} }
ass->InsertAttachment(newAttach); ass->InsertLine(newAttach);
} }
ass->Commit(commit_msg, AssFile::COMMIT_ATTACHMENT); ass->Commit(commit_msg, AssFile::COMMIT_ATTACHMENT);
@ -211,30 +211,6 @@ void DialogAttachments::OnDelete(wxCommandEvent &) {
i = listView->GetNextSelected(i); 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); ass->Commit(_("remove attachment"), AssFile::COMMIT_ATTACHMENT);
UpdateList(); UpdateList();

View File

@ -465,7 +465,7 @@ void DialogStyleEditor::Apply(bool apply, bool close) {
if (store) if (store)
store->push_back(style); store->push_back(style);
else else
c->ass->InsertStyle(style); c->ass->InsertLine(style);
is_new = false; is_new = false;
} }
if (!store) if (!store)

View File

@ -445,7 +445,7 @@ void DialogStyleManager::OnCopyToCurrent() {
} }
} }
if (addStyle) { if (addStyle) {
c->ass->InsertStyle(new AssStyle(*Store[selections[i]])); c->ass->InsertLine(new AssStyle(*Store[selections[i]]));
copied.push_back(styleName); copied.push_back(styleName);
} }
} }
@ -477,7 +477,7 @@ void DialogStyleManager::CopyToClipboard(wxListBox *list, T const& v) {
void DialogStyleManager::PasteToCurrent() { void DialogStyleManager::PasteToCurrent() {
add_styles( add_styles(
std::bind(&AssFile::GetStyle, c->ass, _1), 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); c->ass->Commit(_("style paste"), AssFile::COMMIT_STYLES);
} }
@ -626,7 +626,7 @@ void DialogStyleManager::OnCurrentImport() {
modified = true; modified = true;
AssStyle *tempStyle = new AssStyle; AssStyle *tempStyle = new AssStyle;
*tempStyle = *temp.GetStyle(styles[sel]); *tempStyle = *temp.GetStyle(styles[sel]);
c->ass->InsertStyle(tempStyle); c->ass->InsertLine(tempStyle);
} }
// Update // Update

View File

@ -60,7 +60,7 @@ SubtitlesPreview::SubtitlesPreview(wxWindow *parent, wxSize size, int winStyle,
SetStyle(*style); SetStyle(*style);
subFile->LoadDefault(); subFile->LoadDefault();
subFile->InsertStyle(style); subFile->InsertLine(style);
subFile->Line.push_back(*line); subFile->Line.push_back(*line);
SetSizeHints(size.GetWidth(), size.GetHeight(), -1, -1); SetSizeHints(size.GetWidth(), size.GetHeight(), -1, -1);

View File

@ -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 { void AssSubtitleFormat::WriteFile(const AssFile *src, wxString const& filename, wxString const& encoding) const {
TextFileWriter file(filename, encoding); TextFileWriter file(filename, encoding);
@ -90,15 +120,20 @@ void AssSubtitleFormat::WriteFile(const AssFile *src, wxString const& filename,
file.WriteLineToFile("; http://www.aegisub.org/"); file.WriteLineToFile("; http://www.aegisub.org/");
bool ssa = filename.Right(4).Lower() == ".ssa"; bool ssa = filename.Right(4).Lower() == ".ssa";
wxString group;
wxString group = src->Line.front().group;
for (auto const& line : src->Line) { for (auto const& line : src->Line) {
// Add a blank line between each group
if (line.group != 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; group = line.group;
} }
file.WriteLineToFile(ssa ? line.GetSSAText() : line.GetEntryData(), true); file.WriteLineToFile(ssa ? line.GetSSAText() : line.GetEntryData());
} }
} }