diff --git a/aegisub/Makefile.am b/aegisub/Makefile.am index c9c050496..179b46852 100644 --- a/aegisub/Makefile.am +++ b/aegisub/Makefile.am @@ -10,18 +10,13 @@ SUBDIRS = bitmaps posix bin_PROGRAMS = aegisub -## My automake complains about these not being portable -## Perhaps it should somehow be moved to a shell script that generates an -## include, which is then included in source tarball? -## Similar to what the make-svn-rev-header.py scripts do currently. -## --jfs SVNREV := $(shell svnversion | sed 's/[^0-9]//') DARCSREV := $(strip $(shell head -n 1 ../_darcs/inventory 2>/dev/null)) REVISION := $(if $(SVNREV),-DBUILD_SVN_REVISION=$(SVNREV)) $(if $(DARCSREV),-DBUILD_DARCS) BUILDINFO := -DBUILD_CREDIT="\"$(shell whoami)\"" $(REVISION) AM_CPPFLAGST = -DAEGISUB -Iposix -include posix/defines.h $(BUILDINFO) -aegisub_LDADD = posix/libposix.a ../lua51/src/liblua.a ../FexTrackerSource/libfex.a ../csri/lib/.libs/libcsri.a +aegisub_LDADD = posix/libposix.a ../FexTrackerSource/libfex.a ../csri/lib/.libs/libcsri.a aegisub_LDFLAGS = if USE_LIBASS @@ -44,12 +39,18 @@ AUDIO_PLAYER += audio_player_pulse.cpp aegisub_LDFLAGS += -lpulse endif -if HAVE_RUBY -AUTO4RUBY=auto4_ruby_assfile.cpp auto4_ruby.cpp auto4_ruby_dialog.cpp +AUTOMATION = auto4_base.cpp +if WITH_AUTO4_LUA +AUTOMATION += auto4_lua.cpp auto4_lua_assfile.cpp auto4_lua_dialog.cpp auto4_lua_scriptreader.cpp +aegisub_LDADD += ../lua51/src/liblua.a +endif +if WITH_AUTO3 +AUTOMATION += auto4_auto3.cpp +## FIXME: also link to whatever lib here +endif +if WITH_AUTO4_RUBY +AUTOMATION += auto4_ruby_assfile.cpp auto4_ruby.cpp auto4_ruby_dialog.cpp AM_CPPFLAGS = $(shell ruby -r rbconfig -e "p '-I' + Config::CONFIG['rubylibdir'] + '/' + Config::CONFIG['arch'] ") $(AM_CPPFLAGST) -else -AUTO4RUBY= -AM_CPPFLAGS = $(AM_CPPFLAGST) endif if HAVE_HUNSPELL @@ -74,6 +75,11 @@ EXTRA_aegisub_SOURCES = \ audio_player_pulse.cpp \ audio_provider_avs.cpp \ audio_provider_lavc.cpp \ + auto4_lua.cpp \ + auto4_lua_assfile.cpp \ + auto4_lua_dialog.cpp \ + auto4_lua_scriptreader.cpp \ + auto4_auto3.cpp \ auto4_ruby.cpp \ auto4_ruby_assfile.cpp \ auto4_ruby_dialog.cpp \ @@ -101,7 +107,7 @@ EXTRA_aegisub_SOURCES = \ aegisub_SOURCES = \ $(AUDIO_PLAYER) \ - $(AUTO4RUBY) \ + $(AUTOMATION) \ $(HUNSPELL) \ $(LIBASS) \ $(FFMPEG) \ @@ -124,12 +130,6 @@ aegisub_SOURCES = \ audio_provider_ram.cpp \ audio_provider_stream.cpp \ audio_spectrum.cpp \ - auto4_base.cpp \ - auto4_lua.cpp \ - auto4_lua_assfile.cpp \ - auto4_lua_dialog.cpp \ - auto4_lua_scriptreader.cpp \ - auto4_auto3.cpp \ avisynth_wrap.cpp \ base_grid.cpp \ browse_button.cpp \ diff --git a/aegisub/auto4_auto3.cpp b/aegisub/auto4_auto3.cpp index 243535168..d126425bc 100644 --- a/aegisub/auto4_auto3.cpp +++ b/aegisub/auto4_auto3.cpp @@ -34,9 +34,7 @@ // #include "auto4_auto3.h" -#include "auto4_lua_scriptreader.h" -#include "../lua51/src/lualib.h" -#include "../lua51/src/lauxlib.h" +#include "../auto3/auto3.h" #include "options.h" #include "string_codec.h" #include "vfr.h" @@ -44,176 +42,45 @@ namespace Automation4 { - // Helper functions for reading/writing data - - static inline void L_settable(lua_State *L, int table, const char *key, lua_Number val) - { - lua_pushstring(L, key); - lua_pushnumber(L, val); - if (table > 0 || table < -100) { - lua_settable(L, table); - } else { - lua_settable(L, table-2); - } - } - - static inline void L_settable(lua_State *L, int table, const char *key, wxString val) - { - //wxLogMessage(_T("Adding string at index '%s': %s"), wxString(key, wxConvUTF8), val); - lua_pushstring(L, key); - lua_pushstring(L, val.mb_str(wxConvUTF8)); - if (table > 0 || table < -100) { - lua_settable(L, table); - } else { - lua_settable(L, table-2); - } - } - - static inline void L_settable_bool(lua_State *L, int table, const char *key, bool val) - { - lua_pushstring(L, key); - lua_pushboolean(L, val?1:0); - if (table > 0 || table < -100) { - lua_settable(L, table); - } else { - lua_settable(L, table-2); - } - } - - static inline void L_settable(lua_State *L, int table, wxString &key, lua_Number val) - { - L_settable(L, table, key.mb_str(wxConvUTF8), val); - } - - static inline void L_settable(lua_State *L, int table, wxString &key, wxString val) - { - L_settable(L, table, key.mb_str(wxConvUTF8), val); - } - - static inline void L_settable_bool(lua_State *L, int table, wxString &key, bool val) - { - L_settable_bool(L, table, key.mb_str(wxConvUTF8).data(), val); - } - - static inline void L_settable_kara(lua_State *L, int table, int index, int duration, wxString &kind, wxString &text, wxString &text_stripped) - { - lua_newtable(L); - L_settable(L, -1, "duration", duration); - L_settable(L, -1, "kind", kind); - L_settable(L, -1, "text", text); - L_settable(L, -1, "text_stripped", text_stripped); - if (table > 0 || table < -100) { - lua_rawseti(L, table, index); - } else { - lua_rawseti(L, table-1, index); - } - } - - static inline lua_Number L_gettableN(lua_State *L, const char *key) - { - lua_pushstring(L, key); - lua_gettable(L, -2); - lua_Number res = lua_tonumber(L, -1); - lua_settop(L, -2); - return res; - } - - static inline wxString L_gettableS(lua_State *L, const char *key) - { - lua_pushstring(L, key); - lua_gettable(L, -2); - wxString res(lua_tostring(L, -1), wxConvUTF8); - lua_settop(L, -2); - return res; - } - - static inline bool L_gettableB(lua_State *L, const char *key) - { - lua_pushstring(L, key); - lua_gettable(L, -2); - bool res = lua_toboolean(L, -1) != 0; - lua_settop(L, -2); - return res; - } - - // Auto3ProgressSink - int Auto3ProgressSink::LuaSetStatus(lua_State *L) + void Auto3ProgressSink::SetStatus(void *cbdata, const char *msg) { - Auto3ProgressSink *ps = GetObjPointer(L, lua_upvalueindex(1)); - wxString task(lua_tostring(L, 1), wxConvUTF8); - ps->SetTask(task); - return 0; + Auto3ProgressSink *ps = (Auto3ProgressSink*)cbdata; + ps->SetTask(wxString(msg, wxConvUTF8)); } - int Auto3ProgressSink::LuaOutputDebug(lua_State *L) + void Auto3ProgressSink::OutputDebug(void *cbdata, const char *msg) { - Auto3ProgressSink *ps = GetObjPointer(L, lua_upvalueindex(1)); - wxString msg(lua_tostring(L, 1), wxConvUTF8); - ps->AddDebugOutput(msg); + Auto3ProgressSink *ps = (Auto3ProgressSink*)cbdata; + ps->AddDebugOutput(wxString(msg, wxConvUTF8)); ps->AddDebugOutput(_T("\n")); - return 0; } - int Auto3ProgressSink::LuaReportProgress(lua_State *L) + void Auto3ProgressSink::ReportProgress(void *cbdata, float progress) { - Auto3ProgressSink *ps = GetObjPointer(L, lua_upvalueindex(1)); - float progress = lua_tonumber(L, 1); + Auto3ProgressSink *ps = (Auto3ProgressSink*)cbdata; ps->SetProgress(progress); - return 0; } - Auto3ProgressSink::Auto3ProgressSink(lua_State *_L, wxWindow *parent) + Auto3ProgressSink::Auto3ProgressSink(Auto3Interpreter *_script, wxWindow *parent) : ProgressSink(parent) - , L(_L) + , script(_script) { - Auto3ProgressSink **ud = (Auto3ProgressSink**)lua_newuserdata(L, sizeof(Auto3ProgressSink*)); - *ud = this; - - // register progress reporting stuff - lua_getglobal(L, "aegisub"); - - lua_pushvalue(L, -2); - lua_pushcclosure(L, LuaReportProgress, 1); - lua_setfield(L, -2, "report_progress"); - - lua_pushvalue(L, -2); - lua_pushcclosure(L, LuaOutputDebug, 1); - lua_setfield(L, -2, "output_debug"); - - lua_pushvalue(L, -2); - lua_pushcclosure(L, LuaSetStatus, 1); - lua_setfield(L, -2, "set_status"); - - // reference so other objects can also find the progress sink - lua_pushvalue(L, -2); - lua_setfield(L, LUA_REGISTRYINDEX, "progress_sink"); - - // Remove aegisub table and userdata object from stack - lua_pop(L, 2); + script->cb.logdata = this; + script->cb.log_error = OutputDebug; + script->cb.log_message = OutputDebug; + script->cb.set_progress = ReportProgress; + script->cb.set_status = SetStatus; } Auto3ProgressSink::~Auto3ProgressSink() { - // remove progress reporting stuff - lua_getglobal(L, "aegisub"); - lua_pushnil(L); - lua_setfield(L, -2, "report_progress"); - lua_pushnil(L); - lua_setfield(L, -2, "output_debug"); - lua_pushnil(L); - lua_setfield(L, -2, "set_status"); - lua_pop(L, 1); - lua_pushnil(L); - lua_setfield(L, LUA_REGISTRYINDEX, "progress_sink"); - } - - Auto3ProgressSink* Auto3ProgressSink::GetObjPointer(lua_State *L, int idx) - { - assert(lua_type(L, idx) == LUA_TUSERDATA); - void *ud = lua_touserdata(L, idx); - return *((Auto3ProgressSink**)ud); + script->cb.logdata = 0; + script->cb.log_error = 0; + script->cb.log_message = 0; + script->cb.set_progress = 0; + script->cb.set_status = 0; } @@ -221,36 +88,36 @@ namespace Automation4 { wxWindow* Auto3ConfigDialog::CreateWindow(wxWindow *parent) { - if (options.size() == 0) + if (options->name == 0) return 0; wxPanel *res = new wxPanel(parent, -1); wxFlexGridSizer *sizer = new wxFlexGridSizer(2, 5, 5); - for (std::vector::iterator opt = options.begin(); opt != options.end(); opt++) { + for (Auto3ConfigOption *opt = options; opt->name; opt++) { if (opt->kind == COK_INVALID) continue; Control control; - control.option = &*opt; + control.option = opt; switch (opt->kind) { case COK_LABEL: - control.control = new wxStaticText(res, -1, opt->label); + control.control = new wxStaticText(res, -1, wxString(opt->label, wxConvUTF8)); break; case COK_TEXT: - control.control = new wxTextCtrl(res, -1, opt->value.stringval); + control.control = new wxTextCtrl(res, -1, wxString(opt->value.stringval, wxConvUTF8)); break; case COK_INT: control.control = new wxSpinCtrl(res, -1); - if (opt->min.isset && opt->max.isset) { + if (opt->min.valid && opt->max.valid) { ((wxSpinCtrl*)control.control)->SetRange(opt->min.intval, opt->max.intval); - } else if (opt->min.isset) { + } else if (opt->min.valid) { ((wxSpinCtrl*)control.control)->SetRange(opt->min.intval, 0x7fff); - } else if (opt->max.isset) { + } else if (opt->max.valid) { ((wxSpinCtrl*)control.control)->SetRange(-0x7fff, opt->max.intval); } else { ((wxSpinCtrl*)control.control)->SetRange(-0x7fff, 0x7fff); @@ -263,14 +130,14 @@ namespace Automation4 { break; case COK_BOOL: - control.control = new wxCheckBox(res, -1, opt->label); - ((wxCheckBox*)control.control)->SetValue(opt->value.boolval); + control.control = new wxCheckBox(res, -1, wxString(opt->label, wxConvUTF8)); + ((wxCheckBox*)control.control)->SetValue(!!opt->value.intval); break; case COK_COLOUR: // *FIXME* what to do here? // just put a stupid edit box for now - control.control = new wxTextCtrl(res, -1, opt->value.colourval.GetASSFormatted(false)); + control.control = new wxTextCtrl(res, -1, wxString(opt->value.stringval, wxConvUTF8)); break; case COK_STYLE: @@ -283,13 +150,13 @@ namespace Automation4 { } if (opt->kind != COK_LABEL && opt->kind != COK_BOOL) { - control.label = new wxStaticText(res, -1, opt->label); + control.label = new wxStaticText(res, -1, wxString(opt->label, wxConvUTF8)); sizer->Add(control.label, 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL); } else { control.label = 0; sizer->AddSpacer(0); } - control.control->SetToolTip(opt->hint); + control.control->SetToolTip(wxString(opt->hint, wxConvUTF8)); sizer->Add(control.control, 1, wxEXPAND); controls.push_back(control); @@ -300,228 +167,14 @@ namespace Automation4 { return res; } - Auto3ConfigDialog::Auto3ConfigDialog(lua_State *L) + Auto3ConfigDialog::Auto3ConfigDialog(Auto3Interpreter *script) { - present = false; - if (!lua_istable(L, -1)) { - return; - } - - int i = 1; - while (true) { - // get an element from the array - lua_pushnumber(L, i); - lua_gettable(L, -2); - - // check if it was a table - if (!lua_istable(L, -1)) { - lua_pop(L, 1); - break; - } - - // add a new config option and fill it - { - Auto3ScriptConfigurationOption opt; - options.push_back(opt); - } - Auto3ScriptConfigurationOption &opt = options.back(); - - // get the "kind" - lua_pushstring(L, "kind"); - lua_gettable(L, -2); - if (lua_isstring(L, -1)) { - // use C standard lib functions here, as it's probably faster than messing around with unicode - // lua is known to always properly null-terminate strings, and the strings are known to be pure ascii - const char *kind = lua_tostring(L, -1); - if (strcmp(kind, "label") == 0) { - opt.kind = COK_LABEL; - } else if (strcmp(kind, "text") == 0) { - opt.kind = COK_TEXT; - } else if (strcmp(kind, "int") == 0) { - opt.kind = COK_INT; - } else if (strcmp(kind, "float") == 0) { - opt.kind = COK_FLOAT; - } else if (strcmp(kind, "bool") == 0) { - opt.kind = COK_BOOL; - } else if (strcmp(kind, "colour") == 0) { - opt.kind = COK_COLOUR; - } else if (strcmp(kind, "style") == 0) { - opt.kind = COK_STYLE; - } else { - opt.kind = COK_INVALID; - } - } else { - opt.kind = COK_INVALID; - } - - // remove "kind" string from stack again - lua_pop(L, 1); - - // no need to check for rest if this one is already deemed invalid - if (opt.kind != COK_INVALID) { - // name - lua_pushstring(L, "name"); - lua_gettable(L, -2); - if (lua_isstring(L, -1)) { - opt.name = wxString(lua_tostring(L, -1), wxConvUTF8); - lua_pop(L, 1); - } else { - lua_pop(L, 1); - // no name means invalid option - opt.kind = COK_INVALID; - goto continue_invalid_option; - } - - // label - lua_pushstring(L, "label"); - lua_gettable(L, -2); - if (lua_isstring(L, -1)) { - opt.label = wxString(lua_tostring(L, -1), wxConvUTF8); - lua_pop(L, 1); - } else { - lua_pop(L, 1); - // label is also required - opt.kind = COK_INVALID; - goto continue_invalid_option; - } - assert(opt.kind != COK_INVALID); - - // hint - lua_pushstring(L, "hint"); - lua_gettable(L, -2); - if (lua_isstring(L, -1)) { - opt.hint = wxString(lua_tostring(L, -1), wxConvUTF8); - } else { - opt.hint = _T(""); - } - lua_pop(L, 1); - - // min - lua_pushstring(L, "min"); - lua_gettable(L, -2); - if (lua_isnumber(L, -1)) { - opt.min.isset = true; - opt.min.floatval = lua_tonumber(L, -1); - opt.min.intval = (int)opt.min.floatval; - } else { - opt.min.isset = false; - } - lua_pop(L, 1); - - // max - lua_pushstring(L, "max"); - lua_gettable(L, -2); - if (lua_isnumber(L, -1)) { - opt.max.isset = true; - opt.max.floatval = lua_tonumber(L, -1); - opt.max.intval = (int)opt.max.floatval; - } else { - opt.max.isset = false; - } - lua_pop(L, 1); - - // default (this is going to kill me) - lua_pushstring(L, "default"); - lua_gettable(L, -2); - switch (opt.kind) { - case COK_LABEL: - // nothing to do, nothing expected - break; - case COK_TEXT: - case COK_STYLE: - // expect it to be a string - if (lua_isstring(L, -1)) { - opt.default_val.stringval = wxString(lua_tostring(L, -1), wxConvUTF8); - } else { - // not a string, baaaad scripter - opt.kind = COK_INVALID; - } - break; - case COK_INT: - case COK_FLOAT: - // expect it to be a number - if (lua_isnumber(L, -1)) { - opt.default_val.floatval = lua_tonumber(L, -1); - opt.default_val.intval = (int)opt.default_val.floatval; - } else { - opt.kind = COK_INVALID; - } - break; - case COK_BOOL: - // expect it to be a bool - if (lua_isboolean(L, -1)) { - opt.default_val.boolval = lua_toboolean(L, -1)!=0; - } else { - opt.kind = COK_INVALID; - } - break; - case COK_COLOUR: - // expect it to be a ass hex colour formatted string - if (lua_isstring(L, -1)) { - opt.default_val.stringval = wxString(lua_tostring(L, -1), wxConvUTF8); - opt.default_val.colourval.Parse(opt.default_val.stringval); // and hope this goes well! - } else { - opt.kind = COK_INVALID; - } - break; - case COK_INVALID: - break; - } - opt.value = opt.default_val; - lua_pop(L, 1); - } - - // so we successfully got an option added, so at least there is a configuration present now - present = true; -continue_invalid_option: - // clean up and prepare for next iteration - lua_pop(L, 1); - i++; - } + options = script->config; } Auto3ConfigDialog::~Auto3ConfigDialog() { - // TODO? - } - - int Auto3ConfigDialog::LuaReadBack(lua_State *L) - { - lua_newtable(L); - - for (std::vector::iterator opt = options.begin(); opt != options.end(); opt++) { - switch (opt->kind) { - case COK_INVALID: - case COK_LABEL: - break; - - case COK_TEXT: - case COK_STYLE: - L_settable(L, -1, opt->name, opt->value.stringval); - break; - - case COK_INT: - L_settable(L, -1, opt->name, opt->value.intval); - break; - - case COK_FLOAT: - L_settable(L, -1, opt->name, opt->value.floatval); - break; - - case COK_BOOL: - L_settable_bool(L, -1, opt->name, opt->value.boolval); - break; - - case COK_COLOUR: - L_settable(L, -1, opt->name, opt->value.colourval.GetASSFormatted(false, false)); - break; - - default: - break; - } - } - - return 1; + // Nothing to do here } void Auto3ConfigDialog::ReadBack() @@ -529,35 +182,40 @@ continue_invalid_option: for (std::vector::iterator ctl = controls.begin(); ctl != controls.end(); ctl++) { switch (ctl->option->kind) { case COK_TEXT: - ctl->option->value.stringval = ((wxTextCtrl*)ctl->control)->GetValue(); + Auto3Free(ctl->option->value.stringval); + ctl->option->value.stringval = Auto3Strdup(((wxTextCtrl*)ctl->control)->GetValue().mb_str(wxConvUTF8)); break; case COK_INT: ctl->option->value.intval = ((wxSpinCtrl*)ctl->control)->GetValue(); break; - case COK_FLOAT: - if (!((wxTextCtrl*)ctl->control)->GetValue().ToDouble(&ctl->option->value.floatval)) { + case COK_FLOAT: { + double v; + if (!((wxTextCtrl*)ctl->control)->GetValue().ToDouble(&v)) { wxLogWarning( _T("The value entered for field '%s' (%s) could not be converted to a floating-point number. Default value (%f) substituted for the entered value."), - ctl->option->label.c_str(), + wxString(ctl->option->label, wxConvUTF8).c_str(), ((wxTextCtrl*)ctl->control)->GetValue().c_str(), ctl->option->default_val.floatval); ctl->option->value.floatval = ctl->option->default_val.floatval; } - break; + ctl->option->value.floatval = v; + break; } case COK_BOOL: - ctl->option->value.boolval = ((wxCheckBox*)ctl->control)->GetValue(); + ctl->option->value.intval = (int)((wxCheckBox*)ctl->control)->GetValue(); break; case COK_COLOUR: // *FIXME* needs to be updated to use a proper color control - ctl->option->value.colourval.Parse(((wxTextCtrl*)ctl->control)->GetValue()); + Auto3Free(ctl->option->value.stringval); + ctl->option->value.stringval = Auto3Strdup(((wxTextCtrl*)ctl->control)->GetValue().mb_str(wxConvUTF8)); break; case COK_STYLE: - ctl->option->value.stringval = ((wxChoice*)ctl->control)->GetStringSelection(); + Auto3Free(ctl->option->value.stringval); + ctl->option->value.stringval = Auto3Strdup(((wxChoice*)ctl->control)->GetStringSelection().mb_str(wxConvUTF8)); break; case COK_LABEL: @@ -569,27 +227,25 @@ continue_invalid_option: wxString Auto3ConfigDialog::Serialise() { - if (options.size() == 0) + if (options->name == 0) return _T(""); wxString result; - for (std::vector::iterator opt = options.begin(); opt != options.end(); opt++) { + for (Auto3ConfigOption *opt = options; opt->name; opt++) { + wxString optname(opt->name, wxConvUTF8); switch (opt->kind) { case COK_TEXT: case COK_STYLE: - result << wxString::Format(_T("%s:%s|"), opt->name.c_str(), inline_string_encode(opt->value.stringval).c_str()); - break; + case COK_COLOUR: { + wxString optstrval(opt->value.stringval, wxConvUTF8); + result << wxString::Format(_T("%s:%s|"), optname.c_str(), inline_string_encode(optstrval).c_str()); + break; } case COK_INT: - result << wxString::Format(_T("%s:%d|"), opt->name.c_str(), opt->value.intval); + case COK_BOOL: + result << wxString::Format(_T("%s:%d|"), optname.c_str(), opt->value.intval); break; case COK_FLOAT: - result << wxString::Format(_T("%s:%e|"), opt->name.c_str(), opt->value.floatval); - break; - case COK_BOOL: - result << wxString::Format(_T("%s:%d|"), opt->name.c_str(), opt->value.boolval?1:0); - break; - case COK_COLOUR: - result << wxString::Format(_T("%s:%s|"), opt->name.c_str(), opt->value.colourval.GetASSFormatted(false).c_str()); + result << wxString::Format(_T("%s:%e|"), optname.c_str(), opt->value.floatval); break; default: // The rest aren't stored @@ -612,37 +268,32 @@ continue_invalid_option: wxString optval = setting.AfterFirst(_T(':')); // find the setting in the list loaded from the script - std::vector::iterator opt = options.begin(); - while (opt != options.end() && opt->name != optname) + Auto3ConfigOption *opt = options; + while (opt->name && wxString(opt->name, wxConvUTF8) != optname) opt ++; - if (opt != options.end()) { + if (opt->name) { // ok, found the option! switch (opt->kind) { case COK_TEXT: case COK_STYLE: - opt->value.stringval = inline_string_decode(optval); + case COK_COLOUR: + Auto3Free(opt->value.stringval); + opt->value.stringval = Auto3Strdup(inline_string_decode(optval).mb_str(wxConvUTF8)); break; case COK_INT: - { - long n; - optval.ToLong(&n, 10); - opt->value.intval = n; - } - break; + case COK_BOOL: { + long n; + optval.ToLong(&n, 10); + opt->value.intval = n; + break; } - case COK_FLOAT: - optval.ToDouble(&opt->value.floatval); - break; - - case COK_BOOL: - opt->value.boolval = optval == _T("1"); - break; - - case COK_COLOUR: - opt->value.colourval.Parse(optval); - break; + case COK_FLOAT: { + double v; + optval.ToDouble(&v); + opt->value.floatval = (float)v; + break; } case COK_LABEL: case COK_INVALID: @@ -655,26 +306,17 @@ continue_invalid_option: // Auto3Filter - Auto3Filter::Auto3Filter(const wxString &_name, const wxString &_description, lua_State *_L) + Auto3Filter::Auto3Filter(const wxString &_name, const wxString &_description, Auto3Interpreter *_script) : Feature(SCRIPTFEATURE_FILTER, _name) , FeatureFilter(_name, _description, 0) - , L(_L) + , script(_script) { - // check that the processing function exists - lua_getglobal(L, "process_lines"); - if (!lua_isfunction(L, -1)) { - throw _T("Script error: No 'process_lines' function provided"); - } - - lua_pop(L, 2); + // Nothing more to do } ScriptConfigDialog* Auto3Filter::GenerateConfigDialog(wxWindow *parent) { - // configuration (let the config object do all the loading) - lua_getglobal(L, "configuration"); - config = new Auto3ConfigDialog(L); - + config = new Auto3ConfigDialog(script); return config; } @@ -685,9 +327,9 @@ continue_invalid_option: void Auto3Filter::ProcessSubs(AssFile *subs, wxWindow *export_dialog) { - Auto3ProgressSink *sink = new Auto3ProgressSink(L, export_dialog); + Auto3ProgressSink *sink = new Auto3ProgressSink(script, export_dialog); sink->SetTitle(GetName()); - Auto3ThreadedProcessor thread(L, subs, config, sink); + Auto3ThreadedProcessor thread(script, subs, config, sink); sink->ShowModal(); thread.Wait(); @@ -698,9 +340,9 @@ continue_invalid_option: // Auto3ThreadedProcessor - Auto3ThreadedProcessor::Auto3ThreadedProcessor(lua_State *_L, AssFile *_file, Auto3ConfigDialog *_config, Auto3ProgressSink *_sink) + Auto3ThreadedProcessor::Auto3ThreadedProcessor(Auto3Interpreter *_script, AssFile *_file, Auto3ConfigDialog *_config, Auto3ProgressSink *_sink) : wxThread(wxTHREAD_JOINABLE) - , L(_L) + , script(_script) , file(_file) , config(_config) , sink(_sink) @@ -718,455 +360,86 @@ continue_invalid_option: wxThread::ExitCode Auto3ThreadedProcessor::Entry() { - bool failed = false; + script->cb.rwdata = this; + script->cb.reset_style_pointer = ResetStylePointer; + script->cb.reset_subs_pointer = ResetSubsPointer; + script->cb.get_meta_info = GetMetaInfo; + script->cb.get_next_style = GetNextStyle; + script->cb.get_next_sub = GetNextSub; + script->cb.start_subs_write = StartSubsWrite; + script->cb.write_sub = WriteSub; + + int res = RunAuto3Script(script); + + script->cb.rwdata = 0; + script->cb.reset_style_pointer = 0; + script->cb.reset_subs_pointer = 0; + script->cb.get_meta_info = 0; + script->cb.get_next_style = 0; + script->cb.get_next_sub = 0; + script->cb.start_subs_write = 0; + script->cb.write_sub = 0; - try { - sink->SetTask(_T("Preparing subtitle data")); - sink->SetProgress(0); - - // first put the function itself on the stack - lua_pushstring(L, "process_lines"); - lua_gettable(L, LUA_GLOBALSINDEX); - - // now put the three arguments on the stack - - // first argument: the metadata table - lua_newtable(L); - L_settable(L, -1, "res_x", file->GetScriptInfoAsInt(_T("PlayResX"))); - L_settable(L, -1, "res_y", file->GetScriptInfoAsInt(_T("PlayResY"))); - - // second and third arguments: styles and events tables - lua_newtable(L); - int styletab = lua_gettop(L); - lua_newtable(L); - int eventtab = lua_gettop(L); - - int numstyles = 0, numevents = 0; - - // fill the styles and events tables - int processed_lines = 1; - for (std::list::iterator i = file->Line.begin(); i != file->Line.end(); i++, processed_lines++) { - - AssEntry *e = *i; - - if (!e->Valid) continue; - - if (e->GetType() == ENTRY_STYLE) { - - AssStyle *style = e->GetAsStyle(e); - - // gonna need a table to put the style data into - lua_newtable(L); - // put the table into index N in the style table - lua_pushvalue(L, -1); - lua_rawseti(L, styletab, numstyles); - // and put it into its named index - lua_pushstring(L, style->name.mb_str(wxConvUTF8)); - lua_pushvalue(L, -2); - lua_settable(L, styletab); - - // so now the table is regged and stuff, put some data into it - L_settable (L, -1, "name", style->name); - L_settable (L, -1, "fontname", style->font); - L_settable (L, -1, "fontsize", style->fontsize); - L_settable (L, -1, "color1", style->primary.GetASSFormatted(true, true)); - L_settable (L, -1, "color2", style->secondary.GetASSFormatted(true, true)); - L_settable (L, -1, "color3", style->outline.GetASSFormatted(true, true)); - L_settable (L, -1, "color4", style->shadow.GetASSFormatted(true, true)); - L_settable_bool(L, -1, "bold", style->bold); - L_settable_bool(L, -1, "italic", style->italic); - L_settable_bool(L, -1, "underline", style->underline); - L_settable_bool(L, -1, "strikeout", style->strikeout); - L_settable (L, -1, "scale_x", style->scalex); - L_settable (L, -1, "scale_y", style->scaley); - L_settable (L, -1, "spacing", style->spacing); - L_settable (L, -1, "angle", style->angle); - L_settable (L, -1, "borderstyle", style->borderstyle); - L_settable (L, -1, "outline", style->outline_w); - L_settable (L, -1, "shadow", style->shadow_w); - L_settable (L, -1, "align", style->alignment); - L_settable (L, -1, "margin_l", style->Margin[0]); - L_settable (L, -1, "margin_r", style->Margin[1]); - L_settable (L, -1, "margin_v", style->Margin[2]); - L_settable (L, -1, "encoding", style->encoding); - - // and get that table off the stack again - lua_settop(L, -2); - - numstyles++; - - } else if (e->group == _T("[Events]")) { - - if (e->GetType() != ENTRY_DIALOGUE) { - - // not a dialogue/comment event - - // start checking for a blank line - wxString entryData = e->GetEntryData(); - if (entryData.IsEmpty()) { - lua_newtable(L); - L_settable(L, -1, "kind", wxString(_T("blank"))); - } else if (entryData[0] == _T(';')) { - // semicolon comment - lua_newtable(L); - L_settable(L, -1, "kind", wxString(_T("scomment"))); - L_settable(L, -1, "text", entryData.Mid(1)); - } else { - // not a blank line and not a semicolon comment - // just skip... - continue; - } - - } else { - - // ok, so it is a dialogue/comment event - // massive handling :( - - lua_newtable(L); - - assert(e->GetType() == ENTRY_DIALOGUE); - - AssDialogue *dia = e->GetAsDialogue(e); - - // kind of line - if (dia->Comment) { - L_settable(L, -1, "kind", wxString(_T("comment"))); - } else { - L_settable(L, -1, "kind", wxString(_T("dialogue"))); - } - - L_settable(L, -1, "layer", dia->Layer); - L_settable(L, -1, "start_time", dia->Start.GetMS()/10); - L_settable(L, -1, "end_time", dia->End.GetMS()/10); - L_settable(L, -1, "style", dia->Style); - L_settable(L, -1, "name", dia->Actor); - L_settable(L, -1, "margin_l", dia->Margin[0]); - L_settable(L, -1, "margin_r", dia->Margin[1]); - L_settable(L, -1, "margin_v", dia->Margin[2]); - L_settable(L, -1, "effect", dia->Effect); - L_settable(L, -1, "text", dia->Text); - - // so that's the easy part - // now for the stripped text and *ugh* the karaoke! - - // prepare for stripped text - wxString text_stripped = _T(""); - L_settable(L, -1, "text_stripped", 0); // dummy item - // prepare karaoke table - lua_newtable(L); - lua_pushstring(L, "karaoke"); - lua_pushvalue(L, -2); - lua_settable(L, -4); - // now the top of the stack is the karaoke table, and it's present in the dialogue table - - int kcount = 0; - int kdur = 0; - wxString kkind = _T(""); - wxString ktext = _T(""); - wxString ktext_stripped = _T(""); - - dia->ParseASSTags(); - for (std::vector::iterator block = dia->Blocks.begin(); block != dia->Blocks.end(); block++) { - - switch ((*block)->type) { - - case BLOCK_BASE: - lua_pushliteral(L, "BLOCK_BASE found processing dialogue blocks. This should never happen."); - lua_error(L); - break; - - case BLOCK_PLAIN: - ktext += (*block)->text; - ktext_stripped += (*block)->text; - text_stripped += (*block)->text; - break; - - case BLOCK_DRAWING: - ktext += (*block)->text; - break; - - case BLOCK_OVERRIDE: { - bool brackets_open = false; - std::vector &tags = (*block)->GetAsOverride(*block)->Tags; - - for (std::vector::iterator tag = tags.begin(); tag != tags.end(); tag++) { - - if (!(*tag)->Name.Mid(0,2).CmpNoCase(_T("\\k")) && (*tag)->IsValid()) { - - // it's a karaoke tag - if (brackets_open) { - ktext += _T("}"); - brackets_open = false; - } - L_settable_kara(L, -1, kcount, kdur, kkind, ktext, ktext_stripped); - kcount++; - kdur = (*tag)->Params[0]->AsInt(); // no error checking; this should always be int - kkind = (*tag)->Name.Mid(1); - ktext = _T(""); - ktext_stripped = _T(""); - - } else { - - // it's something else - // don't care if it's a valid tag or not - if (!brackets_open) { - ktext += _T("{"); - brackets_open = true; - } - ktext += (*tag)->ToString(); - - } - - } - - if (brackets_open) { - ktext += _T("}"); - } - - break;} - - } - - } - dia->ClearBlocks(); - - // add the final karaoke block to the table - // (even if there's no karaoke in the line, there's always at least one karaoke block) - // even if the line ends in {\k10} with no text after, an empty block should still be inserted - // (otherwise data are lost) - L_settable_kara(L, -1, kcount, kdur, kkind, ktext, ktext_stripped); - kcount++; - L_settable(L, -1, "n", kcount); // number of syllables in the karaoke - lua_settop(L, -2); // remove karaoke table from the stack again - L_settable(L, -1, "text_stripped", text_stripped); // store the real stripped text - - } - - // now the entry table has been created and placed on top of the stack - // now all that's missing it to insert it into the event table - lua_rawseti(L, eventtab, numevents); - numevents++; - - } else { - // not really a line type automation needs to take care of... ignore it - } - - sink->SetProgress(100.0f * processed_lines / file->Line.size() / 3); - - } - - // finally add the counter elements to the styles and events tables - lua_pushnumber(L, numstyles); - lua_rawseti(L, styletab, -1); - L_settable(L, eventtab, "n", numevents); - // and let the config object create a table for the @config argument - config->LuaReadBack(L); - - sink->SetTask(_T("Running script for processing")); - sink->SetProgress(100.0f/3); - int ret = lua_pcall(L, 4, 1, 0); - if (ret) { - wxString emsg(lua_tostring(L, -1), wxConvUTF8); - emsg.Prepend(_T("The Automation 3 script produced an error:\n")); - emsg.Append(_T("\nThe subtitles have not been altered.")); - lua_pushstring(L, emsg.mb_str(wxConvUTF8)); - throw ret; - } - sink->SetProgress(200.0f/3); - sink->SetTask(_T("Reading back data from script")); - - // phew, survived the call =) - // time to read back the results - - if (!lua_istable(L, -1)) { - throw _T("The script function did not return a table as expected. Unable to process results. (Nothing was changed.)"); - } - - // but start by removing all events - { - std::list::iterator cur, next; - next = file->Line.begin(); - while (next != file->Line.end()) { - cur = next++; - if ((*cur)->group == _T("[Events]")) { - wxString temp = (*cur)->GetEntryData(); - if (temp == _T("[Events]")) { - // skip the section header - continue; - } - if ((*cur)->GetType() != ENTRY_DIALOGUE && temp.Mid(0,1) != _T(";") && temp.Trim() != _T("")) { - // skip non-dialogue non-semicolon comment lines (such as Format) - continue; - } - delete (*cur); - file->Line.erase(cur); - } - } - } - - // so anyway, there is a single table on the stack now - // that table contains a lot of events... - // and it ought to contain an "n" key as well, telling how many events - // but be lenient, and don't expect one to be there, but rather count from zero and let it be nil-terminated - // if the "n" key is there, use it as a progress indicator hint, though - int output_line_count; - lua_pushstring(L, "n"); - lua_gettable(L, -2); - if (lua_isnumber(L, -1)) { - output_line_count = (int) lua_tonumber(L, -1); - } else { - // assume number of output lines == number of input lines - output_line_count = processed_lines; - } - lua_settop(L, -2); - - int outline = 0; - int faketime = file->Line.back()->StartMS; - - // If there's nothing at index 0, start at index 1 instead, to support both zero and one based indexing - lua_pushnumber(L, outline); - lua_gettable(L, -2); - if (!lua_istable(L, -1)) { - outline++; - output_line_count++; - } - lua_pop(L, 1); - - while (lua_pushnumber(L, outline), lua_gettable(L, -2), lua_istable(L, -1)) { - // top of the stack is a table, hopefully with an AssEntry in it - - // start by getting the kind - lua_pushstring(L, "kind"); - lua_gettable(L, -2); - if (!lua_isstring(L, -1)) { - sink->AddDebugOutput(wxString::Format(_T("The output data at index %d is mising a valid 'kind' field, and has been skipped\n"), outline)); - lua_settop(L, -2); - } else { - - wxString kind = wxString(lua_tostring(L, -1), wxConvUTF8).Lower(); - // remove "kind" from stack again - lua_settop(L, -2); - - if (kind == _T("dialogue") || kind == _T("comment")) { - - lua_pushstring(L, "layer"); - lua_gettable(L, -2); - lua_pushstring(L, "start_time"); - lua_gettable(L, -3); - lua_pushstring(L, "end_time"); - lua_gettable(L, -4); - lua_pushstring(L, "style"); - lua_gettable(L, -5); - lua_pushstring(L, "name"); - lua_gettable(L, -6); - lua_pushstring(L, "margin_l"); - lua_gettable(L, -7); - lua_pushstring(L, "margin_r"); - lua_gettable(L, -8); - lua_pushstring(L, "margin_v"); - lua_gettable(L, -9); - lua_pushstring(L, "effect"); - lua_gettable(L, -10); - lua_pushstring(L, "text"); - lua_gettable(L, -11); - - if (lua_isnumber(L, -10) && lua_isnumber(L, -9) && lua_isnumber(L, -8) && - lua_isstring(L, -7) && lua_isstring(L, -6) && lua_isnumber(L, -5) && - lua_isnumber(L, -4) && lua_isnumber(L, -3) && lua_isstring(L, -2) && - lua_isstring(L, -1)) - { - AssDialogue *e = new AssDialogue(); - e->Layer = (int)lua_tonumber(L, -10); - e->Start.SetMS(10*(int)lua_tonumber(L, -9)); - e->End.SetMS(10*(int)lua_tonumber(L, -8)); - e->Style = wxString(lua_tostring(L, -7), wxConvUTF8); - e->Actor = wxString(lua_tostring(L, -6), wxConvUTF8); - e->Margin[0] = (int)lua_tonumber(L, -5); - e->Margin[1] = (int)lua_tonumber(L, -4); - e->Margin[2] = e->Margin[3] = (int)lua_tonumber(L, -3); - e->Effect = wxString(lua_tostring(L, -2), wxConvUTF8); - e->Text = wxString(lua_tostring(L, -1), wxConvUTF8); - e->Comment = kind == _T("comment"); - lua_settop(L, -11); - e->StartMS = e->Start.GetMS(); - //e->ParseASSTags(); - e->UpdateData(); - file->Line.push_back(e); - } else { - sink->AddDebugOutput(wxString::Format(_T("The output data at index %d (kind '%s') has one or more missing/invalid fields, and has been skipped\n"), outline, kind.c_str())); - } - - } else if (kind == _T("scomment")) { - - lua_pushstring(L, "text"); - lua_gettable(L, -2); - if (lua_isstring(L, -1)) { - wxString text(lua_tostring(L, -1), wxConvUTF8); - lua_settop(L, -2); - AssEntry *e = new AssEntry(wxString(_T(";")) + text); - e->StartMS = faketime; - file->Line.push_back(e); - } else { - sink->AddDebugOutput(wxString::Format(_T("The output data at index %d (kind 'scomment') is missing a valid 'text' field, and has been skipped\n"), outline)); - } - - } else if (kind == _T("blank")) { - - AssEntry *e = new AssEntry(_T("")); - e->StartMS = faketime; - file->Line.push_back(e); - - } else { - sink->AddDebugOutput(wxString::Format(_T("The output data at index %d has an invalid value in the 'kind' field, and has been skipped\n"), outline)); - } - - } - - // remove table again - lua_settop(L, -2); - // progress report - if (outline >= output_line_count) { - sink->SetProgress(99.9f); - } else { - sink->SetProgress((200.0f + 100.0f*outline/output_line_count) / 3); - } - - outline++; - } - - sink->SetTask(_T("Completed")); - } - catch (const wchar_t *e) { - failed = true; - sink->AddDebugOutput(e); - } - catch (const char *e) { - failed = true; - wxString s(e, wxConvUTF8); - sink->AddDebugOutput(s); - } - catch (...) { - failed = true; - if (lua_isstring(L, -1)) { - wxString s(lua_tostring(L, -1), wxConvUTF8); - sink->AddDebugOutput(s); - } else { - sink->AddDebugOutput(_T("Unknown error")); - } - } - - if (failed) { - sink->SetTask(_T("Failed")); - } else { - sink->SetProgress(100); - } sink->script_finished = true; wxWakeUpIdle(); - if (failed) { - return (wxThread::ExitCode)1; - } else { - return (wxThread::ExitCode)0; - } + + return (wxThread::ExitCode)res; // no matter what MSVC says, this works (zero/non-zero is all that matters here anyway) + } + + + void Auto3ThreadedProcessor::ResetStylePointer(void *cbdata) + { + Auto3ThreadedProcessor *self = (Auto3ThreadedProcessor*)cbdata; + self->style_pointer = self->file->Line.begin(); + } + + + void Auto3ThreadedProcessor::ResetSubsPointer(void *cbdata) + { + Auto3ThreadedProcessor *self = (Auto3ThreadedProcessor*)cbdata; + self->subs_pointer = self->file->Line.begin(); + } + + + void Auto3ThreadedProcessor::GetMetaInfo(void *cbdata, int *res_x, int *res_y) + { + Auto3ThreadedProcessor *self = (Auto3ThreadedProcessor*)cbdata; + self->file->GetResolution(*res_x, *res_y); + } + + + int Auto3ThreadedProcessor::GetNextStyle( + void *cbdata, char **name, char **fontname, int *fontsize, char **color1, char **color2, char **color3, char **color4, + int *bold, int *italic, int *underline, int *strikeout, float *scale_x, float *scale_y, float *spacing, float *angle, + int *borderstyle, float *outline, float *shadow, int *align, int *margin_l, int *margin_r, int *margin_v, int *encoding) + { + Auto3ThreadedProcessor *self = (Auto3ThreadedProcessor*)cbdata; + // TODO + return 0; + } + + + int Auto3ThreadedProcessor::GetNextSub(void *cbdata, int *layer, int *start_time, int *end_time, char **style, char **actor, + int *margin_l, int *margin_r, int *margin_v, char **effect, char **text, int *comment) + { + Auto3ThreadedProcessor *self = (Auto3ThreadedProcessor*)cbdata; + // TODO + return 0; + } + + + void Auto3ThreadedProcessor::StartSubsWrite(void *cbdata) + { + Auto3ThreadedProcessor *self = (Auto3ThreadedProcessor*)cbdata; + // TODO: clear events section and set subs_pointer + } + + + void Auto3ThreadedProcessor::WriteSub(void *cbdata, int layer, int start_time, int end_time, const char *style, const char *actor, + int margin_l, int margin_r, int margin_v, const char *effect, const char *text, int comment) + { + Auto3ThreadedProcessor *self = (Auto3ThreadedProcessor*)cbdata; + // TODO } @@ -1175,7 +448,7 @@ continue_invalid_option: Auto3Script::Auto3Script(const wxString &filename) : Script(filename) , filter(0) - , L(0) + , script(0) { try { Create(); @@ -1188,58 +461,43 @@ continue_invalid_option: Auto3Script::~Auto3Script() { - if (L) Destroy(); + if (script) Destroy(); } - Auto3Script* Auto3Script::GetScriptObject(lua_State *L) - { - lua_getfield(L, LUA_REGISTRYINDEX, "aegisub"); - void *ptr = lua_touserdata(L, -1); - lua_pop(L, 1); - return (Auto3Script*)ptr; - } - - int Auto3Script::LuaTextExtents(lua_State *L) + void Auto3Script::TextExtents(void *cbdata, char *text, char *fontname, int fontsize, int bold, int italic, int spacing, + float scale_x, float scale_y, int encoding, float *out_width, float *out_height, float *out_descent, float *out_extlead) { double resx, resy, resd, resl; - wxString intext(lua_tostring(L, -1), wxConvUTF8); + wxString intext(text, wxConvUTF8); AssStyle st; - st.font = L_gettableS(L, "fontname"); - st.fontsize = L_gettableN(L, "fontsize"); - st.bold = L_gettableB(L, "bold"); - st.italic = L_gettableB(L, "italic"); - st.underline = L_gettableB(L, "underline"); - st.strikeout = L_gettableB(L, "strikeout"); - st.scalex = L_gettableN(L, "scale_x"); - st.scaley = L_gettableN(L, "scale_y"); - st.spacing = (int)L_gettableN(L, "spacing"); - st.encoding = (int)L_gettableN(L, "encoding"); + st.font = wxString(fontname, wxConvUTF8); + st.fontsize = fontsize; + st.bold = !!bold; + st.italic = !!italic; + st.underline = false; + st.strikeout = false; + st.scalex = scale_x; + st.scaley = scale_y; + st.spacing = spacing; + st.encoding = encoding; - if (!CalculateTextExtents(&st, intext, resx, resy, resd, resl)) { - lua_pushstring(L, "Some internal error occurred calculating text_extents"); - lua_error(L); - } + CalculateTextExtents(&st, intext, resx, resy, resd, resl); + // So no error checking here... FIXME? - lua_pushnumber(L, resx); - lua_pushnumber(L, resy); - lua_pushnumber(L, resd); - lua_pushnumber(L, resl); - return 4; + *out_width = resx; + *out_height = resy; + *out_descent = resd; + *out_extlead = resl; } - int Auto3Script::LuaInclude(lua_State *L) + filename_t Auto3Script::ResolveInclude(void *cbdata, const char *incname) { - Auto3Script *s = GetScriptObject(L); + Auto3Script *s = (Auto3Script*)cbdata; - if (!lua_isstring(L, 1)) { - lua_pushstring(L, "Argument to include must be a string"); - lua_error(L); - return 0; - } - wxString fnames(lua_tostring(L, 1), wxConvUTF8); + wxString fnames(incname, wxConvUTF8); wxFileName fname(fnames); if (fname.GetDirCount() == 0) { @@ -1253,66 +511,38 @@ continue_invalid_option: // absolute path, do nothing } if (!fname.IsOk() || !fname.FileExists()) { - lua_pushfstring(L, "Automation 3 include not found: %s", fnames.mb_str(wxConvUTF8).data()); - lua_error(L); - } - - LuaScriptReader script_reader(fname.GetFullPath()); - if (lua_load(L, script_reader.reader_func, &script_reader, s->GetFilename().mb_str(wxConvUTF8))) { - lua_pushfstring(L, "Error loading Automation 3 include \"%s\":\n\n%s", fname.GetFullPath().mb_str(wxConvUTF8).data(), lua_tostring(L, -1)); - lua_error(L); return 0; } - int pretop = lua_gettop(L) - 1; // don't count the function value itself - lua_call(L, 0, LUA_MULTRET); - return lua_gettop(L) - pretop; + +#ifdef WIN32 + // Get number of widechars in filename string + size_t wfnlen = wcslen(fname.GetFullPath().wc_str()); + // Alloc memory to hold string + filename_t wfn = (filename_t)Auto3Malloc((wfnlen+1) * sizeof(wchar_t)); + // And copy string into memory + wcsncpy(wfn, fname.GetFullPath().wc_str(), wfnlen); + wfn[wfnlen] = 0; + return wfn; +#else + return Auto3Strdup(fname.GetFullPath().mb_str(wxConvFilename)); +#endif } - int Auto3Script::LuaColorstringToRGB(lua_State *L) + int Auto3Script::FrameFromMs(void *cbdata, int ms) { - if (lua_gettop(L) < 1) { - lua_pushstring(L, "colorstring_to_rgb called without arguments"); - lua_error(L); - } - if (!lua_isstring(L, 1)) { - lua_pushstring(L, "colorstring_to_rgb requires a string type argument"); - lua_error(L); - } - - wxString colorstring(lua_tostring(L, -1), wxConvUTF8); - lua_pop(L, 1); - AssColor rgb; - rgb.Parse(colorstring); - lua_pushnumber(L, rgb.r); - lua_pushnumber(L, rgb.g); - lua_pushnumber(L, rgb.b); - lua_pushnumber(L, rgb.a); - return 4; - } - - int Auto3Script::LuaFrameFromMs(lua_State *L) - { - int ms = (int)lua_tonumber(L, -1); - lua_pop(L, 1); if (VFR_Output.IsLoaded()) { - lua_pushnumber(L, VFR_Output.GetFrameAtTime(ms, true)); - return 1; + return VFR_Output.GetFrameAtTime(ms, true); } else { - lua_pushnil(L); - return 1; + return 0; } } - int Auto3Script::LuaMsFromFrame(lua_State *L) + int Auto3Script::MsFromFrame(void *cbdata, int frame) { - int frame = (int)lua_tonumber(L, -1); - lua_pop(L, 1); if (VFR_Output.IsLoaded()) { - lua_pushnumber(L, VFR_Output.GetTimeAtFrame(frame, true)); - return 1; + return VFR_Output.GetTimeAtFrame(frame, true); } else { - lua_pushnil(L); - return 1; + return 0; } } @@ -1320,123 +550,67 @@ continue_invalid_option: { Destroy(); -#if 1 - // HACK: Prevent auto3 scripts from ever loading - // This should be removed when things have been fixed - loaded = false; - name = GetFilename(); - description = _T("Automation 3 scripts do currently not work in this version of Aegisub. Please use version 1.10 if you need to run this script."); -#else + // Fill callbacks struct + Auto3Callbacks cb; + // Logging, implemented only during script execution + cb.logdata = 0; + cb.log_error = 0; + cb.log_message = 0; + cb.set_progress = 0; + cb.set_status = 0; + // Read/write, also only during execution + cb.rwdata = 0; + cb.get_meta_info = 0; + cb.get_next_style = 0; + cb.get_next_sub = 0; + cb.reset_style_pointer = 0; + cb.reset_subs_pointer = 0; + cb.start_subs_write = 0; + cb.write_sub = 0; + // Misc, implemented all the time + cb.rundata = this; + cb.resolve_include = ResolveInclude; + cb.text_extents = TextExtents; + cb.frame_from_ms = FrameFromMs; + cb.ms_from_frame = MsFromFrame; - loaded = true; + char *errormsg = 0; + script = CreateAuto3Script((const filename_t)GetFilename().fn_str(), GetPrettyFilename().mb_str(wxConvUTF8), &cb, &errormsg); - try { - L = lua_open(); + if (script) { + assert(errormsg == 0); + loaded = true; - // register standard libs - lua_pushcfunction(L, luaopen_base); lua_call(L, 0, 0); - lua_pushcfunction(L, luaopen_package); lua_call(L, 0, 0); - lua_pushcfunction(L, luaopen_string); lua_call(L, 0, 0); - lua_pushcfunction(L, luaopen_table); lua_call(L, 0, 0); - lua_pushcfunction(L, luaopen_math); lua_call(L, 0, 0); - // dofile and loadfile are replaced with include - lua_pushnil(L); - lua_setglobal(L, "dofile"); - lua_pushnil(L); - lua_setglobal(L, "loadfile"); - lua_pushcfunction(L, LuaInclude); - lua_setglobal(L, "include"); - - // reference to the script object - lua_pushlightuserdata(L, this); - lua_setfield(L, LUA_REGISTRYINDEX, "aegisub"); - - // make "aegisub" table - lua_newtable(L); - // put helper functions in it - lua_pushcfunction(L, LuaColorstringToRGB); - lua_setfield(L, -2, "colorstring_to_rgb"); - lua_pushcfunction(L, LuaTextExtents); - lua_setfield(L, -2, "text_extents"); - lua_pushcfunction(L, LuaFrameFromMs); - lua_setfield(L, -2, "frame_from_ms"); - lua_pushcfunction(L, LuaMsFromFrame); - lua_setfield(L, -2, "ms_from_frame"); - lua_pushinteger(L, 3); - lua_setfield(L, -2, "lua_automation_version"); - // store table - lua_setfield(L, LUA_GLOBALSINDEX, "aegisub"); - - // load user script - LuaScriptReader script_reader(GetFilename()); - if (lua_load(L, script_reader.reader_func, &script_reader, GetFilename().mb_str(wxConvUTF8))) { - wxString *err = new wxString(lua_tostring(L, -1), wxConvUTF8); - err->Prepend(_T("Error loading Automation 3 script \"") + GetFilename() + _T("\":\n\n")); - throw err->c_str(); - } - // and run it - { - int err = lua_pcall(L, 0, 0, 0); - if (err) { - // error occurred, assumed to be on top of Lua stack - wxString *errs = new wxString(lua_tostring(L, -1), wxConvUTF8); - errs->Prepend(_T("Error initialising Automation 3 script \"") + GetFilename() + _T("\":\n\n")); - throw errs->c_str(); - } - } - - // so, the script should be loaded - // now try to get the script data! - // first the version - lua_getglobal(L, "version"); - if (!lua_isnumber(L, -1)) { - throw _T("Script error: 'version' value not found or not a number"); - } - double engineversion = lua_tonumber(L, -1); - if (engineversion < 3 || engineversion > 4) { - // invalid version - throw _T("Script error: 'version' must be 3 for Automation 3 scripts"); - } + name = wxString(script->name, wxConvUTF8); + description = wxString(script->description, wxConvUTF8); + author = _T(""); version = _T(""); - // skip 'kind', it's useless - // name - lua_getglobal(L, "name"); - if (!lua_isstring(L, -1)) { - name = GetFilename(); - } else { - name = wxString(lua_tostring(L, -1), wxConvUTF8); - } - // description (optional) - lua_getglobal(L, "description"); - if (lua_isstring(L, -1)) { - description = wxString(lua_tostring(L, -1), wxConvUTF8); - } else { - description = _T(""); - } - lua_pop(L, 4); - // create filter feature object, that will check for process_lines function and configuration - filter = new Auto3Filter(name, description, L); - } - catch (...) { - Destroy(); + filter = new Auto3Filter(name, description, script); + + } else { loaded = false; - throw; + name = GetPrettyFilename(); + if (errormsg) { + description = wxString(errormsg, wxConvUTF8); + Auto3Free(errormsg); + } else { + description = _T("Unknown error (auto3 library returned NULL error message)"); + } } -#endif } void Auto3Script::Destroy() { - if (!L) return; + if (!script) return; if (filter) { delete filter; filter = 0; } - lua_close(L); - L = 0; + DestroyAuto3Script(script); + script = 0; loaded = false; } diff --git a/aegisub/auto4_auto3.h b/aegisub/auto4_auto3.h index 0b20ea118..7caa1ea80 100644 --- a/aegisub/auto4_auto3.h +++ b/aegisub/auto4_auto3.h @@ -40,11 +40,10 @@ #ifndef _AUTO4_AUTO3_H #define _AUTO4_AUTO3_H -#include "auto4_base.h" #include #include -#include "../lua51/src/lua.h" -#include "../lua51/src/lauxlib.h" +#include "auto4_base.h" +#include "../auto3/auto3.h" #include "ass_file.h" #include "ass_entry.h" #include "ass_dialogue.h" @@ -54,61 +53,26 @@ namespace Automation4 { class Auto3ProgressSink : public ProgressSink { private: - lua_State *L; + Auto3Interpreter *script; - static int LuaSetStatus(lua_State *L); - static int LuaOutputDebug(lua_State *L); - static int LuaReportProgress(lua_State *L); + static void SetStatus(void *cbdata, const char *msg); + static void OutputDebug(void *cbdata, const char *msg); + static void ReportProgress(void *cbdata, float progress); public: - Auto3ProgressSink(lua_State *_L, wxWindow *parent); + Auto3ProgressSink(Auto3Interpreter *_script, wxWindow *parent); virtual ~Auto3ProgressSink(); - - static Auto3ProgressSink* GetObjPointer(lua_State *L, int idx); }; - enum Auto3ScriptConfigurationOptionKind { - COK_INVALID = 0, - COK_LABEL, - COK_TEXT, - COK_INT, - COK_FLOAT, - COK_BOOL, - COK_COLOUR, - COK_STYLE - }; - - struct Auto3ScriptConfigurationOption { - wxString name; - Auto3ScriptConfigurationOptionKind kind; - wxString label; - wxString hint; - union { - bool isset; - int intval; - double floatval; - } min, max; - struct { - wxString stringval; - int intval; - double floatval; - bool boolval; - AssColor colourval; - } default_val, value; - }; - class Auto3ConfigDialog : public ScriptConfigDialog { - // copypasta private: - bool present; // is there any configuration option set at all? - - std::vector options; + Auto3ConfigOption *options; struct Control { wxStaticText *label; wxControl *control; - Auto3ScriptConfigurationOption *option; + Auto3ConfigOption *option; Control() : label(0), control(0), option(0) {} }; std::vector controls; @@ -117,9 +81,8 @@ namespace Automation4 { wxWindow* CreateWindow(wxWindow *parent); public: - Auto3ConfigDialog(lua_State *L); + Auto3ConfigDialog(Auto3Interpreter *script); virtual ~Auto3ConfigDialog(); - int LuaReadBack(lua_State *L); // read back internal structure to lua structures void ReadBack(); // from auto4 base @@ -132,14 +95,14 @@ namespace Automation4 { private: Auto3ConfigDialog *config; AssFile *_file; - lua_State *L; + Auto3Interpreter *script; protected: ScriptConfigDialog* GenerateConfigDialog(wxWindow *parent); void Init(); public: - Auto3Filter(const wxString &_name, const wxString &_description, lua_State *_L); + Auto3Filter(const wxString &_name, const wxString &_description, Auto3Interpreter *_script); void ProcessSubs(AssFile *subs, wxWindow *export_dialog); }; @@ -147,13 +110,29 @@ namespace Automation4 { class Auto3ThreadedProcessor : public wxThread { private: - lua_State *L; + Auto3Interpreter *script; AssFile *file; Auto3ConfigDialog *config; Auto3ProgressSink *sink; + std::list::iterator style_pointer; + std::list::iterator subs_pointer; + + static void ResetStylePointer(void *cbdata); + static void ResetSubsPointer(void *cbdata); + static void GetMetaInfo(void *cbdata, int *res_x, int *res_y); + static int GetNextStyle( + void *cbdata, char **name, char **fontname, int *fontsize, char **color1, char **color2, char **color3, char **color4, + int *bold, int *italic, int *underline, int *strikeout, float *scale_x, float *scale_y, float *spacing, float *angle, + int *borderstyle, float *outline, float *shadow, int *align, int *margin_l, int *margin_r, int *margin_v, int *encoding); + static int GetNextSub(void *cbdata, int *layer, int *start_time, int *end_time, char **style, char **actor, + int *margin_l, int *margin_r, int *margin_v, char **effect, char **text, int *comment); + static void StartSubsWrite(void *cbdata); + static void WriteSub(void *cbdata, int layer, int start_time, int end_time, const char *style, const char *actor, + int margin_l, int margin_r, int margin_v, const char *effect, const char *text, int comment); + public: - Auto3ThreadedProcessor(lua_State *_L, AssFile *_file, Auto3ConfigDialog *_config, Auto3ProgressSink *_sink); + Auto3ThreadedProcessor(Auto3Interpreter *_script, AssFile *_file, Auto3ConfigDialog *_config, Auto3ProgressSink *_sink); virtual ExitCode Entry(); }; @@ -161,19 +140,18 @@ namespace Automation4 { class Auto3Script : public Script { private: Auto3Filter *filter; - lua_State *L; + Auto3Interpreter *script; - static int LuaTextExtents(lua_State *L); - static int LuaInclude(lua_State *L); - static int LuaColorstringToRGB(lua_State *L); - static int LuaFrameFromMs(lua_State *L); - static int LuaMsFromFrame(lua_State *L); + static filename_t ResolveInclude(void *cbdata, const char *incname); + static void TextExtents(void *cbdata, char *text, char *fontname, int fontsize, int bold, int italic, + int spacing, float scale_x, float scale_y, int encoding, + float *out_width, float *out_height, float *out_descent, float *out_extlead); + static int FrameFromMs(void *cbdata, int ms); + static int MsFromFrame(void *cbdata, int frame); void Create(); void Destroy(); - static Auto3Script* GetScriptObject(lua_State *L); - public: Auto3Script(const wxString &filename); virtual ~Auto3Script(); diff --git a/aegisub/auto4_lua.cpp b/aegisub/auto4_lua.cpp index fb9ac8bc1..27dae2411 100644 --- a/aegisub/auto4_lua.cpp +++ b/aegisub/auto4_lua.cpp @@ -35,7 +35,9 @@ #include "auto4_lua.h" #include "auto4_lua_scriptreader.h" +#if WITH_AUTO3 == 1 #include "auto4_auto3.h" +#endif #include "ass_dialogue.h" #include "ass_style.h" #include "ass_file.h" @@ -199,7 +201,11 @@ namespace Automation4 { lua_pop(L, 1); // just to avoid tripping the stackcheck in debug // So this is an auto3 script... // Throw it as an exception, the script factory manager will catch this and use the auto3 script instead of this script object +#if WITH_AUTO3 == 1 throw new Auto3Script(GetFilename()); +#else + throw _T("Attempted loading an Automation 3 script, but Automation 3 support is not available"); +#endif } } lua_getglobal(L, "script_name"); @@ -238,7 +244,8 @@ namespace Automation4 { description = e; } catch (Script *s) { - throw; + // Be sure to properly propagate any scripts throw + throw s; } catch (...) { Destroy(); @@ -427,7 +434,7 @@ namespace Automation4 { lua_pop(L, 1); lua_gc(L, LUA_GCCOLLECT, 0); - return (wxThread::ExitCode)result; + return (wxThread::ExitCode)result; // works no matter what MSVC says } @@ -479,7 +486,7 @@ namespace Automation4 { lua_newtable(L); for (size_t i = 0; i != ints.size(); ++i) { lua_pushinteger(L, ints[i]+1); - lua_rawseti(L, -2, i+1); + lua_rawseti(L, -2, (int)i+1); } } diff --git a/auto3/auto3.c b/auto3/auto3.c index 22d54c1cd..bf0f32dd2 100644 --- a/auto3/auto3.c +++ b/auto3/auto3.c @@ -344,7 +344,7 @@ static int Auto3ParseConfigData(lua_State *L, struct Auto3Interpreter *script, c // Create a new interpreter -AUTO3_API struct Auto3Interpreter *CreateAuto3Script(filename_t filename, char *prettyname, struct Auto3Callbacks *cb, char **error) +AUTO3_API struct Auto3Interpreter *CreateAuto3Script(const filename_t filename, const char *prettyname, struct Auto3Callbacks *cb, char **error) { struct Auto3Interpreter *script; lua_State *L; @@ -521,6 +521,12 @@ AUTO3_API void *Auto3Malloc(size_t amount) return malloc(amount); } +// Convenience function, use this for duplicating strings this lib should own +AUTO3_API char *Auto3Strdup(const char *str) +{ + return strdup(str); +} + // Our "free" function, free generated error messages with this AUTO3_API void Auto3Free(void *ptr) { diff --git a/auto3/auto3.h b/auto3/auto3.h index 35ffc917c..83e82acf8 100644 --- a/auto3/auto3.h +++ b/auto3/auto3.h @@ -185,13 +185,15 @@ struct Auto3Interpreter { // cb should point to an Auto3Callbacks struct filled in; a copy of this struct will be made // error will be filled with any error message on fail, the application is responsible for freeing this string (use Auto3Free) // Returns pointer to interpreter object if successful, otherwise NULL -AUTO3_API struct Auto3Interpreter *CreateAuto3Script(filename_t filename, char *prettyname, struct Auto3Callbacks *cb, char **error); +AUTO3_API struct Auto3Interpreter *CreateAuto3Script(const filename_t filename, const char *prettyname, struct Auto3Callbacks *cb, char **error); // Release an interpreter AUTO3_API void DestroyAuto3Script(struct Auto3Interpreter *script); // Our "malloc" function, allocate memory for strings with this AUTO3_API void *Auto3Malloc(size_t amount); -// Our "free" function, free generated error messages with this +// Convenience function, use this for duplicating strings this lib should own +AUTO3_API char *Auto3Strdup(const char *str); +// Our "free" function, free strings with this AUTO3_API void Auto3Free(void *ptr); // Start the script execution diff --git a/auto3/callables.c b/auto3/callables.c index 5f89e8eb1..9748d40de 100644 --- a/auto3/callables.c +++ b/auto3/callables.c @@ -62,11 +62,13 @@ static int LuaInclude(lua_State *L) if (filename) { // Load include if (Auto3LuaLoad(L, filename, incname, &error)) { + free(filename); lua_pushfstring(L, "Failed to include file '%s', error: %s", incname, error); lua_error(L); } // Run include (don't protect, we're already in a protected environment, we'd just propagate it anyway) lua_call(L, 0, 0); + free(filename); } else { lua_pushfstring(L, "Failed to resolve include file '%s'", incname); lua_error(L); diff --git a/configure.ac b/configure.ac index 067539996..edf3fe284 100644 --- a/configure.ac +++ b/configure.ac @@ -206,8 +206,14 @@ AM_CONDITIONAL([HAVE_ALSA], [test "$with_alsa" != "no"]) AM_CONDITIONAL([HAVE_PORTAUDIO], [test "$with_portaudio" != "no"]) AM_CONDITIONAL([HAVE_PULSEAUDIO], [test "$with_pulseaudio" != "no"]) AM_CONDITIONAL([HAVE_HUNSPELL], [test "$with_hunspell" != "no"]) -AM_CONDITIONAL([HAVE_RUBY], [test "$with_ruby" != "no"]) AM_CONDITIONAL([HAVE_FFMPEG], [test "$with_ffmpeg" != "no"]) +dnl FIXME: These three should probably get proper --with flags, +dnl and Ruby also keep its detection (warn/fail if you have --with-ruby +dnl but the lib isn't detected.) +AM_CONDITIONAL([WITH_AUTO4_RUBY], [test "$with_ruby" != "no"]) +AM_CONDITIONAL([WITH_AUTO4_LUA], [true]) +dnl TODO: Make the auto3 lib build on non-Win32 so this can be true +AM_CONDITIONAL([WITH_AUTO3], [false]) AC_OUTPUT([ Makefile