// Copyright (c) 2008, Simone Cociancich // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // * Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // * Neither the name of the Aegisub Group nor the names of its contributors // may be used to endorse or promote products derived from this software // without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // // ----------------------------------------------------------------------------- // // AEGISUB // // Website: http://aegisub.cellosoft.com // Contact: mailto:jiifurusu@gmail.com // #ifdef WITH_PERL #include "auto4_perl.h" #include "ass_file.h" #include "ass_entry.h" #include "ass_style.h" #include "ass_dialogue.h" #include "ass_attachment.h" // Disable warning #ifdef __VISUALC__ #pragma warning(disable: 4800) #endif // For wxString::Trim #define right true #define left false #define HASSH_BASIC_INIT(ae, he) \ HV_TOUCH(he, "raw", 3)\ HV_STORE(newSVpv(ae->GetEntryData().mb_str(wx2pl), 0));\ HV_TOUCH(he, "section", 7)\ HV_STORE(newSVpv(ae->group.mb_str(wx2pl), 0));\ wxString he ## _class = GetEntryClass(ae);\ HV_TOUCH(he, "class", 5)\ HV_STORE(newSVpv(he ## _class.mb_str(wx2pl), 0)) #define ASS_BASIC_INIT(he, ae) \ HV_FETCH(he, "raw", 3)\ ae->SetEntryData(wxString(SvPV_nolen(HV_VAL), pl2wx));\ HV_FETCH(he, "section", 7)\ ae->group = wxString(SvPV_nolen(HV_VAL), pl2wx) namespace Automation4 { wxString PerlAss::GetEntryClass(AssEntry *entry) { switch(entry->GetType()) { case ENTRY_DIALOGUE: return _T("dialogue"); case ENTRY_STYLE: return _T("style"); /* TODO: add stylex recognition */ break; case ENTRY_ATTACHMENT: return _T("attachment"); default: case ENTRY_BASE: wxString data(entry->GetEntryData()); if(data.Trim(left).StartsWith(_T(";"))) return _T("comment"); else { if(entry->group == _T("[Script Info]") && data.Matches(_T("*:*"))) return _T("info"); if(data == entry->group) return _T("head"); if(data.StartsWith(_T("Format:"))) return _T("format"); if(data.IsEmpty()) return _T("clear"); } } // Fallback return _T("unknown"); } HV *PerlAss::MakeHasshEntry(AssEntry *e) { switch(e->GetType()) { case ENTRY_DIALOGUE: return MakeHasshDialogue(AssEntry::GetAsDialogue(e)); case ENTRY_STYLE: return MakeHasshStyle(AssEntry::GetAsStyle(e)); case ENTRY_ATTACHMENT: default: case ENTRY_BASE: dHV; HV *entry = newHV(); HASSH_BASIC_INIT(e, entry); if(entry_class == _T("info")) { // Info HV_TOUCH(entry, "key", 3) { wxString _text = e->GetEntryData().BeforeFirst(_T(':')).Strip(wxString::both); HV_STORE(newSVpv(_text.mb_str(wx2pl), 0)); } HV_TOUCH(entry, "value", 5) { wxString _text = e->GetEntryData().AfterFirst(_T(':')).Strip(wxString::both); HV_STORE(newSVpv(_text.mb_str(wx2pl), 0)); } } else if(entry_class == _T("format")) { // Format °_° HV_TOUCH(entry, "fields", 6) { AV *fields_av = newAV(); HV_STORE(newRV_noinc((SV*)fields_av)); for(wxString fields_buf = e->GetEntryData().AfterFirst(_T(':')).Trim(left); !fields_buf.IsEmpty(); fields_buf = fields_buf.AfterFirst(_T(',')).Trim(left)) { av_push(fields_av, newSVpv(fields_buf.BeforeFirst(_T(',')).Trim(right).mb_str(wx2pl), 0)); } } } else if(entry_class == _T("comment")) { // Comment HV_TOUCH(entry, "text", 4) { wxString _text = e->GetEntryData().AfterFirst(_T(';')); HV_STORE(newSVpv(_text.mb_str(wx2pl), 0)); } } return entry; } } HV *PerlAss::MakeHasshStyle(AssStyle *s) { dHV; // Create new empty hash HV *style = newHV(); // Add fields HASSH_BASIC_INIT(s, style); HV_TOUCH(style, "name", 4) HV_STORE(newSVpv(s->name.mb_str(wx2pl), 0)); HV_TOUCH(style, "font", 4) HV_STORE(newSVpv(s->font.mb_str(wx2pl), 0)); HV_FAS(style, "fontsize", 8, nv, s->fontsize); HV_TOUCH(style, "color1", 6) HV_STORE(newSVpv(s->primary.GetASSFormatted(true).mb_str(wx2pl), 0)); HV_TOUCH(style, "color2", 6) HV_STORE(newSVpv(s->secondary.GetASSFormatted(true).mb_str(wx2pl), 0)); HV_TOUCH(style, "color3", 6) HV_STORE(newSVpv(s->outline.GetASSFormatted(true).mb_str(wx2pl), 0)); HV_TOUCH(style, "color4", 6) HV_STORE(newSVpv(s->shadow.GetASSFormatted(true).mb_str(wx2pl), 0)); HV_TOUCH(style, "bold", 4) HV_STORE(newSViv(s->bold)); HV_FAS(style, "italic", 6, iv, s->italic); HV_FAS(style, "underline", 9, iv, s->underline); HV_FAS(style, "strikeout", 9, iv, s->strikeout); HV_FAS(style, "scale_x", 7, nv, s->scalex); HV_FAS(style, "scale_y", 7, nv, s->scaley); HV_FAS(style, "spacing", 7, nv, s->spacing); HV_FAS(style, "angle", 5, nv, s->angle); HV_FAS(style, "borderstyle", 11, iv, s->borderstyle); HV_FAS(style, "outline", 7, nv, s->outline_w); HV_FAS(style, "shadow", 6, nv, s->shadow_w); HV_FAS(style, "align", 5, iv, s->alignment); HV_FAS(style, "margin_l", 8, iv, s->Margin[0]); HV_FAS(style, "margin_r", 8, iv, s->Margin[1]); HV_FAS(style, "margin_t", 8, iv, s->Margin[2]); HV_FAS(style, "margin_b", 8, iv, s->Margin[3]); HV_FAS(style, "encoding", 8, iv, s->encoding); HV_FAS(style, "relative_to", 11, iv, s->relativeTo); // Return the hassh style return style; } HV *PerlAss::MakeHasshDialogue(AssDialogue *d) { dHV; // Create new hash HV *diag = newHV(); // Copy the values from the AssDialogue HASSH_BASIC_INIT(d, diag); HV_FAS(diag, "comment", 7, iv, d->Comment); HV_FAS(diag, "layer", 5, iv, d->Layer); HV_FAS(diag, "start_time", 10, iv, d->Start.GetMS()); HV_FAS(diag, "end_time", 8, iv, d->End.GetMS()); HV_TOUCH(diag, "style", 5) HV_STORE(newSVpv(d->Style.mb_str(wx2pl), 0)); HV_TOUCH(diag, "actor", 5) HV_STORE(newSVpv(d->Actor.mb_str(wx2pl), 0)); HV_FAS(diag, "margin_l", 8, iv, d->Margin[0]); HV_FAS(diag, "margin_r", 8, iv, d->Margin[1]); HV_FAS(diag, "margin_t", 8, iv, d->Margin[2]); HV_FAS(diag, "margin_b", 8, iv, d->Margin[3]); HV_TOUCH(diag, "effect", 6) HV_STORE(newSVpv(d->Effect.mb_str(wx2pl), 0)); HV_TOUCH(diag, "text", 4) HV_STORE(newSVpv(d->Text.mb_str(wx2pl), 0)); // Return the dialogue return diag; } /* TODO: report progress */ AV *PerlAss::MakeHasshLines(AV *lines, AssFile *ass) { if(!lines) { lines = newAV(); } dAV; I32 i = 0; I32 lines_len = av_len(lines); for(std::list::iterator it = ass->Line.begin(); it != ass->Line.end(); it++) { if(i <= lines_len && av_exists(lines, i)) av_delete(lines, i, G_DISCARD); AV_TOUCH(lines, i++) AV_STORE(newRV_noinc((SV*)MakeHasshEntry(*it))); } for(; i <= lines_len; i++) { if(av_exists(lines, i)) av_delete(lines, i, G_DISCARD); } return lines; } AssEntry *PerlAss::MakeAssEntry(HV *entry) { dHV; if(!entry) { // Create an empty line, if NULL entry = newHV(); } // The fallback class wxString cl(_T("unknown")); // Let's get the actual class of the hassh HV_FETCH(entry, "class", 5) cl = wxString(SvPV_nolen(HV_VAL), pl2wx); // We trust the value of entry{class} if(cl == _T("dialogue")) { // It seems to be a dialogue, let's call the specialized function return MakeAssDialogue(entry); } else if(cl == _T("style")) { // It seems to be a style, let's call the specialized function return MakeAssStyle(entry); } else if(cl == _T("attachment")) { /* TODO */ return NULL; } else { // A base entry AssEntry *e = new AssEntry(); ASS_BASIC_INIT(entry, e); if(cl == _T("info")) { wxString key, value; HV_FETCH(entry, "key", 3) { key = wxString(SvPV_nolen(HV_VAL), pl2wx); HV_FETCH(entry, "value", 5) { value = wxString(SvPV_nolen(HV_VAL), pl2wx); e->SetEntryData(key + _T(": ") + value); } } } // Not necessary, format customization isn't even supported by aegi (atm? °_°) /*else if(cl == _T("format")) { HV_FETCH(entry, "fields", 6) { AV *fields = (AV*)SvRV(HV_VAL); for(int i = 0; i < av_len(fields); i++) { SV ** field = av_fetch(fields, i, 0); if(field) { wxString field(SvPV_nolen(*field), pl2wx); } } } }*/ else if(cl == _T("comment")) { HV_FETCH(entry, "text", 4) { e->SetEntryData(_T(";") + wxString(SvPV_nolen(HV_VAL), pl2wx)); } } return e; } } AssStyle *PerlAss::MakeAssStyle(HV *style) { dHV; // Create a default style AssStyle *s = new AssStyle(); // Fill it with the values from the hassh ASS_BASIC_INIT(style, s); HV_FETCH(style, "name", 4) s->name = wxString(SvPV_nolen(HV_VAL), pl2wx); HV_FETCH(style, "font", 4) s->font = wxString(SvPV_nolen(HV_VAL), pl2wx); HV_FAA(style, "fontsize", 8, NV, s->fontsize); HV_FETCH(style, "color1", 6) s->primary.Parse(wxString(SvPV_nolen(HV_VAL), pl2wx)); HV_FETCH(style, "color2", 6) s->secondary.Parse(wxString(SvPV_nolen(HV_VAL), pl2wx)); HV_FETCH(style, "color3", 6) s->outline.Parse(wxString(SvPV_nolen(HV_VAL), pl2wx)); HV_FETCH(style, "color4", 6) s->shadow.Parse(wxString(SvPV_nolen(HV_VAL), pl2wx)); HV_FAA(style, "bold", 4, IV, s->bold); HV_FAA(style, "italic", 6, IV, s->italic); HV_FAA(style, "underline", 9, IV, s->underline); HV_FAA(style, "strikeout", 9, IV, s->strikeout); HV_FAA(style, "scale_x", 7, NV, s->scalex); HV_FAA(style, "scale_y", 7, NV, s->scaley); HV_FAA(style, "spacing", 7, NV, s->spacing); HV_FAA(style, "angle", 5, NV, s->angle); HV_FAA(style, "borderstyle", 11, IV, s->borderstyle); HV_FAA(style, "outline", 7, NV, s->outline_w); HV_FAA(style, "shadow", 6, NV, s->shadow_w); HV_FAA(style, "align", 5, IV, s->alignment); HV_FAA(style, "margin_l", 8, IV, s->Margin[0]); HV_FAA(style, "margin_r", 8, IV, s->Margin[1]); HV_FAA(style, "margin_t", 8, IV, s->Margin[2]); HV_FAA(style, "margin_b", 8, IV, s->Margin[3]); HV_FAA(style, "encoding", 8, IV, s->encoding); HV_FAA(style, "relative_to", 11, IV, s->relativeTo); // Return the style return s; } AssDialogue *PerlAss::MakeAssDialogue(HV *diag) { dHV; // Create a default dialogue AssDialogue *d = new AssDialogue(); ASS_BASIC_INIT(diag, d); HV_FAA(diag, "comment", 7, IV, d->Comment); HV_FAA(diag, "layer", 5, IV, d->Layer); HV_FETCH(diag, "start_time", 10) d->Start.SetMS(SvIV(HV_VAL)); HV_FETCH(diag, "end_time", 8) d->End.SetMS(SvIV(HV_VAL)); HV_FETCH(diag, "style", 5) d->Style = wxString(SvPV_nolen(HV_VAL), pl2wx); HV_FETCH(diag, "actor", 5) d->Actor = wxString(SvPV_nolen(HV_VAL), pl2wx); HV_FAA(diag, "margin_l", 8, IV, d->Margin[0]); HV_FAA(diag, "margin_r", 8, IV, d->Margin[1]); HV_FAA(diag, "margin_t", 8, IV, d->Margin[2]); HV_FAA(diag, "margin_b", 8, IV, d->Margin[3]); HV_FETCH(diag, "effect", 6) d->Effect = wxString(SvPV_nolen(HV_VAL), pl2wx); HV_FETCH(diag, "text", 4) d->Text = wxString(SvPV_nolen(HV_VAL), pl2wx); // Return the dialogue return d; } /* TODO: report progress */ AssFile *PerlAss::MakeAssLines(AssFile *ass, AV *lines) { if(!ass) { /* TODO: create new AssFile if NULL */ return NULL; } // There may be a progress sink to report to PerlProgressSink *ps = PerlProgressSink::GetProgressSink(); dAV; std::list::iterator it = ass->Line.begin(); I32 len = av_len(lines); for(I32 i = 0; i <= len; i++) { if(!av_exists(lines, i)) continue; if(i < (I32)ass->Line.size()) { if(*it) delete *it; AV_FETCH(lines, i) *it++ = MakeAssEntry((HV*)SvRV(AV_VAL)); } else { AV_FETCH(lines, i) ass->Line.push_back(MakeAssEntry((HV*)SvRV(AV_VAL))); } // Report progress if(ps) ps->SetProgress((i+1)/(len+1) * 100); } for(; it != ass->Line.end();) { it = ass->Line.erase(it); } return ass; } }; #endif //WITH_PERL