diff --git a/aegilib/aegilib.vcproj b/aegilib/aegilib.vcproj
index df8209ed33f4aad327921f103e770f7bc44b1883..6949467ae3bb9641f7bd7fc4ef293dd19e17c391 100644
--- a/aegilib/aegilib.vcproj
+++ b/aegilib/aegilib.vcproj
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="Windows-1252"?>
 <VisualStudioProject
 	ProjectType="Visual C++"
-	Version="8,00"
+	Version="8.00"
 	Name="aegilib"
 	ProjectGUID="{A805B34D-B7BE-41E9-9BB8-3FA3A494304A}"
 	RootNamespace="aegilib"
@@ -171,6 +171,10 @@
 				RelativePath=".\include\aegilib\controller.h"
 				>
 			</File>
+			<File
+				RelativePath=".\include\aegilib\exception.h"
+				>
+			</File>
 			<File
 				RelativePath=".\include\aegilib\file.h"
 				>
@@ -227,6 +231,10 @@
 		<Filter
 			Name="Misc"
 			>
+			<File
+				RelativePath=".\src\exception.cpp"
+				>
+			</File>
 			<File
 				RelativePath=".\src\prec.cpp"
 				>
diff --git a/aegilib/include/aegilib/aegilib.h b/aegilib/include/aegilib/aegilib.h
index 201a78c308ca8935f04240f56489b8516caa8208..fbc649be7f6f858d83a311c3c1a5c9b95fce1b82 100644
--- a/aegilib/include/aegilib/aegilib.h
+++ b/aegilib/include/aegilib/aegilib.h
@@ -41,3 +41,4 @@
 #include "aegistring.h"
 #include "format.h"
 #include "manipulator.h"
+#include "exception.h"
diff --git a/aegilib/include/aegilib/aegistring.h b/aegilib/include/aegilib/aegistring.h
index 563d289d775de754f9c308068964a479dff2ade9..f1d774872600dffd8efadd98dd0eedf0e2d621f5 100644
--- a/aegilib/include/aegilib/aegistring.h
+++ b/aegilib/include/aegilib/aegistring.h
@@ -38,6 +38,6 @@
 namespace Aegilib {
 
 	// Define the string type used throughout this library
-	typedef wxString String;
+	typedef std::basic_string<wchar_t> String;
 
 };
diff --git a/aegilib/include/aegilib/exception.h b/aegilib/include/aegilib/exception.h
new file mode 100644
index 0000000000000000000000000000000000000000..dd05e4a28984fa72137f24593c3142a2c854b6f3
--- /dev/null
+++ b/aegilib/include/aegilib/exception.h
@@ -0,0 +1,60 @@
+// Copyright (c) 2008, Rodrigo Braz Monteiro
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+//   * Redistributions of source code must retain the above copyright notice,
+//     this list of conditions and the following disclaimer.
+//   * Redistributions in binary form must reproduce the above copyright notice,
+//     this list of conditions and the following disclaimer in the documentation
+//     and/or other materials provided with the distribution.
+//   * Neither the name of the Aegisub Group nor the names of its contributors
+//     may be used to endorse or promote products derived from this software
+//     without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+// -----------------------------------------------------------------------------
+//
+// AEGISUB/AEGILIB
+//
+// Website: http://www.aegisub.net
+// Contact: mailto:amz@aegisub.net
+//
+
+#pragma once
+
+#include "aegilib.h"
+
+namespace Aegilib {
+
+	// Exception class
+	class Exception {
+	public:
+		enum ExceptionList {
+			Unknown,
+			No_Format_Handler,
+			Invalid_Manipulator
+		};
+
+		Exception(ExceptionList code);
+
+		String GetMessage();
+		int GetCode();
+
+	private:
+		ExceptionList code;
+	};
+
+};
diff --git a/aegilib/include/aegilib/file.h b/aegilib/include/aegilib/file.h
index 82587b4f899149d915b938ce22fcc0281e096795..cd7d56d327a624241c5222351d7ecc8a045e8137 100644
--- a/aegilib/include/aegilib/file.h
+++ b/aegilib/include/aegilib/file.h
@@ -41,6 +41,8 @@ namespace Aegilib {
 	// File reader interface
 	class FileReader {
 	public:
+		virtual ~FileReader() {}
+
 		virtual String ReadLineFromFile() = 0;
 		virtual bool HasMoreLines() = 0;
 		virtual String GetCurrentEncoding() = 0;
@@ -49,6 +51,8 @@ namespace Aegilib {
 	// File writer interface
 	class FileWriter {
 	public:
+		virtual ~FileWriter() {}
+
 		virtual void WriteLineToFile(String line,bool addLineBreak=true) = 0;
 	};
 
diff --git a/aegilib/include/aegilib/format.h b/aegilib/include/aegilib/format.h
index e02b9ef963550d2ce66a6d980735c91e06019715..1488463f9a84b5712daf53158b44b2638eaf4bb9 100644
--- a/aegilib/include/aegilib/format.h
+++ b/aegilib/include/aegilib/format.h
@@ -58,7 +58,7 @@ namespace Aegilib {
 		virtual bool HasMargins() const { return false; }
 		virtual bool HasActors() const { return false; }
 		virtual bool HasUserField() const { return false; }
-		virtual String GetUserFieldName() const { return _T(""); }
+		virtual String GetUserFieldName() const { return L""; }
 
 		virtual int GetTimingPrecision() const { return 10; }	// In milliseconds
 		virtual int GetMaxTime() const { return 36000000-10; }	// In milliseconds, default 9h 59min 59.99s
diff --git a/aegilib/include/aegilib/model.h b/aegilib/include/aegilib/model.h
index aa144afbd3db20feed0987444d05f64cd9e3c38f..37b86b039a8ad4fc0f8ae27997605799fe7be6ac 100644
--- a/aegilib/include/aegilib/model.h
+++ b/aegilib/include/aegilib/model.h
@@ -65,13 +65,13 @@ namespace Aegilib {
 		const Format& GetFormat() const;
 		void AddListener(View *listener);
 
-		void LoadFile(FileReader &file);
-		void SaveFile(FileWriter &file);
+		void LoadFile(FileReader &file,Format *format=NULL);
+		void SaveFile(FileWriter &file,Format *format=NULL);
 
-		bool CanUndo(String owner=_T("")) const;
-		bool CanRedo(String owner=_T("")) const;
-		bool Undo(String owner=_T(""));
-		bool Redo(String owner=_T(""));
+		bool CanUndo(String owner=L"") const;
+		bool CanRedo(String owner=L"") const;
+		bool Undo(String owner=L"");
+		bool Redo(String owner=L"");
 	};
 
 };
diff --git a/aegilib/include/aegilib/notification.h b/aegilib/include/aegilib/notification.h
index 540248a93fe71e67fabdff8f114fb13161ad58f9..35800ce74d78542eecaeaca7fc6764ba77c8f721 100644
--- a/aegilib/include/aegilib/notification.h
+++ b/aegilib/include/aegilib/notification.h
@@ -34,6 +34,7 @@
 //
 
 #pragma once
+#include <vector>
 #include "model.h"
 #include "notification.h"
 
diff --git a/aegilib/src/exception.cpp b/aegilib/src/exception.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8685cc72c597e1e4f60dbc6ae75a0a0752cfb4ac
--- /dev/null
+++ b/aegilib/src/exception.cpp
@@ -0,0 +1,66 @@
+// Copyright (c) 2008, Rodrigo Braz Monteiro
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+//   * Redistributions of source code must retain the above copyright notice,
+//     this list of conditions and the following disclaimer.
+//   * Redistributions in binary form must reproduce the above copyright notice,
+//     this list of conditions and the following disclaimer in the documentation
+//     and/or other materials provided with the distribution.
+//   * Neither the name of the Aegisub Group nor the names of its contributors
+//     may be used to endorse or promote products derived from this software
+//     without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+// -----------------------------------------------------------------------------
+//
+// AEGISUB/AEGILIB
+//
+// Website: http://www.aegisub.net
+// Contact: mailto:amz@aegisub.net
+//
+
+#include "exception.h"
+using namespace Aegilib;
+
+
+///////////////
+// Constructor
+Exception::Exception(ExceptionList _code)
+{
+	code = _code;
+}
+
+
+//////////////////////
+// Get message string
+String Exception::GetMessage()
+{
+	switch (code) {
+		case Unknown: return L"Unknown.";
+		case No_Format_Handler: return L"Could not find a suitable format handler.";
+		case Invalid_Manipulator: return L"Invalid manipulator.";
+	}
+	return L"Invalid code.";
+}
+
+
+////////////
+// Get code
+int Exception::GetCode()
+{
+	return code;
+}
diff --git a/aegilib/src/manipulator.cpp b/aegilib/src/manipulator.cpp
index 82e5b373173ac5192c44c1010ef333e3b1ac8750..b2240b955f8002b674a7f5d825789b2b9662a09a 100644
--- a/aegilib/src/manipulator.cpp
+++ b/aegilib/src/manipulator.cpp
@@ -59,7 +59,7 @@ Manipulator::~Manipulator()
 // Add an action to the queue
 void Manipulator::AddAction(const Action &action)
 {
-	if (!valid) throw 0;	// TODO
+	if (!valid) throw Exception(Exception::Invalid_Manipulator);
 	actions.push_back(action);
 }
 
diff --git a/aegilib/src/model.cpp b/aegilib/src/model.cpp
index e63919e5e5ad2a9f69244f9a569eee324cb14d9d..da3175c925d3a14ce90c9af14fca61500a3e3524 100644
--- a/aegilib/src/model.cpp
+++ b/aegilib/src/model.cpp
@@ -82,3 +82,30 @@ Manipulator Model::CreateAntiManipulator(const Manipulator &src)
 	// TODO
 	return dst;
 }
+
+
+///////////////
+// Load a file
+void Model::LoadFile(FileReader &file,Format *format)
+{
+	// Detect format
+	if (format == NULL) {
+		// TODO
+	}
+
+	// No format found
+	throw Exception(Exception::No_Format_Handler);
+
+	// Load
+	(void) file;
+}
+
+
+//////////////////
+// Save to a file
+void Model::SaveFile(FileWriter &file,Format *format)
+{
+	(void) file;
+	(void) format;
+	// TODO
+}
diff --git a/aegilib/test/src/main.cpp b/aegilib/test/src/main.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f8a11a7184cb4941463db8589d17d5f1775b7ffd
--- /dev/null
+++ b/aegilib/test/src/main.cpp
@@ -0,0 +1,63 @@
+// Copyright (c) 2008, Rodrigo Braz Monteiro
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+//   * Redistributions of source code must retain the above copyright notice,
+//     this list of conditions and the following disclaimer.
+//   * Redistributions in binary form must reproduce the above copyright notice,
+//     this list of conditions and the following disclaimer in the documentation
+//     and/or other materials provided with the distribution.
+//   * Neither the name of the Aegisub Group nor the names of its contributors
+//     may be used to endorse or promote products derived from this software
+//     without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+// -----------------------------------------------------------------------------
+//
+// AEGISUB/AEGILIB
+//
+// Website: http://www.aegisub.net
+// Contact: mailto:amz@aegisub.net
+//
+
+#include <aegilib/aegilib.h>
+#include <iostream>
+#include "text_file_reader.h"
+#include "text_file_writer.h"
+
+int main () {
+	using namespace std;
+	using namespace Aegilib;
+
+	cout << "Aegilib test program by amz.\n\n";
+
+	// Subtitles model
+	Model subs;
+
+	// Load subtitles
+	cout << "Loading file... ";
+	subs.LoadFile(TextFileReader(L"subs_in.ass"));
+	cout << "Done.\n";
+
+	// Modify subtitles
+	cout << "Modifying file...";
+	cout << "Done.\n";
+
+	// Save subtitles
+	cout << "Saving file... ";
+	subs.SaveFile(TextFileWriter(L"subs_out.ass"));
+	cout << "Done.\n";
+}
diff --git a/aegilib/test/src/text_file_reader.cpp b/aegilib/test/src/text_file_reader.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3ae6a4f514201ac38d3eb741c69bce930b9de1da
--- /dev/null
+++ b/aegilib/test/src/text_file_reader.cpp
@@ -0,0 +1,352 @@
+// Copyright (c) 2005, Rodrigo Braz Monteiro
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+//   * Redistributions of source code must retain the above copyright notice,
+//     this list of conditions and the following disclaimer.
+//   * Redistributions in binary form must reproduce the above copyright notice,
+//     this list of conditions and the following disclaimer in the documentation
+//     and/or other materials provided with the distribution.
+//   * Neither the name of the Aegisub Group nor the names of its contributors
+//     may be used to endorse or promote products derived from this software
+//     without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+// -----------------------------------------------------------------------------
+//
+// AEGISUB
+//
+// Website: http://aegisub.cellosoft.com
+// Contact: mailto:zeratul@cellosoft.com
+//
+
+
+///////////
+// Headers
+#include <fstream>
+#include <algorithm>
+#include <string>
+#include "text_file_reader.h"
+
+#ifdef WITH_UNIVCHARDET
+#include "charset_detect.h"
+#endif
+
+
+///////////////
+// Constructor
+TextFileReader::TextFileReader(Aegilib::String _filename,Aegilib::String enc,bool _trim) {
+	// Setup
+	open = false;
+	customConv = false;
+	trim = _trim;
+	filename = _filename;
+
+	// Open file
+	Open();
+
+	// Set encoding
+	encoding = enc.c_str();
+	if (encoding.IsEmpty()) encoding = GetEncoding(filename.c_str());
+	if (encoding == _T("binary")) return;
+	SetEncodingConfiguration();
+}
+
+
+//////////////
+// Destructor
+TextFileReader::~TextFileReader() {
+	Close();
+
+	// Clean up conversion
+	if (customConv) delete conv;
+}
+
+
+///////////////////////////
+// Determine file encoding
+Aegilib::String TextFileReader::GetEncoding(const Aegilib::String _filename) {
+	// Prepare
+	using namespace std;
+	unsigned char b[4];
+	for (int i=0;i<4;i++) b[i] = 0;
+
+	// Read four bytes from file
+#ifdef TEXT_READER_USE_STDIO
+	// TODO: maybe make this use posix-style fopen() api's instead as well?
+	HANDLE ifile = CreateFile(
+		_filename.c_str(),			// filename
+		FILE_READ_DATA,				// access mode
+		FILE_SHARE_READ,			// share mode
+		0,							// security descriptor
+		OPEN_EXISTING,				// creation disposition
+		FILE_FLAG_SEQUENTIAL_SCAN,	// flags
+		0);							// template file
+	if (ifile == INVALID_HANDLE_VALUE) {
+		return _T("unknown");
+	}
+	DWORD numread;
+	if (!ReadFile(ifile, (char*)b, 4, &numread, 0)) {
+		// Unable to open
+		return _T("unknown");
+	}
+	if (numread < 4) {
+		// File too short to decide, assume local
+		return _T("Local");
+	}
+	CloseHandle(ifile);
+#else
+	ifstream ifile;
+#ifdef WIN32
+	ifile.open(_filename.c_str());
+#else
+	ifile.open(wxFNCONV(_filename));
+#endif
+	if (!ifile.is_open()) {
+		return _T("unknown");
+	}
+	ifile.read((char*)b,4);
+	ifile.close();
+#endif
+
+	// Try to get the byte order mark from them
+	if (b[0] == 0xEF && b[1] == 0xBB && b[2] == 0xBF) return _T("UTF-8");
+	else if (b[0] == 0xFF && b[1] == 0xFE && b[2] == 0x00 && b[3] == 0x00) return _T("UTF-32LE");
+	else if (b[0] == 0x00 && b[1] == 0x00 && b[2] == 0xFE && b[3] == 0xFF) return _T("UTF-32BE");
+	else if (b[0] == 0xFF && b[1] == 0xFE) return _T("UTF-16LE");
+	else if (b[0] == 0xFE && b[1] == 0xFF) return _T("UTF-16BE");
+	else if (b[0] == 0x2B && b[1] == 0x2F && b[2] == 0x76) return _T("UTF-7");
+
+	// Try to guess UTF-16
+	else if (b[0] == 0 && b[1] >= 32 && b[2] == 0 && b[3] >= 32) return _T("UTF-16BE");
+	else if (b[0] >= 32 && b[1] == 0 && b[2] >= 32 && b[3] == 0) return _T("UTF-16LE");
+
+	// If any of the first four bytes are under 0x20 (the first printable character),
+	// except for 9-13 range, assume binary
+	for (int i=0;i<4;i++) {
+		if (b[i] < 9 || (b[i] > 13 && b[i] < 32)) return _T("binary");
+	}
+
+	#ifdef WITH_UNIVCHARDET
+	// Use universalchardet library to detect charset
+	CharSetDetect det;
+	return det.GetEncoding(_filename);
+	#else
+	// Fall back to local
+	return _T("Local");
+	#endif
+}
+
+
+//////////////////////////////
+// Set encoding configuration
+void TextFileReader::SetEncodingConfiguration() {
+	// Set encoding configuration
+	swap = false;
+	Is16 = false;
+	customConv = false;
+	conv = NULL;
+	if (encoding == _T("UTF-8")) {
+		conv = new wxMBConvUTF8;
+		customConv = true;
+	}
+	else if (encoding == _T("UTF-16LE")) {
+		Is16 = true;
+	}
+	else if (encoding == _T("UTF-16BE")) {
+		Is16 = true;
+		swap = true;
+	}
+	else if (encoding == _T("UTF-7")) {
+		conv = new wxCSConv(encoding);
+		customConv = true;
+	}
+	else if (encoding == _T("Local")) {
+		conv = wxConvCurrent;
+	}
+	else {
+		conv = new wxCSConv(encoding);
+		customConv = true;
+	}
+}
+
+
+//////////////////////////
+// Reads a line from file
+Aegilib::String TextFileReader::ReadLineFromFile() {
+	Open();
+	wxString wxbuffer;
+	size_t bufAlloc = 1024;
+	wxbuffer.Alloc(bufAlloc);
+#ifdef TEXT_READER_USE_STDIO
+	char buffer[512];
+	buffer[0] = 0;
+#else
+	std::string buffer = "";
+#endif
+
+	// Read UTF-16 line from file
+	if (Is16) {
+		char charbuffer[3];
+		charbuffer[2] = 0;
+		wchar_t ch = 0;
+		size_t len = 0;
+#ifdef TEXT_READER_USE_STDIO
+		while (ch != L'\n' && !feof(file)) {
+			// Read two chars from file
+			fread(charbuffer, 2, 1, file);
+#else
+		while (ch != L'\n' && !file.eof()) {
+			// Read two chars from file
+			charbuffer[0] = 0;
+			charbuffer[1] = 0;
+			file.read(charbuffer,2);
+#endif
+
+			// Swap bytes for big endian
+			if (swap) {
+				register char aux = charbuffer[0];
+				charbuffer[0] = charbuffer[1];
+				charbuffer[1] = aux;
+			}
+
+			// Convert two chars into a widechar and append to string
+			ch = *((wchar_t*)charbuffer);
+			if (len >= bufAlloc - 1) {
+				bufAlloc *= 2;
+				wxbuffer.Alloc(bufAlloc);
+			}
+			wxbuffer += ch;
+			len++;
+		}
+	}
+
+	// Read ASCII/UTF-8 line from file
+	else {
+#ifdef TEXT_READER_USE_STDIO
+		while (1) {
+			buffer[511] = '\1';
+			if (fgets(buffer, 512, file)) {
+				// read succeeded
+				// FIXME, this might break on incomplete multibyte characters
+				wxString linepart(buffer, *conv);
+				wxbuffer += linepart;
+				if (buffer[511] == '\1' || buffer[510] == '\n') {
+					// our sentinel \1 wasn't overwritten, meaning an EOL was found
+					break;
+				}
+				// otherwise the sentinel \1 was overwritten (presumably with \0), so just loop on
+			}
+			else {
+				// hit EOF
+				break;
+			}
+		}
+#else
+		getline(file,buffer);
+		wxbuffer.Clear();
+		if (buffer.length()) wxbuffer = wxString(buffer.c_str(),*conv);
+#endif
+	}
+
+	// Remove line breaks
+	//wxbuffer.Replace(_T("\r"),_T("\0"));
+	//wxbuffer.Replace(_T("\n"),_T("\0"));
+	size_t len=wxbuffer.Length();
+	for (size_t i=0;i<len;i++) {
+		if (wxbuffer[i] == _T('\r') || wxbuffer[i] == _T('\n')) wxbuffer[i] = _T(' ');
+	}
+
+	// Remove BOM
+	if (wxbuffer.Length() > 0 && wxbuffer[0] == 0xFEFF) {
+		wxbuffer = wxbuffer.Mid(1);
+	}
+
+	// Trim
+	if (trim) {
+		wxbuffer.Trim(true);
+		wxbuffer.Trim(false);
+	}
+	return Aegilib::String(wxbuffer.c_str());
+}
+
+
+/////////////
+// Open file
+void TextFileReader::Open() {
+	if (open) return;
+#ifdef TEXT_READER_USE_STDIO
+	// binary mode, because ascii mode is never to be trusted
+	file = _tfopen(filename.c_str(), _T("rb"));
+	if (file == 0) {
+		throw _T("Failed opening file for reading.");
+	}
+#else
+#ifdef WIN32
+	file.open(filename.wc_str(),std::ios::in | std::ios::binary);
+#else
+	file.open(wxFNCONV(filename),std::ios::in | std::ios::binary);
+#endif
+	if (!file.is_open()) {
+		throw _T("Failed opening file for reading.");
+	}
+#endif
+	open = true;
+}
+
+
+//////////////
+// Close file
+void TextFileReader::Close() {
+	if (!open) return;
+#ifdef TEXT_READER_USE_STDIO
+	fclose(file);
+#else
+	file.close();
+#endif
+	open = false;
+}
+
+
+//////////////////////////////////
+// Checks if there's more to read
+bool TextFileReader::HasMoreLines() {
+#ifdef TEXT_READER_USE_STDIO
+	if (encoding == _T("binary")) return false;
+	return !feof(file);
+#else
+	return (!file.eof());
+#endif
+}
+
+
+////////////////////////////////
+// Ensure that charset is valid
+void TextFileReader::EnsureValid(Aegilib::String enc) {
+	if (enc == _T("unknown") || enc == _T("UTF-32BE") || enc == _T("UTF-32LE")) {
+		wxString error = _T("Character set ");
+		error += enc;
+		error += _T(" is not supported.");
+		throw error.c_str();
+	}
+}
+
+
+///////////////////////////
+// Get encoding being used
+Aegilib::String TextFileReader::GetCurrentEncoding() {
+	return encoding.c_str();
+}
diff --git a/aegilib/test/src/text_file_reader.h b/aegilib/test/src/text_file_reader.h
new file mode 100644
index 0000000000000000000000000000000000000000..80375db61f826ea4d7cad087876f69bb8a896e03
--- /dev/null
+++ b/aegilib/test/src/text_file_reader.h
@@ -0,0 +1,87 @@
+// Copyright (c) 2005, Rodrigo Braz Monteiro
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+//   * Redistributions of source code must retain the above copyright notice,
+//     this list of conditions and the following disclaimer.
+//   * Redistributions in binary form must reproduce the above copyright notice,
+//     this list of conditions and the following disclaimer in the documentation
+//     and/or other materials provided with the distribution.
+//   * Neither the name of the Aegisub Group nor the names of its contributors
+//     may be used to endorse or promote products derived from this software
+//     without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+// -----------------------------------------------------------------------------
+//
+// AEGISUB
+//
+// Website: http://aegisub.cellosoft.com
+// Contact: mailto:zeratul@cellosoft.com
+//
+
+
+#pragma once
+
+
+///////////
+// Headers
+#include <aegilib/file.h>
+#include <wx/wxprec.h>
+#include <wx/dynarray.h>
+#include <wx/string.h>
+#ifdef TEXT_READER_USE_STDIO
+#include <stdio.h>
+#else
+#include <fstream>
+#endif
+
+
+/////////
+// Class
+class TextFileReader : public Aegilib::FileReader {
+private:
+	wxString filename;
+	wxString encoding;
+#ifdef TEXT_READER_USE_STDIO
+	FILE *file;
+#else
+	std::ifstream file;
+#endif
+	wxMBConv *conv;
+	bool Is16;
+	bool swap;
+	bool open;
+	bool customConv;
+	bool trim;
+
+	void Open();
+	void Close();
+	void SetEncodingConfiguration();
+
+public:
+	TextFileReader(Aegilib::String filename,Aegilib::String encoding=_T(""),bool trim=true);
+	~TextFileReader();
+
+	Aegilib::String ReadLineFromFile();
+	bool HasMoreLines();
+
+	static void EnsureValid(const Aegilib::String encoding);
+	Aegilib::String GetCurrentEncoding();
+	static Aegilib::String GetEncoding(const Aegilib::String filename);
+};
+
+
diff --git a/aegilib/test/src/text_file_writer.cpp b/aegilib/test/src/text_file_writer.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a838abbe753d926038255e064616194d69c645c4
--- /dev/null
+++ b/aegilib/test/src/text_file_writer.cpp
@@ -0,0 +1,152 @@
+// Copyright (c) 2005, Rodrigo Braz Monteiro
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+//   * Redistributions of source code must retain the above copyright notice,
+//     this list of conditions and the following disclaimer.
+//   * Redistributions in binary form must reproduce the above copyright notice,
+//     this list of conditions and the following disclaimer in the documentation
+//     and/or other materials provided with the distribution.
+//   * Neither the name of the Aegisub Group nor the names of its contributors
+//     may be used to endorse or promote products derived from this software
+//     without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+// -----------------------------------------------------------------------------
+//
+// AEGISUB
+//
+// Website: http://aegisub.cellosoft.com
+// Contact: mailto:zeratul@cellosoft.com
+//
+
+
+///////////
+// Headers
+#include <fstream>
+#include "text_file_writer.h"
+
+
+///////////////
+// Constructor
+TextFileWriter::TextFileWriter(Aegilib::String _filename,Aegilib::String enc) {
+	// Setup
+	open = false;
+	customConv = false;
+	IsFirst = true;
+	filename = _filename;
+
+	// Set encoding
+	encoding = enc;
+	if (encoding == _T("Local")) conv = &wxConvLocal;
+	else {
+		if (encoding.IsEmpty()) encoding = _T("UTF-8");
+		if (encoding == _T("US-ASCII")) encoding = _T("ISO-8859-1");
+		conv = new wxCSConv(encoding);
+		customConv = true;
+		IsUnicode = encoding.Left(3) == _T("UTF");
+	}
+
+	// Open file
+	Open();
+}
+
+
+//////////////
+// Destructor
+TextFileWriter::~TextFileWriter() {
+	Close();
+}
+
+
+/////////////
+// Open file
+void TextFileWriter::Open() {
+	// Open file
+	if (open) return;
+#ifdef WIN32
+	file.open(filename.wc_str(),std::ios::out | std::ios::binary | std::ios::trunc);
+#else
+	file.open(wxFNCONV(filename),std::ios::out | std::ios::binary | std::ios::trunc);
+#endif
+	if (!file.is_open()) {
+		throw _T("Failed opening file for writing.");
+	}
+	open = true;
+
+	// Set encoding
+	SetEncoding();
+}
+
+
+//////////////
+// Close file
+void TextFileWriter::Close() {
+	if (!open) return;
+	file.close();
+	open = false;
+	if (customConv) delete conv;
+}
+
+
+/////////////////
+// Write to file
+void TextFileWriter::WriteLineToFile(Aegilib::String line,bool addLineBreak) {
+	// Make sure it's loaded
+	if (!open) Open();
+
+	// Add line break
+	wxString temp = line;
+	if (addLineBreak) temp += _T("\r\n");
+
+	// Add BOM if it's the first line and the target format is Unicode
+	if (IsFirst && IsUnicode) {
+		wchar_t bom = 0xFEFF;
+		temp = wxString(bom) + temp;
+	}
+	IsFirst = false;
+
+	// 16-bit
+	if (Is16) {
+		wxWCharBuffer buf = temp.wc_str(*conv);
+		if (!buf.data())
+			return;
+		size_t len = wcslen(buf.data());
+		file.write((const char*)buf.data(),(std::streamsize)len*sizeof(wchar_t));
+	}
+
+	// 8-bit
+	else {
+		wxCharBuffer buf = temp.mb_str(*conv);
+		if (!buf.data())
+			return;
+		size_t len = strlen(buf.data());
+		file.write(buf.data(),(std::streamsize)len);
+	}
+}
+
+
+////////////////
+// Set encoding
+void TextFileWriter::SetEncoding() {
+	// Prepare
+	Is16 = false;
+
+	// UTF-16
+	if (encoding.Left(6) == _T("UTF-16")) {
+		Is16 = true;
+	}
+}
diff --git a/aegilib/test/src/text_file_writer.h b/aegilib/test/src/text_file_writer.h
new file mode 100644
index 0000000000000000000000000000000000000000..b5817f922376713bba938161bd58962cc53e9005
--- /dev/null
+++ b/aegilib/test/src/text_file_writer.h
@@ -0,0 +1,76 @@
+// Copyright (c) 2005, Rodrigo Braz Monteiro
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+//   * Redistributions of source code must retain the above copyright notice,
+//     this list of conditions and the following disclaimer.
+//   * Redistributions in binary form must reproduce the above copyright notice,
+//     this list of conditions and the following disclaimer in the documentation
+//     and/or other materials provided with the distribution.
+//   * Neither the name of the Aegisub Group nor the names of its contributors
+//     may be used to endorse or promote products derived from this software
+//     without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+// -----------------------------------------------------------------------------
+//
+// AEGISUB
+//
+// Website: http://aegisub.cellosoft.com
+// Contact: mailto:zeratul@cellosoft.com
+//
+
+
+#ifndef TEXT_FILE_WRITER_H
+#define TEXT_FILE_WRITER_H
+
+
+///////////
+// Headers
+#include <aegilib/file.h>
+#include <wx/wxprec.h>
+#include <wx/string.h>
+#include <fstream>
+
+
+/////////
+// Class
+class TextFileWriter : public Aegilib::FileWriter {
+private:
+	wxString filename;
+	wxString encoding;
+	std::ofstream file;
+
+	wxMBConv *conv;
+	bool customConv;
+	bool open;
+	bool Is16;
+	bool IsFirst;
+	bool IsUnicode;
+
+	void Open();
+	void Close();
+	void SetEncoding();
+
+public:
+	TextFileWriter(Aegilib::String filename,Aegilib::String encoding=_T(""));
+	~TextFileWriter();
+
+	void WriteLineToFile(Aegilib::String line,bool addLineBreak=true);
+};
+
+
+#endif
diff --git a/aegilib/test/test.vcproj b/aegilib/test/test.vcproj
new file mode 100644
index 0000000000000000000000000000000000000000..4b70d41cabb29c8a430becbe10d2b008cfbea654
--- /dev/null
+++ b/aegilib/test/test.vcproj
@@ -0,0 +1,217 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="8.00"
+	Name="aegilib_test"
+	ProjectGUID="{5A278743-375B-4FBE-AA7F-0C3BDCF8F96C}"
+	RootNamespace="test"
+	Keyword="Win32Proj"
+	>
+	<Platforms>
+		<Platform
+			Name="Win32"
+		/>
+	</Platforms>
+	<ToolFiles>
+	</ToolFiles>
+	<Configurations>
+		<Configuration
+			Name="Debug|Win32"
+			OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+			IntermediateDirectory="$(ConfigurationName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="../include"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				Detect64BitPortabilityProblems="true"
+				DebugInformationFormat="4"
+				DisableSpecificWarnings="4996"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCWebDeploymentTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+			IntermediateDirectory="$(ConfigurationName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				AdditionalIncludeDirectories="../include"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
+				RuntimeLibrary="2"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				Detect64BitPortabilityProblems="true"
+				DebugInformationFormat="3"
+				DisableSpecificWarnings="4996"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCWebDeploymentTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+	</Configurations>
+	<References>
+	</References>
+	<Files>
+		<Filter
+			Name="Source Files"
+			Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+			UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+			>
+			<File
+				RelativePath=".\src\main.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\src\text_file_reader.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\src\text_file_writer.cpp"
+				>
+			</File>
+		</Filter>
+		<Filter
+			Name="Header Files"
+			Filter="h;hpp;hxx;hm;inl;inc;xsd"
+			UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+			>
+			<File
+				RelativePath=".\src\text_file_reader.h"
+				>
+			</File>
+			<File
+				RelativePath=".\src\text_file_writer.h"
+				>
+			</File>
+		</Filter>
+		<Filter
+			Name="Resource Files"
+			Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
+			UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+			>
+		</Filter>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>
diff --git a/build/aegisub_vs2005/aegisub_vs2005.vcproj b/build/aegisub_vs2005/aegisub_vs2005.vcproj
index f2b5c557087e158c59516a46d945ff7761e2fe80..fae1132e3920e6b6ff2155cb8304a1838fc2b95a 100644
--- a/build/aegisub_vs2005/aegisub_vs2005.vcproj
+++ b/build/aegisub_vs2005/aegisub_vs2005.vcproj
@@ -1435,6 +1435,10 @@
 				RelativePath="..\..\aegisub\options.h"
 				>
 			</File>
+			<File
+				RelativePath="..\..\aegisub\plugin_manager.cpp"
+				>
+			</File>
 			<File
 				RelativePath="..\..\aegisub\setup.cpp"
 				>
@@ -1575,10 +1579,6 @@
 				RelativePath="..\..\aegisub\video_frame.h"
 				>
 			</File>
-			<File
-				RelativePath="..\..\aegisub\video_provider.cpp"
-				>
-			</File>
 			<File
 				RelativePath="..\..\aegisub\video_provider.h"
 				>
@@ -1611,6 +1611,10 @@
 				RelativePath="..\..\aegisub\video_provider_lavc.cpp"
 				>
 			</File>
+			<File
+				RelativePath="..\..\aegisub\video_provider_manager.cpp"
+				>
+			</File>
 			<File
 				RelativePath="..\..\aegisub\video_slider.cpp"
 				>