From 15d49fb65514e7be21460322c5e4c879f087d1cf Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Sun, 8 Jan 2012 01:34:30 +0000 Subject: [PATCH] Revamp AssFile::AddLine and related functions to eliminate the statics and hopefully make it less brittle Originally committed to SVN as r6231. --- aegisub/src/ass_file.cpp | 326 ++++++++++++---------------- aegisub/src/ass_file.h | 9 +- aegisub/src/ass_style.cpp | 31 +-- aegisub/src/ass_style.h | 1 - aegisub/src/mkv_wrap.cpp | 18 +- aegisub/src/subtitle_format.cpp | 4 +- aegisub/src/subtitle_format.h | 10 +- aegisub/src/subtitle_format_ass.cpp | 37 +--- 8 files changed, 156 insertions(+), 280 deletions(-) diff --git a/aegisub/src/ass_file.cpp b/aegisub/src/ass_file.cpp index 3ba79f696..9aa609965 100644 --- a/aegisub/src/ass_file.cpp +++ b/aegisub/src/ass_file.cpp @@ -273,90 +273,92 @@ bool AssFile::CanSave() { return true; } -// I strongly advice you against touching this function unless you know what you're doing; -// even moving things out of order might break ASS parsing - AMZ. -void AssFile::AddLine(wxString data,wxString group,int &version,wxString *outGroup) { +void AssFile::AddLine(wxString data, int *version, AssAttachment **attach) { + // Is this line an attachment filename? + bool isFilename = 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) { + // 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; + } + } + + // Data is over, add attachment to the file + if (!validData || isFilename) { + (*attach)->Finish(); + Line.push_back(*attach); + *attach = NULL; + } + else { + // Insert data + (*attach)->AddData(data); + + // Done building + if (data.Length() < 80) { + (*attach)->Finish(); + Line.push_back(*attach); + *attach = NULL; + return; + } + } + } + if (data.empty()) return; - // Group - wxString origGroup = group; - static wxString keepGroup; - if (!keepGroup.empty()) group = keepGroup; - if (outGroup) *outGroup = group; + // Section header + if (data[0] == '[' && data.Last() == ']') { + // Ugly hacks to allow intermixed v4 and v4+ style sections + wxString low = data.Lower(); + if (low == "[v4 styles]") { + data = "[V4+ Styles]"; + *version = 0; + } + else if (low == "[v4+ styles]") { + data = "[V4+ Styles]"; + *version = 1; + } + else if (low == "[v4++ styles]") { + data = "[V4+ Styles]"; + *version = 2; + } + + Line.push_back(new AssEntry(data, data)); + return; + } + + // If the first nonblank line isn't a header pretend it starts with [Script Info] + if (Line.empty()) + Line.push_back(new AssEntry("[Script Info]", "[Script Info]")); + + wxString group = Line.back()->group; wxString lowGroup = group.Lower(); // Attachment if (lowGroup == "[fonts]" || lowGroup == "[graphics]") { - // Check if it's valid data - size_t dataLen = data.Length(); - bool validData = (dataLen > 0) && (dataLen <= 80); - for (size_t i=0;i= 97) validData = false; - } - - // Is the filename line? - bool isFilename = (data.StartsWith("fontname: ") || data.StartsWith("filename: ")); - - // The attachment file is static, since it is built through several calls to this - // After it's done building, it's reset to NULL - static AssAttachment *attach = NULL; - - // Attachment exists, and data is over - if (attach && (!validData || isFilename)) { - attach->Finish(); - keepGroup.Clear(); - group = origGroup; - lowGroup = group.Lower(); - Line.push_back(attach); - attach = NULL; - } - - // Create attachment if needed if (isFilename) { - attach = new AssAttachment(data.Mid(10)); - attach->group = group; - keepGroup = group; - return; - } - - // Valid data? - if (attach && validData) { - // Insert data - attach->AddData(data); - - // Done building - if (data.Length() < 80) { - attach->Finish(); - keepGroup.Clear(); - group = origGroup; - lowGroup = group.Lower(); - Line.push_back(attach); - attach = NULL; - } - - // Not done - else { - return; - } + *attach = new AssAttachment(data.Mid(10)); + (*attach)->group = group; } } // Dialogue else if (lowGroup == "[events]") { if (data.StartsWith("Dialogue:") || data.StartsWith("Comment:")) - Line.push_back(new AssDialogue(data,version)); + Line.push_back(new AssDialogue(data, *version)); else if (data.StartsWith("Format:")) Line.push_back(new AssEntry("Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text", group)); - else - Line.push_back(new AssEntry(data, group)); } // Style else if (lowGroup == "[v4+ styles]") { if (data.StartsWith("Style:")) - Line.push_back(new AssStyle(data,version)); + Line.push_back(new AssStyle(data, *version)); else if (data.StartsWith("Format:")) 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)); - else - Line.push_back(new AssEntry(data, group)); } // Script info else if (lowGroup == "[script info]") { @@ -367,20 +369,21 @@ void AssFile::AddLine(wxString data,wxString group,int &version,wxString *outGro return; } - // Version if (data.StartsWith("ScriptType:")) { - wxString versionString = data.Mid(11); - versionString.Trim(true); - versionString.Trim(false); - versionString.MakeLower(); + 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 if (versionString == "v4.00++") trueVersion = 2; - else throw "Unknown SSA file format version"; - if (trueVersion != version) { - if (!(trueVersion == 2 && version == 1)) wxLogMessage("Warning: File has the wrong extension."); - version = trueVersion; + if (versionString == "v4.00") + trueVersion = 0; + else if (versionString == "v4.00+") + trueVersion = 1; + else if (versionString == "v4.00++") + trueVersion = 2; + else throw + "Unknown SSA file format version"; + if (trueVersion != *version) { + if (!(trueVersion == 2 && *version == 1)) + wxLogMessage("Warning: File has the wrong extension."); + *version = trueVersion; } } @@ -407,30 +410,26 @@ void AssFile::LoadDefault(bool defline) { Clear(); // Write headers - AssStyle defstyle; + AssAttachment *attach = 0; int version = 1; - AddLine("[Script Info]","[Script Info]",version); - AddLine("Title: Default Aegisub file","[Script Info]",version); - AddLine("ScriptType: v4.00+","[Script Info]",version); - AddLine("WrapStyle: 0", "[Script Info]",version); - AddLine("ScaledBorderAndShadow: yes","[Script Info]",version); - AddLine("Collisions: Normal","[Script Info]",version); + AddLine("[Script Info]", &version, &attach); + AddLine("Title: Default Aegisub file", &version, &attach); + AddLine("ScriptType: v4.00+", &version, &attach); + AddLine("WrapStyle: 0", &version, &attach); + AddLine("ScaledBorderAndShadow: yes", &version, &attach); + AddLine("Collisions: Normal", &version, &attach); if (!OPT_GET("Subtitle/Default Resolution/Auto")->GetBool()) { - AddLine(wxString::Format("PlayResX: %" PRId64, OPT_GET("Subtitle/Default Resolution/Width")->GetInt()),"[Script Info]",version); - AddLine(wxString::Format("PlayResY: %" PRId64, OPT_GET("Subtitle/Default Resolution/Height")->GetInt()),"[Script Info]",version); + AddLine(wxString::Format("PlayResX: %" PRId64, OPT_GET("Subtitle/Default Resolution/Width")->GetInt()), &version, &attach); + AddLine(wxString::Format("PlayResY: %" PRId64, OPT_GET("Subtitle/Default Resolution/Height")->GetInt()), &version, &attach); } - AddLine("","[Script Info]",version); - AddLine("[V4+ Styles]","[V4+ Styles]",version); - AddLine("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]",version); - AddLine(defstyle.GetEntryData(),"[V4+ Styles]",version); - AddLine("","[V4+ Styles]",version); - AddLine("[Events]","[Events]",version); - AddLine("Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text","[Events]",version); + AddLine("[V4+ Styles]", &version, &attach); + AddLine("Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding", &version, &attach); + AddLine(AssStyle().GetEntryData(), &version, &attach); + AddLine("[Events]", &version, &attach); + AddLine("Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text", &version, &attach); - if (defline) { - AssDialogue def; - AddLine(def.GetEntryData(),"[Events]",version); - } + if (defline) + AddLine(AssDialogue().GetEntryData(), &version, &attach); Commit("", COMMIT_NEW); savedCommitId = commitId; @@ -460,102 +459,50 @@ AssFile& AssFile::operator=(AssFile from) { return *this; } -void AssFile::InsertStyle (AssStyle *style) { - using std::list; - AssEntry *curEntry; - list::iterator lastStyle = Line.end(); - list::iterator cur; - wxString lastGroup; - - // Look for insert position - for (cur=Line.begin();cur!=Line.end();cur++) { - curEntry = *cur; - if (curEntry->GetType() == ENTRY_STYLE || (lastGroup == "[V4+ Styles]" && curEntry->GetEntryData().substr(0,7) == "Format:")) { - lastStyle = cur; +void AssFile::InsertStyle(AssStyle *style) { + // Search for insertion point + std::list::iterator it = Line.end(); + do { + --it; + if ((*it)->group == style->group) { + Line.insert(++it, style); + return; } - lastGroup = curEntry->group; - } + } while (it != Line.begin()); // No styles found, add them - if (lastStyle == Line.end()) { - // Add space - curEntry = new AssEntry(""); - curEntry->group = lastGroup; - Line.push_back(curEntry); - - // Add header - curEntry = new AssEntry("[V4+ Styles]"); - curEntry->group = "[V4+ Styles]"; - Line.push_back(curEntry); - - // Add format line - curEntry = 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"); - curEntry->group = "[V4+ Styles]"; - Line.push_back(curEntry); - - // Add style - style->group = "[V4+ Styles]"; - Line.push_back(style); - } - - // Add to end of list - else { - lastStyle++; - style->group = (*lastStyle)->group; - Line.insert(lastStyle,style); - } + 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) { +void AssFile::InsertAttachment(AssAttachment *attach) { // Search for insertion point - std::list::iterator insPoint=Line.end(),cur; - for (cur=Line.begin();cur!=Line.end();cur++) { - // Check if it's another attachment - AssAttachment *att = dynamic_cast(*cur); - if (att) { - if (attach->group == att->group) insPoint = cur; + std::list::iterator it = Line.end(); + do { + --it; + if ((*it)->group == attach->group) { + Line.insert(++it, attach); + return; } + } while (it != Line.begin()); - // See if it's the start of group - else if ((*cur)->GetType() == ENTRY_BASE) { - AssEntry *entry = (AssEntry*) (*cur); - if (entry->GetEntryData() == attach->group) insPoint = cur; - } - } - - // Found point, insert there - if (insPoint != Line.end()) { - insPoint++; - Line.insert(insPoint,attach); - } - - // Otherwise, create the [Fonts] group and insert - else { - int version=1; - AddLine("",Line.back()->group,version); - AddLine(attach->group,attach->group,version); - Line.push_back(attach); - AddLine("",attach->group,version); - } + // 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) { - wxFileName fname(filename); - AssAttachment *newAttach = new AssAttachment(fname.GetFullName()); - - try { - newAttach->Import(filename); - } - catch (...) { - delete newAttach; - throw; - } + std::auto_ptr newAttach(new AssAttachment(wxFileName(filename).GetFullName())); + newAttach->Import(filename); // Insert wxString ext = filename.Right(4).Lower(); - if (ext == ".ttf" || ext == ".ttc" || ext == ".pfb") newAttach->group = "[Fonts]"; - else newAttach->group = "[Graphics]"; - InsertAttachment(newAttach); + if (ext == ".ttf" || ext == ".ttc" || ext == ".pfb") + newAttach->group = "[Fonts]"; + else + newAttach->group = "[Graphics]"; + InsertAttachment(newAttach.release()); } wxString AssFile::GetScriptInfo(wxString key) { @@ -638,22 +585,18 @@ void AssFile::GetResolution(int &sw,int &sh) { } } -void AssFile::AddComment(const wxString _comment) { - wxString comment = "; "; - comment += _comment; - std::list::iterator cur; +void AssFile::AddComment(wxString comment) { + comment.Prepend("; "); int step = 0; - for (cur=Line.begin();cur!=Line.end();cur++) { + for (std::list::iterator cur = Line.begin(); cur != Line.end(); ++cur) { // Start of group - if (step == 0 && (*cur)->group == "[Script Info]") step = 1; + if (step == 0 && (*cur)->group == "[Script Info]") + step = 1; // First line after a ; else if (step == 1 && !(*cur)->GetEntryData().StartsWith(";")) { - AssEntry *prev = *cur; - AssEntry *comm = new AssEntry(comment); - comm->group = prev->group; - Line.insert(cur,comm); + Line.insert(cur, new AssEntry(comment, "[Script Info]")); break; } } @@ -661,12 +604,9 @@ void AssFile::AddComment(const wxString _comment) { wxArrayString AssFile::GetStyles() { wxArrayString styles; - AssStyle *curstyle; - for (entryIter cur=Line.begin();cur!=Line.end();cur++) { - curstyle = dynamic_cast(*cur); - if (curstyle) { + for (entryIter cur = Line.begin(); cur != Line.end(); ++cur) { + if (AssStyle *curstyle = dynamic_cast(*cur)) styles.Add(curstyle->name); - } } return styles; } diff --git a/aegisub/src/ass_file.h b/aegisub/src/ass_file.h index dac9c7cbf..a0bb9589f 100644 --- a/aegisub/src/ass_file.h +++ b/aegisub/src/ass_file.h @@ -159,13 +159,12 @@ public: /// Set the value of a [Script Info] key. Adds it if it doesn't exist. void SetScriptInfo(wxString const& key, wxString const& value); // Add a ";" comment in the [Script Info] section - void AddComment(const wxString comment); + void AddComment(wxString comment); /// @brief Add a line to the file /// @param data Full text of ASS line - /// @param group Section of the file to add the line to - /// @param[out] version ASS version the line was parsed as - /// @param[out] outGroup Group it was actually added to; attachments do something strange here - void AddLine(wxString data,wxString group,int &version,wxString *outGroup=NULL); + /// @param[in,out] version ASS version the line was parsed as + /// @param[in,out] attach Accumulator for attachment parsing + void AddLine(wxString data, int *version, AssAttachment **attach); /// Type of changes made in a commit enum CommitType { diff --git a/aegisub/src/ass_style.cpp b/aegisub/src/ass_style.cpp index 4682799d7..c190e21ad 100644 --- a/aegisub/src/ass_style.cpp +++ b/aegisub/src/ass_style.cpp @@ -173,35 +173,6 @@ AssStyle::AssStyle() UpdateData(); } -AssStyle::AssStyle(const AssStyle& s) -: AssEntry(s) -, name(s.name) -, font(s.font) -, fontsize(s.fontsize) -, primary(s.primary) -, secondary(s.secondary) -, outline(s.outline) -, shadow(s.shadow) -, bold(s.bold) -, italic(s.italic) -, underline(s.underline) -, strikeout(s.strikeout) -, scalex(s.scalex) -, scaley(s.scaley) -, spacing(s.spacing) -, angle(s.angle) -, borderstyle(s.borderstyle) -, outline_w(s.outline_w) -, shadow_w(s.shadow_w) -, alignment(s.alignment) -, encoding(s.encoding) -, relativeTo(s.relativeTo) -{ - group = "[V4+ Styles]"; - memcpy(Margin, s.Margin, sizeof(Margin)); - SetEntryData(s.GetEntryData()); -} - static wxString get_next_string(wxStringTokenizer &tok) { if (!tok.HasMoreTokens()) throw "Malformed style: not enough fields"; return tok.GetNextToken(); @@ -222,6 +193,8 @@ static int get_next_double(wxStringTokenizer &tok) { } AssStyle::AssStyle(wxString rawData,int version) { + group = "[V4+ Styles]"; + wxStringTokenizer tkn(rawData.Trim(false).Mid(6), ",", wxTOKEN_RET_EMPTY_ALL); name = get_next_string(tkn).Trim(true).Trim(false); diff --git a/aegisub/src/ass_style.h b/aegisub/src/ass_style.h index 5b822b882..f0e2c1071 100644 --- a/aegisub/src/ass_style.h +++ b/aegisub/src/ass_style.h @@ -107,7 +107,6 @@ public: static void GetEncodings(wxArrayString &encodingStrings); AssStyle(); - AssStyle(AssStyle const&); AssStyle(wxString data,int version=1); wxString GetSSAText() const; diff --git a/aegisub/src/mkv_wrap.cpp b/aegisub/src/mkv_wrap.cpp index 6d82e03b1..aca2fe021 100644 --- a/aegisub/src/mkv_wrap.cpp +++ b/aegisub/src/mkv_wrap.cpp @@ -126,10 +126,10 @@ static void read_subtitles(agi::ProgressSink *ps, MatroskaFile *file, MkvStdIO * delete[] readBuf; // Insert into file - wxString group = "[Events]"; int version = ssa; + AssAttachment *attach = 0; for (std::map::iterator it = subList.begin(); it != subList.end(); ++it) { - target->AddLine(it->second, group, version, &group); + target->AddLine(it->second, &version, &attach); } } @@ -196,15 +196,11 @@ void MatroskaWrapper::GetSubtitles(wxString const& filename, AssFile *target) { wxString privString((const char *)trackInfo->CodecPrivate, wxConvUTF8, trackInfo->CodecPrivateSize); // Load into file - wxString group = "[Script Info]"; - int version = 1; - if (CodecID == "S_TEXT/SSA") version = 0; - wxStringTokenizer token(privString,"\r\n",wxTOKEN_STRTOK); - while (token.HasMoreTokens()) { - wxString next = token.GetNextToken(); - if (next[0] == '[') group = next; - target->AddLine(next,group,version,&group); - } + int version = !ssa; + AssAttachment *attach = 0; + wxStringTokenizer token(privString, "\r\n", wxTOKEN_STRTOK); + while (token.HasMoreTokens()) + target->AddLine(token.GetNextToken(), &version, &attach); } // Load default if it's SRT else { diff --git a/aegisub/src/subtitle_format.cpp b/aegisub/src/subtitle_format.cpp index a3f1682a8..8ea59c106 100644 --- a/aegisub/src/subtitle_format.cpp +++ b/aegisub/src/subtitle_format.cpp @@ -104,8 +104,8 @@ void SubtitleFormat::LoadDefault(bool defline) { assFile->LoadDefault(defline); } -void SubtitleFormat::AddLine(wxString data, wxString group, int &version, wxString *outgroup) { - assFile->AddLine(data, group, version, outgroup); +void SubtitleFormat::AddLine(wxString data, int *version, AssAttachment **attach) { + assFile->AddLine(data, version, attach); } /// @brief Ask the user to enter the FPS diff --git a/aegisub/src/subtitle_format.h b/aegisub/src/subtitle_format.h index 6e274c5ee..8a489b663 100644 --- a/aegisub/src/subtitle_format.h +++ b/aegisub/src/subtitle_format.h @@ -45,8 +45,9 @@ #include -class AssFile; +class AssAttachment; class AssEntry; +class AssFile; class FractionalTime; /// DOCME @@ -102,9 +103,10 @@ protected: AssFile *GetAssFile() { return assFile; } /// Add a line to the output file - /// @param data Line data - /// @param group File section - void AddLine(wxString data,wxString group,int &version,wxString *outgroup=NULL); + /// @param data Full text of ASS line + /// @param[in,out] version ASS version the line was parsed as + /// @param[in,out] attach Accumulator for attachment parsing + void AddLine(wxString data, int *version, AssAttachment **attach); /// Prompt the user for a framerate to use /// @param showSMPTE Include SMPTE as an option? FractionalTime AskForFPS(bool showSMPTE=false); diff --git a/aegisub/src/subtitle_format_ass.cpp b/aegisub/src/subtitle_format_ass.cpp index 455c6d282..465c7734a 100644 --- a/aegisub/src/subtitle_format_ass.cpp +++ b/aegisub/src/subtitle_format_ass.cpp @@ -69,45 +69,12 @@ void ASSSubtitleFormat::ReadFile(wxString const& filename, wxString const& encod TextFileReader file(filename, encoding); int version = filename.Right(4).Lower() != ".ssa"; + AssAttachment *attach = 0; - wxString curgroup; while (file.HasMoreLines()) { wxString line = file.ReadLineFromFile(); - - // Make sure that the first non-blank non-comment non-group-header line - // is really [Script Info] - if (curgroup.empty() && !line.empty() && line[0] != ';' && line[0] != '[') { - curgroup = "[Script Info]"; - AddLine(curgroup, curgroup, version, &curgroup); - } - - // Convert v4 styles to v4+ styles - if (!line.empty() && line[0] == '[') { - // Ugly hacks to allow intermixed v4 and v4+ style sections - wxString low = line.Lower(); - if (low == "[v4 styles]") { - line = "[V4+ Styles]"; - curgroup = line; - version = 0; - } - else if (low == "[v4+ styles]") { - line = "[V4+ Styles]"; - curgroup = line; - version = 1; - } - else if (low == "[v4++ styles]") { - line = "[V4+ Styles]"; - curgroup = line; - version = 2; - } - // Not-so-special case for other groups, just set it - else { - curgroup = line; - } - } - try { - AddLine(line, curgroup, version, &curgroup); + AddLine(line, &version, &attach); } catch (const char *err) { Clear();