Fix crash on (auto)save when using CSRI and video is open

Calling AssFile::Save/Load from multiple threads (even on different
objects) was not safe due to that is uses SubtitleFormat internally,
which was inheriently thread-unsafe. To fix this, change
SubtitleFormat's interface to support immutable implementations, and
make all of the current implementations immutable.

This isn't a perfect solution - making a subtitle format implemented in
lua immutable would be rather difficult - so at some point in the future
SubtitleFormat should probably be changed to a factory which returns new
objects from GetReader/GetWriter.

Originally committed to SVN as r6365.
This commit is contained in:
Thomas Goyne 2012-01-26 20:08:38 +00:00
parent d001d66b12
commit 15a4eca7ce
19 changed files with 221 additions and 313 deletions

View File

@ -88,7 +88,7 @@ void AssFile::Load(const wxString &_filename,wxString charset,bool addToRecent)
} }
// Get proper format reader // Get proper format reader
SubtitleFormat *reader = SubtitleFormat::GetReader(_filename); const SubtitleFormat *reader = SubtitleFormat::GetReader(_filename);
if (!reader) { if (!reader) {
wxMessageBox("Unknown file type","Error loading file",wxICON_ERROR | wxOK); wxMessageBox("Unknown file type","Error loading file",wxICON_ERROR | wxOK);
@ -97,8 +97,7 @@ void AssFile::Load(const wxString &_filename,wxString charset,bool addToRecent)
// Read file // Read file
AssFile temp; AssFile temp;
reader->SetTarget(&temp); reader->ReadFile(&temp, _filename, charset);
reader->ReadFile(_filename,charset);
swap(temp); swap(temp);
} }
catch (agi::UserCancelException const&) { catch (agi::UserCancelException const&) {
@ -156,7 +155,7 @@ void AssFile::Load(const wxString &_filename,wxString charset,bool addToRecent)
} }
void AssFile::Save(wxString filename, bool setfilename, bool addToRecent, wxString encoding) { void AssFile::Save(wxString filename, bool setfilename, bool addToRecent, wxString encoding) {
SubtitleFormat *writer = SubtitleFormat::GetWriter(filename); const SubtitleFormat *writer = SubtitleFormat::GetWriter(filename);
if (!writer) if (!writer)
throw "Unknown file type."; throw "Unknown file type.";
@ -168,8 +167,7 @@ void AssFile::Save(wxString filename, bool setfilename, bool addToRecent, wxStri
FileSave(); FileSave();
writer->SetTarget(this); writer->WriteFile(this, filename, encoding);
writer->WriteFile(filename, encoding);
if (addToRecent) { if (addToRecent) {
AddToRecent(filename); AddToRecent(filename);
@ -229,8 +227,7 @@ bool AssFile::CanSave() {
if (ext == ".txt") return false; if (ext == ".txt") return false;
// Check if it's a known extension // Check if it's a known extension
SubtitleFormat *writer = SubtitleFormat::GetWriter(filename); if (!SubtitleFormat::GetWriter(filename)) return false;
if (!writer) return false;
// Scan through the lines // Scan through the lines
AssStyle defstyle; AssStyle defstyle;

View File

@ -59,8 +59,6 @@ using namespace std::tr1::placeholders;
SubtitleFormat::SubtitleFormat(wxString const& name) SubtitleFormat::SubtitleFormat(wxString const& name)
: name(name) : name(name)
, isCopy(0)
, Line(0)
{ {
formats.push_back(this); formats.push_back(this);
} }
@ -69,12 +67,6 @@ SubtitleFormat::~SubtitleFormat() {
formats.remove(this); formats.remove(this);
} }
void SubtitleFormat::SetTarget(AssFile *file) {
ClearCopy();
Line = file ? &file->Line : 0;
assFile = file;
}
bool SubtitleFormat::CanReadFile(wxString const& filename) const { bool SubtitleFormat::CanReadFile(wxString const& filename) const {
return GetReadWildcards().Index(filename.AfterLast('.'), false) != wxNOT_FOUND; return GetReadWildcards().Index(filename.AfterLast('.'), false) != wxNOT_FOUND;
} }
@ -83,33 +75,8 @@ bool SubtitleFormat::CanWriteFile(wxString const& filename) const {
return GetWriteWildcards().Index(filename.AfterLast('.'), false) != wxNOT_FOUND; return GetWriteWildcards().Index(filename.AfterLast('.'), false) != wxNOT_FOUND;
} }
void SubtitleFormat::CreateCopy() {
SetTarget(new AssFile(*assFile));
isCopy = true;
}
void SubtitleFormat::ClearCopy() {
if (isCopy) {
delete assFile;
assFile = NULL;
isCopy = false;
}
}
void SubtitleFormat::Clear() {
assFile->Clear();
}
void SubtitleFormat::LoadDefault(bool defline) {
assFile->LoadDefault(defline);
}
void SubtitleFormat::AddLine(wxString data, int *version, AssAttachment **attach) {
assFile->AddLine(data, version, attach);
}
/// @brief Ask the user to enter the FPS /// @brief Ask the user to enter the FPS
FractionalTime SubtitleFormat::AskForFPS(bool showSMPTE) { FractionalTime SubtitleFormat::AskForFPS(bool showSMPTE) const {
wxArrayString choices; wxArrayString choices;
bool drop = false; bool drop = false;
@ -171,20 +138,16 @@ FractionalTime SubtitleFormat::AskForFPS(bool showSMPTE) {
return FractionalTime(fps, drop); return FractionalTime(fps, drop);
} }
void SubtitleFormat::SortLines() { void SubtitleFormat::StripTags(LineList &lines) const {
AssFile::Sort(*Line); for (LineList::iterator cur = lines.begin(); cur != lines.end(); ++cur) {
}
void SubtitleFormat::StripTags() {
for (std::list<AssEntry*>::iterator cur = Line->begin(); cur != Line->end(); ++cur) {
if (AssDialogue *current = dynamic_cast<AssDialogue*>(*cur)) { if (AssDialogue *current = dynamic_cast<AssDialogue*>(*cur)) {
current->StripTags(); current->StripTags();
} }
} }
} }
void SubtitleFormat::ConvertNewlines(wxString const& newline, bool mergeLineBreaks) { void SubtitleFormat::ConvertNewlines(LineList &lines, wxString const& newline, bool mergeLineBreaks) const {
for (std::list<AssEntry*>::iterator cur = Line->begin(); cur != Line->end(); ++cur) { for (LineList::iterator cur = lines.begin(); cur != lines.end(); ++cur) {
if (AssDialogue *current = dynamic_cast<AssDialogue*>(*cur)) { if (AssDialogue *current = dynamic_cast<AssDialogue*>(*cur)) {
current->Text.Replace("\\h", " "); current->Text.Replace("\\h", " ");
current->Text.Replace("\\n", newline); current->Text.Replace("\\n", newline);
@ -196,25 +159,25 @@ void SubtitleFormat::ConvertNewlines(wxString const& newline, bool mergeLineBrea
} }
} }
void SubtitleFormat::StripComments() { void SubtitleFormat::StripComments(LineList &lines) const {
for (std::list<AssEntry*>::iterator it = Line->begin(); it != Line->end(); ) { for (LineList::iterator it = lines.begin(); it != lines.end(); ) {
AssDialogue *diag = dynamic_cast<AssDialogue*>(*it); AssDialogue *diag = dynamic_cast<AssDialogue*>(*it);
if (!diag || (!diag->Comment && diag->Text.size())) if (!diag || (!diag->Comment && diag->Text.size()))
++it; ++it;
else { else {
delete *it; delete *it;
Line->erase(it++); lines.erase(it++);
} }
} }
} }
void SubtitleFormat::StripNonDialogue() { void SubtitleFormat::StripNonDialogue(LineList &lines) const {
for (std::list<AssEntry*>::iterator it = Line->begin(); it != Line->end(); ) { for (LineList::iterator it = lines.begin(); it != lines.end(); ) {
if (dynamic_cast<AssDialogue*>(*it)) if (dynamic_cast<AssDialogue*>(*it))
++it; ++it;
else { else {
delete *it; delete *it;
Line->erase(it++); lines.erase(it++);
} }
} }
} }
@ -227,11 +190,11 @@ static bool dialog_start_lt(AssEntry *pos, AssDialogue *to_insert) {
/// @brief Split and merge lines so there are no overlapping lines /// @brief Split and merge lines so there are no overlapping lines
/// ///
/// Algorithm described at http://devel.aegisub.org/wiki/Technical/SplitMerge /// Algorithm described at http://devel.aegisub.org/wiki/Technical/SplitMerge
void SubtitleFormat::RecombineOverlaps() { void SubtitleFormat::RecombineOverlaps(LineList &lines) const {
std::list<AssEntry*>::iterator cur, next = Line->begin(); LineList::iterator cur, next = lines.begin();
cur = next++; cur = next++;
for (; next != Line->end(); cur = next++) { for (; next != lines.end(); cur = next++) {
AssDialogue *prevdlg = dynamic_cast<AssDialogue*>(*cur); AssDialogue *prevdlg = dynamic_cast<AssDialogue*>(*cur);
AssDialogue *curdlg = dynamic_cast<AssDialogue*>(*next); AssDialogue *curdlg = dynamic_cast<AssDialogue*>(*next);
@ -240,15 +203,15 @@ void SubtitleFormat::RecombineOverlaps() {
// Use names like in the algorithm description and prepare for erasing // Use names like in the algorithm description and prepare for erasing
// old dialogues from the list // old dialogues from the list
std::list<AssEntry*>::iterator prev = cur; LineList::iterator prev = cur;
cur = next; cur = next;
next++; next++;
// std::list::insert() inserts items before the given iterator, so // std::list::insert() inserts items before the given iterator, so
// we need 'next' for inserting. 'prev' and 'cur' can safely be erased // we need 'next' for inserting. 'prev' and 'cur' can safely be erased
// from the list now. // from the list now.
Line->erase(prev); lines.erase(prev);
Line->erase(cur); lines.erase(cur);
//Is there an A part before the overlap? //Is there an A part before the overlap?
if (curdlg->Start > prevdlg->Start) { if (curdlg->Start > prevdlg->Start) {
@ -258,7 +221,7 @@ void SubtitleFormat::RecombineOverlaps() {
newdlg->End = curdlg->Start; newdlg->End = curdlg->Start;
newdlg->Text = prevdlg->Text; newdlg->Text = prevdlg->Text;
Line->insert(find_if(next, Line->end(), bind(dialog_start_lt, _1, newdlg)), newdlg); lines.insert(find_if(next, lines.end(), bind(dialog_start_lt, _1, newdlg)), newdlg);
} }
// Overlapping A+B part // Overlapping A+B part
@ -269,7 +232,7 @@ void SubtitleFormat::RecombineOverlaps() {
// Put an ASS format hard linewrap between lines // Put an ASS format hard linewrap between lines
newdlg->Text = curdlg->Text + "\\N" + prevdlg->Text; newdlg->Text = curdlg->Text + "\\N" + prevdlg->Text;
Line->insert(find_if(next, Line->end(), bind(dialog_start_lt, _1, newdlg)), newdlg); lines.insert(find_if(next, lines.end(), bind(dialog_start_lt, _1, newdlg)), newdlg);
} }
// Is there an A part after the overlap? // Is there an A part after the overlap?
@ -280,7 +243,7 @@ void SubtitleFormat::RecombineOverlaps() {
newdlg->End = prevdlg->End; newdlg->End = prevdlg->End;
newdlg->Text = prevdlg->Text; newdlg->Text = prevdlg->Text;
Line->insert(find_if(next, Line->end(), bind(dialog_start_lt, _1, newdlg)), newdlg); lines.insert(find_if(next, lines.end(), bind(dialog_start_lt, _1, newdlg)), newdlg);
} }
// Is there a B part after the overlap? // Is there a B part after the overlap?
@ -291,7 +254,7 @@ void SubtitleFormat::RecombineOverlaps() {
newdlg->End = curdlg->End; newdlg->End = curdlg->End;
newdlg->Text = curdlg->Text; newdlg->Text = curdlg->Text;
Line->insert(find_if(next, Line->end(), bind(dialog_start_lt, _1, newdlg)), newdlg); lines.insert(find_if(next, lines.end(), bind(dialog_start_lt, _1, newdlg)), newdlg);
} }
next--; next--;
@ -299,11 +262,11 @@ void SubtitleFormat::RecombineOverlaps() {
} }
/// @brief Merge identical lines that follow each other /// @brief Merge identical lines that follow each other
void SubtitleFormat::MergeIdentical() { void SubtitleFormat::MergeIdentical(LineList &lines) const {
std::list<AssEntry*>::iterator cur, next = Line->begin(); LineList::iterator cur, next = lines.begin();
cur = next++; cur = next++;
for (; next != Line->end(); cur = next++) { for (; next != lines.end(); cur = next++) {
AssDialogue *curdlg = dynamic_cast<AssDialogue*>(*cur); AssDialogue *curdlg = dynamic_cast<AssDialogue*>(*cur);
AssDialogue *nextdlg = dynamic_cast<AssDialogue*>(*next); AssDialogue *nextdlg = dynamic_cast<AssDialogue*>(*next);
@ -314,7 +277,7 @@ void SubtitleFormat::MergeIdentical() {
// Remove duplicate line // Remove duplicate line
delete *cur; delete *cur;
Line->erase(cur); lines.erase(cur);
} }
} }
} }
@ -323,14 +286,14 @@ std::list<SubtitleFormat*> SubtitleFormat::formats;
void SubtitleFormat::LoadFormats() { void SubtitleFormat::LoadFormats() {
if (formats.empty()) { if (formats.empty()) {
new ASSSubtitleFormat(); new ASSSubtitleFormat;
new EncoreSubtitleFormat(); new EncoreSubtitleFormat;
new MKVSubtitleFormat(); new MKVSubtitleFormat;
new MicroDVDSubtitleFormat(); new MicroDVDSubtitleFormat;
new SRTSubtitleFormat(); new SRTSubtitleFormat;
new TTXTSubtitleFormat(); new TTXTSubtitleFormat;
new TXTSubtitleFormat(); new TXTSubtitleFormat;
new TranStationSubtitleFormat(); new TranStationSubtitleFormat;
} }
} }
@ -347,12 +310,12 @@ SubtitleFormat *find_or_null(Cont &container, Pred pred) {
return *it; return *it;
} }
SubtitleFormat *SubtitleFormat::GetReader(wxString const& filename) { const SubtitleFormat *SubtitleFormat::GetReader(wxString const& filename) {
LoadFormats(); LoadFormats();
return find_or_null(formats, bind(&SubtitleFormat::CanReadFile, _1, filename)); return find_or_null(formats, bind(&SubtitleFormat::CanReadFile, _1, filename));
} }
SubtitleFormat *SubtitleFormat::GetWriter(wxString const& filename) { const SubtitleFormat *SubtitleFormat::GetWriter(wxString const& filename) {
LoadFormats(); LoadFormats();
return find_or_null(formats, bind(&SubtitleFormat::CanWriteFile, _1, filename)); return find_or_null(formats, bind(&SubtitleFormat::CanWriteFile, _1, filename));
} }

View File

@ -45,7 +45,6 @@
#include <libaegisub/exception.h> #include <libaegisub/exception.h>
class AssAttachment;
class AssEntry; class AssEntry;
class AssFile; class AssFile;
class FractionalTime; class FractionalTime;
@ -57,8 +56,6 @@ class FractionalTime;
/// DOCME /// DOCME
class SubtitleFormat { class SubtitleFormat {
wxString name; wxString name;
bool isCopy;
AssFile *assFile;
/// Get this format's wildcards for a load dialog /// Get this format's wildcards for a load dialog
virtual wxArrayString GetReadWildcards() const { return wxArrayString(); } virtual wxArrayString GetReadWildcards() const { return wxArrayString(); }
@ -69,47 +66,28 @@ class SubtitleFormat {
static std::list<SubtitleFormat*> formats; static std::list<SubtitleFormat*> formats;
protected: protected:
std::list<AssEntry*> *Line; typedef std::list<AssEntry*> LineList;
/// Copy the input subtitles file; must be called before making any changes
void CreateCopy();
/// Delete the subtitle file if we own it; should be called after processing
/// if CreateCopy was used
void ClearCopy();
/// Sort the lines by start time
void SortLines();
/// Strip override tags /// Strip override tags
void StripTags(); void StripTags(LineList &lines) const;
/// Convert newlines to the specified character(s) /// Convert newlines to the specified character(s)
/// @param lineEnd newline character(s) /// @param lineEnd newline character(s)
/// @param mergeLineBreaks Should multiple consecutive line breaks be merged into one? /// @param mergeLineBreaks Should multiple consecutive line breaks be merged into one?
void ConvertNewlines(wxString const& newline, bool mergeLineBreaks = true); void ConvertNewlines(LineList &lines, wxString const& newline, bool mergeLineBreaks = true) const;
/// Remove All commented and empty lines /// Remove All commented and empty lines
void StripComments(); void StripComments(LineList &lines) const;
/// Remove everything but the dialogue lines /// Remove everything but the dialogue lines
void StripNonDialogue(); void StripNonDialogue(LineList &lines) const;
/// @brief Split and merge lines so there are no overlapping lines /// @brief Split and merge lines so there are no overlapping lines
/// ///
/// Algorithm described at http://devel.aegisub.org/wiki/Technical/SplitMerge /// Algorithm described at http://devel.aegisub.org/wiki/Technical/SplitMerge
void RecombineOverlaps(); void RecombineOverlaps(LineList &lines) const;
/// Merge sequential identical lines /// Merge sequential identical lines
void MergeIdentical(); void MergeIdentical(LineList &lines) const;
/// Clear the subtitle file
void Clear();
/// Load the default file
/// @param defline Add a blank line?
void LoadDefault(bool defline=true);
AssFile *GetAssFile() { return assFile; }
/// Add a line to the output file
/// @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 /// Prompt the user for a framerate to use
/// @param showSMPTE Include SMPTE as an option? /// @param showSMPTE Include SMPTE as an option?
FractionalTime AskForFPS(bool showSMPTE=false); FractionalTime AskForFPS(bool showSMPTE=false) const;
public: public:
/// Constructor /// Constructor
@ -120,42 +98,41 @@ public:
/// @note Automatically unregisters the format /// @note Automatically unregisters the format
virtual ~SubtitleFormat(); virtual ~SubtitleFormat();
/// Set the target file to write
void SetTarget(AssFile *file);
/// Get this format's name /// Get this format's name
wxString GetName() const { return name; } wxString GetName() const { return name; }
/// @brief Check if the given file can be read by this format /// @brief Check if the given file can be read by this format
/// ///
/// Default implemention simply checks if the file's extension is in the /// Default implement ion simply checks if the file's extension is in the
/// format's wildcard list /// format's wildcard list
virtual bool CanReadFile(wxString const& filename) const; virtual bool CanReadFile(wxString const& filename) const;
/// @brief Check if the given file can be written by this format /// @brief Check if the given file can be written by this format
/// ///
/// Default implemention simply checks if the file's extension is in the /// Default implement ion simply checks if the file's extension is in the
/// format's wildcard list /// format's wildcard list
virtual bool CanWriteFile(wxString const& filename) const; virtual bool CanWriteFile(wxString const& filename) const;
/// Load a subtitle file /// Load a subtitle file
/// @param[out] target Destination to read lines into
/// @param filename File to load /// @param filename File to load
/// @param forceEncoding Encoding to use or empty string for default /// @param forceEncoding Encoding to use or empty string for default
virtual void ReadFile(wxString const& filename, wxString const& forceEncoding="") { } virtual void ReadFile(AssFile *target, wxString const& filename, wxString const& forceEncoding="") const { }
/// Save a subtitle file /// Save a subtitle file
/// @param src Data to write
/// @param filename File to write to /// @param filename File to write to
/// @param forceEncoding Encoding to use or empty string for default /// @param forceEncoding Encoding to use or empty string for default
virtual void WriteFile(wxString const& filename, wxString const& encoding="") { } virtual void WriteFile(const AssFile *src, wxString const& filename, wxString const& encoding="") const { }
/// Get the wildcards for a save or load dialog /// Get the wildcards for a save or load dialog
/// @param mode 0: load 1: save /// @param mode 0: load 1: save
static wxString GetWildcards(int mode); static wxString GetWildcards(int mode);
/// Get a subtitle format that can read the given file or NULL if none can /// Get a subtitle format that can read the given file or NULL if none can
static SubtitleFormat *GetReader(wxString const& filename); static const SubtitleFormat *GetReader(wxString const& filename);
/// Get a subtitle format that can write the given file or NULL if none can /// Get a subtitle format that can write the given file or NULL if none can
static SubtitleFormat *GetWriter(wxString const& filename); static const SubtitleFormat *GetWriter(wxString const& filename);
/// Initialize subtitle formats /// Initialize subtitle formats
static void LoadFormats(); static void LoadFormats();
/// Deinitialize subtitle formats /// Deinitialize subtitle formats

View File

@ -39,6 +39,7 @@
#include "subtitle_format_ass.h" #include "subtitle_format_ass.h"
#include "ass_dialogue.h" #include "ass_dialogue.h"
#include "ass_file.h"
#include "compat.h" #include "compat.h"
#include "text_file_reader.h" #include "text_file_reader.h"
#include "text_file_writer.h" #include "text_file_writer.h"
@ -64,9 +65,11 @@ wxArrayString ASSSubtitleFormat::GetWriteWildcards() const {
return formats; return formats;
} }
void ASSSubtitleFormat::ReadFile(wxString const& filename, wxString const& encoding) { void ASSSubtitleFormat::ReadFile(AssFile *target, wxString const& filename, wxString const& encoding) const {
using namespace std; using namespace std;
target->Clear();
TextFileReader file(filename, encoding); TextFileReader file(filename, encoding);
int version = filename.Right(4).Lower() != ".ssa"; int version = filename.Right(4).Lower() != ".ssa";
AssAttachment *attach = 0; AssAttachment *attach = 0;
@ -74,22 +77,22 @@ void ASSSubtitleFormat::ReadFile(wxString const& filename, wxString const& encod
while (file.HasMoreLines()) { while (file.HasMoreLines()) {
wxString line = file.ReadLineFromFile(); wxString line = file.ReadLineFromFile();
try { try {
AddLine(line, &version, &attach); target->AddLine(line, &version, &attach);
} }
catch (const char *err) { catch (const char *err) {
Clear(); target->Clear();
throw AssParseError("Error processing line: " + STD_STR(line) + ": " + err, 0); throw AssParseError("Error processing line: " + STD_STR(line) + ": " + err, 0);
} }
} }
} }
void ASSSubtitleFormat::WriteFile(wxString const& filename, wxString const& encoding) { void ASSSubtitleFormat::WriteFile(const AssFile *src, wxString const& filename, wxString const& encoding) const {
TextFileWriter file(filename, encoding); TextFileWriter file(filename, encoding);
bool ssa = filename.Right(4).Lower() == ".ssa"; bool ssa = filename.Right(4).Lower() == ".ssa";
std::list<AssEntry*>::iterator last = Line->end(); --last; LineList::const_iterator last = src->Line.end(); --last;
wxString group = Line->front()->group; wxString group = src->Line.front()->group;
for (std::list<AssEntry*>::iterator cur=Line->begin(); cur!=Line->end(); ++cur) { for (LineList::const_iterator cur = src->Line.begin(); cur != src->Line.end(); ++cur) {
// Add a blank line between each group // Add a blank line between each group
if ((*cur)->group != group) { if ((*cur)->group != group) {
file.WriteLineToFile(""); file.WriteLineToFile("");

View File

@ -48,6 +48,6 @@ public:
wxArrayString GetReadWildcards() const; wxArrayString GetReadWildcards() const;
wxArrayString GetWriteWildcards() const; wxArrayString GetWriteWildcards() const;
void ReadFile(wxString const& filename, wxString const& forceEncoding); void ReadFile(AssFile *target, wxString const& filename, wxString const& forceEncoding) const;
void WriteFile(wxString const& filename, wxString const& encoding); void WriteFile(const AssFile *src, wxString const& filename, wxString const& encoding) const;
}; };

View File

@ -39,6 +39,7 @@
#include "subtitle_format_encore.h" #include "subtitle_format_encore.h"
#include "ass_dialogue.h" #include "ass_dialogue.h"
#include "ass_file.h"
#include "text_file_writer.h" #include "text_file_writer.h"
EncoreSubtitleFormat::EncoreSubtitleFormat() EncoreSubtitleFormat::EncoreSubtitleFormat()
@ -52,20 +53,20 @@ wxArrayString EncoreSubtitleFormat::GetWriteWildcards() const {
return formats; return formats;
} }
void EncoreSubtitleFormat::WriteFile(wxString const& filename, wxString const& encoding) { void EncoreSubtitleFormat::WriteFile(const AssFile *src, wxString const& filename, wxString const& encoding) const {
FractionalTime ft = AskForFPS(true); FractionalTime ft = AskForFPS(true);
if (!ft.FPS().IsLoaded()) return; if (!ft.FPS().IsLoaded()) return;
TextFileWriter file(filename, encoding); TextFileWriter file(filename, encoding);
// Convert to encore // Convert to encore
CreateCopy(); AssFile copy(*src);
SortLines(); copy.Sort();
StripComments(); StripComments(copy.Line);
RecombineOverlaps(); RecombineOverlaps(copy.Line);
MergeIdentical(); MergeIdentical(copy.Line);
StripTags(); StripTags(copy.Line);
ConvertNewlines("\r\n"); ConvertNewlines(copy.Line, "\r\n");
// Write lines // Write lines
int i = 0; int i = 0;
@ -73,12 +74,10 @@ void EncoreSubtitleFormat::WriteFile(wxString const& filename, wxString const& e
// Encore wants ; instead of : if we're dealing with NTSC dropframe stuff // Encore wants ; instead of : if we're dealing with NTSC dropframe stuff
char sep = ft.IsDrop() ? ';' : ':'; char sep = ft.IsDrop() ? ';' : ':';
for (std::list<AssEntry*>::iterator cur=Line->begin();cur!=Line->end();cur++) { for (LineList::const_iterator cur = copy.Line.begin(); cur != copy.Line.end(); ++cur) {
if (AssDialogue *current = dynamic_cast<AssDialogue*>(*cur)) { if (AssDialogue *current = dynamic_cast<AssDialogue*>(*cur)) {
++i; ++i;
file.WriteLineToFile(wxString::Format("%i %s %s %s", i, ft.ToSMPTE(current->Start, sep), ft.ToSMPTE(current->End, sep), current->Text)); file.WriteLineToFile(wxString::Format("%i %s %s %s", i, ft.ToSMPTE(current->Start, sep), ft.ToSMPTE(current->End, sep), current->Text));
} }
} }
ClearCopy();
} }

View File

@ -46,5 +46,5 @@ class EncoreSubtitleFormat : public SubtitleFormat {
public: public:
EncoreSubtitleFormat(); EncoreSubtitleFormat();
wxArrayString GetWriteWildcards() const; wxArrayString GetWriteWildcards() const;
void WriteFile(wxString const& filename, wxString const& encoding); void WriteFile(const AssFile *src, wxString const& filename, wxString const& encoding) const;
}; };

View File

@ -36,13 +36,15 @@
#include "config.h" #include "config.h"
#include "subtitle_format_microdvd.h"
#ifndef AGI_PRE #ifndef AGI_PRE
#include <wx/regex.h> #include <wx/regex.h>
#endif #endif
#include "ass_dialogue.h" #include "ass_dialogue.h"
#include "ass_file.h"
#include "ass_time.h" #include "ass_time.h"
#include "subtitle_format_microdvd.h"
#include "text_file_reader.h" #include "text_file_reader.h"
#include "text_file_writer.h" #include "text_file_writer.h"
#include "video_context.h" #include "video_context.h"
@ -76,11 +78,11 @@ bool MicroDVDSubtitleFormat::CanReadFile(wxString const& filename) const {
return false; return false;
} }
void MicroDVDSubtitleFormat::ReadFile(wxString const& filename, wxString const& forceEncoding) { void MicroDVDSubtitleFormat::ReadFile(AssFile *target, wxString const& filename, wxString const& encoding) const {
TextFileReader file(filename); TextFileReader file(filename);
wxRegEx exp("^[\\{\\[]([0-9]+)[\\}\\]][\\{\\[]([0-9]+)[\\}\\]](.*)$", wxRE_ADVANCED); wxRegEx exp("^[\\{\\[]([0-9]+)[\\}\\]][\\{\\[]([0-9]+)[\\}\\]](.*)$", wxRE_ADVANCED);
LoadDefault(false); target->LoadDefault(false);
agi::vfr::Framerate fps; agi::vfr::Framerate fps;
@ -117,22 +119,22 @@ void MicroDVDSubtitleFormat::ReadFile(wxString const& filename, wxString const&
diag->Start = fps.TimeAtFrame(f1, agi::vfr::START); diag->Start = fps.TimeAtFrame(f1, agi::vfr::START);
diag->End = fps.TimeAtFrame(f2, agi::vfr::END); diag->End = fps.TimeAtFrame(f2, agi::vfr::END);
diag->Text = text; diag->Text = text;
Line->push_back(diag); target->Line.push_back(diag);
} }
} }
} }
void MicroDVDSubtitleFormat::WriteFile(wxString const& filename, wxString const& encoding) { void MicroDVDSubtitleFormat::WriteFile(const AssFile *src, wxString const& filename, wxString const& encoding) const {
agi::vfr::Framerate fps = AskForFPS().FPS(); agi::vfr::Framerate fps = AskForFPS().FPS();
if (!fps.IsLoaded()) return; if (!fps.IsLoaded()) return;
CreateCopy(); AssFile copy(*src);
SortLines(); copy.Sort();
StripComments(); StripComments(copy.Line);
RecombineOverlaps(); RecombineOverlaps(copy.Line);
MergeIdentical(); MergeIdentical(copy.Line);
StripTags(); StripTags(copy.Line);
ConvertNewlines("|"); ConvertNewlines(copy.Line, "|");
TextFileWriter file(filename, encoding); TextFileWriter file(filename, encoding);
@ -142,7 +144,7 @@ void MicroDVDSubtitleFormat::WriteFile(wxString const& filename, wxString const&
} }
// Write lines // Write lines
for (std::list<AssEntry*>::iterator cur=Line->begin();cur!=Line->end();cur++) { for (LineList::const_iterator cur = copy.Line.begin(); cur != copy.Line.end(); ++cur) {
if (AssDialogue *current = dynamic_cast<AssDialogue*>(*cur)) { if (AssDialogue *current = dynamic_cast<AssDialogue*>(*cur)) {
int start = fps.FrameAtTime(current->Start, agi::vfr::START); int start = fps.FrameAtTime(current->Start, agi::vfr::START);
int end = fps.FrameAtTime(current->End, agi::vfr::END); int end = fps.FrameAtTime(current->End, agi::vfr::END);
@ -150,6 +152,4 @@ void MicroDVDSubtitleFormat::WriteFile(wxString const& filename, wxString const&
file.WriteLineToFile(wxString::Format("{%i}{%i}%s", start, end, current->Text)); file.WriteLineToFile(wxString::Format("{%i}{%i}%s", start, end, current->Text));
} }
} }
ClearCopy();
} }

View File

@ -49,7 +49,7 @@ public:
wxArrayString GetWriteWildcards() const; wxArrayString GetWriteWildcards() const;
bool CanReadFile(wxString const& filename) const; bool CanReadFile(wxString const& filename) const;
void ReadFile(wxString const& filename, wxString const& forceEncoding); void ReadFile(AssFile *target, wxString const& filename, wxString const& forceEncoding) const;
void WriteFile(wxString const& filename, wxString const& encoding); void WriteFile(const AssFile *src, wxString const& filename, wxString const& encoding) const;
}; };

View File

@ -53,6 +53,6 @@ wxArrayString MKVSubtitleFormat::GetReadWildcards() const {
return formats; return formats;
} }
void MKVSubtitleFormat::ReadFile(wxString const& filename, wxString const&) { void MKVSubtitleFormat::ReadFile(AssFile *target, wxString const& filename, wxString const&) const {
MatroskaWrapper::GetSubtitles(filename, GetAssFile()); MatroskaWrapper::GetSubtitles(filename, target);
} }

View File

@ -46,5 +46,5 @@ public:
MKVSubtitleFormat(); MKVSubtitleFormat();
wxArrayString GetReadWildcards() const; wxArrayString GetReadWildcards() const;
void ReadFile(wxString const& filename, wxString const& forceEncoding); void ReadFile(AssFile *target, wxString const& filename, wxString const& forceEncoding) const;
}; };

View File

@ -375,11 +375,11 @@ wxArrayString SRTSubtitleFormat::GetWriteWildcards() const {
return GetReadWildcards(); return GetReadWildcards();
} }
void SRTSubtitleFormat::ReadFile(wxString const& filename, wxString const& encoding) { void SRTSubtitleFormat::ReadFile(AssFile *target, wxString const& filename, wxString const& encoding) const {
using namespace std; using namespace std;
TextFileReader file(filename, encoding); TextFileReader file(filename, encoding);
LoadDefault(false); target->LoadDefault(false);
// See parsing algorithm at <http://devel.aegisub.org/wiki/SubtitleFormats/SRT> // See parsing algorithm at <http://devel.aegisub.org/wiki/SubtitleFormats/SRT>
@ -433,7 +433,7 @@ found_timestamps:
line->Start = ReadSRTTime(timestamp_regex.GetMatch(text_line, 1)); line->Start = ReadSRTTime(timestamp_regex.GetMatch(text_line, 1));
line->End = ReadSRTTime(timestamp_regex.GetMatch(text_line, 2)); line->End = ReadSRTTime(timestamp_regex.GetMatch(text_line, 2));
// store pointer to subtitle, we'll continue working on it // store pointer to subtitle, we'll continue working on it
Line->push_back(line); target->Line.push_back(line);
// next we're reading the text // next we're reading the text
state = 3; state = 3;
break; break;
@ -496,20 +496,20 @@ found_timestamps:
line->Text = tag_parser.ToAss(line->Text); line->Text = tag_parser.ToAss(line->Text);
} }
void SRTSubtitleFormat::WriteFile(wxString const& filename, wxString const& encoding) { void SRTSubtitleFormat::WriteFile(const AssFile *src, wxString const& filename, wxString const& encoding) const {
TextFileWriter file(filename,encoding); TextFileWriter file(filename, encoding);
// Convert to SRT // Convert to SRT
CreateCopy(); AssFile copy(*src);
SortLines(); copy.Sort();
StripComments(); StripComments(copy.Line);
RecombineOverlaps(); RecombineOverlaps(copy.Line);
MergeIdentical(); MergeIdentical(copy.Line);
ConvertNewlines("\r\n", false); ConvertNewlines(copy.Line, "\r\n", false);
// Write lines // Write lines
int i=1; int i=1;
for (std::list<AssEntry*>::iterator cur = Line->begin(); cur != Line->end(); ++cur) { for (LineList::const_iterator cur = copy.Line.begin(); cur != copy.Line.end(); ++cur) {
if (AssDialogue *current = dynamic_cast<AssDialogue*>(*cur)) { if (AssDialogue *current = dynamic_cast<AssDialogue*>(*cur)) {
file.WriteLineToFile(wxString::Format("%d", i++)); file.WriteLineToFile(wxString::Format("%d", i++));
file.WriteLineToFile(WriteSRTTime(current->Start) + " --> " + WriteSRTTime(current->End)); file.WriteLineToFile(WriteSRTTime(current->Start) + " --> " + WriteSRTTime(current->End));
@ -517,11 +517,9 @@ void SRTSubtitleFormat::WriteFile(wxString const& filename, wxString const& enco
file.WriteLineToFile(""); file.WriteLineToFile("");
} }
} }
ClearCopy();
} }
wxString SRTSubtitleFormat::ConvertTags(AssDialogue *diag) { wxString SRTSubtitleFormat::ConvertTags(AssDialogue *diag) const {
wxString final; wxString final;
std::map<char, bool> tag_states; std::map<char, bool> tag_states;
tag_states['i'] = false; tag_states['i'] = false;

View File

@ -42,12 +42,12 @@
/// ///
/// DOCME /// DOCME
class SRTSubtitleFormat : public SubtitleFormat { class SRTSubtitleFormat : public SubtitleFormat {
wxString ConvertTags(AssDialogue *diag); wxString ConvertTags(AssDialogue *diag) const;
public: public:
SRTSubtitleFormat(); SRTSubtitleFormat();
wxArrayString GetReadWildcards() const; wxArrayString GetReadWildcards() const;
wxArrayString GetWriteWildcards() const; wxArrayString GetWriteWildcards() const;
void ReadFile(wxString const& filename, wxString const& forceEncoding); void ReadFile(AssFile *target, wxString const& filename, wxString const& forceEncoding) const;
void WriteFile(wxString const& filename, wxString const& encoding); void WriteFile(const AssFile *src, wxString const& filename, wxString const& encoding) const;
}; };

View File

@ -59,25 +59,25 @@ wxArrayString TranStationSubtitleFormat::GetWriteWildcards() const {
return formats; return formats;
} }
void TranStationSubtitleFormat::WriteFile(wxString const& filename, wxString const& encoding) { void TranStationSubtitleFormat::WriteFile(const AssFile *src, wxString const& filename, wxString const& encoding) const {
FractionalTime ft = AskForFPS(true); FractionalTime ft = AskForFPS(true);
if (!ft.FPS().IsLoaded()) return; if (!ft.FPS().IsLoaded()) return;
TextFileWriter file(filename, encoding); TextFileWriter file(filename, encoding);
// Convert to TranStation // Convert to TranStation
CreateCopy(); AssFile copy(*src);
SortLines(); copy.Sort();
StripComments(); StripComments(copy.Line);
RecombineOverlaps(); RecombineOverlaps(copy.Line);
MergeIdentical(); MergeIdentical(copy.Line);
AssDialogue *prev = 0; AssDialogue *prev = 0;
for (std::list<AssEntry*>::iterator it = Line->begin(); it != Line->end(); ++it) { for (std::list<AssEntry*>::iterator it = copy.Line.begin(); it != copy.Line.end(); ++it) {
AssDialogue *cur = dynamic_cast<AssDialogue*>(*it); AssDialogue *cur = dynamic_cast<AssDialogue*>(*it);
if (prev && cur) { if (prev && cur) {
file.WriteLineToFile(ConvertLine(prev, &ft, cur->Start)); file.WriteLineToFile(ConvertLine(&copy, prev, &ft, cur->Start));
file.WriteLineToFile(""); file.WriteLineToFile("");
} }
@ -87,19 +87,17 @@ void TranStationSubtitleFormat::WriteFile(wxString const& filename, wxString con
// flush last line // flush last line
if (prev) if (prev)
file.WriteLineToFile(ConvertLine(prev, &ft, -1)); file.WriteLineToFile(ConvertLine(&copy, prev, &ft, -1));
// Every file must end with this line // Every file must end with this line
file.WriteLineToFile("SUB["); file.WriteLineToFile("SUB[");
ClearCopy();
} }
wxString TranStationSubtitleFormat::ConvertLine(AssDialogue *current, FractionalTime *ft, int nextl_start) { wxString TranStationSubtitleFormat::ConvertLine(AssFile *file, AssDialogue *current, FractionalTime *ft, int nextl_start) const {
int valign = 0; int valign = 0;
const char *halign = " "; // default is centered const char *halign = " "; // default is centered
const char *type = "N"; // no special style const char *type = "N"; // no special style
if (AssStyle *style = GetAssFile()->GetStyle(current->Style)) { if (AssStyle *style = file->GetStyle(current->Style)) {
if (style->alignment >= 4) valign = 4; if (style->alignment >= 4) valign = 4;
if (style->alignment >= 7) valign = 9; if (style->alignment >= 7) valign = 9;
if (style->alignment == 1 || style->alignment == 4 || style->alignment == 7) halign = "L"; if (style->alignment == 1 || style->alignment == 4 || style->alignment == 7) halign = "L";

View File

@ -44,10 +44,10 @@ class AssDialogue;
/// ///
/// DOCME /// DOCME
class TranStationSubtitleFormat : public SubtitleFormat { class TranStationSubtitleFormat : public SubtitleFormat {
wxString ConvertLine(AssDialogue *line, FractionalTime *ft, int nextl_start); wxString ConvertLine(AssFile *file, AssDialogue *line, FractionalTime *ft, int nextl_start) const;
public: public:
TranStationSubtitleFormat(); TranStationSubtitleFormat();
wxArrayString GetWriteWildcards() const; wxArrayString GetWriteWildcards() const;
void WriteFile(wxString const& filename, wxString const& encoding); void WriteFile(const AssFile *src, wxString const& filename, wxString const& encoding) const;
}; };

View File

@ -38,6 +38,10 @@
#include "subtitle_format_ttxt.h" #include "subtitle_format_ttxt.h"
#ifndef AGI_PRE
#include <wx/xml/xml.h>
#endif
#include "ass_dialogue.h" #include "ass_dialogue.h"
#include "ass_file.h" #include "ass_file.h"
#include "ass_time.h" #include "ass_time.h"
@ -61,8 +65,8 @@ wxArrayString TTXTSubtitleFormat::GetWriteWildcards() const {
return GetReadWildcards(); return GetReadWildcards();
} }
void TTXTSubtitleFormat::ReadFile(wxString const& filename, wxString const& forceEncoding) { void TTXTSubtitleFormat::ReadFile(AssFile *target, wxString const& filename, wxString const& encoding) const {
LoadDefault(false); target->LoadDefault(false);
// Load XML document // Load XML document
wxXmlDocument doc; wxXmlDocument doc;
@ -73,106 +77,96 @@ void TTXTSubtitleFormat::ReadFile(wxString const& filename, wxString const& forc
// Check version // Check version
wxString verStr = doc.GetRoot()->GetAttribute("version", ""); wxString verStr = doc.GetRoot()->GetAttribute("version", "");
version = -1; int version = -1;
if (verStr == "1.0") version = 0; if (verStr == "1.0")
else if (verStr == "1.1") version = 1; version = 0;
else throw TTXTParseError("Unknown TTXT version: " + STD_STR(verStr), 0); else if (verStr == "1.1")
version = 1;
else
throw TTXTParseError("Unknown TTXT version: " + STD_STR(verStr), 0);
// Get children // Get children
diag = NULL; AssDialogue *diag = 0;
wxXmlNode *child = doc.GetRoot()->GetChildren();
int lines = 0; int lines = 0;
while (child) { for (wxXmlNode *child = doc.GetRoot()->GetChildren(); child; child = child->GetNext()) {
// Line // Line
if (child->GetName() == "TextSample") { if (child->GetName() == "TextSample") {
if (ProcessLine(child)) lines++; if (diag = ProcessLine(child, diag, version)) {
lines++;
target->Line.push_back(diag);
}
} }
// Header // Header
else if (child->GetName() == "TextStreamHeader") { else if (child->GetName() == "TextStreamHeader") {
ProcessHeader(child); ProcessHeader(child);
} }
// Proceed to next child
child = child->GetNext();
} }
// No lines? // No lines?
if (lines == 0) { if (lines == 0)
AssDialogue *line = new AssDialogue(); target->Line.push_back(new AssDialogue);
line->group = "[Events]";
line->Style = "Default";
line->Start = 0;
line->End = 5000;
Line->push_back(line);
}
} }
bool TTXTSubtitleFormat::ProcessLine(wxXmlNode *node) { AssDialogue *TTXTSubtitleFormat::ProcessLine(wxXmlNode *node, AssDialogue *prev, int version) const {
// Get time // Get time
wxString sampleTime = node->GetAttribute("sampleTime", "00:00:00.000"); wxString sampleTime = node->GetAttribute("sampleTime", "00:00:00.000");
AssTime time; AssTime time;
time.ParseASS(sampleTime); time.ParseASS(sampleTime);
// Set end time of last line // Set end time of last line
if (diag) diag->End = time; if (prev)
diag = NULL; prev->End = time;
// Get text // Get text
wxString text; wxString text;
if (version == 0) text = node->GetAttribute("text", ""); if (version == 0)
else text = node->GetNodeContent(); text = node->GetAttribute("text", "");
else
text = node->GetNodeContent();
// Create line // Create line
if (!text.IsEmpty()) { if (text.empty()) return 0;
// Create dialogue
diag = new AssDialogue();
diag->Start = time;
diag->End = 36000000-10;
diag->group = "[Events]";
diag->Style = "Default";
diag->Comment = false;
// Process text for 1.0 // Create dialogue
if (version == 0) { AssDialogue *diag = new AssDialogue;
wxString finalText; diag->Start = time;
finalText.Alloc(text.Length()); diag->End = 36000000-10;
bool in = false;
bool first = true; // Process text for 1.0
for (size_t i=0;i<text.Length();i++) { if (version == 0) {
if (text[i] == '\'') { wxString finalText;
if (!in && !first) finalText += "\\N"; finalText.reserve(text.size());
first = false; bool in = false;
in = !in; bool first = true;
} for (size_t i = 0; i < text.size(); ++i) {
else if (in) finalText += text[i]; if (text[i] == '\'') {
if (!in && !first) finalText += "\\N";
first = false;
in = !in;
} }
diag->Text = finalText; else if (in) finalText += text[i];
} }
diag->Text = finalText;
// Process text for 1.1
else {
text.Replace("\r", "");
text.Replace("\n", "\\N");
diag->Text = text;
}
// Insert dialogue
Line->push_back(diag);
return true;
} }
else return false; // Process text for 1.1
else {
text.Replace("\r", "");
text.Replace("\n", "\\N");
diag->Text = text;
}
return diag;
} }
void TTXTSubtitleFormat::ProcessHeader(wxXmlNode *node) { void TTXTSubtitleFormat::ProcessHeader(wxXmlNode *node) const {
// TODO // TODO
} }
void TTXTSubtitleFormat::WriteFile(wxString const& filename, wxString const& encoding) { void TTXTSubtitleFormat::WriteFile(const AssFile *src, wxString const& filename, wxString const& encoding) const {
// Convert to TTXT // Convert to TTXT
CreateCopy(); AssFile copy(*src);
ConvertToTTXT(); ConvertToTTXT(copy);
// Create XML structure // Create XML structure
wxXmlDocument doc; wxXmlDocument doc;
@ -184,28 +178,22 @@ void TTXTSubtitleFormat::WriteFile(wxString const& filename, wxString const& enc
WriteHeader(root); WriteHeader(root);
// Create lines // Create lines
int i=1; AssDialogue *prev = 0;
using std::list; for (LineList::iterator cur = copy.Line.begin(); cur != copy.Line.end(); ++cur) {
prev = NULL;
for (list<AssEntry*>::iterator cur=Line->begin();cur!=Line->end();cur++) {
AssDialogue *current = dynamic_cast<AssDialogue*>(*cur); AssDialogue *current = dynamic_cast<AssDialogue*>(*cur);
if (current && !current->Comment) { if (current && !current->Comment) {
WriteLine(root, current); WriteLine(root, prev, current);
i++; prev = current;
} }
else else
throw TTXTParseError("Unexpected line type in TTXT file", 0); throw TTXTParseError("Unexpected line type in TTXT file", 0);
} }
// Save XML // Save XML
//prevNode->SetNext(NULL);
doc.Save(filename); doc.Save(filename);
// Clear
ClearCopy();
} }
void TTXTSubtitleFormat::WriteHeader(wxXmlNode *root) { void TTXTSubtitleFormat::WriteHeader(wxXmlNode *root) const {
// Write stream header // Write stream header
wxXmlNode *node = new wxXmlNode(wxXML_ELEMENT_NODE, "TextStreamHeader"); wxXmlNode *node = new wxXmlNode(wxXML_ELEMENT_NODE, "TextStreamHeader");
node->AddAttribute("width", "400"); node->AddAttribute("width", "400");
@ -255,7 +243,7 @@ void TTXTSubtitleFormat::WriteHeader(wxXmlNode *root) {
root->AddChild(node); root->AddChild(node);
} }
void TTXTSubtitleFormat::WriteLine(wxXmlNode *root, AssDialogue *line) { void TTXTSubtitleFormat::WriteLine(wxXmlNode *root, AssDialogue *prev, AssDialogue *line) const {
// If it doesn't start at the end of previous, add blank // If it doesn't start at the end of previous, add blank
if (prev && prev->End != line->Start) { if (prev && prev->End != line->Start) {
wxXmlNode *node = new wxXmlNode(wxXML_ELEMENT_NODE, "TextSample"); wxXmlNode *node = new wxXmlNode(wxXML_ELEMENT_NODE, "TextSample");
@ -271,22 +259,19 @@ void TTXTSubtitleFormat::WriteLine(wxXmlNode *root, AssDialogue *line) {
node->AddAttribute("xml:space", "preserve"); node->AddAttribute("xml:space", "preserve");
root->AddChild(node); root->AddChild(node);
node->AddChild(new wxXmlNode(wxXML_TEXT_NODE, "", line->Text)); node->AddChild(new wxXmlNode(wxXML_TEXT_NODE, "", line->Text));
// Set as previous
prev = line;
} }
void TTXTSubtitleFormat::ConvertToTTXT () { void TTXTSubtitleFormat::ConvertToTTXT(AssFile &file) const {
SortLines(); file.Sort();
StripComments(); StripComments(file.Line);
RecombineOverlaps(); RecombineOverlaps(file.Line);
MergeIdentical(); MergeIdentical(file.Line);
StripTags(); StripTags(file.Line);
ConvertNewlines("\r\n"); ConvertNewlines(file.Line, "\r\n");
// Find last line // Find last line
AssTime lastTime; AssTime lastTime;
for (std::list<AssEntry*>::reverse_iterator cur=Line->rbegin();cur!=Line->rend();cur++) { for (LineList::reverse_iterator cur = file.Line.rbegin(); cur != file.Line.rend(); ++cur) {
if (AssDialogue *prev = dynamic_cast<AssDialogue*>(*cur)) { if (AssDialogue *prev = dynamic_cast<AssDialogue*>(*cur)) {
lastTime = prev->End; lastTime = prev->End;
break; break;
@ -294,11 +279,11 @@ void TTXTSubtitleFormat::ConvertToTTXT () {
} }
// Insert blank line at the end // Insert blank line at the end
AssDialogue *diag = new AssDialogue(); AssDialogue *diag = new AssDialogue;
diag->Start = lastTime; diag->Start = lastTime;
diag->End = lastTime+OPT_GET("Timing/Default Duration")->GetInt(); diag->End = lastTime+OPT_GET("Timing/Default Duration")->GetInt();
diag->group = "[Events]"; diag->group = "[Events]";
diag->Style = "Default"; diag->Style = "Default";
diag->Comment = false; diag->Comment = false;
Line->push_back(diag); file.Line.push_back(diag);
} }

View File

@ -34,14 +34,10 @@
/// @ingroup subtitle_io /// @ingroup subtitle_io
/// ///
#ifndef AGI_PRE
#include <wx/xml/xml.h>
#endif
#include "subtitle_format.h" #include "subtitle_format.h"
class AssDialogue; class AssDialogue;
class wxXmlNode;
/// DOCME /// DOCME
/// @class TTXTSubtitleFormat /// @class TTXTSubtitleFormat
@ -49,28 +45,19 @@ class AssDialogue;
/// ///
/// DOCME /// DOCME
class TTXTSubtitleFormat : public SubtitleFormat { class TTXTSubtitleFormat : public SubtitleFormat {
/// DOCME AssDialogue *ProcessLine(wxXmlNode *node, AssDialogue *prev, int version) const;
int version; void ProcessHeader(wxXmlNode *node) const;
/// DOCME void WriteHeader(wxXmlNode *root) const;
AssDialogue *diag; void WriteLine(wxXmlNode *root, AssDialogue *prev, AssDialogue *line) const;
/// DOCME void ConvertToTTXT(AssFile &file) const;
AssDialogue *prev;
bool ProcessLine(wxXmlNode *node);
void ProcessHeader(wxXmlNode *node);
void WriteHeader(wxXmlNode *root);
void WriteLine(wxXmlNode *root, AssDialogue *line);
void ConvertToTTXT();
public: public:
TTXTSubtitleFormat(); TTXTSubtitleFormat();
wxArrayString GetReadWildcards() const; wxArrayString GetReadWildcards() const;
wxArrayString GetWriteWildcards() const; wxArrayString GetWriteWildcards() const;
void ReadFile(wxString const& filename, wxString const& forceEncoding); void ReadFile(AssFile *target, wxString const& filename, wxString const& forceEncoding) const;
void WriteFile(wxString const& filename, wxString const& encoding); void WriteFile(const AssFile *src, wxString const& filename, wxString const& encoding) const;
}; };

View File

@ -39,6 +39,7 @@
#include "subtitle_format_txt.h" #include "subtitle_format_txt.h"
#include "ass_dialogue.h" #include "ass_dialogue.h"
#include "ass_file.h"
#include "compat.h" #include "compat.h"
#include "dialog_text_import.h" #include "dialog_text_import.h"
#include "main.h" #include "main.h"
@ -66,14 +67,14 @@ bool TXTSubtitleFormat::CanWriteFile(wxString const& filename) const {
return (filename.Right(4).Lower() == ".txt" && filename.Right(11).Lower() != ".encore.txt" && filename.Right(16).Lower() != ".transtation.txt"); return (filename.Right(4).Lower() == ".txt" && filename.Right(11).Lower() != ".encore.txt" && filename.Right(16).Lower() != ".transtation.txt");
} }
void TXTSubtitleFormat::ReadFile(wxString const& filename, wxString const& encoding) { void TXTSubtitleFormat::ReadFile(AssFile *target, wxString const& filename, wxString const& encoding) const {
using namespace std; using namespace std;
DialogTextImport dlg; DialogTextImport dlg;
if (dlg.ShowModal() == wxID_CANCEL) return; if (dlg.ShowModal() == wxID_CANCEL) return;
TextFileReader file(filename, encoding, false); TextFileReader file(filename, encoding, false);
LoadDefault(false); target->LoadDefault(false);
wxString actor; wxString actor;
wxString separator = lagi_wxString(OPT_GET("Tool/Import/Text/Actor Separator")->GetString()); wxString separator = lagi_wxString(OPT_GET("Tool/Import/Text/Actor Separator")->GetString());
@ -123,7 +124,7 @@ void TXTSubtitleFormat::ReadFile(wxString const& filename, wxString const& encod
line->End = 0; line->End = 0;
// Adds line // Adds line
Line->push_back(line); target->Line.push_back(line);
lines++; lines++;
} }
@ -131,15 +132,15 @@ void TXTSubtitleFormat::ReadFile(wxString const& filename, wxString const& encod
if (lines == 0) { if (lines == 0) {
AssDialogue *line = new AssDialogue; AssDialogue *line = new AssDialogue;
line->End = OPT_GET("Timing/Default Duration")->GetInt(); line->End = OPT_GET("Timing/Default Duration")->GetInt();
Line->push_back(line); target->Line.push_back(line);
} }
} }
void TXTSubtitleFormat::WriteFile(wxString const& filename, wxString const& encoding) { void TXTSubtitleFormat::WriteFile(const AssFile *src, wxString const& filename, wxString const& encoding) const {
size_t num_actor_names = 0, num_dialogue_lines = 0; size_t num_actor_names = 0, num_dialogue_lines = 0;
// Detect number of lines with Actor field filled out // Detect number of lines with Actor field filled out
for (std::list<AssEntry*>::iterator l = Line->begin(); l != Line->end(); ++l) { for (LineList::const_iterator l = src->Line.begin(); l != src->Line.end(); ++l) {
AssDialogue *dia = dynamic_cast<AssDialogue*>(*l); AssDialogue *dia = dynamic_cast<AssDialogue*>(*l);
if (dia && !dia->Comment) { if (dia && !dia->Comment) {
num_dialogue_lines++; num_dialogue_lines++;
@ -156,7 +157,7 @@ void TXTSubtitleFormat::WriteFile(wxString const& filename, wxString const& enco
file.WriteLineToFile(wxString("# Exported by Aegisub ") + GetAegisubShortVersionString()); file.WriteLineToFile(wxString("# Exported by Aegisub ") + GetAegisubShortVersionString());
// Write the file // Write the file
for (std::list<AssEntry*>::iterator l = Line->begin(); l != Line->end(); ++l) { for (LineList::const_iterator l = src->Line.begin(); l != src->Line.end(); ++l) {
AssDialogue *dia = dynamic_cast<AssDialogue*>(*l); AssDialogue *dia = dynamic_cast<AssDialogue*>(*l);
if (dia) { if (dia) {

View File

@ -48,6 +48,6 @@ public:
wxArrayString GetWriteWildcards() const; wxArrayString GetWriteWildcards() const;
bool CanWriteFile(wxString const& filename) const; bool CanWriteFile(wxString const& filename) const;
void ReadFile(wxString const& filename, wxString const& forceEncoding); void ReadFile(AssFile *target, wxString const& filename, wxString const& forceEncoding) const;
void WriteFile(wxString const& filename, wxString const& encoding); void WriteFile(const AssFile *src, wxString const& filename, wxString const& encoding) const;
}; };