Add native support for MoonScript
This commit is contained in:
parent
99d74e18b3
commit
19854e207a
10 changed files with 5155 additions and 866 deletions
|
@ -1,31 +0,0 @@
|
||||||
local tr = aegisub.gettext
|
|
||||||
script_name = tr("Select overlaps")
|
|
||||||
script_description = tr("Select lines which begin while another non-comment line is active")
|
|
||||||
script_author = "Thomas Goyne"
|
|
||||||
script_version = "2"
|
|
||||||
local select_overlaps
|
|
||||||
select_overlaps = function(subs)
|
|
||||||
local dialogue = { }
|
|
||||||
for i, line in ipairs(subs) do
|
|
||||||
if line.class == "dialogue" then
|
|
||||||
line.i = i
|
|
||||||
table.insert(dialogue, line)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
table.sort(dialogue, function(a, b)
|
|
||||||
return a.start_time < b.start_time or (a.start_time == b.start_time and a.i < b.i)
|
|
||||||
end)
|
|
||||||
local end_time = 0
|
|
||||||
local overlaps = { }
|
|
||||||
local _list_0 = dialogue
|
|
||||||
for _index_0 = 1, #_list_0 do
|
|
||||||
local line = _list_0[_index_0]
|
|
||||||
if line.start_time >= end_time then
|
|
||||||
end_time = line.end_time
|
|
||||||
else
|
|
||||||
table.insert(overlaps, line.i)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return overlaps
|
|
||||||
end
|
|
||||||
return aegisub.register_macro(script_name, script_description, select_overlaps)
|
|
|
@ -1,314 +0,0 @@
|
||||||
local regex = aegisub.__init_regex()
|
|
||||||
local select_first
|
|
||||||
select_first = function(n, a, ...)
|
|
||||||
if n == 0 then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
return a, select_first(n - 1, ...)
|
|
||||||
end
|
|
||||||
local unpack_args
|
|
||||||
unpack_args = function(...)
|
|
||||||
local userdata_start = nil
|
|
||||||
for i = 1, select('#', ...) do
|
|
||||||
local v = select(i, ...)
|
|
||||||
if type(v) == 'userdata' then
|
|
||||||
userdata_start = i
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if not (userdata_start) then
|
|
||||||
return 0, ...
|
|
||||||
end
|
|
||||||
local flags = regex.process_flags(select(userdata_start, ...))
|
|
||||||
if type(flags) == 'string' then
|
|
||||||
error(flags, 3)
|
|
||||||
end
|
|
||||||
return flags, select_first(userdata_start - 1, ...)
|
|
||||||
end
|
|
||||||
local check_arg
|
|
||||||
check_arg = function(arg, expected_type, argn, func_name, level)
|
|
||||||
if type(arg) ~= expected_type then
|
|
||||||
return error("Argument " .. tostring(argn) .. " to " .. tostring(func_name) .. " should be a '" .. tostring(expected_type) .. "', is '" .. tostring(type(arg)) .. "' (" .. tostring(arg) .. ")", level + 1)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local replace_match
|
|
||||||
replace_match = function(match, func, str, last, acc)
|
|
||||||
if last < match.last then
|
|
||||||
acc[#acc + 1] = str:sub(last, match.first - 1)
|
|
||||||
end
|
|
||||||
local repl = func(match.str, match.first, match.last)
|
|
||||||
if type(repl) == 'string' then
|
|
||||||
acc[#acc + 1] = repl
|
|
||||||
else
|
|
||||||
acc[#acc + 1] = match.str
|
|
||||||
end
|
|
||||||
return match.first, match.last + 1
|
|
||||||
end
|
|
||||||
local do_single_replace_fun
|
|
||||||
do_single_replace_fun = function(re, func, str, acc, pos)
|
|
||||||
local matches = re:match(str, pos)
|
|
||||||
if not (matches) then
|
|
||||||
return pos
|
|
||||||
end
|
|
||||||
local start
|
|
||||||
if #matches == 1 then
|
|
||||||
start = 1
|
|
||||||
else
|
|
||||||
start = 2
|
|
||||||
end
|
|
||||||
local last = pos
|
|
||||||
local first
|
|
||||||
for i = start, #matches do
|
|
||||||
first, last = replace_match(matches[i], func, str, last, acc)
|
|
||||||
end
|
|
||||||
if first == last then
|
|
||||||
acc[#acc + 1] = str:sub(last, last)
|
|
||||||
last = last + 1
|
|
||||||
end
|
|
||||||
return last, matches[1].first <= str:len()
|
|
||||||
end
|
|
||||||
local do_replace_fun
|
|
||||||
do_replace_fun = function(re, func, str, max)
|
|
||||||
local acc = { }
|
|
||||||
local pos = 1
|
|
||||||
local i
|
|
||||||
for i = 1, max do
|
|
||||||
local more
|
|
||||||
pos, more = do_single_replace_fun(re, func, str, acc, pos)
|
|
||||||
if not (more) then
|
|
||||||
max = i
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return table.concat(acc, '') .. str:sub(pos)
|
|
||||||
end
|
|
||||||
local RegEx
|
|
||||||
do
|
|
||||||
local start
|
|
||||||
local _parent_0 = nil
|
|
||||||
local _base_0 = {
|
|
||||||
_check_self = function(self)
|
|
||||||
if not (self.__class == RegEx) then
|
|
||||||
return error('re method called with invalid self. You probably used . when : is needed.', 3)
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
gsplit = function(self, str, skip_empty, max_split)
|
|
||||||
self:_check_self()
|
|
||||||
check_arg(str, 'string', 2, 'gsplit', self._level)
|
|
||||||
if not max_split or max_split <= 0 then
|
|
||||||
max_split = str:len()
|
|
||||||
end
|
|
||||||
start = 1
|
|
||||||
local prev = 1
|
|
||||||
local do_split
|
|
||||||
do_split = function()
|
|
||||||
if not str or str:len() == 0 then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local first, last
|
|
||||||
if max_split > 0 then
|
|
||||||
first, last = regex.search(self._regex, str, start)
|
|
||||||
end
|
|
||||||
if not first or first > str:len() then
|
|
||||||
local ret = str:sub(prev, str:len())
|
|
||||||
str = nil
|
|
||||||
return ret
|
|
||||||
end
|
|
||||||
local ret = str:sub(prev, first - 1)
|
|
||||||
prev = last + 1
|
|
||||||
start = 1 + (function()
|
|
||||||
if start >= last then
|
|
||||||
return start
|
|
||||||
else
|
|
||||||
return last
|
|
||||||
end
|
|
||||||
end)()
|
|
||||||
if skip_empty and ret:len() == 0 then
|
|
||||||
return do_split()
|
|
||||||
else
|
|
||||||
max_split = max_split - 1
|
|
||||||
return ret
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return do_split
|
|
||||||
end,
|
|
||||||
split = function(self, str, skip_empty, max_split)
|
|
||||||
self:_check_self()
|
|
||||||
check_arg(str, 'string', 2, 'split', self._level)
|
|
||||||
return (function()
|
|
||||||
local _accum_0 = { }
|
|
||||||
local _len_0 = 1
|
|
||||||
for v in self:gsplit(str, skip_empty, max_split) do
|
|
||||||
_accum_0[_len_0] = v
|
|
||||||
_len_0 = _len_0 + 1
|
|
||||||
end
|
|
||||||
return _accum_0
|
|
||||||
end)()
|
|
||||||
end,
|
|
||||||
gfind = function(self, str)
|
|
||||||
self:_check_self()
|
|
||||||
check_arg(str, 'string', 2, 'gfind', self._level)
|
|
||||||
start = 1
|
|
||||||
return function()
|
|
||||||
local first, last = regex.search(self._regex, str, start)
|
|
||||||
if not (first) then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
if last >= start then
|
|
||||||
start = last + 1
|
|
||||||
else
|
|
||||||
start = start + 1
|
|
||||||
end
|
|
||||||
return str:sub(first, last), first, last
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
find = function(self, str)
|
|
||||||
self:_check_self()
|
|
||||||
check_arg(str, 'string', 2, 'find', self._level)
|
|
||||||
local ret = (function()
|
|
||||||
local _accum_0 = { }
|
|
||||||
local _len_0 = 1
|
|
||||||
for s, f, l in self:gfind(str) do
|
|
||||||
_accum_0[_len_0] = {
|
|
||||||
str = s,
|
|
||||||
first = f,
|
|
||||||
last = l
|
|
||||||
}
|
|
||||||
_len_0 = _len_0 + 1
|
|
||||||
end
|
|
||||||
return _accum_0
|
|
||||||
end)()
|
|
||||||
return next(ret) and ret
|
|
||||||
end,
|
|
||||||
sub = function(self, str, repl, max_count)
|
|
||||||
self:_check_self()
|
|
||||||
check_arg(str, 'string', 2, 'sub', self._level)
|
|
||||||
if max_count ~= nil then
|
|
||||||
check_arg(max_count, 'number', 4, 'sub', self._level)
|
|
||||||
end
|
|
||||||
if not max_count or max_count == 0 then
|
|
||||||
max_count = str:len() + 1
|
|
||||||
end
|
|
||||||
if type(repl) == 'function' then
|
|
||||||
return do_replace_fun(self, repl, str, max_count)
|
|
||||||
elseif type(repl) == 'string' then
|
|
||||||
return regex.replace(self._regex, repl, str, max_count)
|
|
||||||
else
|
|
||||||
return error("Argument 2 to sub should be a string or function, is '" .. tostring(type(repl)) .. "' (" .. tostring(repl) .. ")", self._level)
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
gmatch = function(self, str, start)
|
|
||||||
self:_check_self()
|
|
||||||
check_arg(str, 'string', 2, 'gmatch', self._level)
|
|
||||||
if start then
|
|
||||||
start = start - 1
|
|
||||||
else
|
|
||||||
start = 0
|
|
||||||
end
|
|
||||||
local match = regex.match(self._regex, str, start)
|
|
||||||
local i = 1
|
|
||||||
return function()
|
|
||||||
if not (match) then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local first, last = regex.get_match(match, i)
|
|
||||||
if not (first) then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
i = i + 1
|
|
||||||
return {
|
|
||||||
str = str:sub(first + start, last + start),
|
|
||||||
first = first + start,
|
|
||||||
last = last + start
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
match = function(self, str, start)
|
|
||||||
self:_check_self()
|
|
||||||
check_arg(str, 'string', 2, 'match', self._level)
|
|
||||||
local ret = (function()
|
|
||||||
local _accum_0 = { }
|
|
||||||
local _len_0 = 1
|
|
||||||
for v in self:gmatch(str, start) do
|
|
||||||
_accum_0[_len_0] = v
|
|
||||||
_len_0 = _len_0 + 1
|
|
||||||
end
|
|
||||||
return _accum_0
|
|
||||||
end)()
|
|
||||||
if next(ret) == nil then
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
return ret
|
|
||||||
end
|
|
||||||
}
|
|
||||||
_base_0.__index = _base_0
|
|
||||||
if _parent_0 then
|
|
||||||
setmetatable(_base_0, _parent_0.__base)
|
|
||||||
end
|
|
||||||
local _class_0 = setmetatable({
|
|
||||||
__init = function(self, _regex, _level)
|
|
||||||
self._regex, self._level = _regex, _level
|
|
||||||
end,
|
|
||||||
__base = _base_0,
|
|
||||||
__name = "RegEx",
|
|
||||||
__parent = _parent_0
|
|
||||||
}, {
|
|
||||||
__index = function(cls, name)
|
|
||||||
local val = rawget(_base_0, name)
|
|
||||||
if val == nil and _parent_0 then
|
|
||||||
return _parent_0[name]
|
|
||||||
else
|
|
||||||
return val
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
__call = function(cls, ...)
|
|
||||||
local _self_0 = setmetatable({}, _base_0)
|
|
||||||
cls.__init(_self_0, ...)
|
|
||||||
return _self_0
|
|
||||||
end
|
|
||||||
})
|
|
||||||
_base_0.__class = _class_0
|
|
||||||
local self = _class_0
|
|
||||||
start = 1
|
|
||||||
if _parent_0 and _parent_0.__inherited then
|
|
||||||
_parent_0.__inherited(_parent_0, _class_0)
|
|
||||||
end
|
|
||||||
RegEx = _class_0
|
|
||||||
end
|
|
||||||
local real_compile
|
|
||||||
real_compile = function(pattern, level, flags, stored_level)
|
|
||||||
if pattern == '' then
|
|
||||||
error('Regular expression must not be empty', level + 1)
|
|
||||||
end
|
|
||||||
local re = regex.compile(pattern, flags)
|
|
||||||
if type(re) == 'string' then
|
|
||||||
error(regex, level + 1)
|
|
||||||
end
|
|
||||||
return RegEx(re, stored_level or level + 1)
|
|
||||||
end
|
|
||||||
local invoke
|
|
||||||
invoke = function(str, pattern, fn, flags, ...)
|
|
||||||
local compiled_regex = real_compile(pattern, 3, flags)
|
|
||||||
return compiled_regex[fn](compiled_regex, str, ...)
|
|
||||||
end
|
|
||||||
local gen_wrapper
|
|
||||||
gen_wrapper = function(impl_name)
|
|
||||||
return function(str, pattern, ...)
|
|
||||||
check_arg(str, 'string', 1, impl_name, 2)
|
|
||||||
check_arg(pattern, 'string', 2, impl_name, 2)
|
|
||||||
return invoke(str, pattern, impl_name, unpack_args(...))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local re = regex.init_flags(re)
|
|
||||||
re.compile = function(pattern, ...)
|
|
||||||
check_arg(pattern, 'string', 1, 'compile', 2)
|
|
||||||
return real_compile(pattern, 2, regex.process_flags(...), 2)
|
|
||||||
end
|
|
||||||
re.split = gen_wrapper('split')
|
|
||||||
re.gsplit = gen_wrapper('gsplit')
|
|
||||||
re.find = gen_wrapper('find')
|
|
||||||
re.gfind = gen_wrapper('gfind')
|
|
||||||
re.match = gen_wrapper('match')
|
|
||||||
re.gmatch = gen_wrapper('gmatch')
|
|
||||||
re.sub = gen_wrapper('sub')
|
|
||||||
return re
|
|
|
@ -1,58 +0,0 @@
|
||||||
local unicode
|
|
||||||
unicode = {
|
|
||||||
charwidth = function(s, i)
|
|
||||||
local b = s:byte(i or 1)
|
|
||||||
if not b then
|
|
||||||
return 1
|
|
||||||
elseif b < 128 then
|
|
||||||
return 1
|
|
||||||
elseif b < 224 then
|
|
||||||
return 2
|
|
||||||
elseif b < 240 then
|
|
||||||
return 3
|
|
||||||
else
|
|
||||||
return 4
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
chars = function(s)
|
|
||||||
local curchar, i = 0, 1
|
|
||||||
return function()
|
|
||||||
if i > s:len() then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local j = i
|
|
||||||
curchar = curchar + 1
|
|
||||||
i = i + unicode.charwidth(s, i)
|
|
||||||
return s:sub(j, i - 1), curchar
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
len = function(s)
|
|
||||||
local n = 0
|
|
||||||
for c in unicode.chars(s) do
|
|
||||||
n = n + 1
|
|
||||||
end
|
|
||||||
return n
|
|
||||||
end,
|
|
||||||
codepoint = function(s)
|
|
||||||
local b = s:byte(1)
|
|
||||||
if b < 128 then
|
|
||||||
return b
|
|
||||||
end
|
|
||||||
local res, w
|
|
||||||
if b < 224 then
|
|
||||||
res = b - 192
|
|
||||||
w = 2
|
|
||||||
elseif b < 240 then
|
|
||||||
res = b - 224
|
|
||||||
w = 3
|
|
||||||
else
|
|
||||||
res = b - 240
|
|
||||||
w = 4
|
|
||||||
end
|
|
||||||
for i = 2, w do
|
|
||||||
res = res * 64 + s:byte(i) - 128
|
|
||||||
end
|
|
||||||
return res
|
|
||||||
end
|
|
||||||
}
|
|
||||||
return unicode
|
|
|
@ -1,242 +0,0 @@
|
||||||
local sformat = string.format
|
|
||||||
local copy
|
|
||||||
copy = function(tbl)
|
|
||||||
return (function()
|
|
||||||
local _tbl_0 = { }
|
|
||||||
for k, v in pairs(tbl) do
|
|
||||||
_tbl_0[k] = v
|
|
||||||
end
|
|
||||||
return _tbl_0
|
|
||||||
end)()
|
|
||||||
end
|
|
||||||
local deep_copy
|
|
||||||
deep_copy = function(tbl)
|
|
||||||
local seen = { }
|
|
||||||
copy = function(val)
|
|
||||||
if type(tbl) ~= 'table' then
|
|
||||||
return val
|
|
||||||
end
|
|
||||||
if seen[tbl] then
|
|
||||||
return seen[val]
|
|
||||||
end
|
|
||||||
seen[val] = tbl
|
|
||||||
return (function()
|
|
||||||
local _tbl_0 = { }
|
|
||||||
for k, v in pairs(val) do
|
|
||||||
_tbl_0[k] = copy(v)
|
|
||||||
end
|
|
||||||
return _tbl_0
|
|
||||||
end)()
|
|
||||||
end
|
|
||||||
return copy(tbl)
|
|
||||||
end
|
|
||||||
local ass_color
|
|
||||||
ass_color = function(r, g, b)
|
|
||||||
return sformat("&H%02X%02X%02X&", b, g, r)
|
|
||||||
end
|
|
||||||
local ass_alpha
|
|
||||||
ass_alpha = function(a)
|
|
||||||
return sformat("&H%02X&", a)
|
|
||||||
end
|
|
||||||
local ass_style_color
|
|
||||||
ass_style_color = function(r, g, b, a)
|
|
||||||
return sformat("&H%02X%02X%02X%02X", a, b, g, r)
|
|
||||||
end
|
|
||||||
local extract_color
|
|
||||||
extract_color = function(s)
|
|
||||||
local a, b, g, r
|
|
||||||
a, b, g, r = s:match('&H(%x%x)(%x%x)(%x%x)(%x%x)')
|
|
||||||
if a then
|
|
||||||
return tonumber(r, 16), tonumber(g, 16), tonumber(b, 16), tonumber(a, 16)
|
|
||||||
end
|
|
||||||
b, g, r = s:match('&H(%x%x)(%x%x)(%x%x)&')
|
|
||||||
if b then
|
|
||||||
return tonumber(r, 16), tonumber(g, 16), tonumber(b, 16), 0
|
|
||||||
end
|
|
||||||
a = s:match('&H(%x%x)&')
|
|
||||||
if a then
|
|
||||||
return 0, 0, 0, tonumber(a, 16)
|
|
||||||
end
|
|
||||||
r, g, b, a = s:match('#(%x%x)(%x?%x?)(%x?%x?)(%x?%x?)')
|
|
||||||
if r then
|
|
||||||
return tonumber(r, 16), tonumber(g, 16) or 0, tonumber(b, 16) or 0, tonumber(a, 16) or 0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local alpha_from_style
|
|
||||||
alpha_from_style = function(scolor)
|
|
||||||
return ass_alpha(select(4, extract_color(scolor)))
|
|
||||||
end
|
|
||||||
local color_from_style
|
|
||||||
color_from_style = function(scolor)
|
|
||||||
local r, g, b = extract_color(scolor)
|
|
||||||
return ass_color(r or 0, g or 0, b or 0)
|
|
||||||
end
|
|
||||||
local HSV_to_RGB
|
|
||||||
HSV_to_RGB = function(H, S, V)
|
|
||||||
local r, g, b = 0, 0, 0
|
|
||||||
if S == 0 then
|
|
||||||
r = self:clamp(V * 255, 0, 255)
|
|
||||||
g = r
|
|
||||||
b = r
|
|
||||||
else
|
|
||||||
H = math.abs(H) % 360
|
|
||||||
local Hi = math.floor(H / 60)
|
|
||||||
local f = H / 60.0 - Hi
|
|
||||||
local p = V * (1 - S)
|
|
||||||
local q = V * (1 - f * S)
|
|
||||||
local t = V * (1 - (1 - f) * S)
|
|
||||||
if Hi == 0 then
|
|
||||||
r = V * 255.0
|
|
||||||
g = t * 255.0
|
|
||||||
b = p * 255.0
|
|
||||||
elseif Hi == 1 then
|
|
||||||
r = q * 255.0
|
|
||||||
g = V * 255.0
|
|
||||||
b = p * 255.0
|
|
||||||
elseif Hi == 2 then
|
|
||||||
r = p * 255.0
|
|
||||||
g = V * 255.0
|
|
||||||
b = t * 255.0
|
|
||||||
elseif Hi == 3 then
|
|
||||||
r = p * 255.0
|
|
||||||
g = q * 255.0
|
|
||||||
b = V * 255.0
|
|
||||||
elseif Hi == 4 then
|
|
||||||
r = t * 255.0
|
|
||||||
g = p * 255.0
|
|
||||||
b = V * 255.0
|
|
||||||
elseif Hi == 5 then
|
|
||||||
r = V * 255.0
|
|
||||||
g = p * 255.0
|
|
||||||
b = q * 255.0
|
|
||||||
else
|
|
||||||
error("math.floor(H % 360 / 60) should be [0, 6), is " .. tostring(Hi) .. "?")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return r, g, b
|
|
||||||
end
|
|
||||||
local HSL_to_RGB
|
|
||||||
HSL_to_RGB = function(H, S, L)
|
|
||||||
local r, g, b
|
|
||||||
H = math.abs(H) % 360
|
|
||||||
S = clamp(S, 0, 1)
|
|
||||||
L = clamp(L, 0, 1)
|
|
||||||
if S == 0 then
|
|
||||||
r = L
|
|
||||||
g = L
|
|
||||||
b = L
|
|
||||||
else
|
|
||||||
local Q
|
|
||||||
if L < 0.5 then
|
|
||||||
Q = L * (1.0 + S)
|
|
||||||
else
|
|
||||||
Q = L + S - (L * S)
|
|
||||||
end
|
|
||||||
local P = 2.0 * L - Q
|
|
||||||
local Hk = H / 360
|
|
||||||
local Tr, Tg, Tb
|
|
||||||
if Hk < 1 / 3 then
|
|
||||||
Tr = Hk + 1 / 3
|
|
||||||
Tg = Hk
|
|
||||||
Tb = Hk + 2 / 3
|
|
||||||
elseif Hk > 2 / 3 then
|
|
||||||
Tr = Hk - 2 / 3
|
|
||||||
Tg = Hk
|
|
||||||
Tb = Hk - 1 / 3
|
|
||||||
else
|
|
||||||
Tr = Hk + 1 / 3
|
|
||||||
Tg = Hk
|
|
||||||
Tb = Hk - 1 / 3
|
|
||||||
end
|
|
||||||
local get_component
|
|
||||||
get_component = function(T)
|
|
||||||
if T < 1 / 6 then
|
|
||||||
return P + ((Q - P) * 6.0 * T)
|
|
||||||
elseif 1 / 6 <= T and T < 1 / 2 then
|
|
||||||
return Q
|
|
||||||
elseif 1 / 2 <= T and T < 2 / 3 then
|
|
||||||
return P + ((Q - P) * (2 / 3 - T) * 6.0)
|
|
||||||
else
|
|
||||||
return P
|
|
||||||
end
|
|
||||||
end
|
|
||||||
r = get_component(Tr)
|
|
||||||
g = get_component(Tg)
|
|
||||||
b = get_component(Tb)
|
|
||||||
end
|
|
||||||
return math.floor(r * 255 + 0.5), math.floor(g * 255 + 0.5), math.floor(b * 255 + 0.5)
|
|
||||||
end
|
|
||||||
local trim
|
|
||||||
trim = function(s)
|
|
||||||
return s:gsub('^%s*(.-)%s*$', '%1')
|
|
||||||
end
|
|
||||||
local headtail
|
|
||||||
headtail = function(s)
|
|
||||||
local a, b, head, tail = s:find('(.-)%s+(.*)')
|
|
||||||
if a then
|
|
||||||
return head, tail
|
|
||||||
else
|
|
||||||
return s, ''
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local words
|
|
||||||
words = function(s)
|
|
||||||
return function()
|
|
||||||
if s == '' then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local head, tail = headtail(s)
|
|
||||||
s = tail
|
|
||||||
return head
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local clamp
|
|
||||||
clamp = function(val, min, max)
|
|
||||||
if val < min then
|
|
||||||
return min
|
|
||||||
elseif val > max then
|
|
||||||
return max
|
|
||||||
else
|
|
||||||
return val
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local interpolate
|
|
||||||
interpolate = function(pct, min, max)
|
|
||||||
if pct <= 0 then
|
|
||||||
return min
|
|
||||||
elseif pct >= 1 then
|
|
||||||
return max
|
|
||||||
else
|
|
||||||
return pct * (max - min) + min
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local interpolate_color
|
|
||||||
interpolate_color = function(pct, first, last)
|
|
||||||
local r1, g1, b1 = extract_color(first)
|
|
||||||
local r2, g2, b2 = extract_color(last)
|
|
||||||
local r, g, b = interpolate(pct, r1, r2), interpolate(pct, g1, g2), interpolate(pct, b1, b2)
|
|
||||||
return ass_color(r, g, b)
|
|
||||||
end
|
|
||||||
local interpolate_alpha
|
|
||||||
interpolate_alpha = function(pct, first, last)
|
|
||||||
return ass_alpha(interpolate(pct, select(4, extract_color(first)), select(4, extract_color(last))))
|
|
||||||
end
|
|
||||||
return {
|
|
||||||
copy = copy,
|
|
||||||
deep_copy = deep_copy,
|
|
||||||
ass_color = ass_color,
|
|
||||||
ass_alpha = ass_alpha,
|
|
||||||
ass_style_color = ass_style_color,
|
|
||||||
extract_color = extract_color,
|
|
||||||
alpha_from_style = alpha_from_style,
|
|
||||||
color_from_style = color_from_style,
|
|
||||||
HSV_to_RGB = HSV_to_RGB,
|
|
||||||
HSL_to_RGB = HSL_to_RGB,
|
|
||||||
trim = trim,
|
|
||||||
headtail = headtail,
|
|
||||||
words = words,
|
|
||||||
clamp = clamp,
|
|
||||||
interpolate = interpolate,
|
|
||||||
interpolate_color = interpolate_color,
|
|
||||||
interpolate_alpha = interpolate_alpha
|
|
||||||
}
|
|
4952
aegisub/automation/include/moonscript.lua
Normal file
4952
aegisub/automation/include/moonscript.lua
Normal file
File diff suppressed because it is too large
Load diff
|
@ -51,6 +51,7 @@
|
||||||
#include <libaegisub/fs.h>
|
#include <libaegisub/fs.h>
|
||||||
#include <libaegisub/path.h>
|
#include <libaegisub/path.h>
|
||||||
|
|
||||||
|
#include <boost/algorithm/string/replace.hpp>
|
||||||
#include <boost/algorithm/string/trim.hpp>
|
#include <boost/algorithm/string/trim.hpp>
|
||||||
#include <boost/format.hpp>
|
#include <boost/format.hpp>
|
||||||
#include <boost/tokenizer.hpp>
|
#include <boost/tokenizer.hpp>
|
||||||
|
@ -517,8 +518,10 @@ namespace Automation4 {
|
||||||
if (fact->GetEngineName().empty() || fact->GetFilenamePattern().empty())
|
if (fact->GetEngineName().empty() || fact->GetFilenamePattern().empty())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
fnfilter += str(boost::format("%s scripts (%s)|%s|") % fact->GetEngineName() % fact->GetFilenamePattern() % fact->GetFilenamePattern());
|
std::string filter(fact->GetFilenamePattern());
|
||||||
catchall += fact->GetFilenamePattern() + ";";
|
boost::replace_all(filter, ",", ";");
|
||||||
|
fnfilter += str(boost::format("%s scripts (%s)|%s|") % fact->GetEngineName() % fact->GetFilenamePattern() % filter);
|
||||||
|
catchall += filter + ";";
|
||||||
}
|
}
|
||||||
fnfilter += from_wx(_("All Files")) + " (*.*)|*.*";
|
fnfilter += from_wx(_("All Files")) + " (*.*)|*.*";
|
||||||
|
|
||||||
|
|
|
@ -191,6 +191,103 @@ namespace {
|
||||||
TableIter::init(L);
|
TableIter::init(L);
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int frame_from_ms(lua_State *L)
|
||||||
|
{
|
||||||
|
const agi::Context *c = get_context(L);
|
||||||
|
int ms = lua_tointeger(L, -1);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
if (c && c->videoController->TimecodesLoaded())
|
||||||
|
push_value(L, c->videoController->FrameAtTime(ms, agi::vfr::START));
|
||||||
|
else
|
||||||
|
lua_pushnil(L);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ms_from_frame(lua_State *L)
|
||||||
|
{
|
||||||
|
const agi::Context *c = get_context(L);
|
||||||
|
int frame = lua_tointeger(L, -1);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
if (c && c->videoController->TimecodesLoaded())
|
||||||
|
push_value(L, c->videoController->TimeAtFrame(frame, agi::vfr::START));
|
||||||
|
else
|
||||||
|
lua_pushnil(L);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int video_size(lua_State *L)
|
||||||
|
{
|
||||||
|
const agi::Context *c = get_context(L);
|
||||||
|
if (c && c->videoController->IsLoaded()) {
|
||||||
|
push_value(L, c->videoController->GetWidth());
|
||||||
|
push_value(L, c->videoController->GetHeight());
|
||||||
|
push_value(L, c->videoController->GetAspectRatioValue());
|
||||||
|
push_value(L, (int)c->videoController->GetAspectRatioType());
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
lua_pushnil(L);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_keyframes(lua_State *L)
|
||||||
|
{
|
||||||
|
const agi::Context *c = get_context(L);
|
||||||
|
if (!c) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<int> const& kf = c->videoController->GetKeyFrames();
|
||||||
|
|
||||||
|
lua_newtable(L);
|
||||||
|
for (size_t i = 0; i < kf.size(); ++i) {
|
||||||
|
push_value(L, kf[i]);
|
||||||
|
lua_rawseti(L, -2, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int decode_path(lua_State *L)
|
||||||
|
{
|
||||||
|
std::string path = luaL_checkstring(L, 1);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
push_value(L, config::path->Decode(path));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cancel_script(lua_State *L)
|
||||||
|
{
|
||||||
|
lua_pushnil(L);
|
||||||
|
return lua_error(L);
|
||||||
|
}
|
||||||
|
|
||||||
|
int lua_text_textents(lua_State *L)
|
||||||
|
{
|
||||||
|
luaL_argcheck(L, lua_istable(L, 1), 1, "");
|
||||||
|
luaL_argcheck(L, lua_isstring(L, 2), 2, "");
|
||||||
|
|
||||||
|
lua_pushvalue(L, 1);
|
||||||
|
agi::scoped_ptr<AssEntry> et(Automation4::LuaAssFile::LuaToAssEntry(L));
|
||||||
|
AssStyle *st = dynamic_cast<AssStyle*>(et.get());
|
||||||
|
lua_pop(L, 1);
|
||||||
|
if (!st)
|
||||||
|
return luaL_error(L, "Not a style entry");
|
||||||
|
|
||||||
|
double width, height, descent, extlead;
|
||||||
|
if (!Automation4::CalculateTextExtents(st, luaL_checkstring(L, 2), width, height, descent, extlead))
|
||||||
|
return luaL_error(L, "Some internal error occurred calculating text_extents");
|
||||||
|
|
||||||
|
push_value(L, width);
|
||||||
|
push_value(L, height);
|
||||||
|
push_value(L, descent);
|
||||||
|
push_value(L, extlead);
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" int luaopen_lpeg (lua_State *L);
|
extern "C" int luaopen_lpeg (lua_State *L);
|
||||||
|
@ -198,19 +295,56 @@ extern "C" int luaopen_lpeg (lua_State *L);
|
||||||
namespace Automation4 {
|
namespace Automation4 {
|
||||||
int regex_init(lua_State *L);
|
int regex_init(lua_State *L);
|
||||||
|
|
||||||
// LuaScript
|
class LuaScript : public Script {
|
||||||
|
lua_State *L;
|
||||||
|
|
||||||
|
std::string name;
|
||||||
|
std::string description;
|
||||||
|
std::string author;
|
||||||
|
std::string version;
|
||||||
|
|
||||||
|
std::vector<cmd::Command*> macros;
|
||||||
|
std::vector<ExportFilter*> filters;
|
||||||
|
|
||||||
|
/// load script and create internal structures etc.
|
||||||
|
void Create();
|
||||||
|
/// destroy internal structures, unreg features and delete environment
|
||||||
|
void Destroy();
|
||||||
|
|
||||||
|
static int LuaInclude(lua_State *L);
|
||||||
|
static int LuaModuleLoader(lua_State *L);
|
||||||
|
|
||||||
|
public:
|
||||||
|
LuaScript(agi::fs::path const& filename);
|
||||||
|
~LuaScript() { Destroy(); }
|
||||||
|
|
||||||
|
void RegisterCommand(LuaCommand *command);
|
||||||
|
void UnregisterCommand(LuaCommand *command);
|
||||||
|
void RegisterFilter(LuaExportFilter *filter);
|
||||||
|
|
||||||
|
static LuaScript* GetScriptObject(lua_State *L);
|
||||||
|
|
||||||
|
// Script implementation
|
||||||
|
void Reload() { Create(); }
|
||||||
|
|
||||||
|
std::string GetName() const { return name; }
|
||||||
|
std::string GetDescription() const { return description; }
|
||||||
|
std::string GetAuthor() const { return author; }
|
||||||
|
std::string GetVersion() const { return version; }
|
||||||
|
bool GetLoadedState() const { return L != 0; }
|
||||||
|
|
||||||
|
std::vector<cmd::Command*> GetMacros() const { return macros; }
|
||||||
|
std::vector<ExportFilter*> GetFilters() const { return filters; }
|
||||||
|
std::vector<SubtitleFormat*> GetFormats() const { return std::vector<SubtitleFormat*>(); }
|
||||||
|
};
|
||||||
|
|
||||||
LuaScript::LuaScript(agi::fs::path const& filename)
|
LuaScript::LuaScript(agi::fs::path const& filename)
|
||||||
: Script(filename)
|
: Script(filename)
|
||||||
, L(0)
|
, L(nullptr)
|
||||||
{
|
{
|
||||||
Create();
|
Create();
|
||||||
}
|
}
|
||||||
|
|
||||||
LuaScript::~LuaScript()
|
|
||||||
{
|
|
||||||
Destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
void LuaScript::Create()
|
void LuaScript::Create()
|
||||||
{
|
{
|
||||||
Destroy();
|
Destroy();
|
||||||
|
@ -283,13 +417,13 @@ namespace Automation4 {
|
||||||
|
|
||||||
set_field(L, "register_macro", LuaCommand::LuaRegister);
|
set_field(L, "register_macro", LuaCommand::LuaRegister);
|
||||||
set_field(L, "register_filter", LuaExportFilter::LuaRegister);
|
set_field(L, "register_filter", LuaExportFilter::LuaRegister);
|
||||||
set_field(L, "text_extents", LuaTextExtents);
|
set_field(L, "text_extents", lua_text_textents);
|
||||||
set_field(L, "frame_from_ms", LuaFrameFromMs);
|
set_field(L, "frame_from_ms", frame_from_ms);
|
||||||
set_field(L, "ms_from_frame", LuaMsFromFrame);
|
set_field(L, "ms_from_frame", ms_from_frame);
|
||||||
set_field(L, "video_size", LuaVideoSize);
|
set_field(L, "video_size", video_size);
|
||||||
set_field(L, "keyframes", LuaGetKeyframes);
|
set_field(L, "keyframes", get_keyframes);
|
||||||
set_field(L, "decode_path", LuaDecodePath);
|
set_field(L, "decode_path", decode_path);
|
||||||
set_field(L, "cancel", LuaCancel);
|
set_field(L, "cancel", cancel_script);
|
||||||
set_field(L, "lua_automation_version", 4);
|
set_field(L, "lua_automation_version", 4);
|
||||||
set_field(L, "__init_regex", regex_init);
|
set_field(L, "__init_regex", regex_init);
|
||||||
set_field(L, "__init_clipboard", clipboard_init);
|
set_field(L, "__init_clipboard", clipboard_init);
|
||||||
|
@ -301,8 +435,7 @@ namespace Automation4 {
|
||||||
_stackcheck.check_stack(0);
|
_stackcheck.check_stack(0);
|
||||||
|
|
||||||
// load user script
|
// load user script
|
||||||
LuaScriptReader script_reader(GetFilename());
|
if (!LoadFile(L, GetFilename())) {
|
||||||
if (lua_load(L, script_reader.reader_func, &script_reader, GetPrettyFilename().string().c_str())) {
|
|
||||||
std::string err = str(boost::format("Error loading Lua script \"%s\":\n\n%s") % GetPrettyFilename().string() % get_string_or_default(L, -1));
|
std::string err = str(boost::format("Error loading Lua script \"%s\":\n\n%s") % GetPrettyFilename().string() % get_string_or_default(L, -1));
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
throw ScriptLoadError(err);
|
throw ScriptLoadError(err);
|
||||||
|
@ -359,12 +492,7 @@ namespace Automation4 {
|
||||||
delete_clear(filters);
|
delete_clear(filters);
|
||||||
|
|
||||||
lua_close(L);
|
lua_close(L);
|
||||||
L = 0;
|
L = nullptr;
|
||||||
}
|
|
||||||
|
|
||||||
void LuaScript::Reload()
|
|
||||||
{
|
|
||||||
Create();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LuaScript::RegisterCommand(LuaCommand *command)
|
void LuaScript::RegisterCommand(LuaCommand *command)
|
||||||
|
@ -397,29 +525,6 @@ namespace Automation4 {
|
||||||
return (LuaScript*)ptr;
|
return (LuaScript*)ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
int LuaScript::LuaTextExtents(lua_State *L)
|
|
||||||
{
|
|
||||||
luaL_argcheck(L, lua_istable(L, 1), 1, "");
|
|
||||||
luaL_argcheck(L, lua_isstring(L, 2), 2, "");
|
|
||||||
|
|
||||||
lua_pushvalue(L, 1);
|
|
||||||
agi::scoped_ptr<AssEntry> et(LuaAssFile::LuaToAssEntry(L));
|
|
||||||
AssStyle *st = dynamic_cast<AssStyle*>(et.get());
|
|
||||||
lua_pop(L, 1);
|
|
||||||
if (!st)
|
|
||||||
return luaL_error(L, "Not a style entry");
|
|
||||||
|
|
||||||
double width, height, descent, extlead;
|
|
||||||
if (!CalculateTextExtents(st, luaL_checkstring(L, 2), width, height, descent, extlead))
|
|
||||||
return luaL_error(L, "Some internal error occurred calculating text_extents");
|
|
||||||
|
|
||||||
push_value(L, width);
|
|
||||||
push_value(L, height);
|
|
||||||
push_value(L, descent);
|
|
||||||
push_value(L, extlead);
|
|
||||||
return 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Module loader which uses our include rather than Lua's, for unicode file support
|
/// @brief Module loader which uses our include rather than Lua's, for unicode file support
|
||||||
/// @param L The Lua state
|
/// @param L The Lua state
|
||||||
/// @return Always 1 per loader_Lua?
|
/// @return Always 1 per loader_Lua?
|
||||||
|
@ -431,18 +536,27 @@ namespace Automation4 {
|
||||||
|
|
||||||
// Get the lua package include path (which the user may have modified)
|
// Get the lua package include path (which the user may have modified)
|
||||||
lua_getglobal(L, "package");
|
lua_getglobal(L, "package");
|
||||||
push_value(L, "path");
|
lua_getfield(L, -1, "path");
|
||||||
lua_gettable(L, -2);
|
|
||||||
std::string package_paths(luaL_checkstring(L, -1));
|
std::string package_paths(luaL_checkstring(L, -1));
|
||||||
lua_pop(L, 2);
|
lua_pop(L, 2);
|
||||||
|
|
||||||
boost::char_separator<char> sep(";");
|
boost::char_separator<char> sep(";");
|
||||||
for (auto filename : boost::tokenizer<boost::char_separator<char>>(package_paths, sep)) {
|
for (auto filename : boost::tokenizer<boost::char_separator<char>>(package_paths, sep)) {
|
||||||
boost::replace_all(filename, "?", module);
|
boost::replace_all(filename, "?", module);
|
||||||
|
|
||||||
|
// If there's a .moon file at that path, load it instead of the
|
||||||
|
// .lua file
|
||||||
|
agi::fs::path path = filename;
|
||||||
|
if (agi::fs::HasExtension(path, "lua")) {
|
||||||
|
agi::fs::path moonpath = path;
|
||||||
|
moonpath.replace_extension("moon");
|
||||||
|
if (agi::fs::FileExists(moonpath))
|
||||||
|
path = moonpath;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
LuaScriptReader script_reader(filename);
|
if (!LoadFile(L, path))
|
||||||
if (lua_load(L, script_reader.reader_func, &script_reader, filename.c_str()))
|
return luaL_error(L, "Error loading Lua module \"%s\":\n\n%s", path.string().c_str(), luaL_checkstring(L, -1));
|
||||||
return luaL_error(L, "Error loading Lua module \"%s\":\n\n%s", filename.c_str(), luaL_checkstring(L, -1));
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
catch (agi::fs::FileNotFound const&) {
|
catch (agi::fs::FileNotFound const&) {
|
||||||
|
@ -452,7 +566,7 @@ namespace Automation4 {
|
||||||
// Not an error so swallow and continue on
|
// Not an error so swallow and continue on
|
||||||
}
|
}
|
||||||
catch (agi::Exception const& e) {
|
catch (agi::Exception const& e) {
|
||||||
return luaL_error(L, "Error loading Lua module \"%s\":\n\n%s", filename.c_str(), e.GetChainedMessage().c_str());
|
return luaL_error(L, "Error loading Lua module \"%s\":\n\n%s", path.string().c_str(), e.GetChainedMessage().c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -480,8 +594,7 @@ namespace Automation4 {
|
||||||
if (!agi::fs::FileExists(filepath))
|
if (!agi::fs::FileExists(filepath))
|
||||||
return luaL_error(L, "Lua include not found: %s", filename.c_str());
|
return luaL_error(L, "Lua include not found: %s", filename.c_str());
|
||||||
|
|
||||||
LuaScriptReader script_reader(filepath);
|
if (!LoadFile(L, filepath))
|
||||||
if (lua_load(L, script_reader.reader_func, &script_reader, filename.c_str()))
|
|
||||||
return luaL_error(L, "Error loading Lua include \"%s\":\n\n%s", filename.c_str(), luaL_checkstring(L, -1));
|
return luaL_error(L, "Error loading Lua include \"%s\":\n\n%s", filename.c_str(), luaL_checkstring(L, -1));
|
||||||
|
|
||||||
int pretop = lua_gettop(L) - 1; // don't count the function value itself
|
int pretop = lua_gettop(L) - 1; // don't count the function value itself
|
||||||
|
@ -489,80 +602,6 @@ namespace Automation4 {
|
||||||
return lua_gettop(L) - pretop;
|
return lua_gettop(L) - pretop;
|
||||||
}
|
}
|
||||||
|
|
||||||
int LuaScript::LuaFrameFromMs(lua_State *L)
|
|
||||||
{
|
|
||||||
const agi::Context *c = get_context(L);
|
|
||||||
int ms = lua_tointeger(L, -1);
|
|
||||||
lua_pop(L, 1);
|
|
||||||
if (c && c->videoController->TimecodesLoaded())
|
|
||||||
push_value(L, c->videoController->FrameAtTime(ms, agi::vfr::START));
|
|
||||||
else
|
|
||||||
lua_pushnil(L);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int LuaScript::LuaMsFromFrame(lua_State *L)
|
|
||||||
{
|
|
||||||
const agi::Context *c = get_context(L);
|
|
||||||
int frame = lua_tointeger(L, -1);
|
|
||||||
lua_pop(L, 1);
|
|
||||||
if (c && c->videoController->TimecodesLoaded())
|
|
||||||
push_value(L, c->videoController->TimeAtFrame(frame, agi::vfr::START));
|
|
||||||
else
|
|
||||||
lua_pushnil(L);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int LuaScript::LuaVideoSize(lua_State *L)
|
|
||||||
{
|
|
||||||
const agi::Context *c = get_context(L);
|
|
||||||
if (c && c->videoController->IsLoaded()) {
|
|
||||||
push_value(L, c->videoController->GetWidth());
|
|
||||||
push_value(L, c->videoController->GetHeight());
|
|
||||||
push_value(L, c->videoController->GetAspectRatioValue());
|
|
||||||
push_value(L, (int)c->videoController->GetAspectRatioType());
|
|
||||||
return 4;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
lua_pushnil(L);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int LuaScript::LuaGetKeyframes(lua_State *L)
|
|
||||||
{
|
|
||||||
const agi::Context *c = get_context(L);
|
|
||||||
if (!c) {
|
|
||||||
lua_pushnil(L);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<int> const& kf = c->videoController->GetKeyFrames();
|
|
||||||
|
|
||||||
lua_newtable(L);
|
|
||||||
for (size_t i = 0; i < kf.size(); ++i) {
|
|
||||||
push_value(L, kf[i]);
|
|
||||||
lua_rawseti(L, -2, i);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int LuaScript::LuaDecodePath(lua_State *L)
|
|
||||||
{
|
|
||||||
std::string path = luaL_checkstring(L, 1);
|
|
||||||
lua_pop(L, 1);
|
|
||||||
push_value(L, config::path->Decode(path));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int LuaScript::LuaCancel(lua_State *L)
|
|
||||||
{
|
|
||||||
lua_pushnil(L);
|
|
||||||
return lua_error(L);
|
|
||||||
}
|
|
||||||
|
|
||||||
void LuaThreadedCall(lua_State *L, int nargs, int nresults, std::string const& title, wxWindow *parent, bool can_open_config)
|
void LuaThreadedCall(lua_State *L, int nargs, int nresults, std::string const& title, wxWindow *parent, bool can_open_config)
|
||||||
{
|
{
|
||||||
bool failed = false;
|
bool failed = false;
|
||||||
|
@ -934,18 +973,15 @@ namespace Automation4 {
|
||||||
}
|
}
|
||||||
|
|
||||||
LuaScriptFactory::LuaScriptFactory()
|
LuaScriptFactory::LuaScriptFactory()
|
||||||
: ScriptFactory("Lua", "*.lua")
|
: ScriptFactory("Lua", "*.lua,*.moon")
|
||||||
{
|
{
|
||||||
Register(this);
|
Register(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
Script* LuaScriptFactory::Produce(agi::fs::path const& filename) const
|
Script* LuaScriptFactory::Produce(agi::fs::path const& filename) const
|
||||||
{
|
{
|
||||||
// Just check if file extension is .lua
|
if (agi::fs::HasExtension(filename, "lua") || agi::fs::HasExtension(filename, "moon"))
|
||||||
// Reject anything else
|
|
||||||
if (agi::fs::HasExtension(filename, "lua"))
|
|
||||||
return new LuaScript(filename);
|
return new LuaScript(filename);
|
||||||
else
|
return nullptr;
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -276,54 +276,4 @@ namespace Automation4 {
|
||||||
|
|
||||||
void ProcessSubs(AssFile *subs, wxWindow *export_dialog);
|
void ProcessSubs(AssFile *subs, wxWindow *export_dialog);
|
||||||
};
|
};
|
||||||
|
|
||||||
class LuaScript : public Script {
|
|
||||||
lua_State *L;
|
|
||||||
|
|
||||||
std::string name;
|
|
||||||
std::string description;
|
|
||||||
std::string author;
|
|
||||||
std::string version;
|
|
||||||
|
|
||||||
std::vector<cmd::Command*> macros;
|
|
||||||
std::vector<ExportFilter*> filters;
|
|
||||||
|
|
||||||
/// load script and create internal structures etc.
|
|
||||||
void Create();
|
|
||||||
/// destroy internal structures, unreg features and delete environment
|
|
||||||
void Destroy();
|
|
||||||
|
|
||||||
static int LuaTextExtents(lua_State *L);
|
|
||||||
static int LuaInclude(lua_State *L);
|
|
||||||
static int LuaModuleLoader(lua_State *L);
|
|
||||||
static int LuaFrameFromMs(lua_State *L);
|
|
||||||
static int LuaMsFromFrame(lua_State *L);
|
|
||||||
static int LuaVideoSize(lua_State *L);
|
|
||||||
static int LuaGetKeyframes(lua_State *L);
|
|
||||||
static int LuaDecodePath(lua_State *L);
|
|
||||||
static int LuaCancel(lua_State *L);
|
|
||||||
|
|
||||||
public:
|
|
||||||
LuaScript(agi::fs::path const& filename);
|
|
||||||
~LuaScript();
|
|
||||||
|
|
||||||
void RegisterCommand(LuaCommand *command);
|
|
||||||
void UnregisterCommand(LuaCommand *command);
|
|
||||||
void RegisterFilter(LuaExportFilter *filter);
|
|
||||||
|
|
||||||
static LuaScript* GetScriptObject(lua_State *L);
|
|
||||||
|
|
||||||
// Script implementation
|
|
||||||
void Reload();
|
|
||||||
|
|
||||||
std::string GetName() const { return name; }
|
|
||||||
std::string GetDescription() const { return description; }
|
|
||||||
std::string GetAuthor() const { return author; }
|
|
||||||
std::string GetVersion() const { return version; }
|
|
||||||
bool GetLoadedState() const { return L != 0; }
|
|
||||||
|
|
||||||
std::vector<cmd::Command*> GetMacros() const { return macros; }
|
|
||||||
std::vector<ExportFilter*> GetFilters() const { return filters; }
|
|
||||||
std::vector<SubtitleFormat*> GetFormats() const { return std::vector<SubtitleFormat*>(); }
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,33 +22,41 @@
|
||||||
#include "auto4_lua_scriptreader.h"
|
#include "auto4_lua_scriptreader.h"
|
||||||
|
|
||||||
#include <libaegisub/io.h>
|
#include <libaegisub/io.h>
|
||||||
|
#include <libaegisub/fs.h>
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <lua.hpp>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
namespace Automation4 {
|
namespace Automation4 {
|
||||||
LuaScriptReader::LuaScriptReader(agi::fs::path const& filename)
|
bool LoadFile(lua_State *L, agi::fs::path const& filename) {
|
||||||
: file(agi::io::Open(filename))
|
std::unique_ptr<std::istream> file(agi::io::Open(filename, true));
|
||||||
, first(true)
|
file->seekg(0, std::ios::end);
|
||||||
{
|
size_t size = file->tellg();
|
||||||
}
|
file->seekg(0, std::ios::beg);
|
||||||
|
|
||||||
LuaScriptReader::~LuaScriptReader() { }
|
std::string buff;
|
||||||
|
buff.resize(size);
|
||||||
|
|
||||||
const char *LuaScriptReader::Read(size_t *bytes_read) {
|
// Discard the BOM if present
|
||||||
file->read(buf, sizeof(buf));
|
file->read(&buff[0], 3);
|
||||||
*bytes_read = file->gcount();
|
size_t start = file->gcount();
|
||||||
if (first) {
|
if (start == 3 && buff[0] == -17 && buff[1] == -69 && buff[2] == -65) {
|
||||||
first = false;
|
buff.resize(size - 3);
|
||||||
// Skip the bom
|
start = 0;
|
||||||
if (*bytes_read >= 3 && buf[0] == -17 && buf[1] == -69 && buf[2] == -65) {
|
|
||||||
*bytes_read -= 3;
|
|
||||||
return buf + 3;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* LuaScriptReader::reader_func(lua_State *, void *data, size_t *size) {
|
file->read(&buff[start], size - start);
|
||||||
return static_cast<LuaScriptReader*>(data)->Read(size);
|
|
||||||
|
if (!agi::fs::HasExtension(filename, "moon"))
|
||||||
|
return luaL_loadbuffer(L, &buff[0], buff.size(), filename.string().c_str()) == 0;
|
||||||
|
|
||||||
|
// We have a MoonScript file, so we need to load it with that
|
||||||
|
// It might be nice to have a dedicated lua state for compiling
|
||||||
|
// MoonScript to Lua
|
||||||
|
if (luaL_dostring(L, "return require('moonscript').loadstring"))
|
||||||
|
return false; // Leaves error message on stack
|
||||||
|
lua_pushlstring(L, &buff[0], buff.size());
|
||||||
|
return lua_pcall(L, 1, 1, 0) == 0; // Leaves script or error message on stack
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,23 +19,8 @@
|
||||||
|
|
||||||
#include <libaegisub/fs_fwd.h>
|
#include <libaegisub/fs_fwd.h>
|
||||||
|
|
||||||
#include <iosfwd>
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
struct lua_State;
|
struct lua_State;
|
||||||
|
|
||||||
namespace Automation4 {
|
namespace Automation4 {
|
||||||
class LuaScriptReader {
|
bool LoadFile(lua_State *L, agi::fs::path const& filename);
|
||||||
std::unique_ptr<std::istream> file;
|
|
||||||
bool first;
|
|
||||||
char buf[512];
|
|
||||||
|
|
||||||
const char *Read(size_t *bytes_read);
|
|
||||||
public:
|
|
||||||
LuaScriptReader(agi::fs::path const& filename);
|
|
||||||
~LuaScriptReader();
|
|
||||||
|
|
||||||
static const char* reader_func(lua_State *, void *data, size_t *size);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue