mirror of https://github.com/odrling/Aegisub
484 lines
13 KiB
C++
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
|