Extract some bits that don't need to be templated from templates
Cuts compile time by about 10% and shrinks the final binary a little.
This commit is contained in:
parent
372b9fe115
commit
a6b1639320
24 changed files with 383 additions and 289 deletions
|
@ -125,6 +125,7 @@
|
|||
</ClCompile>
|
||||
<ClCompile Include="$(SrcDir)ass\dialogue_parser.cpp" />
|
||||
<ClCompile Include="$(SrcDir)ass\time.cpp" />
|
||||
<ClCompile Include="$(SrcDir)ass\uuencode.cpp" />
|
||||
<ClCompile Include="$(SrcDir)audio\provider.cpp" />
|
||||
<ClCompile Include="$(SrcDir)audio\provider_convert.cpp" />
|
||||
<ClCompile Include="$(SrcDir)audio\provider_dummy.cpp" />
|
||||
|
@ -151,6 +152,7 @@
|
|||
<ClCompile Include="$(SrcDir)common\kana_table.cpp" />
|
||||
<ClCompile Include="$(SrcDir)common\karaoke_matcher.cpp" />
|
||||
<ClCompile Include="$(SrcDir)common\keyframe.cpp" />
|
||||
<ClCompile Include="$(SrcDir)common\line_iterator.cpp" />
|
||||
<ClCompile Include="$(SrcDir)common\log.cpp" />
|
||||
<ClCompile Include="$(SrcDir)common\mru.cpp" />
|
||||
<ClCompile Include="$(SrcDir)common\option.cpp" />
|
||||
|
|
|
@ -238,6 +238,9 @@
|
|||
<ClCompile Include="$(SrcDir)windows\log_win.cpp">
|
||||
<Filter>Source Files\Windows</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="$(SrcDir)common\line_iterator.cpp">
|
||||
<Filter>Source Files\Common</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="$(SrcDir)common\log.cpp">
|
||||
<Filter>Source Files\Common</Filter>
|
||||
</ClCompile>
|
||||
|
@ -286,6 +289,9 @@
|
|||
<ClCompile Include="$(SrcDir)ass\time.cpp">
|
||||
<Filter>ASS</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="$(SrcDir)ass\uuencode.cpp">
|
||||
<Filter>ASS</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="$(SrcDir)common\color.cpp">
|
||||
<Filter>Source Files\Common</Filter>
|
||||
</ClCompile>
|
||||
|
|
|
@ -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 \
|
||||
|
|
85
libaegisub/ass/uuencode.cpp
Normal file
85
libaegisub/ass/uuencode.cpp
Normal file
|
@ -0,0 +1,85 @@
|
|||
// Copyright (c) 2013, Thomas Goyne <plorkyeran@aegisub.org>
|
||||
//
|
||||
// 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 <libaegisub/ass/uuencode.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
// 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<size_t>(3u, size - pos));
|
||||
|
||||
unsigned char dst[4] = {
|
||||
static_cast<unsigned char>(src[0] >> 2),
|
||||
static_cast<unsigned char>(((src[0] & 0x3) << 4) | ((src[1] & 0xF0) >> 4)),
|
||||
static_cast<unsigned char>(((src[1] & 0xF) << 2) | ((src[2] & 0xC0) >> 6)),
|
||||
static_cast<unsigned char>(src[2] & 0x3F)
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < std::min<size_t>(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<char> UUDecode(const char *begin, const char *end) {
|
||||
std::vector<char> 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;
|
||||
}
|
||||
} }
|
79
libaegisub/common/line_iterator.cpp
Normal file
79
libaegisub/common/line_iterator.cpp
Normal file
|
@ -0,0 +1,79 @@
|
|||
// Copyright (c) 2013, Thomas Goyne <plorkyeran@aegisub.org>
|
||||
//
|
||||
// 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 <libaegisub/line_iterator.h>
|
||||
#include <libaegisub/charset_conv.h>
|
||||
|
||||
#include <boost/algorithm/string/case_conv.hpp>
|
||||
|
||||
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<char *>(&cr), sizeof(int));
|
||||
c.Convert("\n", 1, reinterpret_cast<char *>(&lf), sizeof(int));
|
||||
width = c.RequiredBufferSize("\n");
|
||||
conv = std::make_shared<agi::charset::IconvWrapper>(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;
|
||||
}
|
||||
}
|
|
@ -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<OptionValueType>(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<OptionValueListString>(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<class T>
|
||||
void put_array(json::Object &obj, const std::string &path, const char *element_key, std::vector<T> const& value) {
|
||||
json::Array array;
|
||||
for (T const& elem : value) {
|
||||
array.push_back(json::Object());
|
||||
static_cast<json::Object&>(array.back())[element_key] = (json::UnknownElement)elem;
|
||||
}
|
||||
|
||||
array.resize(value.size());
|
||||
for (size_t i = 0, size = value.size(); i < size; ++i)
|
||||
static_cast<json::Object&>(array[i])[element_key] = (json::UnknownElement)value[i];
|
||||
put_option(obj, path, std::move(array));
|
||||
}
|
||||
|
||||
|
|
|
@ -14,79 +14,13 @@
|
|||
//
|
||||
// Aegisub Project http://www.aegisub.org/
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// 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<typename RandomAccessRange>
|
||||
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<size_t>(3u, size - pos));
|
||||
|
||||
unsigned char dst[4] = {
|
||||
static_cast<unsigned char>(src[0] >> 2),
|
||||
static_cast<unsigned char>(((src[0] & 0x3) << 4) | ((src[1] & 0xF0) >> 4)),
|
||||
static_cast<unsigned char>(((src[1] & 0xF) << 2) | ((src[2] & 0xC0) >> 6)),
|
||||
static_cast<unsigned char>(src[2] & 0x3F)
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < std::min<size_t>(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<typename String>
|
||||
std::vector<char> UUDecode(String const& str) {
|
||||
std::vector<char> 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<char> UUDecode(const char *begin, const char *end);
|
||||
} }
|
||||
|
|
|
@ -23,22 +23,44 @@
|
|||
|
||||
#include <cstdint>
|
||||
#include <boost/interprocess/streams/bufferstream.hpp>
|
||||
#include <boost/algorithm/string/case_conv.hpp>
|
||||
|
||||
#include <libaegisub/charset_conv.h>
|
||||
|
||||
namespace agi {
|
||||
|
||||
namespace charset { class IconvWrapper; }
|
||||
|
||||
class line_iterator_base {
|
||||
std::istream *stream = nullptr; ///< Stream to iterate over
|
||||
std::shared_ptr<agi::charset::IconvWrapper> 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 OutputType = std::string>
|
||||
class line_iterator final : public std::iterator<std::input_iterator_tag, OutputType> {
|
||||
std::istream *stream = nullptr; ///< Stream to iterator over
|
||||
class line_iterator final : public line_iterator_base, public std::iterator<std::input_iterator_tag, OutputType> {
|
||||
OutputType value; ///< Value to return when this is dereference
|
||||
std::shared_ptr<agi::charset::IconvWrapper> 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<std::input_iterator_tag, Output
|
|||
/// their desired output type or simply provide a specialization of this
|
||||
/// method which does the conversion.
|
||||
inline bool convert(std::string &str);
|
||||
/// @brief Get the next line from the stream
|
||||
/// @param[out] str String to fill with the next line
|
||||
void getline(std::string &str);
|
||||
|
||||
/// @brief Get the next value from the stream
|
||||
void next();
|
||||
|
@ -60,19 +79,8 @@ public:
|
|||
/// lifetime of the iterator and that it get cleaned up.
|
||||
/// @param encoding Encoding of the text read from the stream
|
||||
line_iterator(std::istream &stream, std::string encoding = "utf-8")
|
||||
: stream(&stream)
|
||||
, cr('\r')
|
||||
, lf('\n')
|
||||
, width(1)
|
||||
: line_iterator_base(stream, std::move(encoding))
|
||||
{
|
||||
if (boost::to_lower_copy(encoding) != "utf-8") {
|
||||
agi::charset::IconvWrapper c("utf-8", encoding.c_str());
|
||||
c.Convert("\r", 1, reinterpret_cast<char *>(&cr), sizeof(int));
|
||||
c.Convert("\n", 1, reinterpret_cast<char *>(&lf), sizeof(int));
|
||||
width = c.RequiredBufferSize("\n");
|
||||
conv = std::make_shared<agi::charset::IconvWrapper>(encoding.c_str(), "utf-8");
|
||||
}
|
||||
|
||||
++(*this);
|
||||
}
|
||||
|
||||
|
@ -96,30 +104,11 @@ public:
|
|||
return tmp;
|
||||
}
|
||||
|
||||
bool operator==(line_iterator<OutputType> const& rgt) const { return stream == rgt.stream; }
|
||||
bool operator!=(line_iterator<OutputType> 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<OutputType>& operator=(line_iterator<OutputType> that) {
|
||||
using std::swap;
|
||||
swap(*this, that);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void swap(line_iterator<OutputType> &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<T>& begin(line_iterator<T>& it) { return it; }
|
|||
template<typename T>
|
||||
line_iterator<T> end(line_iterator<T>&) { return agi::line_iterator<T>(); }
|
||||
|
||||
template<class OutputType>
|
||||
void line_iterator<OutputType>::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<class OutputType>
|
||||
void line_iterator<OutputType>::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<std::string>::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<class OutputType>
|
||||
|
@ -210,9 +140,4 @@ inline bool line_iterator<OutputType>::convert(std::string &str) {
|
|||
return !ss.fail();
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void swap(agi::line_iterator<T> &lft, agi::line_iterator<T> &rgt) {
|
||||
lft.swap(rgt);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -20,19 +20,17 @@
|
|||
#include <lua.hpp>
|
||||
|
||||
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<const char *> types);
|
||||
|
||||
static void register_lib_functions(lua_State *) {
|
||||
// Base case of recursion; nothing to do
|
||||
}
|
||||
|
||||
template<typename Func, typename... Rest>
|
||||
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<Func*>::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<Func*>::name().c_str(), (void *)func);
|
||||
register_lib_functions(L, rest...);
|
||||
}
|
||||
|
||||
|
@ -40,20 +38,7 @@ template<typename... Args>
|
|||
void register_lib_table(lua_State *L, std::initializer_list<const char *> 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
|
||||
|
|
|
@ -77,10 +77,7 @@ void push_value(lua_State *L, std::vector<T> const& value) {
|
|||
}
|
||||
}
|
||||
|
||||
/// Wrap a function which may throw exceptions and make it trigger lua errors
|
||||
/// whenever it throws
|
||||
template<int (*func)(lua_State *L)>
|
||||
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 (*func)(lua_State *L)>
|
||||
int exception_wrapper(lua_State *L) {
|
||||
return exception_wrapper(L, func);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void set_field(lua_State *L, const char *name, T value) {
|
||||
push_value(L, value);
|
||||
|
|
|
@ -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<const char *> 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
|
||||
}
|
||||
} }
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
#include <libaegisub/io.h>
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/range/iterator_range.hpp>
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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?
|
||||
|
|
|
@ -94,6 +94,23 @@ struct validate_sel_multiple : public Command {
|
|||
}
|
||||
};
|
||||
|
||||
template<typename String>
|
||||
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<typename Paster>
|
||||
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<char> sep("\r\n");
|
||||
for (auto curdata : boost::tokenizer<boost::char_separator<char>>(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<typename T>
|
||||
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<AssDialogueBlockOverride>()) {
|
||||
for (auto const& tag : ovr->Tags | reversed) {
|
||||
if (tag.Name == tag_name || tag.Name == alt)
|
||||
return tag.Params[0].template Get<T>(initial);
|
||||
return &tag;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
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<T>(initial);
|
||||
return initial;
|
||||
}
|
||||
|
||||
|
@ -1076,18 +1086,22 @@ struct edit_line_split_by_karaoke final : public validate_sel_nonempty {
|
|||
}
|
||||
};
|
||||
|
||||
template<typename Func>
|
||||
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<typename Func>
|
||||
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);
|
||||
|
|
|
@ -143,12 +143,16 @@ DialogDummyVideo::DialogDummyVideo(wxWindow *parent)
|
|||
});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
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<typename T>
|
||||
void DialogDummyVideo::AddCtrl(wxString const& label, T *ctrl) {
|
||||
add_label(&d, sizer, label);
|
||||
sizer->Add(ctrl, wxSizerFlags().Expand().Center().Left());
|
||||
}
|
||||
|
||||
|
|
|
@ -38,7 +38,10 @@ class DialogManager {
|
|||
template<typename Event>
|
||||
void OnClose(Event &evt) {
|
||||
evt.Skip();
|
||||
auto dialog = static_cast<wxWindow *>(evt.GetEventObject());
|
||||
Destroy(static_cast<wxWindow *>(evt.GetEventObject()));
|
||||
}
|
||||
|
||||
void Destroy(wxWindow *dialog) {
|
||||
while (!dialog->IsTopLevel()) dialog = dialog->GetParent();
|
||||
dialog->Destroy();
|
||||
|
||||
|
@ -50,10 +53,9 @@ class DialogManager {
|
|||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::vector<dialog_pair>::iterator Find() {
|
||||
std::vector<dialog_pair>::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<DialogType>());
|
||||
created_dialogs.erase(Find(typeid(DialogType)));
|
||||
throw;
|
||||
}
|
||||
created_dialogs.erase(Find<DialogType>());
|
||||
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<class DialogType>
|
||||
DialogType *Get() const {
|
||||
auto it = const_cast<DialogManager *>(this)->Find<DialogType>();
|
||||
auto it = const_cast<DialogManager *>(this)->Find(typeid(DialogType));
|
||||
return it != created_dialogs.end() ? static_cast<DialogType*>(it->second) : nullptr;
|
||||
}
|
||||
|
||||
|
|
|
@ -215,35 +215,40 @@ struct GridColumnActor final : GridColumn {
|
|||
}
|
||||
};
|
||||
|
||||
template<int Index>
|
||||
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"))
|
||||
};
|
||||
|
|
|
@ -443,13 +443,9 @@ void SubsEditBox::OnChange(wxStyledTextEvent &event) {
|
|||
}
|
||||
}
|
||||
|
||||
template<class setter>
|
||||
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<class setter>
|
||||
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<class T>
|
||||
void SubsEditBox::SetSelectedRows(T AssDialogueBase::*field, T value, wxString const& desc, int type, bool amend) {
|
||||
SetSelectedRows([&](AssDialogue *d) { d->*field = value; }, desc, type, amend);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -168,6 +168,22 @@ bool DoubleSpinValidator::TransferFromWindow() {
|
|||
return true;
|
||||
}
|
||||
|
||||
int EnumBinderBase::Get() {
|
||||
if (auto rb = dynamic_cast<wxRadioBox*>(GetWindow()))
|
||||
return rb->GetSelection();
|
||||
if (auto rb = dynamic_cast<wxComboBox*>(GetWindow()))
|
||||
return rb->GetSelection();
|
||||
throw agi::InternalError("Control type not supported by EnumBinder");
|
||||
}
|
||||
|
||||
void EnumBinderBase::Set(int value) {
|
||||
if (auto rb = dynamic_cast<wxRadioBox*>(GetWindow()))
|
||||
rb->SetSelection(value);
|
||||
else if (auto rb = dynamic_cast<wxComboBox*>(GetWindow()))
|
||||
rb->SetSelection(value);
|
||||
throw agi::InternalError("Control type not supported by EnumBinder");
|
||||
}
|
||||
|
||||
bool StringBinder::TransferFromWindow() {
|
||||
wxWindow *window = GetWindow();
|
||||
if (wxTextCtrl *ctrl = dynamic_cast<wxTextCtrl*>(window))
|
||||
|
|
|
@ -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<typename T>
|
||||
class EnumBinder final : public wxValidator {
|
||||
class EnumBinder final : public EnumBinderBase {
|
||||
T *value;
|
||||
|
||||
wxObject *Clone() const override { return new EnumBinder<T>(value); }
|
||||
bool Validate(wxWindow *) override { return true; }
|
||||
|
||||
bool TransferFromWindow() override {
|
||||
if (auto rb = dynamic_cast<wxRadioBox*>(GetWindow()))
|
||||
*value = static_cast<T>(rb->GetSelection());
|
||||
else if (auto rb = dynamic_cast<wxComboBox*>(GetWindow()))
|
||||
*value = static_cast<T>(rb->GetSelection());
|
||||
else
|
||||
throw agi::InternalError("Control type not supported by EnumBinder");
|
||||
*value = static_cast<T>(Get());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TransferToWindow() override {
|
||||
if (auto rb = dynamic_cast<wxRadioBox*>(GetWindow()))
|
||||
rb->SetSelection(static_cast<int>(*value));
|
||||
else if (auto cb = dynamic_cast<wxComboBox*>(GetWindow()))
|
||||
cb->SetSelection(static_cast<int>(*value));
|
||||
else
|
||||
throw agi::InternalError("Control type not supported by EnumBinder");
|
||||
Set(static_cast<int>(*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<typename T>
|
||||
|
|
|
@ -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 <libaegisub/charset_conv.h>
|
||||
#include <libaegisub/line_iterator.h>
|
||||
|
||||
#include <main.h>
|
||||
|
|
|
@ -24,31 +24,34 @@ using namespace agi::ass;
|
|||
|
||||
TEST(lagi_uuencode, short_blobs) {
|
||||
std::vector<char> 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<char> 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<char> 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());
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue