diff --git a/aegisub/build/libaegisub/libaegisub.vcxproj b/aegisub/build/libaegisub/libaegisub.vcxproj index 0861fc556..1911fbde8 100644 --- a/aegisub/build/libaegisub/libaegisub.vcxproj +++ b/aegisub/build/libaegisub/libaegisub.vcxproj @@ -33,17 +33,30 @@ lagi_pre.h + - - + + + + + + + + + + + + + + @@ -52,68 +65,58 @@ + + + + + - - - - - - - - - - - - - - - - - + Create lagi_pre.h + + + + + + + + + - - - - - - - - - + - + diff --git a/aegisub/build/libaegisub/libaegisub.vcxproj.filters b/aegisub/build/libaegisub/libaegisub.vcxproj.filters index 559cec348..e7d275f76 100644 --- a/aegisub/build/libaegisub/libaegisub.vcxproj.filters +++ b/aegisub/build/libaegisub/libaegisub.vcxproj.filters @@ -152,6 +152,9 @@ Header Files + + ASS + @@ -250,6 +253,9 @@ Source Files\Common + + ASS + diff --git a/aegisub/build/tests/tests.vcxproj b/aegisub/build/tests/tests.vcxproj index e4c401b99..7ab92bbb7 100644 --- a/aegisub/build/tests/tests.vcxproj +++ b/aegisub/build/tests/tests.vcxproj @@ -56,6 +56,7 @@ + true diff --git a/aegisub/build/tests/tests.vcxproj.filters b/aegisub/build/tests/tests.vcxproj.filters index 559651e9d..b5a4d942c 100644 --- a/aegisub/build/tests/tests.vcxproj.filters +++ b/aegisub/build/tests/tests.vcxproj.filters @@ -77,6 +77,9 @@ Tests + + Tests + diff --git a/aegisub/libaegisub/Makefile b/aegisub/libaegisub/Makefile index 59e95abb0..aa25f5720 100644 --- a/aegisub/libaegisub/Makefile +++ b/aegisub/libaegisub/Makefile @@ -13,6 +13,7 @@ unix/path.o: CXXFLAGS += -DP_DATA=\"$(P_DATA)\" -DP_DOC=\"$(P_DOC)\" -DP_LOCALE= SRC += \ ass/dialogue_parser.cpp \ + ass/uuencode.cpp \ common/cajun/elements.cpp \ common/cajun/reader.cpp \ common/cajun/writer.cpp \ diff --git a/aegisub/libaegisub/ass/uuencode.cpp b/aegisub/libaegisub/ass/uuencode.cpp new file mode 100644 index 000000000..ffe7c89c9 --- /dev/null +++ b/aegisub/libaegisub/ass/uuencode.cpp @@ -0,0 +1,79 @@ +// Copyright (c) 2013, Thomas Goyne +// +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +// +// Aegisub Project http://www.aegisub.org/ + +#include "../config.h" + +#include "libaegisub/ass/uuencode.h" + +// 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::vector UUDecode(std::string const& str) { + std::vector ret; + ret.reserve(str.size() * 3 / 4); + + for(size_t pos = 0; pos + 1 < str.size(); pos += 4) { + size_t bytes = std::min(str.size() - pos, 4); + + unsigned char src[4] = { '\0', '\0', '\0', '\0' }; + for (size_t i = 0; i < bytes; ++i) + src[i] = str[pos + i] - 33; + + 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::string UUEncode(std::vector const& data) { + std::string ret; + ret.reserve((data.size() * 4 + 2) / 3 + data.size() / 80 * 2); + + size_t written = 0; + for (size_t pos = 0; pos < data.size(); pos += 3) { + unsigned char src[3] = { '\0', '\0', '\0' }; + memcpy(src, &data[pos], std::min(3u, data.size() - pos)); + + unsigned char dst[4] = { + src[0] >> 2, + ((src[0] & 0x3) << 4) | ((src[1] & 0xF0) >> 4), + ((src[1] & 0xF) << 2) | ((src[2] & 0xC0) >> 6), + src[2] & 0x3F + }; + + for (size_t i = 0; i < std::min(data.size() - pos + 1, 4u); ++i) { + ret += dst[i] + 33; + + if (++written == 80 && pos + 3 < data.size()) { + written = 0; + ret += "\r\n"; + } + } + } + + return ret; +} + +} } diff --git a/aegisub/libaegisub/include/libaegisub/ass/uuencode.h b/aegisub/libaegisub/include/libaegisub/ass/uuencode.h new file mode 100644 index 000000000..9b61153d8 --- /dev/null +++ b/aegisub/libaegisub/include/libaegisub/ass/uuencode.h @@ -0,0 +1,28 @@ +// Copyright (c) 2013, Thomas Goyne +// +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +// +// Aegisub Project http://www.aegisub.org/ + +#include +#include + +namespace agi { + namespace ass { + /// Encode a blob of data, using ASS's nonstandard variant + std::string UUEncode(std::vector const& data); + + /// Decode an ASS uuencoded string which has had its newlines stripped + std::vector UUDecode(std::string const& str); + } +} diff --git a/aegisub/src/ass_attachment.cpp b/aegisub/src/ass_attachment.cpp index 7b40d597e..0cac4923f 100644 --- a/aegisub/src/ass_attachment.cpp +++ b/aegisub/src/ass_attachment.cpp @@ -36,14 +36,14 @@ #include "ass_attachment.h" +#include #include #include #include AssAttachment::AssAttachment(std::string const& name, AssEntryGroup group) -: data(new std::vector) -, filename(name) +: filename(name) , group(group) { } @@ -70,32 +70,9 @@ AssEntry *AssAttachment::Clone() const { } const std::string AssAttachment::GetEntryData() const { - size_t size = data->size(); - size_t written = 0; - std::string entryData = (group == ENTRY_FONT ? "fontname: " : "filename: ") + filename + "\r\n"; - entryData.reserve(size * 4 / 3 + size / 80 * 2 + entryData.size() + 2); - - for (size_t pos = 0; pos < size; pos += 3) { - unsigned char src[3] = { '\0', '\0', '\0' }; - memcpy(src, &(*data)[pos], std::min(3u, size - pos)); - - unsigned char dst[4]; - dst[0] = src[0] >> 2; - dst[1] = ((src[0] & 0x3) << 4) | ((src[1] & 0xF0) >> 4); - dst[2] = ((src[1] & 0xF) << 2) | ((src[2] & 0xC0) >> 6); - dst[3] = src[2] & 0x3F; - - for (size_t i = 0; i < std::min(size - pos + 1, 4u); ++i) { - entryData += dst[i] + 33; - - if (++written == 80 && pos + 3 < size) { - written = 0; - entryData += "\r\n"; - } - } - } - + if (data) + entryData += agi::ass::UUEncode(*data); return entryData; } @@ -115,28 +92,7 @@ std::string AssAttachment::GetFileName(bool raw) const { } void AssAttachment::Finish() { - unsigned char src[4]; - unsigned char dst[3]; - - data->reserve(buffer.size() * 3 / 4); - - for(size_t pos = 0; pos + 1 < buffer.size(); ) { - size_t read = std::min(buffer.size() - pos, 4); - - // Move 4 bytes from buffer to src - for (size_t i = 0; i < read; ++i) - src[i] = (unsigned char)buffer[pos++] - 33; - for (size_t i = read; i < 4; ++i) - src[i] = 0; - - // Convert the 4 bytes from source to 3 in dst - dst[0] = (src[0] << 2) | (src[1] >> 4); - dst[1] = ((src[1] & 0xF) << 4) | (src[2] >> 2); - dst[2] = ((src[2] & 0x3) << 6) | (src[3]); - - copy(dst, dst + read - 1, back_inserter(*data)); - } - + data = std::make_shared>(agi::ass::UUDecode(buffer)); buffer.clear(); buffer.shrink_to_fit(); } diff --git a/aegisub/src/ass_attachment.h b/aegisub/src/ass_attachment.h index 06ac842ac..71e38b43c 100644 --- a/aegisub/src/ass_attachment.h +++ b/aegisub/src/ass_attachment.h @@ -45,7 +45,7 @@ class AssAttachment : public AssEntry { std::shared_ptr> data; /// Encoded data which has been read from the script but not yet decoded - std::vector buffer; + std::string buffer; /// Name of the attached file, with SSA font mangling if it is a ttf std::string filename; @@ -58,7 +58,7 @@ public: /// Add a line of data (without newline) read from a subtitle file to the /// buffer waiting to be decoded - void AddData(std::string const& data) { buffer.insert(buffer.end(), data.begin(), data.end()); } + void AddData(std::string const& data) { buffer += data; } /// Decode all data passed with AddData void Finish(); diff --git a/aegisub/tests/Makefile b/aegisub/tests/Makefile index 604779c62..9841207d9 100644 --- a/aegisub/tests/Makefile +++ b/aegisub/tests/Makefile @@ -35,6 +35,7 @@ SRC = \ tests/syntax_highlight.cpp \ tests/thesaurus.cpp \ tests/util.cpp \ + tests/uuencode.cpp \ tests/vfr.cpp \ tests/word_split.cpp \ ${GTEST_ROOT}/src/gtest-all.cc diff --git a/aegisub/tests/tests/uuencode.cpp b/aegisub/tests/tests/uuencode.cpp new file mode 100644 index 000000000..1591c6d48 --- /dev/null +++ b/aegisub/tests/tests/uuencode.cpp @@ -0,0 +1,54 @@ +// Copyright (c) 2013, Thomas Goyne +// +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +// +// Aegisub Project http://www.aegisub.org/ + +#include + +#include "main.h" + +#include + +using namespace agi::ass; + +TEST(lagi_uuencode, short_blobs) { + std::vector data; + + data.push_back(120); + EXPECT_STREQ("?!", UUEncode(data).c_str()); + data.push_back(121); + EXPECT_STREQ("?(E", UUEncode(data).c_str()); + data.push_back(122); + EXPECT_STREQ("?(F[", UUEncode(data).c_str()); +} + +TEST(lagi_uuencode, short_strings) { + std::vector data; + + data.push_back(120); + EXPECT_EQ(data, UUDecode("?!")); + data.push_back(121); + EXPECT_EQ(data, UUDecode("?(E")); + data.push_back(122); + EXPECT_EQ(data, UUDecode("?(F[")); +} + +TEST(lagi_uuencode, random_blobs_roundtrip) { + std::vector data; + + for (size_t len = 0; len < 200; ++len) { + EXPECT_EQ(data, UUDecode(boost::replace_all_copy(UUEncode(data), "\r\n", ""))); + data.push_back(rand()); + } +}