diff --git a/aegisub/build/tests_vs2008/tests_vs2008.vcproj b/aegisub/build/tests_vs2008/tests_vs2008.vcproj
index 34ad939ed..539537a6a 100644
--- a/aegisub/build/tests_vs2008/tests_vs2008.vcproj
+++ b/aegisub/build/tests_vs2008/tests_vs2008.vcproj
@@ -232,6 +232,10 @@
RelativePath="..\..\tests\libaegisub_iconv.cpp"
>
+
+
diff --git a/aegisub/libaegisub/include/libaegisub/charset_conv.h b/aegisub/libaegisub/include/libaegisub/charset_conv.h
index 2c1c17e9e..53392cfa6 100644
--- a/aegisub/libaegisub/include/libaegisub/charset_conv.h
+++ b/aegisub/libaegisub/include/libaegisub/charset_conv.h
@@ -18,6 +18,8 @@
/// @brief Wrapper for libiconv to present a more C++-friendly API
/// @ingroup libaegisub
+#pragma once
+
#ifndef LAGI_PRE
#include
#include
diff --git a/aegisub/libaegisub/include/libaegisub/line_iterator.h b/aegisub/libaegisub/include/libaegisub/line_iterator.h
new file mode 100644
index 000000000..cf0c5b2a4
--- /dev/null
+++ b/aegisub/libaegisub/include/libaegisub/line_iterator.h
@@ -0,0 +1,218 @@
+// Copyright (c) 2010, 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.
+//
+// $Id$
+
+/// @file line_iterator.h
+/// @brief An iterator over lines in a stream
+/// @ingroup libaegisub
+
+#pragma once
+
+#if !defined(AGI_PRE) && !defined(LAGI_PRE)
+#include
+#ifdef _WIN32
+#include
+#else
+#include
+#endif
+#include
+
+#include
+#endif
+
+#include
+
+namespace agi {
+
+/// @class line_iterator
+/// @brief An iterator over lines in a stream
+template
+class line_iterator : public std::iterator {
+ std::istream *stream; ///< Stream to iterator over
+ bool valid; ///< Are there any more values to read?
+ OutputType value; ///< Value to return when this is dereference
+ std::string encoding; ///< Encoding of source stream
+ std::tr1::shared_ptr conv;
+ int cr; ///< CR character in the source encoding
+ int lf; ///< LF character in the source encoding
+ int width; ///< width of LF character in the source encoding
+
+ /// @brief Convert a string to the output type
+ /// @param str Line read from the file
+ ///
+ /// line_iterator users can either ensure that operator>> is defined for
+ /// their desired output type or simply provide a specialization of this
+ /// method which does the conversion.
+ inline bool convert(std::string &str);
+ /// Called after construction for specializations that need to do things
+ void init() { };
+ /// @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();
+public:
+ /// @brief Constructor
+ /// @param stream The stream to read from. The calling code is responsible
+ /// for ensuring that the stream remains valid for the
+ /// 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)
+ , valid(true)
+ , encoding(encoding)
+ , cr(0)
+ , lf(0)
+ , width(0)
+ {
+ agi::charset::IconvWrapper c("utf-8", encoding.c_str());
+ c.Convert("\r", 1, reinterpret_cast(&cr), sizeof(int));
+ c.Convert("\n", 1, reinterpret_cast(&lf), sizeof(int));
+ width = c.RequiredBufferSize("\n");
+
+ if (encoding != "utf-8") {
+ conv.reset(new agi::charset::IconvWrapper(encoding.c_str(), "utf-8"));
+ }
+ init();
+ ++(*this);
+ }
+ /// @brief Invalid iterator constructor; use for end iterator
+ line_iterator()
+ : stream(0)
+ , valid(false)
+ {
+ }
+ /// @brief Copy constructor
+ /// @param that line_iterator to copy from
+ line_iterator(line_iterator const& that)
+ : stream(that.stream)
+ , valid(that.valid)
+ , value(that.value)
+ , encoding(that.encoding)
+ , conv(that.conv)
+ , cr(that.cr)
+ , lf(that.lf)
+ , width(that.width)
+ {
+ }
+ OutputType operator*() const {
+ return value;
+ }
+ line_iterator& operator++() {
+ next();
+ return *this;
+ }
+ line_iterator operator++(int) {
+ line_iterator tmp(*this);
+ ++(*this);
+ return tmp;
+ }
+ bool operator==(line_iterator const& rgt) const {
+ return valid == rgt.valid;
+ }
+ bool operator!=(line_iterator const& rgt) const {
+ return !operator==(rgt);
+ }
+
+ // typedefs needed by some stl algorithms
+ typedef OutputType* pointer;
+ typedef OutputType& reference;
+ typedef const OutputType* const_pointer;
+ typedef const OutputType& const_reference;
+
+ line_iterator operator=(line_iterator that) {
+ using std::swap;
+ swap(*this, that);
+ return *this;
+ }
+ void swap(line_iterator &that) throw() {
+ using std::swap;
+ swap(stream, that.stream);
+ swap(valid, that.valid);
+ swap(value, that.value);
+ swap(encoding, that.encoding);
+ swap(conv, that.conv);
+ swap(lf, that.lf);
+ swap(cr, that.cr);
+ swap(width, that.width);
+ }
+};
+
+template
+void line_iterator::getline(std::string &str) {
+ union {
+ int32_t chr;
+ char buf[4];
+ };
+
+ for (;;) {
+ chr = 0;
+#ifdef _WIN32
+ int read = stream->rdbuf()->_Sgetn_s(buf, 4, width);
+#else
+ int read = stream->rdbuf()->sgetn(buf, width);
+#endif
+ if (read < width) {
+ for (int i = 0; i < read; i++) {
+ str += buf[i];
+ }
+ stream->setstate(std::ios::eofbit);
+ return;
+ }
+ if (chr == cr) continue;
+ if (chr == lf) return;
+ for (int i = 0; i < read; i++) {
+ str += buf[i];
+ }
+ }
+}
+
+template
+void line_iterator::next() {
+ if (!valid) return;
+ if (!stream->good()) {
+ valid = false;
+ return;
+ }
+ std::string str;
+ getline(str);
+ if (conv.get()) {
+ str = conv->Convert(str);
+ }
+ if (!convert(str)) {
+ next();
+ return;
+ }
+}
+
+template
+inline bool line_iterator::convert(std::string &str) {
+ std::istringstream ss(str);
+ ss >> value;
+ return !ss.fail();
+}
+template<>
+inline bool line_iterator::convert(std::string &str) {
+ value = str;
+ return true;
+}
+
+template
+void swap(agi::line_iterator &lft, agi::line_iterator &rgt) {
+ lft.swap(rgt);
+}
+
+}
diff --git a/aegisub/libaegisub/include/libaegisub/log.h b/aegisub/libaegisub/include/libaegisub/log.h
index 9703f136a..7e22747fb 100644
--- a/aegisub/libaegisub/include/libaegisub/log.h
+++ b/aegisub/libaegisub/include/libaegisub/log.h
@@ -24,6 +24,7 @@
#include
#include
+#include
#ifdef __DEPRECATED // Dodge GCC warnings
# undef __DEPRECATED
# include
diff --git a/aegisub/libaegisub/lagi_pre.h b/aegisub/libaegisub/lagi_pre.h
index d010a5105..1fbcc7ebe 100644
--- a/aegisub/libaegisub/lagi_pre.h
+++ b/aegisub/libaegisub/lagi_pre.h
@@ -28,7 +28,11 @@
#include
#include
#include