Aegisub/aegisub/auto4_perl_ass.cpp

484 lines
13 KiB
C++

// 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
//
#include "config.h"
#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<AssEntry*>::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<AssEntry*>::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