mirror of https://github.com/odrling/Aegisub
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
|
@ -125,6 +125,7 @@
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="$(SrcDir)ass\dialogue_parser.cpp" />
|
<ClCompile Include="$(SrcDir)ass\dialogue_parser.cpp" />
|
||||||
<ClCompile Include="$(SrcDir)ass\time.cpp" />
|
<ClCompile Include="$(SrcDir)ass\time.cpp" />
|
||||||
|
<ClCompile Include="$(SrcDir)ass\uuencode.cpp" />
|
||||||
<ClCompile Include="$(SrcDir)audio\provider.cpp" />
|
<ClCompile Include="$(SrcDir)audio\provider.cpp" />
|
||||||
<ClCompile Include="$(SrcDir)audio\provider_convert.cpp" />
|
<ClCompile Include="$(SrcDir)audio\provider_convert.cpp" />
|
||||||
<ClCompile Include="$(SrcDir)audio\provider_dummy.cpp" />
|
<ClCompile Include="$(SrcDir)audio\provider_dummy.cpp" />
|
||||||
|
@ -151,6 +152,7 @@
|
||||||
<ClCompile Include="$(SrcDir)common\kana_table.cpp" />
|
<ClCompile Include="$(SrcDir)common\kana_table.cpp" />
|
||||||
<ClCompile Include="$(SrcDir)common\karaoke_matcher.cpp" />
|
<ClCompile Include="$(SrcDir)common\karaoke_matcher.cpp" />
|
||||||
<ClCompile Include="$(SrcDir)common\keyframe.cpp" />
|
<ClCompile Include="$(SrcDir)common\keyframe.cpp" />
|
||||||
|
<ClCompile Include="$(SrcDir)common\line_iterator.cpp" />
|
||||||
<ClCompile Include="$(SrcDir)common\log.cpp" />
|
<ClCompile Include="$(SrcDir)common\log.cpp" />
|
||||||
<ClCompile Include="$(SrcDir)common\mru.cpp" />
|
<ClCompile Include="$(SrcDir)common\mru.cpp" />
|
||||||
<ClCompile Include="$(SrcDir)common\option.cpp" />
|
<ClCompile Include="$(SrcDir)common\option.cpp" />
|
||||||
|
|
|
@ -238,6 +238,9 @@
|
||||||
<ClCompile Include="$(SrcDir)windows\log_win.cpp">
|
<ClCompile Include="$(SrcDir)windows\log_win.cpp">
|
||||||
<Filter>Source Files\Windows</Filter>
|
<Filter>Source Files\Windows</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="$(SrcDir)common\line_iterator.cpp">
|
||||||
|
<Filter>Source Files\Common</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="$(SrcDir)common\log.cpp">
|
<ClCompile Include="$(SrcDir)common\log.cpp">
|
||||||
<Filter>Source Files\Common</Filter>
|
<Filter>Source Files\Common</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
@ -286,6 +289,9 @@
|
||||||
<ClCompile Include="$(SrcDir)ass\time.cpp">
|
<ClCompile Include="$(SrcDir)ass\time.cpp">
|
||||||
<Filter>ASS</Filter>
|
<Filter>ASS</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="$(SrcDir)ass\uuencode.cpp">
|
||||||
|
<Filter>ASS</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="$(SrcDir)common\color.cpp">
|
<ClCompile Include="$(SrcDir)common\color.cpp">
|
||||||
<Filter>Source Files\Common</Filter>
|
<Filter>Source Files\Common</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
|
|
@ -4,6 +4,7 @@ aegisub_OBJ := \
|
||||||
$(d)common/parser.o \
|
$(d)common/parser.o \
|
||||||
$(d)ass/dialogue_parser.o \
|
$(d)ass/dialogue_parser.o \
|
||||||
$(d)ass/time.o \
|
$(d)ass/time.o \
|
||||||
|
$(d)ass/uuencode.o \
|
||||||
$(subst .cpp,.o,$(wildcard $(d)audio/*.cpp)) \
|
$(subst .cpp,.o,$(wildcard $(d)audio/*.cpp)) \
|
||||||
$(subst .cpp,.o,$(wildcard $(d)common/cajun/*.cpp)) \
|
$(subst .cpp,.o,$(wildcard $(d)common/cajun/*.cpp)) \
|
||||||
$(subst .cpp,.o,$(wildcard $(d)lua/modules/*.cpp)) \
|
$(subst .cpp,.o,$(wildcard $(d)lua/modules/*.cpp)) \
|
||||||
|
@ -25,6 +26,7 @@ aegisub_OBJ := \
|
||||||
$(d)common/kana_table.o \
|
$(d)common/kana_table.o \
|
||||||
$(d)common/karaoke_matcher.o \
|
$(d)common/karaoke_matcher.o \
|
||||||
$(d)common/keyframe.o \
|
$(d)common/keyframe.o \
|
||||||
|
$(d)common/line_iterator.o \
|
||||||
$(d)common/log.o \
|
$(d)common/log.o \
|
||||||
$(d)common/mru.o \
|
$(d)common/mru.o \
|
||||||
$(d)common/option.o \
|
$(d)common/option.o \
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
} }
|
|
@ -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) {
|
void ReadArray(json::Array const& src, std::string const& array_type) {
|
||||||
typename OptionValueType::value_type arr;
|
typename OptionValueType::value_type arr;
|
||||||
arr.reserve(src.size());
|
arr.reserve(src.size());
|
||||||
|
for (json::Object const& obj : src)
|
||||||
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");
|
|
||||||
|
|
||||||
arr.push_back((typename OptionValueType::value_type::value_type)(obj.begin()->second));
|
arr.push_back((typename OptionValueType::value_type::value_type)(obj.begin()->second));
|
||||||
}
|
|
||||||
|
|
||||||
values.push_back(agi::make_unique<OptionValueType>(name, std::move(arr)));
|
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");
|
return Error("Invalid array member");
|
||||||
|
|
||||||
auto const& array_type = front.begin()->first;
|
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")
|
if (array_type == "string")
|
||||||
ReadArray<OptionValueListString>(array, array_type);
|
ReadArray<OptionValueListString>(array, array_type);
|
||||||
else if (array_type == "int")
|
else if (array_type == "int")
|
||||||
|
@ -158,11 +158,9 @@ void put_option(json::Object &obj, const std::string &path, json::UnknownElement
|
||||||
template<class T>
|
template<class T>
|
||||||
void put_array(json::Object &obj, const std::string &path, const char *element_key, std::vector<T> const& value) {
|
void put_array(json::Object &obj, const std::string &path, const char *element_key, std::vector<T> const& value) {
|
||||||
json::Array array;
|
json::Array array;
|
||||||
for (T const& elem : value) {
|
array.resize(value.size());
|
||||||
array.push_back(json::Object());
|
for (size_t i = 0, size = value.size(); i < size; ++i)
|
||||||
static_cast<json::Object&>(array.back())[element_key] = (json::UnknownElement)elem;
|
static_cast<json::Object&>(array[i])[element_key] = (json::UnknownElement)value[i];
|
||||||
}
|
|
||||||
|
|
||||||
put_option(obj, path, std::move(array));
|
put_option(obj, path, std::move(array));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,79 +14,13 @@
|
||||||
//
|
//
|
||||||
// Aegisub Project http://www.aegisub.org/
|
// Aegisub Project http://www.aegisub.org/
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#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 {
|
namespace agi { namespace ass {
|
||||||
|
|
||||||
/// Encode a blob of data, using ASS's nonstandard variant
|
/// Encode a blob of data, using ASS's nonstandard variant
|
||||||
template<typename RandomAccessRange>
|
std::string UUEncode(const char *begin, const char *end, bool insert_linebreaks=true);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Decode an ASS uuencoded string
|
/// Decode an ASS uuencoded string
|
||||||
template<typename String>
|
std::vector<char> UUDecode(const char *begin, const char *end);
|
||||||
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;
|
|
||||||
}
|
|
||||||
} }
|
} }
|
||||||
|
|
|
@ -23,22 +23,44 @@
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <boost/interprocess/streams/bufferstream.hpp>
|
#include <boost/interprocess/streams/bufferstream.hpp>
|
||||||
#include <boost/algorithm/string/case_conv.hpp>
|
|
||||||
|
|
||||||
#include <libaegisub/charset_conv.h>
|
|
||||||
|
|
||||||
namespace agi {
|
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
|
/// @class line_iterator
|
||||||
/// @brief An iterator over lines in a stream
|
/// @brief An iterator over lines in a stream
|
||||||
template<class OutputType = std::string>
|
template<class OutputType = std::string>
|
||||||
class line_iterator final : public std::iterator<std::input_iterator_tag, OutputType> {
|
class line_iterator final : public line_iterator_base, public std::iterator<std::input_iterator_tag, OutputType> {
|
||||||
std::istream *stream = nullptr; ///< Stream to iterator over
|
|
||||||
OutputType value; ///< Value to return when this is dereference
|
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
|
/// @brief Convert a string to the output type
|
||||||
/// @param str Line read from the file
|
/// @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
|
/// their desired output type or simply provide a specialization of this
|
||||||
/// method which does the conversion.
|
/// method which does the conversion.
|
||||||
inline bool convert(std::string &str);
|
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
|
/// @brief Get the next value from the stream
|
||||||
void next();
|
void next();
|
||||||
|
@ -60,19 +79,8 @@ public:
|
||||||
/// lifetime of the iterator and that it get cleaned up.
|
/// lifetime of the iterator and that it get cleaned up.
|
||||||
/// @param encoding Encoding of the text read from the stream
|
/// @param encoding Encoding of the text read from the stream
|
||||||
line_iterator(std::istream &stream, std::string encoding = "utf-8")
|
line_iterator(std::istream &stream, std::string encoding = "utf-8")
|
||||||
: stream(&stream)
|
: line_iterator_base(stream, std::move(encoding))
|
||||||
, cr('\r')
|
|
||||||
, lf('\n')
|
|
||||||
, width(1)
|
|
||||||
{
|
{
|
||||||
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);
|
++(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,30 +104,11 @@ public:
|
||||||
return tmp;
|
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
|
// typedefs needed by some stl algorithms
|
||||||
typedef OutputType* pointer;
|
typedef OutputType* pointer;
|
||||||
typedef OutputType& reference;
|
typedef OutputType& reference;
|
||||||
typedef const OutputType* const_pointer;
|
typedef const OutputType* const_pointer;
|
||||||
typedef const OutputType& const_reference;
|
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
|
// Enable range-based for
|
||||||
|
@ -129,78 +118,19 @@ line_iterator<T>& begin(line_iterator<T>& it) { return it; }
|
||||||
template<typename T>
|
template<typename T>
|
||||||
line_iterator<T> end(line_iterator<T>&) { return agi::line_iterator<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>
|
template<class OutputType>
|
||||||
void line_iterator<OutputType>::next() {
|
void line_iterator<OutputType>::next() {
|
||||||
if (!stream) return;
|
std::string str;
|
||||||
if (!stream->good()) {
|
if (!getline(str))
|
||||||
stream = nullptr;
|
|
||||||
return;
|
return;
|
||||||
}
|
if (!convert(str))
|
||||||
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))
|
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
inline void line_iterator<std::string>::next() {
|
inline void line_iterator<std::string>::next() {
|
||||||
if (!stream) return;
|
value.clear();
|
||||||
if (!stream->good()) {
|
getline(value);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class OutputType>
|
template<class OutputType>
|
||||||
|
@ -210,9 +140,4 @@ inline bool line_iterator<OutputType>::convert(std::string &str) {
|
||||||
return !ss.fail();
|
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>
|
#include <lua.hpp>
|
||||||
|
|
||||||
namespace agi { namespace lua {
|
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 *) {
|
static void register_lib_functions(lua_State *) {
|
||||||
// Base case of recursion; nothing to do
|
// Base case of recursion; nothing to do
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Func, typename... Rest>
|
template<typename Func, typename... Rest>
|
||||||
void register_lib_functions(lua_State *L, const char *name, Func *func, Rest... 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
|
// This cast isn't legal, but LuaJIT internally requires that it work, so we can rely on it too
|
||||||
lua_pushlightuserdata(L, (void *)func);
|
do_register_lib_function(L, name, type_name<Func*>::name().c_str(), (void *)func);
|
||||||
lua_call(L, 2, 1);
|
|
||||||
lua_setfield(L, -2, name);
|
|
||||||
|
|
||||||
register_lib_functions(L, rest...);
|
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) {
|
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");
|
static_assert((sizeof...(functions) & 1) == 0, "Functions must be alternating names and function pointers");
|
||||||
|
|
||||||
lua_getglobal(L, "require");
|
do_register_lib_table(L, types); // leaves ffi.cast on the stack
|
||||||
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
|
|
||||||
|
|
||||||
lua_createtable(L, 0, sizeof...(functions) / 2);
|
lua_createtable(L, 0, sizeof...(functions) / 2);
|
||||||
register_lib_functions(L, functions...);
|
register_lib_functions(L, functions...);
|
||||||
lua_remove(L, -2); // ffi.cast function
|
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
|
inline int exception_wrapper(lua_State *L, int (*func)(lua_State *L)) {
|
||||||
/// whenever it throws
|
|
||||||
template<int (*func)(lua_State *L)>
|
|
||||||
int exception_wrapper(lua_State *L) {
|
|
||||||
try {
|
try {
|
||||||
return func(L);
|
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>
|
template<typename T>
|
||||||
void set_field(lua_State *L, const char *name, T value) {
|
void set_field(lua_State *L, const char *name, T value) {
|
||||||
push_value(L, value);
|
push_value(L, value);
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
#include "libaegisub/lua/modules.h"
|
#include "libaegisub/lua/modules.h"
|
||||||
|
|
||||||
|
#include "libaegisub/lua/ffi.h"
|
||||||
#include "libaegisub/lua/utils.h"
|
#include "libaegisub/lua/utils.h"
|
||||||
|
|
||||||
extern "C" int luaopen_luabins(lua_State *L);
|
extern "C" int luaopen_luabins(lua_State *L);
|
||||||
|
@ -40,5 +41,33 @@ void preload_modules(lua_State *L) {
|
||||||
set_field(L, "luabins", luaopen_luabins);
|
set_field(L, "luabins", luaopen_luabins);
|
||||||
|
|
||||||
lua_pop(L, 2);
|
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 <libaegisub/io.h>
|
||||||
|
|
||||||
#include <boost/algorithm/string/predicate.hpp>
|
#include <boost/algorithm/string/predicate.hpp>
|
||||||
#include <boost/range/iterator_range.hpp>
|
|
||||||
|
|
||||||
AssAttachment::AssAttachment(AssAttachment const& rgt)
|
AssAttachment::AssAttachment(AssAttachment const& rgt)
|
||||||
: entry_data(rgt.entry_data)
|
: entry_data(rgt.entry_data)
|
||||||
|
@ -49,7 +48,7 @@ AssAttachment::AssAttachment(agi::fs::path const& name, AssEntryGroup group)
|
||||||
agi::read_file_mapping file(name);
|
agi::read_file_mapping file(name);
|
||||||
auto buff = file.read();
|
auto buff = file.read();
|
||||||
entry_data = (group == AssEntryGroup::FONT ? "fontname: " : "filename: ") + filename.get() + "\r\n";
|
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 {
|
size_t AssAttachment::GetSize() const {
|
||||||
|
@ -59,7 +58,7 @@ size_t AssAttachment::GetSize() const {
|
||||||
|
|
||||||
void AssAttachment::Extract(agi::fs::path const& filename) const {
|
void AssAttachment::Extract(agi::fs::path const& filename) const {
|
||||||
auto header_end = entry_data.get().find('\n');
|
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());
|
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);
|
value = inline_string_decode(value);
|
||||||
} else if (valuetype == "u") {
|
} else if (valuetype == "u") {
|
||||||
// ass uuencoded
|
// 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());
|
value = std::string(valuedata.begin(), valuedata.end());
|
||||||
} else {
|
} else {
|
||||||
// unknown, error?
|
// 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>
|
template<typename Paster>
|
||||||
void paste_lines(agi::Context *c, bool paste_over, Paster&& paste_line) {
|
void paste_lines(agi::Context *c, bool paste_over, Paster&& paste_line) {
|
||||||
std::string data = GetClipboard();
|
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");
|
boost::char_separator<char> sep("\r\n");
|
||||||
for (auto curdata : boost::tokenizer<boost::char_separator<char>>(data, sep)) {
|
for (auto curdata : boost::tokenizer<boost::char_separator<char>>(data, sep)) {
|
||||||
boost::trim(curdata);
|
AssDialogue *inserted = paste_line(get_dialogue(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);
|
|
||||||
if (!inserted)
|
if (!inserted)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -167,14 +170,21 @@ struct parsed_line {
|
||||||
parsed_line(parsed_line&& r) = default;
|
parsed_line(parsed_line&& r) = default;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
template<typename T>
|
const AssOverrideTag *find_tag(int blockn, std::string const& tag_name, std::string const& alt) const {
|
||||||
T get_value(int blockn, T initial, std::string const& tag_name, std::string alt = "") const {
|
|
||||||
for (auto ovr : blocks | sliced(0, blockn + 1) | reversed | agi::of_type<AssDialogueBlockOverride>()) {
|
for (auto ovr : blocks | sliced(0, blockn + 1) | reversed | agi::of_type<AssDialogueBlockOverride>()) {
|
||||||
for (auto const& tag : ovr->Tags | reversed) {
|
for (auto const& tag : ovr->Tags | reversed) {
|
||||||
if (tag.Name == tag_name || tag.Name == alt)
|
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;
|
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, AssDialogue *&n1, AssDialogue *&n2) {
|
||||||
void split_lines(agi::Context *c, Func&& set_time) {
|
|
||||||
int pos = c->textSelectionController->GetSelectionStart();
|
int pos = c->textSelectionController->GetSelectionStart();
|
||||||
|
|
||||||
AssDialogue *n1 = c->selectionController->GetActiveLine();
|
n1 = c->selectionController->GetActiveLine();
|
||||||
auto n2 = new AssDialogue(*n1);
|
n2 = new AssDialogue(*n1);
|
||||||
c->ass->Events.insert(++c->ass->iterator_to(*n1), *n2);
|
c->ass->Events.insert(++c->ass->iterator_to(*n1), *n2);
|
||||||
|
|
||||||
std::string orig = n1->Text;
|
std::string orig = n1->Text;
|
||||||
n1->Text = boost::trim_right_copy(orig.substr(0, pos));
|
n1->Text = boost::trim_right_copy(orig.substr(0, pos));
|
||||||
n2->Text = boost::trim_left_copy(orig.substr(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);
|
set_time(n1, n2);
|
||||||
|
|
||||||
c->ass->Commit(_("split"), AssFile::COMMIT_DIAG_ADDREM | AssFile::COMMIT_DIAG_FULL);
|
c->ass->Commit(_("split"), AssFile::COMMIT_DIAG_ADDREM | AssFile::COMMIT_DIAG_FULL);
|
||||||
|
|
|
@ -143,12 +143,16 @@ DialogDummyVideo::DialogDummyVideo(wxWindow *parent)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
static void add_label(wxWindow *parent, wxSizer *sizer, wxString const& label) {
|
||||||
void DialogDummyVideo::AddCtrl(wxString const& label, T *ctrl) {
|
|
||||||
if (!label)
|
if (!label)
|
||||||
sizer->AddStretchSpacer();
|
sizer->AddStretchSpacer();
|
||||||
else
|
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());
|
sizer->Add(ctrl, wxSizerFlags().Expand().Center().Left());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,10 @@ class DialogManager {
|
||||||
template<typename Event>
|
template<typename Event>
|
||||||
void OnClose(Event &evt) {
|
void OnClose(Event &evt) {
|
||||||
evt.Skip();
|
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();
|
while (!dialog->IsTopLevel()) dialog = dialog->GetParent();
|
||||||
dialog->Destroy();
|
dialog->Destroy();
|
||||||
|
|
||||||
|
@ -50,10 +53,9 @@ class DialogManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
std::vector<dialog_pair>::iterator Find(std::type_info const& type) {
|
||||||
std::vector<dialog_pair>::iterator Find() {
|
|
||||||
for (auto it = begin(created_dialogs); it != end(created_dialogs); ++it) {
|
for (auto it = begin(created_dialogs); it != end(created_dialogs); ++it) {
|
||||||
if (*it->first == typeid(T))
|
if (*it->first == type)
|
||||||
return it;
|
return it;
|
||||||
}
|
}
|
||||||
return end(created_dialogs);
|
return end(created_dialogs);
|
||||||
|
@ -92,10 +94,10 @@ public:
|
||||||
diag.ShowModal();
|
diag.ShowModal();
|
||||||
}
|
}
|
||||||
catch (...) {
|
catch (...) {
|
||||||
created_dialogs.erase(Find<DialogType>());
|
created_dialogs.erase(Find(typeid(DialogType)));
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
created_dialogs.erase(Find<DialogType>());
|
created_dialogs.erase(Find(typeid(DialogType)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the dialog of the given type
|
/// 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
|
/// @return A pointer to a DialogType or nullptr if no dialog of the given type has been created
|
||||||
template<class DialogType>
|
template<class DialogType>
|
||||||
DialogType *Get() const {
|
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;
|
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 {
|
struct GridColumnMargin : GridColumn {
|
||||||
|
int index;
|
||||||
|
GridColumnMargin(int index) : index(index) { }
|
||||||
|
|
||||||
bool Centered() const override { return true; }
|
bool Centered() const override { return true; }
|
||||||
|
|
||||||
wxString Value(const AssDialogue *d, const agi::Context *) const override {
|
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 Width(const agi::Context *c, WidthHelper &helper) const override {
|
||||||
int max = 0;
|
int max = 0;
|
||||||
for (AssDialogue const& line : c->ass->Events) {
|
for (AssDialogue const& line : c->ass->Events) {
|
||||||
if (line.Margin[Index] > max)
|
if (line.Margin[index] > max)
|
||||||
max = line.Margin[Index];
|
max = line.Margin[index];
|
||||||
}
|
}
|
||||||
return max == 0 ? 0 : helper(std::to_wstring(max));
|
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_HEADER(_("Left"))
|
||||||
COLUMN_DESCRIPTION(_("Left Margin"))
|
COLUMN_DESCRIPTION(_("Left Margin"))
|
||||||
};
|
};
|
||||||
|
|
||||||
struct GridColumnMarginRight final : GridColumnMargin<1> {
|
struct GridColumnMarginRight final : GridColumnMargin {
|
||||||
|
GridColumnMarginRight() : GridColumnMargin(1) { }
|
||||||
COLUMN_HEADER(_("Right"))
|
COLUMN_HEADER(_("Right"))
|
||||||
COLUMN_DESCRIPTION(_("Right Margin"))
|
COLUMN_DESCRIPTION(_("Right Margin"))
|
||||||
};
|
};
|
||||||
|
|
||||||
struct GridColumnMarginVert final : GridColumnMargin<2> {
|
struct GridColumnMarginVert final : GridColumnMargin {
|
||||||
|
GridColumnMarginVert() : GridColumnMargin(2) { }
|
||||||
COLUMN_HEADER(_("Vert"))
|
COLUMN_HEADER(_("Vert"))
|
||||||
COLUMN_DESCRIPTION(_("Vertical Margin"))
|
COLUMN_DESCRIPTION(_("Vertical Margin"))
|
||||||
};
|
};
|
||||||
|
|
|
@ -443,13 +443,9 @@ void SubsEditBox::OnChange(wxStyledTextEvent &event) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class setter>
|
void SubsEditBox::Commit(wxString const& desc, int type, bool amend, AssDialogue *line) {
|
||||||
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);
|
|
||||||
|
|
||||||
file_changed_slot.Block();
|
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();
|
file_changed_slot.Unblock();
|
||||||
last_commit_type = desc;
|
last_commit_type = desc;
|
||||||
last_time_commit_type = -1;
|
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);
|
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>
|
template<class T>
|
||||||
void SubsEditBox::SetSelectedRows(T AssDialogueBase::*field, T value, wxString const& desc, int type, bool amend) {
|
void SubsEditBox::SetSelectedRows(T AssDialogueBase::*field, T value, wxString const& desc, int type, bool amend) {
|
||||||
SetSelectedRows([&](AssDialogue *d) { d->*field = value; }, desc, type, 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
|
/// @brief Commits the current edit box contents
|
||||||
/// @param desc Undo description to use
|
/// @param desc Undo description to use
|
||||||
void CommitText(wxString const& desc);
|
void CommitText(wxString const& desc);
|
||||||
|
void Commit(wxString const& desc, int type, bool amend, AssDialogue *line);
|
||||||
|
|
||||||
/// Last commit ID for undo coalescing
|
/// Last commit ID for undo coalescing
|
||||||
int commit_id = -1;
|
int commit_id = -1;
|
||||||
|
|
|
@ -140,7 +140,7 @@ struct Writer {
|
||||||
// the inline_string encoding grew the data by more than uuencoding would
|
// the inline_string encoding grew the data by more than uuencoding would
|
||||||
// so base64 encode it instead
|
// so base64 encode it instead
|
||||||
line += "u"; // marker for uuencoding
|
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 {
|
} else {
|
||||||
line += "e"; // marker for inline_string encoding (escaping)
|
line += "e"; // marker for inline_string encoding (escaping)
|
||||||
line += encoded_data;
|
line += encoded_data;
|
||||||
|
|
|
@ -168,6 +168,22 @@ bool DoubleSpinValidator::TransferFromWindow() {
|
||||||
return true;
|
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() {
|
bool StringBinder::TransferFromWindow() {
|
||||||
wxWindow *window = GetWindow();
|
wxWindow *window = GetWindow();
|
||||||
if (wxTextCtrl *ctrl = dynamic_cast<wxTextCtrl*>(window))
|
if (wxTextCtrl *ctrl = dynamic_cast<wxTextCtrl*>(window))
|
||||||
|
|
|
@ -71,36 +71,33 @@ public:
|
||||||
DoubleSpinValidator(double *value) : value(value) { }
|
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>
|
template<typename T>
|
||||||
class EnumBinder final : public wxValidator {
|
class EnumBinder final : public EnumBinderBase {
|
||||||
T *value;
|
T *value;
|
||||||
|
|
||||||
wxObject *Clone() const override { return new EnumBinder<T>(value); }
|
wxObject *Clone() const override { return new EnumBinder<T>(value); }
|
||||||
bool Validate(wxWindow *) override { return true; }
|
|
||||||
|
|
||||||
bool TransferFromWindow() override {
|
bool TransferFromWindow() override {
|
||||||
if (auto rb = dynamic_cast<wxRadioBox*>(GetWindow()))
|
*value = static_cast<T>(Get());
|
||||||
*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");
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TransferToWindow() override {
|
bool TransferToWindow() override {
|
||||||
if (auto rb = dynamic_cast<wxRadioBox*>(GetWindow()))
|
Set(static_cast<int>(*value));
|
||||||
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");
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit EnumBinder(T *value) : value(value) { }
|
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>
|
template<typename T>
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
|
#include <libaegisub/charset_conv.h>
|
||||||
#include <libaegisub/line_iterator.h>
|
#include <libaegisub/line_iterator.h>
|
||||||
|
|
||||||
#include <main.h>
|
#include <main.h>
|
||||||
|
|
|
@ -24,31 +24,34 @@ using namespace agi::ass;
|
||||||
|
|
||||||
TEST(lagi_uuencode, short_blobs) {
|
TEST(lagi_uuencode, short_blobs) {
|
||||||
std::vector<char> data;
|
std::vector<char> data;
|
||||||
|
auto encode = [&] { return UUEncode(&data[0], &data.back() + 1); };
|
||||||
|
|
||||||
data.push_back(120);
|
data.push_back(120);
|
||||||
EXPECT_STREQ("?!", UUEncode(data).c_str());
|
EXPECT_STREQ("?!", encode().c_str());
|
||||||
data.push_back(121);
|
data.push_back(121);
|
||||||
EXPECT_STREQ("?(E", UUEncode(data).c_str());
|
EXPECT_STREQ("?(E", encode().c_str());
|
||||||
data.push_back(122);
|
data.push_back(122);
|
||||||
EXPECT_STREQ("?(F[", UUEncode(data).c_str());
|
EXPECT_STREQ("?(F[", encode().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(lagi_uuencode, short_strings) {
|
TEST(lagi_uuencode, short_strings) {
|
||||||
std::vector<char> data;
|
std::vector<char> data;
|
||||||
|
auto decode = [](const char *str) { return UUDecode(str, str + strlen(str)); };
|
||||||
|
|
||||||
data.push_back(120);
|
data.push_back(120);
|
||||||
EXPECT_EQ(data, UUDecode("?!"));
|
EXPECT_EQ(data, decode("?!"));
|
||||||
data.push_back(121);
|
data.push_back(121);
|
||||||
EXPECT_EQ(data, UUDecode("?(E"));
|
EXPECT_EQ(data, decode("?(E"));
|
||||||
data.push_back(122);
|
data.push_back(122);
|
||||||
EXPECT_EQ(data, UUDecode("?(F["));
|
EXPECT_EQ(data, decode("?(F["));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(lagi_uuencode, random_blobs_roundtrip) {
|
TEST(lagi_uuencode, random_blobs_roundtrip) {
|
||||||
std::vector<char> data;
|
std::vector<char> data;
|
||||||
|
|
||||||
for (size_t len = 0; len < 200; ++len) {
|
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());
|
data.push_back(rand());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue