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());
+ }
+}