diff --git a/build/libaegisub/libaegisub.vcxproj b/build/libaegisub/libaegisub.vcxproj
index 998e13337..9a70323fa 100644
--- a/build/libaegisub/libaegisub.vcxproj
+++ b/build/libaegisub/libaegisub.vcxproj
@@ -125,6 +125,7 @@
+
@@ -151,6 +152,7 @@
+
diff --git a/build/libaegisub/libaegisub.vcxproj.filters b/build/libaegisub/libaegisub.vcxproj.filters
index 06a808a0d..3fb34bf49 100644
--- a/build/libaegisub/libaegisub.vcxproj.filters
+++ b/build/libaegisub/libaegisub.vcxproj.filters
@@ -238,6 +238,9 @@
Source Files\Windows
+
+ Source Files\Common
+
Source Files\Common
@@ -286,6 +289,9 @@
ASS
+
+ ASS
+
Source Files\Common
diff --git a/libaegisub/Makefile b/libaegisub/Makefile
index 64204ef91..934ee5cf5 100644
--- a/libaegisub/Makefile
+++ b/libaegisub/Makefile
@@ -4,6 +4,7 @@ aegisub_OBJ := \
$(d)common/parser.o \
$(d)ass/dialogue_parser.o \
$(d)ass/time.o \
+ $(d)ass/uuencode.o \
$(subst .cpp,.o,$(wildcard $(d)audio/*.cpp)) \
$(subst .cpp,.o,$(wildcard $(d)common/cajun/*.cpp)) \
$(subst .cpp,.o,$(wildcard $(d)lua/modules/*.cpp)) \
@@ -25,6 +26,7 @@ aegisub_OBJ := \
$(d)common/kana_table.o \
$(d)common/karaoke_matcher.o \
$(d)common/keyframe.o \
+ $(d)common/line_iterator.o \
$(d)common/log.o \
$(d)common/mru.o \
$(d)common/option.o \
diff --git a/libaegisub/ass/uuencode.cpp b/libaegisub/ass/uuencode.cpp
new file mode 100644
index 000000000..182472926
--- /dev/null
+++ b/libaegisub/ass/uuencode.cpp
@@ -0,0 +1,85 @@
+// Copyright (c) 2013, Thomas Goyne
+//
+// Permission to use, copy, modify, and distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+//
+// Aegisub Project http://www.aegisub.org/
+
+#include
+
+#include
+
+// Despite being called uuencoding by ass_specs.doc, the format is actually
+// somewhat different from real uuencoding. Each 3-byte chunk is split into 4
+// 6-bit pieces, then 33 is added to each piece. Lines are wrapped after 80
+// characters, and files with non-multiple-of-three lengths are padded with
+// zero.
+
+namespace agi { namespace ass {
+
+std::string UUEncode(const char *begin, const char *end, bool insert_linebreaks) {
+ size_t size = std::distance(begin, end);
+ std::string ret;
+ ret.reserve((size * 4 + 2) / 3 + size / 80 * 2);
+
+ size_t written = 0;
+ for (size_t pos = 0; pos < size; pos += 3) {
+ unsigned char src[3] = { '\0', '\0', '\0' };
+ memcpy(src, begin + pos, std::min(3u, size - pos));
+
+ unsigned char dst[4] = {
+ static_cast(src[0] >> 2),
+ static_cast(((src[0] & 0x3) << 4) | ((src[1] & 0xF0) >> 4)),
+ static_cast(((src[1] & 0xF) << 2) | ((src[2] & 0xC0) >> 6)),
+ static_cast(src[2] & 0x3F)
+ };
+
+ for (size_t i = 0; i < std::min(size - pos + 1, 4u); ++i) {
+ ret += dst[i] + 33;
+
+ if (insert_linebreaks && ++written == 80 && pos + 3 < size) {
+ written = 0;
+ ret += "\r\n";
+ }
+ }
+ }
+
+ return ret;
+}
+
+std::vector UUDecode(const char *begin, const char *end) {
+ std::vector ret;
+ size_t len = end - begin;
+ ret.reserve(len * 3 / 4);
+
+ for (size_t pos = 0; pos + 1 < len; ) {
+ size_t bytes = 0;
+ unsigned char src[4] = { '\0', '\0', '\0', '\0' };
+ for (size_t i = 0; i < 4 && pos < len; ++pos) {
+ char c = begin[pos];
+ if (c && c != '\n' && c != '\r') {
+ src[i++] = c - 33;
+ ++bytes;
+ }
+ }
+
+ if (bytes > 1)
+ ret.push_back((src[0] << 2) | (src[1] >> 4));
+ if (bytes > 2)
+ ret.push_back(((src[1] & 0xF) << 4) | (src[2] >> 2));
+ if (bytes > 3)
+ ret.push_back(((src[2] & 0x3) << 6) | (src[3]));
+ }
+
+ return ret;
+}
+} }
diff --git a/libaegisub/common/line_iterator.cpp b/libaegisub/common/line_iterator.cpp
new file mode 100644
index 000000000..b288c831b
--- /dev/null
+++ b/libaegisub/common/line_iterator.cpp
@@ -0,0 +1,79 @@
+// Copyright (c) 2013, Thomas Goyne
+//
+// Permission to use, copy, modify, and distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+#include
+#include
+
+#include
+
+namespace agi {
+
+line_iterator_base::line_iterator_base(std::istream &stream, std::string encoding)
+: stream(&stream)
+{
+ boost::to_lower(encoding);
+ if (encoding != "utf-8") {
+ agi::charset::IconvWrapper c("utf-8", encoding.c_str());
+ c.Convert("\r", 1, reinterpret_cast(&cr), sizeof(int));
+ c.Convert("\n", 1, reinterpret_cast(&lf), sizeof(int));
+ width = c.RequiredBufferSize("\n");
+ conv = std::make_shared(encoding.c_str(), "utf-8");
+ }
+}
+
+bool line_iterator_base::getline(std::string &str) {
+ if (!stream) return false;
+ if (!stream->good()) {
+ stream = nullptr;
+ return false;
+ }
+
+ if (width == 1) {
+ std::getline(*stream, str);
+ if (str.size() && str.back() == '\r')
+ str.pop_back();
+ }
+ else {
+ union {
+ int32_t chr;
+ char buf[4];
+ } u;
+
+ for (;;) {
+ u.chr = 0;
+ std::streamsize read = stream->rdbuf()->sgetn(u.buf, width);
+ if (read < (std::streamsize)width) {
+ for (int i = 0; i < read; i++) {
+ str += u.buf[i];
+ }
+ stream->setstate(std::ios::eofbit);
+ break;
+ }
+ if (u.chr == cr) continue;
+ if (u.chr == lf) break;
+ for (int i = 0; i < read; i++) {
+ str += u.buf[i];
+ }
+ }
+ }
+
+ if (conv.get()) {
+ std::string tmp;
+ conv->Convert(str, tmp);
+ str = std::move(tmp);
+ }
+
+ return true;
+}
+}
diff --git a/libaegisub/common/option.cpp b/libaegisub/common/option.cpp
index 0fda90e4f..cdf5a07b8 100644
--- a/libaegisub/common/option.cpp
+++ b/libaegisub/common/option.cpp
@@ -61,15 +61,8 @@ class ConfigVisitor final : public json::ConstVisitor {
void ReadArray(json::Array const& src, std::string const& array_type) {
typename OptionValueType::value_type arr;
arr.reserve(src.size());
-
- for (json::Object const& obj : src) {
- if (obj.size() != 1)
- return Error("Invalid array member");
- if (obj.begin()->first != array_type)
- return Error("Attempt to insert value into array of wrong type");
-
+ for (json::Object const& obj : src)
arr.push_back((typename OptionValueType::value_type::value_type)(obj.begin()->second));
- }
values.push_back(agi::make_unique(name, std::move(arr)));
}
@@ -92,6 +85,13 @@ class ConfigVisitor final : public json::ConstVisitor {
return Error("Invalid array member");
auto const& array_type = front.begin()->first;
+ for (json::Object const& obj : array) {
+ if (obj.size() != 1)
+ return Error("Invalid array member");
+ if (obj.begin()->first != array_type)
+ return Error("Attempt to insert value into array of wrong type");
+ }
+
if (array_type == "string")
ReadArray(array, array_type);
else if (array_type == "int")
@@ -158,11 +158,9 @@ void put_option(json::Object &obj, const std::string &path, json::UnknownElement
template
void put_array(json::Object &obj, const std::string &path, const char *element_key, std::vector const& value) {
json::Array array;
- for (T const& elem : value) {
- array.push_back(json::Object());
- static_cast(array.back())[element_key] = (json::UnknownElement)elem;
- }
-
+ array.resize(value.size());
+ for (size_t i = 0, size = value.size(); i < size; ++i)
+ static_cast(array[i])[element_key] = (json::UnknownElement)value[i];
put_option(obj, path, std::move(array));
}
diff --git a/libaegisub/include/libaegisub/ass/uuencode.h b/libaegisub/include/libaegisub/ass/uuencode.h
index b6b30e442..98e37b44d 100644
--- a/libaegisub/include/libaegisub/ass/uuencode.h
+++ b/libaegisub/include/libaegisub/ass/uuencode.h
@@ -14,79 +14,13 @@
//
// Aegisub Project http://www.aegisub.org/
-#include
#include
#include
-// Despite being called uuencoding by ass_specs.doc, the format is actually
-// somewhat different from real uuencoding. Each 3-byte chunk is split into 4
-// 6-bit pieces, then 33 is added to each piece. Lines are wrapped after 80
-// characters, and files with non-multiple-of-three lengths are padded with
-// zero.
-
namespace agi { namespace ass {
-
/// Encode a blob of data, using ASS's nonstandard variant
-template
-std::string UUEncode(RandomAccessRange const& data, bool insert_linebreaks=true) {
- using std::begin;
- using std::end;
-
- size_t size = std::distance(begin(data), end(data));
- std::string ret;
- ret.reserve((size * 4 + 2) / 3 + size / 80 * 2);
-
- size_t written = 0;
- for (size_t pos = 0; pos < size; pos += 3) {
- unsigned char src[3] = { '\0', '\0', '\0' };
- memcpy(src, &data[pos], std::min(3u, size - pos));
-
- unsigned char dst[4] = {
- static_cast(src[0] >> 2),
- static_cast(((src[0] & 0x3) << 4) | ((src[1] & 0xF0) >> 4)),
- static_cast(((src[1] & 0xF) << 2) | ((src[2] & 0xC0) >> 6)),
- static_cast(src[2] & 0x3F)
- };
-
- for (size_t i = 0; i < std::min(size - pos + 1, 4u); ++i) {
- ret += dst[i] + 33;
-
- if (insert_linebreaks && ++written == 80 && pos + 3 < size) {
- written = 0;
- ret += "\r\n";
- }
- }
- }
-
- return ret;
-}
+std::string UUEncode(const char *begin, const char *end, bool insert_linebreaks=true);
/// Decode an ASS uuencoded string
-template
-std::vector UUDecode(String const& str) {
- std::vector ret;
- size_t len = std::end(str) - std::begin(str);
- ret.reserve(len * 3 / 4);
-
- for (size_t pos = 0; pos + 1 < len; ) {
- size_t bytes = 0;
- unsigned char src[4] = { '\0', '\0', '\0', '\0' };
- for (size_t i = 0; i < 4 && pos < len; ++pos) {
- char c = str[pos];
- if (c && c != '\n' && c != '\r') {
- src[i++] = c - 33;
- ++bytes;
- }
- }
-
- if (bytes > 1)
- ret.push_back((src[0] << 2) | (src[1] >> 4));
- if (bytes > 2)
- ret.push_back(((src[1] & 0xF) << 4) | (src[2] >> 2));
- if (bytes > 3)
- ret.push_back(((src[2] & 0x3) << 6) | (src[3]));
- }
-
- return ret;
-}
+std::vector UUDecode(const char *begin, const char *end);
} }
diff --git a/libaegisub/include/libaegisub/line_iterator.h b/libaegisub/include/libaegisub/line_iterator.h
index 127014c76..223aeb96b 100644
--- a/libaegisub/include/libaegisub/line_iterator.h
+++ b/libaegisub/include/libaegisub/line_iterator.h
@@ -23,22 +23,44 @@
#include
#include
-#include
-
-#include
namespace agi {
+namespace charset { class IconvWrapper; }
+
+class line_iterator_base {
+ std::istream *stream = nullptr; ///< Stream to iterate over
+ std::shared_ptr conv;
+ int cr = '\r'; ///< CR character in the source encoding
+ int lf = '\n'; ///< LF character in the source encoding
+ size_t width = 1; ///< width of LF character in the source encoding
+
+protected:
+ bool getline(std::string &str);
+
+public:
+ line_iterator_base(std::istream &stream, std::string encoding = "utf-8");
+
+ line_iterator_base() = default;
+ line_iterator_base(line_iterator_base const&) = default;
+#ifndef _MSC_VER
+ line_iterator_base(line_iterator_base&&) = default;
+#endif
+
+ line_iterator_base& operator=(line_iterator_base const&) = default;
+#ifndef _MSC_VER
+ line_iterator_base& operator=(line_iterator_base&&) = default;
+#endif
+
+ bool operator==(line_iterator_base const& rgt) const { return stream == rgt.stream; }
+ bool operator!=(line_iterator_base const& rgt) const { return !operator==(rgt); }
+};
+
/// @class line_iterator
/// @brief An iterator over lines in a stream
template
-class line_iterator final : public std::iterator {
- std::istream *stream = nullptr; ///< Stream to iterator over
+class line_iterator final : public line_iterator_base, public std::iterator {
OutputType value; ///< Value to return when this is dereference
- std::shared_ptr conv;
- int cr; ///< CR character in the source encoding
- int lf; ///< LF character in the source encoding
- size_t width; ///< width of LF character in the source encoding
/// @brief Convert a string to the output type
/// @param str Line read from the file
@@ -47,9 +69,6 @@ class line_iterator final : public std::iterator(&cr), sizeof(int));
- c.Convert("\n", 1, reinterpret_cast(&lf), sizeof(int));
- width = c.RequiredBufferSize("\n");
- conv = std::make_shared(encoding.c_str(), "utf-8");
- }
-
++(*this);
}
@@ -96,30 +104,11 @@ public:
return tmp;
}
- bool operator==(line_iterator const& rgt) const { return stream == rgt.stream; }
- bool operator!=(line_iterator const& rgt) const { return !operator==(rgt); }
-
// typedefs needed by some stl algorithms
typedef OutputType* pointer;
typedef OutputType& reference;
typedef const OutputType* const_pointer;
typedef const OutputType& const_reference;
-
- line_iterator& operator=(line_iterator that) {
- using std::swap;
- swap(*this, that);
- return *this;
- }
-
- void swap(line_iterator &that) throw() {
- using std::swap;
- swap(stream, that.stream);
- swap(value, that.value);
- swap(conv, that.conv);
- swap(lf, that.lf);
- swap(cr, that.cr);
- swap(width, that.width);
- }
};
// Enable range-based for
@@ -129,78 +118,19 @@ line_iterator& begin(line_iterator& it) { return it; }
template
line_iterator end(line_iterator&) { return agi::line_iterator(); }
-template
-void line_iterator::getline(std::string &str) {
- union {
- int32_t chr;
- char buf[4];
- } u;
-
- for (;;) {
- u.chr = 0;
- std::streamsize read = stream->rdbuf()->sgetn(u.buf, width);
- if (read < (std::streamsize)width) {
- for (int i = 0; i < read; i++) {
- str += u.buf[i];
- }
- stream->setstate(std::ios::eofbit);
- return;
- }
- if (u.chr == cr) continue;
- if (u.chr == lf) return;
- for (int i = 0; i < read; i++) {
- str += u.buf[i];
- }
- }
-}
-
template
void line_iterator::next() {
- if (!stream) return;
- if (!stream->good()) {
- stream = nullptr;
+ std::string str;
+ if (!getline(str))
return;
- }
- std::string str, cstr, *target;
- if (width == 1) {
- std::getline(*stream, str);
- if (str.size() && str.back() == '\r')
- str.pop_back();
- }
- else {
- getline(str);
- }
- if (conv.get()) {
- conv->Convert(str, cstr);
- target = &cstr;
- }
- else {
- target = &str;
- }
- if (!convert(*target))
+ if (!convert(str))
next();
}
template<>
inline void line_iterator::next() {
- if (!stream) return;
- if (!stream->good()) {
- stream = nullptr;
- return;
- }
- std::string cstr;
- std::string *target = conv ? &cstr : &value;
- if (width == 1) {
- std::getline(*stream, *target);
- if (target->size() && target->back() == '\r')
- target->pop_back();
- }
- else
- getline(*target);
- if (conv.get()) {
- value.clear();
- conv->Convert(*target, value);
- }
+ value.clear();
+ getline(value);
}
template
@@ -210,9 +140,4 @@ inline bool line_iterator::convert(std::string &str) {
return !ss.fail();
}
-template
-void swap(agi::line_iterator &lft, agi::line_iterator &rgt) {
- lft.swap(rgt);
-}
-
}
diff --git a/libaegisub/include/libaegisub/lua/ffi.h b/libaegisub/include/libaegisub/lua/ffi.h
index 0dbf166cb..848cf0805 100644
--- a/libaegisub/include/libaegisub/lua/ffi.h
+++ b/libaegisub/include/libaegisub/lua/ffi.h
@@ -20,19 +20,17 @@
#include
namespace agi { namespace lua {
+void do_register_lib_function(lua_State *L, const char *name, const char *type_name, void *func);
+void do_register_lib_table(lua_State *L, std::initializer_list types);
+
static void register_lib_functions(lua_State *) {
// Base case of recursion; nothing to do
}
template
void register_lib_functions(lua_State *L, const char *name, Func *func, Rest... rest) {
- lua_pushvalue(L, -2); // push cast function
- lua_pushstring(L, type_name::name().c_str());
// This cast isn't legal, but LuaJIT internally requires that it work, so we can rely on it too
- lua_pushlightuserdata(L, (void *)func);
- lua_call(L, 2, 1);
- lua_setfield(L, -2, name);
-
+ do_register_lib_function(L, name, type_name::name().c_str(), (void *)func);
register_lib_functions(L, rest...);
}
@@ -40,20 +38,7 @@ template
void register_lib_table(lua_State *L, std::initializer_list types, Args... functions) {
static_assert((sizeof...(functions) & 1) == 0, "Functions must be alternating names and function pointers");
- lua_getglobal(L, "require");
- lua_pushstring(L, "ffi");
- lua_call(L, 1, 1);
-
- // Register all passed type with the ffi
- for (auto type : types) {
- lua_getfield(L, -1, "cdef");
- lua_pushfstring(L, "typedef struct %s %s;", type, type);
- lua_call(L, 1, 0);
- }
-
- lua_getfield(L, -1, "cast");
- lua_remove(L, -2); // ffi table
-
+ do_register_lib_table(L, types); // leaves ffi.cast on the stack
lua_createtable(L, 0, sizeof...(functions) / 2);
register_lib_functions(L, functions...);
lua_remove(L, -2); // ffi.cast function
diff --git a/libaegisub/include/libaegisub/lua/utils.h b/libaegisub/include/libaegisub/lua/utils.h
index ff57e863e..a7beb8ebc 100644
--- a/libaegisub/include/libaegisub/lua/utils.h
+++ b/libaegisub/include/libaegisub/lua/utils.h
@@ -77,10 +77,7 @@ void push_value(lua_State *L, std::vector const& value) {
}
}
-/// Wrap a function which may throw exceptions and make it trigger lua errors
-/// whenever it throws
-template
-int exception_wrapper(lua_State *L) {
+inline int exception_wrapper(lua_State *L, int (*func)(lua_State *L)) {
try {
return func(L);
}
@@ -101,6 +98,13 @@ int exception_wrapper(lua_State *L) {
}
}
+/// Wrap a function which may throw exceptions and make it trigger lua errors
+/// whenever it throws
+template
+int exception_wrapper(lua_State *L) {
+ return exception_wrapper(L, func);
+}
+
template
void set_field(lua_State *L, const char *name, T value) {
push_value(L, value);
diff --git a/libaegisub/lua/modules.cpp b/libaegisub/lua/modules.cpp
index 4777bb396..061c0b8c0 100644
--- a/libaegisub/lua/modules.cpp
+++ b/libaegisub/lua/modules.cpp
@@ -16,6 +16,7 @@
#include "libaegisub/lua/modules.h"
+#include "libaegisub/lua/ffi.h"
#include "libaegisub/lua/utils.h"
extern "C" int luaopen_luabins(lua_State *L);
@@ -40,5 +41,33 @@ void preload_modules(lua_State *L) {
set_field(L, "luabins", luaopen_luabins);
lua_pop(L, 2);
+
+ register_lib_functions(L); // silence an unused static function warning
+}
+
+void do_register_lib_function(lua_State *L, const char *name, const char *type_name, void *func) {
+ lua_pushvalue(L, -2); // push cast function
+ lua_pushstring(L, type_name);
+ lua_pushlightuserdata(L, func);
+ lua_call(L, 2, 1);
+ lua_setfield(L, -2, name);
+}
+
+void do_register_lib_table(lua_State *L, std::initializer_list types) {
+ lua_getglobal(L, "require");
+ lua_pushstring(L, "ffi");
+ lua_call(L, 1, 1);
+
+ // Register all passed type with the ffi
+ for (auto type : types) {
+ lua_getfield(L, -1, "cdef");
+ lua_pushfstring(L, "typedef struct %s %s;", type, type);
+ lua_call(L, 1, 0);
+ }
+
+ lua_getfield(L, -1, "cast");
+ lua_remove(L, -2); // ffi table
+
+ // leaves ffi.cast on the stack
}
} }
diff --git a/src/ass_attachment.cpp b/src/ass_attachment.cpp
index ae967c85c..6f5bd3a08 100644
--- a/src/ass_attachment.cpp
+++ b/src/ass_attachment.cpp
@@ -21,7 +21,6 @@
#include
#include
-#include
AssAttachment::AssAttachment(AssAttachment const& rgt)
: entry_data(rgt.entry_data)
@@ -49,7 +48,7 @@ AssAttachment::AssAttachment(agi::fs::path const& name, AssEntryGroup group)
agi::read_file_mapping file(name);
auto buff = file.read();
entry_data = (group == AssEntryGroup::FONT ? "fontname: " : "filename: ") + filename.get() + "\r\n";
- entry_data = entry_data.get() + agi::ass::UUEncode(boost::make_iterator_range(buff, buff + file.size()));
+ entry_data = entry_data.get() + agi::ass::UUEncode(buff, buff + file.size());
}
size_t AssAttachment::GetSize() const {
@@ -59,7 +58,7 @@ size_t AssAttachment::GetSize() const {
void AssAttachment::Extract(agi::fs::path const& filename) const {
auto header_end = entry_data.get().find('\n');
- auto decoded = agi::ass::UUDecode(boost::make_iterator_range(entry_data.get().begin() + header_end + 1, entry_data.get().end()));
+ auto decoded = agi::ass::UUDecode(entry_data.get().c_str() + header_end + 1, &entry_data.get().back() + 1);
agi::io::Save(filename, true).Get().write(&decoded[0], decoded.size());
}
diff --git a/src/ass_parser.cpp b/src/ass_parser.cpp
index fa1df54eb..f55fc3567 100644
--- a/src/ass_parser.cpp
+++ b/src/ass_parser.cpp
@@ -210,7 +210,7 @@ void AssParser::ParseExtradataLine(std::string const &data) {
value = inline_string_decode(value);
} else if (valuetype == "u") {
// ass uuencoded
- auto valuedata = agi::ass::UUDecode(value);
+ auto valuedata = agi::ass::UUDecode(value.c_str(), value.c_str() + value.size());
value = std::string(valuedata.begin(), valuedata.end());
} else {
// unknown, error?
diff --git a/src/command/edit.cpp b/src/command/edit.cpp
index f6a0a1e9e..8d0b2b273 100644
--- a/src/command/edit.cpp
+++ b/src/command/edit.cpp
@@ -94,6 +94,23 @@ struct validate_sel_multiple : public Command {
}
};
+template
+AssDialogue *get_dialogue(String data) {
+ boost::trim(data);
+ try {
+ // Try to interpret the line as an ASS line
+ return new AssDialogue(data);
+ }
+ catch (...) {
+ // Line didn't parse correctly, assume it's plain text that
+ // should be pasted in the Text field only
+ auto d = new AssDialogue;
+ d->End = 0;
+ d->Text = data;
+ return d;
+ }
+}
+
template
void paste_lines(agi::Context *c, bool paste_over, Paster&& paste_line) {
std::string data = GetClipboard();
@@ -104,21 +121,7 @@ void paste_lines(agi::Context *c, bool paste_over, Paster&& paste_line) {
boost::char_separator sep("\r\n");
for (auto curdata : boost::tokenizer>(data, sep)) {
- boost::trim(curdata);
- AssDialogue *curdiag;
- try {
- // Try to interpret the line as an ASS line
- curdiag = new AssDialogue(curdata);
- }
- catch (...) {
- // Line didn't parse correctly, assume it's plain text that
- // should be pasted in the Text field only
- curdiag = new AssDialogue;
- curdiag->End = 0;
- curdiag->Text = curdata;
- }
-
- AssDialogue *inserted = paste_line(curdiag);
+ AssDialogue *inserted = paste_line(get_dialogue(curdata));
if (!inserted)
break;
@@ -167,14 +170,21 @@ struct parsed_line {
parsed_line(parsed_line&& r) = default;
#endif
- template
- T get_value(int blockn, T initial, std::string const& tag_name, std::string alt = "") const {
+ const AssOverrideTag *find_tag(int blockn, std::string const& tag_name, std::string const& alt) const {
for (auto ovr : blocks | sliced(0, blockn + 1) | reversed | agi::of_type()) {
for (auto const& tag : ovr->Tags | reversed) {
if (tag.Name == tag_name || tag.Name == alt)
- return tag.Params[0].template Get(initial);
+ return &tag;
}
}
+ return nullptr;
+ }
+
+ template
+ T get_value(int blockn, T initial, std::string const& tag_name, std::string const& alt = "") const {
+ auto tag = find_tag(blockn, tag_name, alt);
+ if (tag)
+ return tag->Params[0].template Get(initial);
return initial;
}
@@ -1076,18 +1086,22 @@ struct edit_line_split_by_karaoke final : public validate_sel_nonempty {
}
};
-template
-void split_lines(agi::Context *c, Func&& set_time) {
+void split_lines(agi::Context *c, AssDialogue *&n1, AssDialogue *&n2) {
int pos = c->textSelectionController->GetSelectionStart();
- AssDialogue *n1 = c->selectionController->GetActiveLine();
- auto n2 = new AssDialogue(*n1);
+ n1 = c->selectionController->GetActiveLine();
+ n2 = new AssDialogue(*n1);
c->ass->Events.insert(++c->ass->iterator_to(*n1), *n2);
std::string orig = n1->Text;
n1->Text = boost::trim_right_copy(orig.substr(0, pos));
n2->Text = boost::trim_left_copy(orig.substr(pos));
+}
+template
+void split_lines(agi::Context *c, Func&& set_time) {
+ AssDialogue *n1, *n2;
+ split_lines(c, n1, n2);
set_time(n1, n2);
c->ass->Commit(_("split"), AssFile::COMMIT_DIAG_ADDREM | AssFile::COMMIT_DIAG_FULL);
diff --git a/src/dialog_dummy_video.cpp b/src/dialog_dummy_video.cpp
index 90023864b..8b6a6ee4f 100644
--- a/src/dialog_dummy_video.cpp
+++ b/src/dialog_dummy_video.cpp
@@ -143,12 +143,16 @@ DialogDummyVideo::DialogDummyVideo(wxWindow *parent)
});
}
-template
-void DialogDummyVideo::AddCtrl(wxString const& label, T *ctrl) {
+static void add_label(wxWindow *parent, wxSizer *sizer, wxString const& label) {
if (!label)
sizer->AddStretchSpacer();
else
- sizer->Add(new wxStaticText(&d, -1, label), wxSizerFlags().Center().Left());
+ sizer->Add(new wxStaticText(parent, -1, label), wxSizerFlags().Center().Left());
+}
+
+template
+void DialogDummyVideo::AddCtrl(wxString const& label, T *ctrl) {
+ add_label(&d, sizer, label);
sizer->Add(ctrl, wxSizerFlags().Expand().Center().Left());
}
diff --git a/src/dialog_manager.h b/src/dialog_manager.h
index 3bda704a2..b81e2be67 100644
--- a/src/dialog_manager.h
+++ b/src/dialog_manager.h
@@ -38,7 +38,10 @@ class DialogManager {
template
void OnClose(Event &evt) {
evt.Skip();
- auto dialog = static_cast(evt.GetEventObject());
+ Destroy(static_cast(evt.GetEventObject()));
+ }
+
+ void Destroy(wxWindow *dialog) {
while (!dialog->IsTopLevel()) dialog = dialog->GetParent();
dialog->Destroy();
@@ -50,10 +53,9 @@ class DialogManager {
}
}
- template
- std::vector::iterator Find() {
+ std::vector::iterator Find(std::type_info const& type) {
for (auto it = begin(created_dialogs); it != end(created_dialogs); ++it) {
- if (*it->first == typeid(T))
+ if (*it->first == type)
return it;
}
return end(created_dialogs);
@@ -92,10 +94,10 @@ public:
diag.ShowModal();
}
catch (...) {
- created_dialogs.erase(Find());
+ created_dialogs.erase(Find(typeid(DialogType)));
throw;
}
- created_dialogs.erase(Find());
+ created_dialogs.erase(Find(typeid(DialogType)));
}
/// Get the dialog of the given type
@@ -103,7 +105,7 @@ public:
/// @return A pointer to a DialogType or nullptr if no dialog of the given type has been created
template
DialogType *Get() const {
- auto it = const_cast(this)->Find();
+ auto it = const_cast(this)->Find(typeid(DialogType));
return it != created_dialogs.end() ? static_cast(it->second) : nullptr;
}
diff --git a/src/grid_column.cpp b/src/grid_column.cpp
index 0cdec482b..8c6444742 100644
--- a/src/grid_column.cpp
+++ b/src/grid_column.cpp
@@ -215,35 +215,40 @@ struct GridColumnActor final : GridColumn {
}
};
-template
struct GridColumnMargin : GridColumn {
+ int index;
+ GridColumnMargin(int index) : index(index) { }
+
bool Centered() const override { return true; }
wxString Value(const AssDialogue *d, const agi::Context *) const override {
- return d->Margin[Index] ? wxString(std::to_wstring(d->Margin[Index])) : wxString();
+ return d->Margin[index] ? wxString(std::to_wstring(d->Margin[index])) : wxString();
}
int Width(const agi::Context *c, WidthHelper &helper) const override {
int max = 0;
for (AssDialogue const& line : c->ass->Events) {
- if (line.Margin[Index] > max)
- max = line.Margin[Index];
+ if (line.Margin[index] > max)
+ max = line.Margin[index];
}
return max == 0 ? 0 : helper(std::to_wstring(max));
}
};
-struct GridColumnMarginLeft final : GridColumnMargin<0> {
+struct GridColumnMarginLeft final : GridColumnMargin {
+ GridColumnMarginLeft() : GridColumnMargin(0) { }
COLUMN_HEADER(_("Left"))
COLUMN_DESCRIPTION(_("Left Margin"))
};
-struct GridColumnMarginRight final : GridColumnMargin<1> {
+struct GridColumnMarginRight final : GridColumnMargin {
+ GridColumnMarginRight() : GridColumnMargin(1) { }
COLUMN_HEADER(_("Right"))
COLUMN_DESCRIPTION(_("Right Margin"))
};
-struct GridColumnMarginVert final : GridColumnMargin<2> {
+struct GridColumnMarginVert final : GridColumnMargin {
+ GridColumnMarginVert() : GridColumnMargin(2) { }
COLUMN_HEADER(_("Vert"))
COLUMN_DESCRIPTION(_("Vertical Margin"))
};
diff --git a/src/subs_edit_box.cpp b/src/subs_edit_box.cpp
index 2fd2fcf70..7ed9111bc 100644
--- a/src/subs_edit_box.cpp
+++ b/src/subs_edit_box.cpp
@@ -443,13 +443,9 @@ void SubsEditBox::OnChange(wxStyledTextEvent &event) {
}
}
-template
-void SubsEditBox::SetSelectedRows(setter set, wxString const& desc, int type, bool amend) {
- auto const& sel = c->selectionController->GetSelectedSet();
- for_each(sel.begin(), sel.end(), set);
-
+void SubsEditBox::Commit(wxString const& desc, int type, bool amend, AssDialogue *line) {
file_changed_slot.Block();
- commit_id = c->ass->Commit(desc, type, (amend && desc == last_commit_type) ? commit_id : -1, sel.size() == 1 ? *sel.begin() : nullptr);
+ commit_id = c->ass->Commit(desc, type, (amend && desc == last_commit_type) ? commit_id : -1, line);
file_changed_slot.Unblock();
last_commit_type = desc;
last_time_commit_type = -1;
@@ -457,6 +453,13 @@ void SubsEditBox::SetSelectedRows(setter set, wxString const& desc, int type, bo
undo_timer.Start(30000, wxTIMER_ONE_SHOT);
}
+template
+void SubsEditBox::SetSelectedRows(setter set, wxString const& desc, int type, bool amend) {
+ auto const& sel = c->selectionController->GetSelectedSet();
+ for_each(sel.begin(), sel.end(), set);
+ Commit(desc, type, amend, sel.size() == 1 ? *sel.begin() : nullptr);
+}
+
template
void SubsEditBox::SetSelectedRows(T AssDialogueBase::*field, T value, wxString const& desc, int type, bool amend) {
SetSelectedRows([&](AssDialogue *d) { d->*field = value; }, desc, type, amend);
diff --git a/src/subs_edit_box.h b/src/subs_edit_box.h
index 81b82aed6..3cf9497e1 100644
--- a/src/subs_edit_box.h
+++ b/src/subs_edit_box.h
@@ -110,6 +110,7 @@ class SubsEditBox final : public wxPanel {
/// @brief Commits the current edit box contents
/// @param desc Undo description to use
void CommitText(wxString const& desc);
+ void Commit(wxString const& desc, int type, bool amend, AssDialogue *line);
/// Last commit ID for undo coalescing
int commit_id = -1;
diff --git a/src/subtitle_format_ass.cpp b/src/subtitle_format_ass.cpp
index a2a134ef7..6815c229f 100644
--- a/src/subtitle_format_ass.cpp
+++ b/src/subtitle_format_ass.cpp
@@ -140,7 +140,7 @@ struct Writer {
// the inline_string encoding grew the data by more than uuencoding would
// so base64 encode it instead
line += "u"; // marker for uuencoding
- line += agi::ass::UUEncode(edi.value, false);
+ line += agi::ass::UUEncode(edi.value.c_str(), edi.value.c_str() + edi.value.size(), false);
} else {
line += "e"; // marker for inline_string encoding (escaping)
line += encoded_data;
diff --git a/src/validators.cpp b/src/validators.cpp
index 2ec12cd65..20b4c596b 100644
--- a/src/validators.cpp
+++ b/src/validators.cpp
@@ -168,6 +168,22 @@ bool DoubleSpinValidator::TransferFromWindow() {
return true;
}
+int EnumBinderBase::Get() {
+ if (auto rb = dynamic_cast(GetWindow()))
+ return rb->GetSelection();
+ if (auto rb = dynamic_cast(GetWindow()))
+ return rb->GetSelection();
+ throw agi::InternalError("Control type not supported by EnumBinder");
+}
+
+void EnumBinderBase::Set(int value) {
+ if (auto rb = dynamic_cast(GetWindow()))
+ rb->SetSelection(value);
+ else if (auto rb = dynamic_cast(GetWindow()))
+ rb->SetSelection(value);
+ throw agi::InternalError("Control type not supported by EnumBinder");
+}
+
bool StringBinder::TransferFromWindow() {
wxWindow *window = GetWindow();
if (wxTextCtrl *ctrl = dynamic_cast(window))
diff --git a/src/validators.h b/src/validators.h
index 07afe6037..ae55d192c 100644
--- a/src/validators.h
+++ b/src/validators.h
@@ -71,36 +71,33 @@ public:
DoubleSpinValidator(double *value) : value(value) { }
};
+class EnumBinderBase : public wxValidator {
+ bool Validate(wxWindow *) override { return true; }
+
+protected:
+ int Get();
+ void Set(int value);
+};
+
template
-class EnumBinder final : public wxValidator {
+class EnumBinder final : public EnumBinderBase {
T *value;
wxObject *Clone() const override { return new EnumBinder(value); }
- bool Validate(wxWindow *) override { return true; }
bool TransferFromWindow() override {
- if (auto rb = dynamic_cast(GetWindow()))
- *value = static_cast(rb->GetSelection());
- else if (auto rb = dynamic_cast(GetWindow()))
- *value = static_cast(rb->GetSelection());
- else
- throw agi::InternalError("Control type not supported by EnumBinder");
+ *value = static_cast(Get());
return true;
}
bool TransferToWindow() override {
- if (auto rb = dynamic_cast(GetWindow()))
- rb->SetSelection(static_cast(*value));
- else if (auto cb = dynamic_cast(GetWindow()))
- cb->SetSelection(static_cast(*value));
- else
- throw agi::InternalError("Control type not supported by EnumBinder");
+ Set(static_cast(*value));
return true;
}
public:
explicit EnumBinder(T *value) : value(value) { }
- EnumBinder(EnumBinder const& rhs) : wxValidator(rhs), value(rhs.value) { }
+ EnumBinder(EnumBinder const& rhs) : EnumBinderBase(rhs), value(rhs.value) { }
};
template
diff --git a/tests/tests/line_iterator.cpp b/tests/tests/line_iterator.cpp
index e3901be29..41e6ae2f9 100644
--- a/tests/tests/line_iterator.cpp
+++ b/tests/tests/line_iterator.cpp
@@ -12,6 +12,7 @@
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#include
#include
#include
diff --git a/tests/tests/uuencode.cpp b/tests/tests/uuencode.cpp
index 09688675c..c7c105e1d 100644
--- a/tests/tests/uuencode.cpp
+++ b/tests/tests/uuencode.cpp
@@ -24,31 +24,34 @@ using namespace agi::ass;
TEST(lagi_uuencode, short_blobs) {
std::vector data;
+ auto encode = [&] { return UUEncode(&data[0], &data.back() + 1); };
data.push_back(120);
- EXPECT_STREQ("?!", UUEncode(data).c_str());
+ EXPECT_STREQ("?!", encode().c_str());
data.push_back(121);
- EXPECT_STREQ("?(E", UUEncode(data).c_str());
+ EXPECT_STREQ("?(E", encode().c_str());
data.push_back(122);
- EXPECT_STREQ("?(F[", UUEncode(data).c_str());
+ EXPECT_STREQ("?(F[", encode().c_str());
}
TEST(lagi_uuencode, short_strings) {
std::vector data;
+ auto decode = [](const char *str) { return UUDecode(str, str + strlen(str)); };
data.push_back(120);
- EXPECT_EQ(data, UUDecode("?!"));
+ EXPECT_EQ(data, decode("?!"));
data.push_back(121);
- EXPECT_EQ(data, UUDecode("?(E"));
+ EXPECT_EQ(data, decode("?(E"));
data.push_back(122);
- EXPECT_EQ(data, UUDecode("?(F["));
+ EXPECT_EQ(data, decode("?(F["));
}
TEST(lagi_uuencode, random_blobs_roundtrip) {
std::vector data;
for (size_t len = 0; len < 200; ++len) {
- EXPECT_EQ(data, UUDecode(UUEncode(data)));
+ auto encoded = UUEncode(data.data(), data.data() + data.size());
+ EXPECT_EQ(data, UUDecode(encoded.data(), encoded.data() + encoded.size()));
data.push_back(rand());
}
}